VM power state not updated and returned correctly - powercli

I'm trying to power on VMs which I previously powered off using PowerCLI.
When I try to run the following script (part of a bigger one) I still get a status which is not "PoweredOn", even though I can see on the VSphere console that the machine was powered on.
I get this also in other situations and I try to re-get the virtual machines, but I fail to make this work.
If I don't re-get the VMs, I sometimes get error claiming the VM I'm referring to is null.
What am I doing wrong? What am I missing?
Here are the script lines:
$VMs = get-vm | Where-object {($_.Name -like $vmNamePatternToSearch)}# | Out-Null
foreach ($vm in $VMs) {
#$vm = Get-VM -Name $vm.Name #| Out-Null
if ($vm.powerstate -ieq "poweredoff") {
Start-VM -VM $vm -Confirm:$False | Out-Null
Write-Host -NoNewline 'Powering On' $vm.Name.ToString().PadRight(22)
do {
Start-Sleep -Seconds 1
Write-Host -NoNewline '|' $vm.powerstate
} until ($vm.powerstate -ieq "PoweredOn")
Write-Host
}
}
So my output is "| PoweredOff| PoweredOff| PoweredOff| PoweredOff| PoweredOff|..."
Even though the machine is already up.
Even if I un-comment the "#$vm = Get-VM -Name $vm.Name #| Out-Null" line - still no go.
I would appreciate your input.
Thanks!

PowerShell's objects are point in time references. So your vm variable will continue to reflect the status of the VM at the time you ran the get-vm cmdlet.
To help overcome this you could run something like the following to reference the updated state of the VM during your loop:
do {
Start-Sleep -Seconds 1
Write-Host -NoNewline '|' $vm.powerstate
} until ((Get-VM $vm).powerstate -ieq "PoweredOn")

Related

why can't I execute a simple powershell rasdial command in my runspace in a vb.net winforms app?

I've built an app that contains a PowerShell runspace.
It works perfectly for everything I've thrown at it.... until today.
One bit of functionality I've added detects when the user is trying to access corporate resources and will highlight if their VPN has become disconnected.
We use an always on VPN with certificate auth, so no username/password is required.
The runspace, and the app it is contained within run in the context of the user.
I've been able to demonstrate this by outputting the current environment user from within the runspace, which comes back as me (i.e. not system or administrator or anything silly).
I have a really simple bit of PowerShell that will attempt 4 times to reconnect the VPN before reporting back a failure, checking before each retry for a success.
This code works perfectly if I run ISE (standard, NOT elevated), but when I pass this code to be executed I get an error coming back from rasdial, which simply says 'the data is invalid'.
The PowerShell is simple enough...
$myvpn = Get-VpnConnection | Where-Object {$_.ServerAddress -eq "vpn.example.com"}
$VPNStatus = ( $myvpn ).ConnectionStatus
if ($VPNStatus -eq "Connected")
{
$output= 0
}
else
{
$attempt = 0
DO
{
Write-output "not connected"
rasdial $myvpn.name
$check = (Get-VpnConnection | Where-Object {$_.ServerAddress -eq "vpn.example.com"}).ConnectionStatus
start-sleep -Seconds 4
$attempt ++
} Until ($attempt -gt 3 -or $check -eq "Connected")
if ($check -eq "Connected")
{
$output= 0
}
else
{
$output= 1
}
}
I've got no idea why it won't work in the runspace, but will work in normal PowerShell with ease. My first suspicion was the context, but like I said I've disproven that theory.
I've googled the hell out of the error and can't seem to find any related results.
I've also tried to work around it, spawning an actual PowerShell session (ie not in a runspace) and passing in the command, but that also fails when spawned from my app.
Has anyone seen this before or similar behaviour when using runspaces/pipelines etc. within vb.net?
Are there any weird permissions pitfalls that I may have overlooked?
Edit: Expanded the powershell to be fully inclusive
So I've got to the bottom of this just now... Turns out the PS command will only work in an x64 shell, and my app's runspace was being spawned x86.
I changed the app to target x64 and it worked first time.
This post on Technet was the breadcrumbs that got me over the finish line.

