Search code examples
powershellinvoke-commandscriptblock

PowerShell "=" not a recognised cmdlet error


Further to the response given here: PowerShell Enter Session find path bug, I've hit another wall in my script that I can't work around. The below script returns the error:

The term '=' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

$sb = [ScriptBlock]::Create(@"
$Acl = (Get-Item -path D:\Websites\$Sitename).GetAccessControl('Access')
$Ar = New-Object System.Security.AccessControl.FileSystemAccessRule('BUILTIN\IIS_IUSRS', 'Modify', 'ContainerInherit,ObjectInherit', 'None', 'Allow')
$Acl.SetAccessRule($Ar)
Set-Acl -path $Path -AclObject $Acl
"@)

Invoke-Command -Session $Session -ScriptBlock $sb

I can create the script block variable ($sb), but when I invoke it, I get the error. I have narrowed it down to the setting of the $Acl variable, and tried rewriting various ways with no luck. What am I missing?


Solution

  • When you use a double-quoted here-string, it behaves just like a regular double-quoted string - the parser will evaluate and expand any variable or sub-expression between the quotation marks.

    Since the variables in the scriptblock definition doesn't already exist in the defining context, you end up with a scriptblock with the following definition:

     = (Get-Item -path D:\Websites\).GetAccessControl('Access')
     = New-Object System.Security.AccessControl.FileSystemAccessRule('BUILTIN\IIS_IUSRS', 'Modify', 'ContainerInherit,ObjectInherit', 'None', 'Allow')
    .SetAccessRule()
    Set-Acl -path  -AclObject 
    

    As you can see, the first two statements start out with just a bare = as the first non-whitespace character, and this is the reason for the error you're seeing.

    Switch to a single-quoted here-string instead:

    $sb = [ScriptBlock]::Create(@'
    $Acl = (Get-Item -path D:\Websites\$Sitename).GetAccessControl('Access')
    $Ar = New-Object System.Security.AccessControl.FileSystemAccessRule('BUILTIN\IIS_IUSRS', 'Modify', 'ContainerInherit,ObjectInherit', 'None', 'Allow')
    $Acl.SetAccessRule($Ar)
    Set-Acl -path $Path -AclObject $Acl
    '@)
    

    If you need to pass in a variable value from the defining scope, I'd suggest either defining a param block in the scriptblock:

    $sb = [ScriptBlock]::Create(@'
    param($Sitename)
    $Acl = (Get-Item -path D:\Websites\$Sitename).GetAccessControl('Access')
    $Ar = New-Object System.Security.AccessControl.FileSystemAccessRule('BUILTIN\IIS_IUSRS', 'Modify', 'ContainerInherit,ObjectInherit', 'None', 'Allow')
    $Acl.SetAccessRule($Ar)
    Set-Acl -path $Path -AclObject $Acl
    '@)
     Invoke-Command -Session $Session -ScriptBlock $sb -ArgumentList $Sitename
    

    or use the -f string format operator to replace it in the string before creating the scriptblock:

    $sb = [ScriptBlock]::Create(@'
    $Acl = (Get-Item -path D:\Websites\{0}).GetAccessControl('Access')
    $Ar = New-Object System.Security.AccessControl.FileSystemAccessRule('BUILTIN\IIS_IUSRS', 'Modify', 'ContainerInherit,ObjectInherit', 'None', 'Allow')
    $Acl.SetAccessRule($Ar)
    Set-Acl -path $Path -AclObject $Acl
    '@ -f $Sitename)
    

    See the about_Quoting_Rules help topic for more information about quoting and variable expansion