1

I have got a powershell script file which contains the function below. its called my_functions.ps1

function Get-My-PlainText()
{
    [CmdletBinding()]
    param
    (
        [parameter(Mandatory = $true)][System.Security.SecureString]$SecureString
    )
    BEGIN { }
    PROCESS
    {
        $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecureString);

        try
        {
            return [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr);
        }
        finally
        {
            [Runtime.InteropServices.Marshal]::FreeBSTR($bstr);
        }
    }
    END { }
}

I have got another script (called scriptB.ps1) that is supposed to call this function file before using the functions referenced by the file

scriptB.ps1 calls the function file by running the following inline within the file.

powershell .\my_functions.ps1
Import-Module .\my_functions.ps1

I get the error below.

The term 'Get-My-Plaintext' is not recognized as a name of a cmdlet, function, script file, or executable program. Check the spelling of the name, or if a path was included, verify that the path is
     | correct and try again.

Currently this behaviour happens on powershell terminal running powershell v 7.45, and the only way to fix it is to copy the function and paste it on the terminal.

Is there a way to resolve this problem and avoid the need to copy and paste the functions, its only this function that has this issue.

Thanks in advance.

5
  • 2
    The executable for PowerShell Core 7.4.5 is pwsh.exe. Using powershell will invoke Windows PowerShell; probably 5.1. Commented Nov 18, 2024 at 21:58
  • 3
    Don't include () after function name while including a param (...) section. You are defining both 0 parameters and one parameter at the same time. Commented Nov 18, 2024 at 21:59
  • 3
    After sourcing the file in using . .\my_functions.ps1, the command Get-Command -Name Get-My-PlainText shows that it is loaded . Commented Nov 18, 2024 at 22:05
  • Note that Import-Module .\SomeScript.ps1 - while ill-advised for conceptual reasons alone, given that a .ps1 file is just a script, not a module - is in effect the same as . .\SomeScript.ps1 (dot-sourcing), which is what you need. (Adding -Verbose to the Import-Module call shows this.) However, a side effect of using Import-Module is that a pseudo-module SomeScript is then reported by Get-Module. Also, repeating the Import-Module call in the same session is a quiet no-op - even if the script's content has changed; reloading requires -Force. Commented Nov 19, 2024 at 14:29
  • Two asides: To better align with PowerShell's naming conventions, Get-My-PlainText should be Get-MyPlainText; a simpler version of your function body would be [pscredential]::new('unused', $SecureString).GetNetworkCredential().Password Commented Nov 19, 2024 at 14:35

1 Answer 1

2

There are 2 clear solutions to your issue.

1. dot sourcing

Change powershell to a dot

. .\my_functions.ps1

When you call powershell you are actually calling powershell.exe which launches a different process/session, loads the function into that session, then the session ends and takes that definition with it. Dot sourcing will pull that function into your current scope.

2. Rename .ps1 to .psm1 and use Import-Module

Import-Module .\my_functions.psm1

This will Import functions into the current session.

Also, as Darin pointed out in the comments, you have an extra pair of parenthesis. When you define a function, you can use the inline syntax

Function Get-Something ($Param1, $Param2){
    ...
}

or with a Param() block. (Decorating the param block with [cmdletbinding()] or at least one Parameter with [parameter()] makes it an advanced function)

Function Get-Something {
    [cmdletbinding()]
    Param(
        $Param1,
        $Param2
    )
    ...
}

It doesn't appear to cause issues as you have it, but is confusing for others at a minimum.

EDIT

mklement0 pointed out the Import-Module works on .ps1 as well. He is correct as I just confirmed. I'll leave the answer as is but renaming to psm1 is NOT required.

Sign up to request clarification or add additional context in comments.

4 Comments

Good point about dot-sourcing, but note that Import-Module *.ps1 is effectively the same, which you can verify by adding -Verbose. In other words: Import-Module .\my_functions.ps1, as shown in the question, should have worked (though it's probably not a good idea to use this confusing form). I suggest mentioning the ramifications of turning a .ps1 file into a .psm1 (separate "session state" (scope domain), variables won't be visible, ...)
Quibble: the param(...) syntax alone doesn't make a function and advanced one; for that you need a [CmdletBinding()] attribute and/or parameter-individual [Parameter()] attributes; the former can only be used with the param(...) syntax (by placing it above param(...); with the inline syntax, it is quietly ignored), while the latter can be used with the inline syntax too. To avoid surprises, it's best to always use the param(...) syntax, which is a must in script files anyway. See this answer for details.
Thanks Michael for the detailed feedback. I forgot it was the [parameter()] attribute that implied the cmdletbinding, I'll update that. Wow I swear I tried the Import-Module on the ps1 and it didn't work, but trying again just now it worked. It was late, though. I'll update the post.
Good question: about_Functions doesn't give the two syntax forms names. Informally, I've used inline syntax (perhaps alternatively: C#-like) vs. param-keyword syntax.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.