I have a script to start VMs/Stop them at a specific time. The issue is, I want the script to only exit when all the VMs are started/running or when they are deallocated/stopped. My current problem is when a VM is starting, it waits until it has started before moving to the next one.
eg: vm1 is starting..
vm2 is starting..
vm3 is starting..
vm1 is now running..
vm2 is still starting
vm3 is still starting
vm1 is now running..
vm2 is now running..
vm3 is now running..
Then script exits.
Full script here
$ACTION="start"
Write-Output "Number of Virtual Machines: $($GetVMS.Name.Count)" `n
$GetVMS | Format-Table
$startstopvm = {
$ResourceGroupName = $args[0]
$Name = $args[1]
$ACTION = $args[2]
# Get VM status
try {
$VMs = Get-AzureRmVM -Name $Name -ResourceGroupName $ResourceGroupName -Status -WarningAction SilentlyContinue
} catch {
Write-Output ("Cloud not get vm $Name in $ResourceGroupName")
$Error[0]
Exit 1
}
if ($ACTION -eq "start")
{
foreach ($VM in $VMs)
{
if ($VM.Statuses[1].Code -eq "PowerState/running")
{
Write-Output ($VM.Name + " in " + $VM.ResourceGroupName + " is already running")
}
else
{
# The VM needs to be started
Write-Output ("Starting VM " + $VM.Name)
$startVM += Start-AzureRmVM -Name $VM.Name -ResourceGroupName $VM.ResourceGroupName -AsJob -ErrorAction Continue
$startTime = Get-Date
$timeElapsed = $((Get-Date) - $startTime).TotalMinutes
while ($timeElapsed -lt 2)
{
$startVM = Get-AzureRmVM -Name $VM.Name -ResourceGroupName $VM.ResourceGroupName -Status -WarningAction SilentlyContinue
if ($startVM.Statuses[1].Code -match "PowerState/(running|starting)")
{
# The VM started, so send notice
Write-Output ($VM.Name + " in " + $VM.ResourceGroupName + " has been started`n")
break
}
Start-Sleep -s 30
}
if ($getStat.Statuses[1].Code -ne "PowerState/(running|starting)")
{
# The VM failed to start, so send notice
Write-Output ($VM.Name + " failed to start`n")
}
}
}
}
NOTE: I am reading the VM name & RG from a file
try {
$VMList = Get-Content C:\Users\local\Desktop\VMs.csv | ConvertFrom-Csv
} catch {
Write-Output ("Cannot open file...")
exit -1
}
$Result = #()
foreach ($vm in $VMList) {
Invoke-Command -ArgumentList $vm.ResourceGroupName, $vm.Name, $ACTION -Verbose -ScriptBlock $startstopvm
}
The simplest way I have done this in the past is to use the -Wait parameter on a Start-Process cmdlet.
You should be able to implement this with small changes.
Saving your current code block into a seperate .ps1 i.e. startstopvm.ps1
You could then change the Invoke-Commmand line to read something like:
Invoke-Command -ArgumentList $vm.ResourceGroupName, $vm.Name, $ACTION -Verbose -ScriptBlock {Start-Process powershell.exe -Argument "C:\Scripts\Backup.ps1 TestBackup" -Wait}
Definitely some other ways to do it but an approach like this has always worked for me
Related
Context : I am automating starting of 3 Azure VM through a PowerShell script, and each VM is taking 4-5min to start. I want to run the start command parallel and after 5-6min verify them whether they are started.
function restartLoadAgents ($AzuresecretValue,$AzureApplicationID,$AzureObjectID,$AzureDirectoryID,$AzureUserName,$AzureSubscriptionID) {
$password = ConvertTo-SecureString $AzuresecretValue -AsPlainText -Force;
$LoadAgentResourceGroup = "test-performance-rg01";
#Connecting to the Azure VM using the Service Principle
$pscredential = New-Object -TypeName System.Management.Automation.PSCredential($AzureApplicationID, $password);
Connect-AzAccount -ServicePrincipal -Tenant $AzureDirectoryID -Credential $pscredential | Out-null;
#List all the Load Agents
$VMList = Get-AzVm -ResourceGroupName $LoadAgentResourceGroup -Status;
ForEach($VM in $VMList) {
#Skipping the Master Machine and DB machine
if ($VM.Name -eq "test-load-appmachine01" -or $VM.Name -eq "test-load-appmachine02") {
continue;
}
$VMLoadAgentStatus = (Get-AzVm -ResourceGroupName $LoadAgentResourceGroup -Name $VM.Name -status).Statuses
$CurrentLoadAgentRunningStatus = $VMLoadAgentStatus[1].DisplayStatus;
if($CurrentLoadAgentRunningStatus -match "deallocated" -or $CurrentLoadAgentRunningStatus -match "VM deallocated"){
Start-AzVM -ResourceGroupName $LoadAgentResourceGroup -Name $VM.Name | Out-null
commandVerifier;
checkVMStatus($VM.Name);
}
else {
Write-Host $VM.Name " Current State is "$CurrentLoadAgentRunningStatus;
}
}
}
function commandVerifier() {
if ($?){
Write-Host "Successfully Started "$VM.Name;
}
else {
Write-Host "Start Unsuccessful "$VM.Name;
}
}
function checkVMStatus($VM_NAME) {
$VMLoadAgentStatus = (Get-AzVm -ResourceGroupName $LoadAgentResourceGroup -Name $$VM_NAME -status).Statuses
$VMRunningStatusAfterTriggered = $VMLoadAgentStatus[1].DisplayStatus;
if($VMRunningStatusAfterTriggered -eq "running" -or $VMRunningStatusAfterTriggered -eq "VM running"){
Write-Host "Successfully Started VM"
}
else{
Write-Host "Something went with starting VM and current status is"$VMRunningStatusAfterTriggered
}
}
function getServicePrincipleDetails () {
$AzuresecretValue = "<secretValue>";
$AzureApplicationID = "<app_id>";
$AzureObjectID = "<obj_id>";
$AzureDirectoryID = "<dir_id>";
$AzureUserName = "SVCUSER";
$AzureSubscriptionID = "<sub_id>";
restartLoadAgents $AzuresecretValue $AzureApplicationID $AzureObjectID $AzureDirectoryID $AzureUserName $AzureSubscriptionID
}
getServicePrincipleDetails
There are 5 VM in total and first two need not to be stopped or started. test-load-appmachine03,test-load-appmachine04 & test-load-appmachine05 are the target VM and I want to start them parallelly and check after.
Am using this script, but its gathering all the vms and stopping it one by one even when the VM is already in stopped state
$vm = Get-Azvm
foreach($vms in $vm)
{
$resource = Get-Azvm | where {$_.Statuses -eq "Running"}
if($resource -ne $null)
{
Write-Output "Stopping virtual machine..." + $vms
Stop-AzVM -ResourceGroupName $resource.ResourceGroupName -Name $vms -Force
}
else
{
Write-output "Virtual machine not found:" + $vms
}
}
Based on the above shared requirement , we have modified the PowerShell script to check the virtual machines status ( whether it is running or not ), if virtual Machine is running you need to stop it using stop-Azvm cmdlet.
Checked the below script(while testing we passed resource group flag to the Get-Azvm ) in our local environment which is working fine.
$vm = Get-Azvm -Status
foreach($vms in $vm)
{
$statuscheck = Get-AzVM -ResourceGroupName $vms.ResourceGroupName -Name $vms.Name -Status
if($statuscheck.Statuses.DisplayStatus[1] -eq "VM running")
{
Write-Output "Stopping virtual machine...$($vms.Name)"
Stop-AzVM -ResourceGroupName $vms.ResourceGroupName -Name $vms.Name -Force
}
else
{
Write-output "Virtual machine $($vms.Name) is already in stopped state"
}
}
Here is the sample output for reference:
I have a Powershell script that checks on a set of VM status before starting them. If the VM'S are in deallocating mode there should be a sleep and retry on 30 seconds. The code does not do a a retry. The code does a vm start in batches on 2 for vm's with wilcards as mentioned below in an order.
Need help if possible
$ResName= "resvmtest"
$action="start"
if($action -eq "start"){
$vnames=#('*dom*','*DBs*','*')
foreach($vname in $vnames) {
Write-Host "Starting VM with "$vname
$vmList = Get-AzVM -ResourceGroupName $ResName -Name $vname -Status | Select-Object Name, PowerState, ResourceGroupName
do{
$batch = #{
Skip = 0
First = 2
}
do{
foreach($vm in ($vmList | Select-Object #batch)){
$Stoploop = $false
[int]$Retrycount = "0"
do {
try {
if($vm.PowerState -eq "VM Deallocated"){
Write-Host "Job completed"
$Stoploop = $true
}
}
catch {
if ($vm.PowerState -eq "VM Deallocatting") {
Write-Host "VM Still not Deallocated"
Start-Sleep -Seconds 10
$Retrycount = $Retrycount + 1
}
}
}
While ($Stoploop -eq $false)
$params = #($vm.Name, $vm.ResourceGroupName,$vm.PowerState)
$job = Start-Job -ScriptBlock {
param($ComputerName,$serviceName,$statuses)
Start-AzVM -Name $ComputerName -ResourceGroupName $serviceName
} -ArgumentList $params
}
Wait-Job -Job $job
Get-Job | Receive-Job
$batch.Skip += 2
}
until($batch.skip -ge $vmList.count)
}
while($job.state -ne "Completed")
}
}
If you just want to wait for a VM's PowerState turns to VM Deallocated from VM Deallocatting, try the code below:
$vms = Get-AzVm -Status| Select-Object Name, PowerState,ResourceGroupName
#pick certain vm for demo here.
$vm = $vms[1]
$retryCount = 0
while($vm.PowerState -eq 'VM deallocating'){
Write-Host "waiting for VM deallocated..."
Start-Sleep -Seconds 5
$retryCount +=1
Write-Host "check count:$retryCount"
#get latest vm status
$vm.PowerState = (Get-AzVM -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -Status).Statuses[1].DisplayStatus
Write-Host "vm current state:"$vm.PowerState
}
Write-Host "vm new state:" + $vm.PowerState
Result:
Need your help to find a runbook/automation script through which I could start/stop the VM's in Azure at a specific schedule & then in case we have to delay the shutdown schedule for a particular VM, it allows us to do so. Ideally, it should notify the end user, VM is going to shutdown in 30 min or so & gives option to delay the shutdown if need be.
Is there any existing runbook available in runbook gallary within automation account? Can anyone please advise or confirm?
You can simply do this by creating power shell runbook . This is for Starting VM for working hours. you can attached this to schedule inside workbook as required.
param (
[Parameter(Mandatory=$false)]
[String] $AzureConnectionAssetName = 'AzureRunAsConnection',
[Parameter(Mandatory=$false)]
[String] $ResourceGroupName = 'Your-VM-RG'
)
#Check for Weekends
$dayOfWeek = (Get-Date).DayOfWeek
if($dayOfWeek -eq 'Saturday' -or $dayOfWeek -eq 'Sunday'){
exit
}
# Get the connection
$Conn = Get-AutomationConnection -Name $AzureConnectionAssetName -ErrorAction Stop
$null = Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $Conn.TenantId `
-ApplicationId $Conn.ApplicationId `
-CertificateThumbprint $Conn.CertificateThumbprint `
-ErrorAction Stop `
-ErrorVariable err
if($err) {
throw $err
}
# Vet all VMs in the resource group
$VMs = Get-AzureRmVM -ResourceGroupName $ResourceGroupName
# Start each of the VMs
foreach ($VM in $VMs)
{
$StartRtn = $VM | Start-AzureRmVM -ErrorAction Continue
if ($StartRtn.IsSuccessStatusCode -ne $True)
{
# The VM failed to start, so send notice
Write-Output ($VM.Name + " failed to start")
Write-Error ($VM.Name + " failed to start. Error was:") -ErrorAction Continue
Write-Error (ConvertTo-Json $StartRtn) -ErrorAction Continue
}
else
{
# The VM stopped, so send notice
Write-Output ($VM.Name + " has been started")
}
}
I am trying to use Azure powershell to get VM name (Eg: demovm01, where the VM name is demovm and the suffix is 01.
I want to get the output and automatically append a new suffix of 02 if 01 already exists.
Sample script to get vm name:
$getvm = Get-AzVM -Name "$vmname" -ResourceGroupName "eodemofunction" -ErrorVariable notPresent -ErrorAction SilentlyContinue
if ($notPresent) {
Write-Output "VM not found. Creating now"
}
else {
Write-Output "VM exists."
return $true
}
I want to be able to inject this new vm name to an arm deploy to deploy
This should do it. Will increment until no VM is found and use a simple -replace to inject to your json file. Will also return all thr VM values that are already present in Azure
$i=1
$vmname_base = "vmserver"
$VMexists = #()
do {
#invert int value to double digit string
$int = $i.tostring('00')
$getvm = Get-AzVM -Name "$vmname_base$int" -ResourceGroupName "eodemofunction" -ErrorVariable notPresent -ErrorAction SilentlyContinue
if ($notPresent) {
Write-Output "VM not found. Creating now"
Write-Output "VM created name is $vmname_base$int"
#Set condition to end do while loop
VMcreated = "true"
#commands to inject to json here. I always find replace method the easiest
$JSON = Get-Content azuredeploy.parameters.json
$JSON = $JSON -replace ("Servername","$vmname_base$int")
$JSON | Out-File azuredeploy.parameters.json -Force
}
else {
Write-Output "VMexists."
# Add existing VM to array
$VMexists += "$vmname_base$int"
# Increment version, ie. 01 to 02 to 03 etc
$i++
}
} while ($VMcreated -ne "true")
return $VMexists
Your command could be like below, the $newvmname is that you want.
$vmname = "demovm01"
$getvm = Get-AzVM -Name "$vmname" -ResourceGroupName "<group name>" -ErrorVariable notPresent -ErrorAction SilentlyContinue
if ($notPresent) {
Write-Output "VM not found. Creating now"
}
else {
Write-Output "VM exists."
if($getvm.Name -like '*01'){
$newvmname = $vmname.TrimEnd('01')+"02"
}
Write-Output $newvmname
return $true
}