Function behavior changes when converted to a module resource

This may be a security thing, but basically I have some functions that I originally kept in a file.ps1, and would simply “import-module file.ps1”, use the functions, then move on with my life.

I’ve sense decided to turn them into a more formal module by converting it into a file.schema.psm1 and accompanying file.psd1 manifest. LItterally little more than a rename and adding a manifest.

But one of my functions no longer works after this change. It’ a function that does something a little less conventional: it reads an xml file of username and passwords and then converts them all to credentials. This way I can quickly “import” an xml file of username/passwords to be called by whatever other random scripts I want to run … less typing … yippie.

Now that it’s a more “formal module” the function “appears” to work … but after running I have no variables in memory to be called. Sample snippet here:

Function New-PasswordScriptVariable
{
param(
    [Parameter(Mandatory=$True)][String]$Name,
    [Parameter(Mandatory=$True)][String]$User,
    [Parameter(Mandatory=$True)][String]$Password
    )

    $SecurePass = ConvertTo-SecureString $Password -AsPlainText -Force
    Invoke-Expression ("`$$Name = New-Object System.Management.Automation.PSCredential $User, `$SecurePass")
}

So run this in a prompt or save as a ps1, import, and run it and you can do something easily like "new-passwordScriptVariable -name sample -user “contoso\jdoe” -password “blah”

then notice you have $sample ready to go. However … package it away as a module … nothing happens (or maybe sometime happens but I never see the variable in my user context).

Tips?

There’s very little value in having your function be responsible for creating the variable. You can just return the credential object, and have the calling code assign it to a variable if it wants to. For example:

Function New-PasswordScriptVariable
{
param(
    [Parameter(Mandatory=$True)][String]$User,
    [Parameter(Mandatory=$True)][String]$Password
    )

    $SecurePass = ConvertTo-SecureString $Password -AsPlainText -Force
    New-Object System.Management.Automation.PSCredential $User, $SecurePass
}

# calling code

$cred = New-Credential -User Bob -Password Whatever

If you really want your function to set a variable out in the scope of the calling function (and a module is involved), you need to use the $PSCmdlet variable.

Incidentally, Invoke-Expression is very dangerous if you’re working with input that is not completely safe / trusted. This example will avoid that, since it’s not needed anyway:

Function New-PasswordScriptVariable
{
param(
    [Parameter(Mandatory=$True)][String]$Name,
    [Parameter(Mandatory=$True)][String]$User,
    [Parameter(Mandatory=$True)][String]$Password
    )

    $SecurePass = ConvertTo-SecureString $Password -AsPlainText -Force
    $cred = New-Object System.Management.Automation.PSCredential $User, $SecurePass
    
    $PSCmdlet.SessionState.PSVariable.Set($Name, $cred)
}

I appreciate the help, perhaps my “poor man’s password vault” is just a bad idea.

Essentially your first example doesn’t spit out a “variable variable”. it spits out credentials to a fixed name variable. That’s not what I’m trying to accomplish, as I’m hoping to be able to create multiple variables so that I can do things like essentially dump some common credentials out then call them ass needed for different things.

install-sql.ps1 -creds $SQLSVC
install-DC.ps1 -creds $DomainAdmin

Basically I pull them up in one line … run all my various credential required cmdlets … close the session when I’m done. It’s a convenience thing.

Your second example may be better if I"m planning to run this function from within another script … if I understand what it’s doing … but I’m not certain I do … but Ill give it a go and see what happens :slight_smile:

Edit: looks like I got it working (thank you!)

Your second example was key … with one missing piece being I needed [cmdletbinding()] defined … got that info from your blog :slight_smile: