Antonio Matheus
Antonio Matheus

Reputation: 77

PowerShell - Cannot validate argument on parameter 'Identity'. The argument is null

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

Answers (1)

codewario
codewario

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 a ScriptBlock functions similarly to a script or... well, function, adding the Param( .... ) bit will cause the arguments provided to be assigned to positional parameters defined in the ScriptBlock; in this case, the friendlier names $user and $pass. The param names do not need to be the same as the original variable names in the parent scope.

Upvotes: 1

Related Questions