Reputation: 77
Hi I am new developing scripts in powershell and Active Directory, I am trying to run the following .ps1
#! /usr/bin/pwsh
param ([String]$dns, [String]$adminUser, [String]$adminPassword, [String]$user, [String]$newPassword)
$password = ConvertTo-SecureString -String $adminPassword -AsPlainText -Force
$pass = ConvertTo-SecureString -String $newPassword -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ($adminUser, $password)
$session = New-PSSession -cn $dns -Credential $credential -Authentication Negotiate
Invoke-Command -Session $session -ScriptBlock {Set-ADAccountPassword -Identity $user -Reset -NewPassword ($pass)}
passing the arguments like this
./changePasswordAD.ps1 mydns myuser mypassword userToEdit NewPassword
the result obtained is the following
Set-ADAccountPassword: Cannot validate argument on parameter 'Identity'. The argument is null. Provide a valid value for the argument, and then try running the command again.
Why does this happen and how can I solve it?
I am grateful in advance for your contributions
Upvotes: 1
Views: 6507
Reputation: 21418
It's a scope problem. A ScriptBlock
can't directly reference variables defined in another PowerShell session. New sessions are used when remoting or relying on jobs. This is hinted at by the presence and use of New-PSSession
, Enter-PSSession
, etc. when PSRemoting, but isn't as obvious when using jobs.
You can prefix the variable reference in the ScriptBlock
with $using:
like so in order to "use" the variable defined within the calling session:
{
Set-ADAccountPassword -Identity $using:user -Reset -NewPassword $using:pass
}
However, the using
scope can't be used when running a ScriptBlock
on the local system. Referencing a local variable would work fine if you executed the ScriptBlock
locally within the same session (using the call operator &
, Invoke-Command
is not usually recommended for local use since it unnecessarily relies on PSRemoting
). So what if we want it to run anywhere?
If the ScriptBlock
needs to be able to function regardless of execution context, you can use the-ArgumentList
parameter of Invoke-Command
(this also applies to cmdlets like Start-Job
when execution would be local but within a new session) instead to pass the variables into the ScriptBlock
:
Invoke-Command -ArgumentList $user, $pass {
Param( $user, $pass )
Set-ADAccountPassword -Identity $user -Reset -NewPassword $pass
}
I added line breaks for readability but both ScriptBlocks
above should function as a one-liner as well.
Note: Technically, parameters passed via
-ArgumentList
would be referenced as$args[0]
,$args[1]
, etc. in the order provided such as when processing raw script arguments, but as executing aScriptBlock
functions similarly to a script or... well, function, adding theParam( .... )
bit will cause the arguments provided to be assigned to positional parameters defined in theScriptBlock
; in this case, the friendlier names$user
and$pass
. Theparam
names do not need to be the same as the original variable names in the parent scope.
Upvotes: 1