I have an Azure powershell script to start a list of VMs. When it runs it sucessfully starts the first VM in the list then nothing. No errors are reported, it appears to never return from the start command
Code as follows:
$vmList = "VM1", "VM2", "VM3"
Write-Output "Starting VMs in '$($AzureResourceGroup)' resource group";
Foreach ($vm in $vmList)
{
Write-Output "Starting VM...$vm"
$result = Start-AzureRmVM -Name $vm -ResourceGroupName AzureResourceGroup
Write-Output "Result of Start VM...$result"
}
When this run it outputs the "Starting VM....VM1", it starts the VM then nothing...
It sounds like your Start-AzureVM call is simply waiting for the VM to finish starting up.
That is, Start-AzureVm is by default a blocking, synchronous operation - despite its name[1]
To make it asynchronous, use the -AsJob switch, which uses a background job to start the VM; the call ends once that job has been created and returns a job object that you can use to track start-up progress later, via the usual *-Job cmdlets, such as Receive-Job.
[1] Regarding the name Start-AzureVM:
It is a misnomer in that PowerShell's nomenclature calls for the Start verb to exhibit asynchronous behavior:
From https://learn.microsoft.com/en-us/powershell/developer/cmdlet/approved-verbs-for-windows-powershell-commands (emphasis added):
Invoke vs. Start
The Invoke verb is used to perform an operation that is generally a synchronous operation, such as running a command. The Start verb is used to begin an operation that is generally an asynchronous operation, such as starting a process.
Note that the core Start-Sleep cmdlet is similarly misnamed.
With synchronous-by-default behavior, there is no good naming solution, because the name Invoke-AzureVm would be confusing.
The better approach - which would obviously be a breaking change - would have been to make the cmdlet asynchronous by default and offer a -Wait switch for to opt-into synchronous operation.
Related
I want to pass a value in Octopus from one step to another of a project via output variable, the value is "VM running" or "VM deallocated".
There are two server, one of the server is down, another one is running so values should be passed accordingly. Now when I use the exact syntax of Output variable, it is passing Null value to next step.
Octopus deploy Project Step 1:
$RG = $RGName
$VM = "#{StepTemplate_VMName}"
$WarningPreference = 'SilentlyContinue'
$VMStats = (Get-AzureRmVM -Name $VM -ResourceGroupName $RG -Status).Statuses
$stats = ($VMStats | Where Code -Like 'PowerState/*')[0].DisplayStatus
Set-OctopusVariable -name "RunStatus" -value $stats
write-host $stats #value can either be "VM running" or "VM deallocated"
Octopus deploy Project Step 2:
$VM = "#{StepTemplate_VMName}"
$Runstatus = $OctopusParameters["Octopus.Action[Step1].Output[$VM].RunStatus"]
write-host $Runstatus
If I do not use [$VM] in the code of step 2, it give only 1 value to both the machine as "VM running"
As per the syntax given in Octopus website, we should use the VM name to pass machine specific different values.
so I used [$VM] but it gives null values to both of the machine
Edit: Event If I hardcode the value of $VM to any one VMName, it still gives me null.
Based on what I do in Octopus it should be:
$Runstatus = $OctopusParameters["Octopus.Action[Step1].Output.RunStatus"]
Even states that format on the help documentation. https://octopus.com/docs/projects/variables/output-variables
Is the step that's creating the output variable actually being run on the server that's down? When the output variable is created in a step that's run on multiple machines, multiple machine-scoped values for that variable will be created. When the subsequent step is run on the same machines, Octopus will pick the correct value based on that scope. So maybe a little off topic, but if both steps are run on both targets, specifying the $VM scope is redundant, and is only needed if you want a different machine's value.
Since hardcoding the target name here returns null it seems like the output variable creation step wasn't run on that target. I'd suggest debugging this by creating a variable called OctopusPrintEvaluatedVariables with value True in your project. That'll increase the task log verbosity and log how all variables are evaluated through each step of the deployment.
I am looking for way to find if Azure VM has been idle for over an hour.
The criteria to qualify for idle if there is no mouse movement for over an hour.
I want to save the State of VM and shut it down.
Can this be achieved using Powershell comands ?
I have configured something similar in the past, and unfortunately the only reliable way of doing this, was via a Task Scheduled on Windows (you don't specify OS in your original question). Now the following code which you should execute in PowerShell will:
If there is no activity for past 30 minutes, it will trigger a shutdown command with another 60 seconds to give user time to perform any action in order to cancel it and wait for the next triggering period. Notice the switch RunOnlyIfIdle
Start checking at 8PM for the next 12 hours every 1 hour whether there was any activity on the machine (including clicks, mouse moves, etc.), it excludes the system processes as it is build to do exactly this
RunLevel is set to 1, so OS has permissions to execute the task with no regards to the fact whether the user is still logged in or not
As you can see two last lines actually create the scheduled task, and it is configured to create it under the context of currently logged in user which executed the script, but can be adjusted if necessary
Now this will only shut down the OS, but the VM resource in Azure will only reach "Stopped" state, thus still charging you money for the compute power. In order to get it to "Stopped (Deallocated)" state and stop charging you, I had an Automation Account in Azure which had a script that was scheduled to execute every hour and scan all VMs with state "Stopped" and then it executed the command Az-StopVM which actually deallocated the VM and stopped charging money.
$TaskName = 'Idle_Shutdown'
$Service = New-Object -ComObject('Schedule.Service')
$Service.Connect()
$RootFolder = $Service.GetFolder('')
$TaskDefinition = $Service.NewTask(0)
$Settings = $TaskDefinition.Settings
$Settings.AllowDemandStart = $true
$Settings.Compatibility = 2
$Settings.Enabled = $true
$Settings.RunOnlyIfIdle = $true
$Settings.IdleSettings.IdleDuration = 'PT30M'
$Settings.IdleSettings.WaitTimeout = 'PT5M'
$Settings.IdleSettings.StopOnIdleEnd = $true
$Trigger = $TaskDefinition.Triggers.Create(2)
$Trigger.Repetition.Interval = 'PT1H'
$Trigger.Repetition.Duration = 'PT12H'
$Trigger.StartBoundary = ([datetime]::Now).ToString("yyyy-MM-dd'T'20:00:00")
$Trigger.Enabled = $true
$Action = $TaskDefinition.Actions.Create(0)
$Action.Path = 'shutdown'
$Action.Arguments = '/s /t 60'
$LocalUser = $TaskDefinition.Principal
$LocalUser.RunLevel = '1'
$user = [environment]::UserDomainName + '\' + [environment]::UserName
$null = $RootFolder.RegisterTaskDefinition($TaskName, $TaskDefinition, 6, $user, $null, 2)
I am trying to multithread something and ran across this:
http://learn-powershell.net/2015/03/31/introducing-poshrsjob-as-an-alternative-to-powershell-jobs/
Attempting to use but getting errors and not able to access the data I need. Let me explain. I have an extremely simple example here:
#('12.34.56.78')|Start-RSJob -Name {"TEST_$($_)"} -ScriptBlock {
Param($Computername)
Test-Connection -ComputerName $Computername -Quiet
}
get-RSJob|Receive-RSJob
This actually errors saying computername is null. Any type of command I place in here same error comes up saying computername is null. How to get rid of this and how to access the Boolean value that should be returned.
This returns a true or false on the command line but I cannot access this value when run in this script.
Eventually this will need to take an array of IP's and I will need to access all values returned for each machine. I don't have to use this posh module but need threads and thought this a good choice. Any advice here is appreciated.
It looks like you found a bug in my module with using Param() to host the incoming data in the pipeline. Fortunately, you can work around this by just using $_ in the scriptblock.
#('12.34.56.78')|Start-RSJob -Name {"TEST_$($_)"} -ScriptBlock {
Test-Connection -ComputerName $_ -Quiet
}
get-RSJob|Receive-RSJob
That being said, I definitely need to stomp that bug.
Edit: The issue with returning a $False is a bug ($True returns fine) in Receive-RSJob. I'll fix that as well.
I am trying to retrieve list of print queues from PowerShell as shown below.
But I am getting
The calling thread cannot access this object because a different thread owns it.
Is it happeneing because PowerShell not being able to support multiple threads?
Is there a way to get around this problem?
As far as I understand, you have to start PowerShell with -STA (single thread appartment) parameter to have your code working :
PowerShell -STA
Add-Type -AssemblyName "system.Printing"
$f = #([system.Printing.EnumeratedPrintQueueTypes]::local, [system.Printing.EnumeratedPrintQueueTypes]::shared)
$ps = New-Object system.Printing.localprintserver
$pq = $ps.GetPrintQueues($f)
I am trying to retrieve list of print queues from PowerShell as shown below.
But I am getting
The calling thread cannot access this object because a different thread owns it.
Is it happeneing because PowerShell not being able to support multiple threads?
Is there a way to get around this problem?
As far as I understand, you have to start PowerShell with -STA (single thread appartment) parameter to have your code working :
PowerShell -STA
Add-Type -AssemblyName "system.Printing"
$f = #([system.Printing.EnumeratedPrintQueueTypes]::local, [system.Printing.EnumeratedPrintQueueTypes]::shared)
$ps = New-Object system.Printing.localprintserver
$pq = $ps.GetPrintQueues($f)