Execute Powershell Add-Computer remotely via Invoke-Command

Scenario:

When executed locally on the Azure VM, succesfully adds the machine to the AD, and restarts.

$DomainName = "test.local"
$AdminUserName = "sysadmin"
$Password = <mypass>
$SecurePassword = ConvertTo-SecureString $Password -asplaintext -force
$Credential = New-Object -Typename System.Management.Automation.PSCredential -Argumentlist $AdminUserName, $SecurePassword
$Credential

Add-Computer -DomainName $DomainName -Credential $Credential -Restart -Passthru -Verbose

Question:

Using the same variables, but now running the script on my machine, with another Azure VM as the target, via remote Powershell:

$ScriptBlockContent = { 
Param ($Arg1,$Arg2)
Add-Computer -DomainName $Arg1 -Credential $Arg2 -Restart -Passthru -Verbose}

$Session = New-PSSession -ConnectionUri $Uri -Credential $Credential
Invoke-Command -Session $Session -ScriptBlock $ScriptBlockContent -ArgumentList ($DomainName,$Credential)

This fails when executed remotely. Why ?

PS C:\> Invoke-Command -Session $Session -ScriptBlock $ScriptBlockContent -ArgumentList $DomainName, $Credential
VERBOSE: Performing the operation "Join in domain 'test.local'" on target "testvm2".
Computer 'rzlab1sql1' failed to join domain 'test.local' from its current workgroup 'WORKGROUP' with following error
message: Unable to update the password. The value provided as the current password is incorrect.
    + CategoryInfo          : OperationStopped: (testvm2:String) [Add-Computer], InvalidOperationException
    + FullyQualifiedErrorId : FailToJoinDomainFromWorkgroup,Microsoft.PowerShell.Commands.AddComputerCommand
    + PSComputerName        : mylab.cloudapp.net

Yet something more basic, without arguments, has no problems running remotely, so my $Uri, $Credential and general syntax seem ok, the sessions starts and runs my code:

$Path = "C:\"
$Attribute = "d"

$ScriptBlockContent = { 
Param ($Arg1,$Arg2)
Get-ChildItem -Path $Arg1 -Attributes $Arg2}

$Session = New-PSSession -ConnectionUri $Uri -Credential $Credential
Invoke-Command -Session $Session -ScriptBlock $ScriptBlockContent -ArgumentList $Path, $Attribute

Is there a problem with Invoke-Command and the way I'm storing the credentials ? Any other options to get this done (to add new VM to domain from a PS script) ?

Solution

Use test.local\sysadmin instead of the sysadmin user to connect to AD.

$DomainName = "test.local"
$AdminUserName = "sysadmin"
$DomainUserName = $DomainName+"\"+$AdminUserName
$Password = <mypass>
$SecurePassword = ConvertTo-SecureString $Password -asplaintext -force
$Credential = New-Object -Typename System.Management.Automation.PSCredential -Argumentlist ($AdminUserName, $SecurePassword)
$DomainCredential = New-Object -Typename System.Management.Automation.PSCredential -Argumentlist ($DomainUserName, $SecurePassword)

$ScriptBlockContent = { 
Param ($Arg1,$Arg2)
Add-Computer -DomainName $Arg1 -Credential $Arg2 -Restart -Passthru -Verbose}

$Session = New-PSSession -ConnectionUri $Uri -Credential $Credential
Invoke-Command -Session $Session -ScriptBlock $ScriptBlockContent -ArgumentList ($DomainName, $DomainCredential)

Another solution (less secure) is to send the plain text user and password to the remote session, and create the credential there:

$ScriptBlockContent = { 
Param ($Arg1,$Arg2,$Arg3,$Arg4)
Add-Computer -ComputerName $Arg4 -DomainName $Arg1 -Credential (New-Object -Typename System.Management.Automation.PSCredential -Argumentlist ($Arg1+"\"+$Arg2), (ConvertTo-SecureString $Arg3 -asplaintext -force)) -Restart -Passthru -Verbose}

$Session = New-PSSession -ConnectionUri $Uri -Credential $Credential
Invoke-Command -Session $Session -ScriptBlock $ScriptBlockContent -ArgumentList ($DomainName,$AdminUserName,$Password,$VMName)

Solution 1:

I think the problem might be with the way you're generating the credential using ConvertTo-SecureString. By default, that cmdlet uses an encryption key specific to the current host (I think) unless you provide an explicit encryption key with the -Key parameter. On the remote side, it needs to decrypt the string using the same encryption key which it doesn't have (because the host key is different).

First, I'd try passing the plaintext username and password as another parameter to the invoke and doing the credential creation within the script block. That will at least prove whether this is actually your problem or not.

*Edit: Here's a link to the documentation for ConvertTo-SecureString

Solution 2:

Taken from: http://www.gi-architects.co.uk/2017/01/powershell-add-computer-error-when-executed-remotely/

The root of the problem is (given that your password is correct) when running interactively the domain is pre-appended and as such you only need to provide the user. But in a non-interactive environment, the domain is not known. Make sure you either include the short domain names like contoso\DMAdmin or the full FQDN [email protected].

You can use the following PowerShell script if you pass the username and password as variables securely via azure automation:

$PasswordSec = ConvertTo-SecureString $Password -AsPlainText -Force $djuser = new-object -typename System.Management.Automation.PSCredential -argumentlist $Username, $PasswordSec Add-Computer -DomainName "contoso.com" -Credential $djuser -Restart