Powershell process wait for SQL query to complete

I have a Powershell script that runs daily. The script is supposed to run a SQL query and create a file with the results.
Import-Module SqlPs
Invoke-Sqlcmd -InputFile "C:\SQL Queries\dailyexport1pm.sql" | Out-File -filepath "I:\HTPN Training and Workflow\Daily Epic Completion\$(get-date -f yyyy-MM-dd)dailyexport1pm.txt"
This used to work, however we recently added a large amount of data that causes the query to take up to 3.5 minutes. I do not have a strong understanding of powershell and need to have the out-file process run once the SQL query is complete. Any assistance would be appreciated.
The script outputs a blank txt file. When I check the task scheduler last run result, that the powershell script is the only action of, it says "The operation completed successfully. (0x0)"
I think there might be some error in the sql-file. Requesting you to try this once:
$Error.Clear()
try {
Import-Module SqlPs
Invoke-Sqlcmd -InputFile "C:\SQL Queries\dailyexport1pm.sql" -WarningAction SilentlyContinue -OutputSqlErrors $false
}
catch {
$Error | out-file -filepath "C:\temp\SqlLog.txt"
}
Note: Later if you see no error and the output is coming properly then you can pipe it to 'Out-File -filepath "I:\HTPN Training and Workflow\Daily Epic Completion\$(get-date -f yyyy-MM-dd)dailyexport1pm.txt" '
Hope it helps.

Powershell Job within SQL Server Agent

