Intermittent errors with automation of virtual machines using Powershell - powershell

I have an intermittent problem with Powershell when automating some tests on a virtual machine.
The scenario and set up is as follows:
Server running HyperV
One virtual machine with multiple snapshots
Powershell script that restores a given snapshot, copies files over, runs a test and retrieves log files
Batch file that calls the Powershell script multiple times with different parameters
The batch file parameters specify things like which snapshot to use, which test to run, etc.
The problem is as follows:
I can run the batch and some of the tests will fail to copy files / fail to create a scheduled task / fail to retrieve log files / etc. It varies which if any (or all) sections fail. Some of the tests will work completely. If I re-run the same batch file, again some tests may fail and others will work; there is no consistency in terms of which fail and which run. Sometimes I have two adjacent tests that use the same snapshot, 1 will work and 1 won’t (see errors below).
To restore the snapshots I am using the “PowerShell Management Library for Hyper-V” from: (http://pshyperv.codeplex.com/releases)
Below is some of the code:
Powershell (minus a few functions / variable declarations / reading xml config file / reading and validating command line inputs / and other non-relevant sections):
Function ApplySnapshot
{
LogAction "Starting apply snapshot"
LogAction $("Restoring snapshot {0}" -f $ss)
#Stop a running VM, restore snapshot, start it up and connect to it
$vmstate = get-vmstate $vmname
$vmstate = $vmstate.EnabledState
if ($vmstate -ne "Stopped")
{
stop-vm $vmname -force
Start-Sleep -Second 15
}
get-vmsnapshot $vmname | where {$_.ElementName -eq $ss} | Restore-VMSnapshot -force
start-vm $vmname -force
Start-Sleep -Second 20
LogAction $("Snapshot {0} restored" -f $ss)
LogAction "End apply snapshot"
}
Function CopyFiles
{
LogAction "Start copy installation files"
$from = "\\server\folderx"
$to = "\\" + $hostname + "\C$\test"
Enter-PSSession -ComputerName $hostname -Credential $cred
Copy-Item $from $to -Recurse
LogAction "End copy installation files"
}
Function CreateSchedule ($hn, $tn, $tr, $sd, $st, $un, $pw)
{
LogAction "Starting create schedule"
Invoke-Command -ComputerName $hn -ScriptBlock {
param($hn, $tn, $tr, $sd, $st, $un, $pw)
Write-Host $("Host name: [{0}]" -f $hn);
$cmd = $("schtasks.exe /create /S ""{0}"" /tn ""{1}"" /tr ""{2}"" /sc once /sd {3} /st {4} /ru ""{5}"" /rp ""{6}"" /rl highest /V1" -f $hn, $tn, $tr, $sd, $st, $un, $pw);
Invoke-Expression $cmd;
} -ArgumentList #($hn, $tn, $tr, $sd, $st, $un, $pw)
LogAction "End create schedule"
}
...setting variables etc...
ApplySnapshot
CopyFiles
CreateSchedule -hn $hostname -tn $taskname -tr $taskrun -sd $setdate -st $settime -un $username -pw $password
Batch file:
PowerShell -Command "& C:\Auto.ps1" <...params...>
PowerShell -Command "& C:\Auto.ps1" <...params...>
PowerShell -Command "& C:\Auto.ps1" <...params...>
PowerShell -Command "& C:\Auto.ps1" <...params...>
pause
Example output:
C:\Auto>PowerShell -Command "& C:\Auto.ps1" <...params...>
WARNING: The job to Change state of VM TestVM to Stopped is still
running in the background.
You can check its progress with Test-wmiJob or Test-wmiJob -statusOnly using
the following job id:
\\Server\root\virtualization:Msvm_ConcreteJob.InstanceID="A207CEBA-F582-4A42-
BCDE-3312C7FB6DCC"
JobStarted
WARNING: The job to Change state of VM TestVM to Running is still
running in the background.
You can check its progress with Test-wmiJob or Test-wmiJob -statusOnly using
the following job id:
\\Server\root\virtualization:Msvm_ConcreteJob.InstanceID="42C31CEF-00E2-40A7-
AF70-578B0B91B05D"
JobStarted
Enter-PSSession : Connecting to remote server failed with the following error m
essage : The WinRM client cannot complete the operation within the time specifi
ed. Check if the machine name is valid and is reachable over the network and fi
rewall exception for Windows Remote Management service is enabled. For more inf
ormation, see the about_Remote_Troubleshooting Help topic.
At C:\Auto.ps1:192 char:18
+ Enter-PSSession <<<< -ComputerName $hostname -Credential $cred
+ CategoryInfo : InvalidArgument: (TestVM:String) [Enter-PSS
ession], PSRemotingTransportException
+ FullyQualifiedErrorId : CreateRemoteRunspaceFailed
[TestVM] Connecting to remote server failed with the following error messa
ge : The WinRM client cannot complete the operation within the time specified.
Check if the machine name is valid and is reachable over the network and firewa
ll exception for Windows Remote Management service is enabled. For more informa
tion, see the about_Remote_Troubleshooting Help topic.
+ CategoryInfo : OpenError: (:) [], PSRemotingTransportException
+ FullyQualifiedErrorId : PSSessionStateBroken
So, in this example, the snapshot has been successfully applied (despite the warnings). The “Enter-PSSession” error appears after the files have been copied to the virtual machine.
As a test, I tried this on a different server (also running HyperV etc etc), and I found that I still get the initial error (after the file copying stage) but I do not get the error creating the scheduled task.
All my efforts to search for information on the “Connecting to remote server failed with the following error message : The WinRM client cannot complete the operation within the time specified.” Error seem to say “make sure the machine is set up for remote use”; well I know it is because sometimes it works and if I run just an “Enter-PSSession” command by itself, I can connect.
The server(s) and virtual machine(s) are on the same domain.
I know there’s a lot to take in here, but I would really appreciate some help in how to troubleshoot / fix this problem.
Thank you

Maybe the targets are not always up when the connection attempts are being made.

Related

Invoke-AzVMRunCommand and Start-Process under specific user on remote VM using Azure Runbook

I need to run Start-Process on a remote VM with specific user account using Azure Powershell Runbook
function Install-Postgres {
$username = "aact-import-vm1\aact-importer"
$password = "ChangeMe!"
$cred = New-Object System.Management.Automation.PSCredential -ArgumentList `
#($username,(ConvertTo-SecureString -String $password -AsPlainText -Force))
write-output $cred
# run pg installer
Start-Process "C:\Program Files\WindowsPowerShell\Modules\Install-Postgres\postgresql.exe" -ArgumentList `
"--mode unattended", "--unattendedmodeui none",`
"--prefix `"C:\Program Files\PostgreSQL\10`"", "--datadir `"C:\Program Files\PostgreSQL\10\data`"",
"--superpassword `"ChangeMe!`"",`
"--servicename `"postgres`"", "--serviceaccount `"postgres`"", "--servicepassword `"ChangeMe!`""`
-Wait -Credential $cred;
}
$script = Get-Content Function:\Install-Postgres
Out-File -FilePath Install.ps1 -InputObject $script
#Note that the -ScriptPath should not point to the remote path(in remote vm), it should point to the local path where you execute the command Invoke-AzureRmVMRunCommand
$output = Invoke-AzVMRunCommand -ResourceGroupName $resourceGroupName -Name $vmName -CommandId 'RunPowerShellScript' -ScriptPath Install.ps1
write-output $output.Value
#after execution, you can remove the file
Remove-Item -Path Install.ps1
The script above produces the following error:
Start-Process : This command cannot be run due to the error: Access is denied.
If I run the script above without specific credentials the postgres installer produces this error in the log:
Executing icacls "C:\Windows\Temp/postgresql_installer_1ef9b3f2c6" /T /Q /grant "WORKGROUP\aact-import-vm1$:(OI)(CI)F"
Script exit code: 1332
Script output:
Successfully processed 0 files; Failed processing 1 files
Script stderr:
WORKGROUP\aact-import-vm1**$**: No mapping between account names and security IDs was done.
Please notice that there is symbol $ instead of user name.
However, if I run it on the VM it works fine and produces this line in the log:
Executing icacls "C:\Users\aact-importer\AppData\Local\Temp\2/postgresql_installer_2662c862ff" /T /Q /grant "aact-import-vm1\aact-importer:(OI)(CI)F"
Script exit code: 0
As far as I can see, If I run runbook script remotely without credentials it runs under NTAUTHORITY\SYSTEM that's why there is symbol $ instead of user name in the postgres installer log. If I run it locally it uses proper user and everything works fine.
The question is: how can I specify a user account to run Start-Process on the remote VM?
Same question on msdn https://social.msdn.microsoft.com/Forums/en-US/a7fa0ca8-5cba-42bb-8076-9a8d4a654beb/invokeazvmruncommand-and-startprocess-under-specific-user-on-remote-vm-using-azure-runbook?forum=azureautomation#a7fa0ca8-5cba-42bb-8076-9a8d4a654beb
For those who are interested:
After investigation with MS support they confirmed that runbook (not hybrid) always runs under NTAUTHORITY\SYSTEM

MDT Module Updating Media through JEA Endpoint fails adding BCD entry

I am running into an issue with remotely updating MDT offline media on a JEA endpoint. The error has something to do with permissions passed to BCDEdit and the virtual account created by JEA (WinRM User...). BCDEdit returns
An error occurred while attempting the specified create operation. This security ID may not be assigned as the owner of this object.
when trying to update the BCD file with the x64 boot config.
Command:
Invoke-Command -ComputerName $DeploymentServerName -ConfigurationName MDTUpdate -ScriptBlock {
New-PSDrive -Name "DS002" -PSProvider MDTProvider -Root "$Using:LocalDeploymentShareFolder" -ErrorAction Stop
Update-MDTMedia -Path "DS002:\Media\MEDIA001" -Verbose
} -Credential $MDTCreds -ErrorAction Stop
Command that MDT module runs:
'C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\AMD64\BCDBoot\bcdedit.exe' -store "C:\MyVMs\MDT\USB\Content\Boot\bcd" /create "{f31cce1a-e314-4481-9ac9-e519f65dff65}" -d "Litetouch Boot [MEDIA001] (x64)" -application OSLOADER
Error from JEA Transcript:
VERBOSE: Error detected running command: 'C:\Program Files (x86)\Windows Kits\10\Assessment and Deployment Kit\Deployment Tools\AMD64\BCDBoot\bcdedit.exe -store "C:\MyVMs\MDT\USB\Content\Content\Boot\bcd" /create "{f31cce1a-e314-4481-9ac9-e519f65dff65}" -d "Litetouch Boot [MEDIA001] (x64)" -application OSLOADER' Exit code is: 1
VERBOSE: Error text is: An error occurred while attempting the specified create operation. This security ID may not be assigned as the owner of this object.
Update-MDTMedia : BcdEdit returned an error.
At line:5 char:9
+ Update-MDTMedia -Path "DS002:\Media\MEDIA001" -Verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (MEDIA001:String) [Update-MDTMedia], DeploymentPointException
+ FullyQualifiedErrorId : BcdEditError,Microsoft.BDD.PSSnapIn.GenerateMDTMedia
Relevant information from session config:
#{
SchemaVersion = '2.0.0.0'
SessionType = 'Default'
ExecutionPolicy = 'Unrestricted'
LanguageMode = 'FullLanguage'
TranscriptDirectory = 'C:\JEA\Transcripts'
RunAsVirtualAccount = $true
RoleDefinitions = #{
'ExampleDomain\ExampleUserOrGroup' = #{
'RoleCapabilities' = 'MDTUpdate'
}
}
}
Relevant content from role config:
#{
ModulesToImport = 'C:\Program Files\Microsoft Deployment Toolkit\Bin\MicrosoftDeploymentToolkit.psd1'
VisibleCmdlets = 'Get-Command','Out-Default','Exit-PSSession','Measure-Object','Select-Object','Get-FormatData','Start-Transcript','Stop-Transcript','Import-Module','Get-Module','New-PSDrive','Write-Output','Update-MDTDeploymentShare','Remove-Item','Update-MDTMedia','New-Item','Remove-PSDrive'
VisibleProviders = 'FileSystem', 'MDTProvider'
VisibleExternalCommands = 'bcdedit.exe'
}
How can I give BCDEdit the proper permissions when running under the virtual account? Or do I have to drop JEA and give a service account local admin rights and run it under the default PSSession?
The thing that comes to mind, is make sure that the group that the account is a part of, has more than just Read-Only permissions. I've had a case where I could run any powershell command, but when it came to invoking a non-powershell native program, it would give me permission issues.
The only other thing besides that is to use a runas within the script block, but that kinda goes against the whole purpose of JEA.

Wait-Process not working in Windows 10

I have a .ps1 script for installing a piece of software with Wait-Process that was created with ISE on a W7 machine that when I try to run it on W10, it errors out. Here is the script up until failure:
#Runs EnterpriseRx installer configured for PROD:
.\"EnterpriseRx.exe" /q /installType=0 /facilityID=999 /targetEnv=PROD
/encryptFacility=0 /S /D=C:\McKesson\EnterpriseRx Production
#15 second notification letting user know install is running:
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup("This process typically takes about 1 minute. You will be
notified when install is complete.",15,"EnterpriseRx - PROD is
installing...",0x1)
#Wait for application to finish installing:
Wait-Process -name "EnterpriseRx.exe"
The error returned is this:
Wait-Process : Cannot find a process with the name "EnterpriseRx.exe".
Verify the process name and call the cmdlet again.
At C:\temp\EnterpriseRx Install TEST\EnterpriseRx Production - Desktop.ps1:9
char:1
+ Wait-Process -name "EnterpriseRx.exe"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (EnterpriseRx.exe:String) [Wait-
Process], ProcessCommandException
+ FullyQualifiedErrorId :
NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.WaitProcessCommand
I've tried changing the name to match what is showing in Task Manager, but with no success; I get the same error message. Note: This process is a run-on-command.exe, I don't need to verify if it's running, I know it is running, I just need to know what it's running as.
Any ideas or suggestions?
While your installation is in progress, use another PowerShell window to run the following to find out what PowerShell sees for the process.
Get-Process | Where-Object {$_.path -match "EnterpriseRx.exe"}
It most likely sees the process name as simply "EnterpriseRx" without the .exe on the end.
An other attempt would be, starting the Process by Start-Process, as this Cmdlet will give you your Process-Object back. You can utilize this to wait:
#Runs EnterpriseRx installer configured for PROD:
$Process = Start-Process .\"EnterpriseRx.exe" -ArgumentList /q, /installType=0, /facilityID=999, /targetEnv=PROD,
/encryptFacility=0, /S, /D=C:\McKesson\EnterpriseRx, Production -PassThru
#15 second notification letting user know install is running:
$wshell = New-Object -ComObject Wscript.Shell
$wshell.Popup("This process typically takes about 1 minute. You will be
notified when install is complete.",15,"EnterpriseRx - PROD is
installing...",0x1)
#Wait for application to finish installing:
$Process | Wait-Process

Run interactive 'master' PowerShell script on local PC as well as remote PCs and log

I have a massive (983 line) interactive PowerShell script that I wrote to manage pretty much all aspects of a local server. I have a text file with a list of servers. I want to get crazy with it.
Text File Contents:
Server1Name
Server2Name
PowerShell Interactive Menu:
Write-Host "Welcome to the environment control script."
Write-Host " "
Write-Host "What would you like to do? Select the number:"
Write-Host "========================================================================="
Write-Host "1. Create a backup of the environment (existing servers)"
Write-Host "2. Backup environment and deploy updated environment (existing servers)"
Write-Host "3. Deploy new environment including IIS (clean servers)"
Write-Host "4. Deploy just IIS (clean servers)"
Write-Host "5. Remove Build w/ backup and cleanup IIS (existing servers)"
Write-Host "6. Remove Build w/out backup and cleanup IIS (existing servers)"
Write-Host "7. Remove and cleanup only IIS (existing servers)"
Write-Host "8. Deploy new DLL's (existing servers)"
Write-Host "9. [EXIT]" -foregroundcolor "yellow"
Write-Host " "
Purpose:
Trying to get away from having 1000 batch files on my servers
Example:
I run script on "Master" server and there are 10 other servers in the text file. At first, the script is ONLY running on the master server. Once I pick option 6, a command is run to do the same commands across all of the 11 servers (master + 10 others).
I know how to run the same script across servers if it was say a simple file copy or service reset (no interaction from the user) using stuff like invoke-command or enter-pssession,etc...however, in my case I'll have to remote to each server because it's interactive...unless there is a way to do this?
Also, if possible, enable logging so I can confirm that each PC ran the script and did everything successfully. Right now, I have it starting a transcript and stopping at the end. Works great locally.
Any help would be appreciated.
Update: 3/8/16
So, #Cobster has helped me out and we are 99% there.
Using the follow code, it looks like the script is attempting to run, however there is one more issue.
$sessions = New-PSSession -ComputerName $servers
$job = Invoke-Command -Session $sessions -ScriptBlock ${function:Backup} -AsJob
If you want a different job for each machine, you can do the following:
$jobs = $sessions | ForEach-Object { Invoke-Command -Session $_ -ScriptBlock ${function:Backup} -AsJob }
Wait-Job -Job $jobs
The issue is that when this is done, the invoke-command doesn't seem to import all of my previously declared variables at the top of my script (which includes virtually ALL of my paths, etc. because they are not defined within that function.
This results in a bunch of these types of errors (7 to be exact, one for each of my test-path checks before performing a step):
Cannot bind argument to parameter 'Path' because it is null.
+ CategoryInfo : InvalidData: (:) [Test-Path], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.TestPathCommand
+ PSComputerName : *ServerName*
Solution:
Use -ArgumentList $variable1, $variable2 in the invoke-command and
param
(
$variable1,
$variable2
) method within the function...courtesy of Cobster.
Invoke-Command can be run in the background by using the -AsJob switch.
Logging is also possible by using the Receive-Job cmdlet to get the output of each of the background jobs upon completion.

Powershell remote execution not writing log on remote machine

I am currently stuck with an installation issue using a powershell script. I can run the script without any problem on Host B from command line. But when I attempt to launch it remotely, the script is not able to create the log file (which I monitor to see when I need to send the password to the command line). The error is at very bottom...
I launch the installer from Host A (see commands below). It remotely executes the powershell script cognosInstall.ps1 on Host B. The script code is at bottom. It begins to copy files over and kick-off the installation, but for some reason it has a problem with creating the log file. I check local directory on HostB and do not find the file either.
Host A (data collector): Triggers remote execution of powershell script which installs IBM Cognos.
$s=new-pssession -computername HostB -credential $creds
invoke-command -session $s {$filename=Split-Path c:\temp\auto-install\*stats*.iso -leaf -resolve;echo $filename;}
invoke-command -session $s {c:\temp\auto-install\cognosInstall.ps1 $filename;}
Host B (Cognos is being installed on):
#Open a command window
invoke-item C:\Windows\System32\cmd.exe
start-sleep -s 2
# Write output of install command to file
select-window cmd |send-keys "c:\temp\cognos-install\cognos_install.bat c:\temp\cognos-install\cognos_mssql.ini C:\temp\IBM_Cognos_10.1.1_and_FP1 > c:\temp\Cognos_Install_log 2>&1 {ENTER}"
#check file for password prompt
do {
Start-Sleep -s 8;
write-output "Waiting for Password Prompt"
}
until (Select-string -Path c:\temp\Cognos_Install_Log -pattern Password )
select-window cmd |send-keys "Passwd123{ENTER}"
Error I am getting:
Install starts to run..... then hits this issue.....
Copying install config file to cognos-install directory
C:\temp\auto-install\cognos_mssql.ini
1 File(s) copied
Beginning of Cognos Install - wait for completion
Waiting for Password Prompt
Cannot find path 'C:\temp\Cognos_Install_Log' because it does not exist.
+ CategoryInfo : ObjectNotFound: (C:\temp\Cognos_Install_Log:Stri
ng) [Select-String], ItemNotFoundException
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.Selec
tStringCommand