I am having trouble with a powershell job returning a result set when it runs, it runs successfully but no results. I am running it against a list of servers. If I run the script against a specific server that I know I can connect to, it runs fine and gives results.
This is a script within the sql server agent running powershell. Am I using the SMO object right? Ive tried to use a try/catch (job fails), ive tried to add -ErrorAction "Continue" to the script (job fails), using smo to resolve a server name in order to use the if statement (job succeeds) but no results. Here is the script:
$ErrorActionPreference = "Continue";
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO") | Out-Null
$instanceNameList = Get-Content "c:\Scripts\InPutFiles\servers.txt";
$results = #()
foreach($instanceName in $instanceNameList)
{
$serverObject = New-Object Microsoft.SqlServer.Management.Smo.Server($instanceName)
$serverName = $serverObject.ComputerNamePhysicalNetBIOS;
if($serverName -ne $null) {
$results += Invoke-Sqlcmd -Query "
(My Query is in here!)
" -ServerInstance $instanceName.Name}
$instanceName| Where-Object {$_} | Export-csv 'C:\scripts\HungJobs_UnabletoConnect.csv' -NoTypeInformation
}
$results| Where-Object {$_} | Export-csv 'C:\scripts\HungJobs.csv' -NoTypeInformation
Mid-way through your script, is the $scripts variable being populated properly by the Invoke-Sqlcmd?
If so, I'm thinking that you should be able to do away with Where-Object {$_} on your last two commands. Where-Object is used as a filter, and in these two cases it doesn't seem to be filtering anything. You should just be able to directly export the $instanceName and $results directly to csv.

Add and Remove Snapshots with PowerCLI for multiple servers

I am trying to get a snapshot from multiple servers with PowerCLI.
Connect-VIServer -server 192.168.0.1 -user acconut -password xxx
$vmlist = Get-Content C:\Users\Desktop\Test\Servers.txt
foreach($VM in $VMlist) {
New-Snapshot -VM $vm -Name Temp-SnapShot -description (get-date),'Created for patching'
}
Disconnect-VIServer -Confirm:$false
If I delete get-date, the script will work. But I need to type date in descriptions. How should I change the Script above to have Get-Date in snapshot's descriptions?
Also, I need to delete these snapshot after a couple days:
Connect-VIServer -server 192.168.0.1 -user acconut -password xxx
$vmlist = Get-Content C:\Users\Desktop\Test\Servers.txt
foreach($VM in $VMlist) {
Remove-Snapshot -VM $vm -snapshot -confirm:$false
}
Disconnect-VIServer -Confirm:$false
I could not delete snapshot with Remove-Snapshot because I get this error:
Remove-Snapshot : Missing an argument for parameter 'Snapshot'. Specify a parameter of type 'VMware.VimAutomation.ViCore.Types.V1.VM.Snapshot[]' and try again.
Thank you for your help.
On the description part you can put $date = get-date and do -description $date. That should work.
before you can remove the snapshot you need to get the snapshot. I would say edit your remove-snapshot line to include this:
Get-Snapshot -VM $vm | Remove-Snapshot -confirm:$false
you might even want to add -RemoveChildren:$true ( this will remove "All" snapshots )
This should help with what you are looking for. It finds the snaps and removes any greater than 10 days old.
$snaps = Get-VM | Get-snapshot | Select vm,name,created
if ($snaps.created -le (get-date).adddays(-10))
{
$remsnaps = $snaps | Where {$_.created -le (get-date).adddays(-10)}
$remsnaps | Out-File "C:\Scripts\RemoveSnaps\logs\remsnapsVC.txt"
$remobject = Get-Content "C:\Scripts\RemoveSnaps\logs\remsnapsVC.txt"
$rmsnap = Get-Snapshot $remsnaps.vm
Remove-Snapshot -Snapshot $rmsnap -RemoveChildren -Confirm:$false #-WhatIf
$MessageSubject = "The following snaps were removed from NTVCenter01!"
$MessageBody = $remobject | fl | out-string
SendEmail
}

Equivalent to bash "expect" in powershell

I'm using powershell to run another powershell script, at some point the other script asks for some input, I would like to be able to read the output from the other script and based on that supply input to it. Similar to what you can do with expect on bash.
Any ideas?
Thanks
Just posting my solution so that it can help someone. I faced the same problem while running some other script that will ask for answers. First create a file "inputFileLocation.txt" with answers to each question in each line in sequence. Then run the script in below syntax. And it will do the work.
`cmd.exe /c "script.bat < inputFileLocation.txt"`
You just use Expect program in your powershell. It works. Powershell is a shell too, you can run code wrote by powershell, which call bash code, which call powershell again.
Bellow is a test, it passed.
It "can work with tcl expect" {
$bs = #'
echo "Do you wish to install this program?"
select yn in "Yes" "No"; do
case $yn in
Yes ) echo "install"; break;;
No ) exit;;
esac
done
'#
$bsf = New-TemporaryFile
$bs | Set-Content -Path $bsf
$tcls = #'
#!/bin/sh
# exp.tcl \
exec tclsh "$0" ${1+"$#"}
package require Expect
set timeout 100000
spawn {spawn-command}
expect {
"Enter password: $" {
exp_send "$password\r"
exp_continue
}
"#\? $" {
exp_send "1"
}
eof {}
timeout {}
}
'#
$tclf = New-TemporaryFile
$tcls -replace "{spawn-command}",("bash",$bsf -join " ") | Set-Content -Path $tclf
"bash", $tclf -join " " | Invoke-Expression
Remove-Item $bsf
Remove-Item $tclf
}
Let me explain the test.
create a bash file which expect an input.
create a tcl file which call bash created in step one.
invoke tcl program from powershell, it works, will not waiting for input.
Sample to solve part of the problem
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
Start-Process -FilePath C:\myexecbatchfile.bat
# Wait the application start for 2 sec
Start-Sleep -m 2000
# Send keys
[System.Windows.Forms.SendKeys]::SendWait("input1")
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
Start-Sleep -m 3000
[System.Windows.Forms.SendKeys]::SendWait("input2")
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
I am not aware of any native capability to duplicate exact. This question has an answer that claims to be able to pass content to/from a process, so it might work with what you want.
How to run interactive commands in another application window from powershell
Good Luck!
Lee Holmes put out an "Expect for Powershell" in 2014 on the Powershell Gallery called Await. Turns out emulating expect is a lot more complicated than you'd imagine, involving the Win32 calls.
Package
https://www.powershellgallery.com/packages/Await/0.8
Demo
https://www.youtube.com/watch?v=tKyAVm7bXcQ