I want to autoshutdown/startup virtual machines in azure. I have found this runbook but I have these last few errors that I cannot sort out. Any help would be greatly appreciated.<\/p>\n
Here is the code I am running:<\/p>\n
[CmdletBinding()]\nparam(\n [parameter(Mandatory=$false)]\n [bool]$Simulate = $false,\n [parameter(Mandatory=$false)]\n [string]$DefaultScheduleIfNotPresent,\n [parameter(Mandatory=$false)]\n [String] $Timezone = \"Eastern Standard Time\"\n)\n\n$VERSION = '3.3.0'\n$autoShutdownTagName = 'AutoShutdownSchedule'\n$autoShutdownOrderTagName = 'ProcessingOrder'\n$autoShutdownDisabledTagName = 'AutoShutdownDisabled'\n$defaultOrder = 1000\n\n$ResourceProcessors = @(\n @{\n ResourceType = 'Microsoft.ClassicCompute/virtualMachines'\n PowerStateAction = { param([object]$Resource, [string]$DesiredState) (Get-AzResource -ResourceId $Resource.ResourceId).Properties.InstanceView.PowerState }\n StartAction = { param([string]$ResourceId) Invoke-AzResourceAction -ResourceId $ResourceId -Action 'start' -Force } \n DeallocateAction = { param([string]$ResourceId) Invoke-AzResourceAction -ResourceId $ResourceId -Action 'shutdown' -Force } \n },\n @{\n ResourceType = 'Microsoft.Compute/virtualMachines'\n PowerStateAction = { \n param([object]$Resource, [string]$DesiredState)\n \n $vm = Get-AzVM -ResourceGroupName $Resource.ResourceGroupName -Name $Resource.Name -Status\n $currentStatus = $vm.Statuses | Where-Object Code -like 'PowerState*' \n $currentStatus.Code -replace 'PowerState/',''\n }\n StartAction = { param([string]$ResourceId) Invoke-AzResourceAction -ResourceId $ResourceId -Action 'start' -Force } \n DeallocateAction = { param([string]$ResourceId) Invoke-AzResourceAction -ResourceId $ResourceId -Action 'deallocate' -Force } \n },\n @{\n ResourceType = 'Microsoft.Compute/virtualMachineScaleSets'\n #since there is no way to get the status of a VMSS, we assume it is in the inverse state to force the action on the whole VMSS\n PowerStateAction = { param([object]$Resource, [string]$DesiredState) if($DesiredState -eq 'StoppedDeallocated') { 'Started' } else { 'StoppedDeallocated' } }\n StartAction = { param([string]$ResourceId) Invoke-AzResourceAction -ResourceId $ResourceId -Action 'start' -Parameters @{ instanceIds = @('*') } -Force } \n DeallocateAction = { param([string]$ResourceId) Invoke-AzResourceAction -ResourceId $ResourceId -Action 'deallocate' -Parameters @{ instanceIds = @('*') } -Force } \n }\n)\n\n# Define function to get current date using the TimeZone Paremeter\nfunction GetCurrentDate\n{\n return [system.timezoneinfo]::ConvertTime($(Get-Date),$([system.timezoneinfo]::GetSystemTimeZones() | ? id -eq $Timezone))\n}\n\n# Define function to check current time against specified range\nfunction Test-ScheduleEntry ([string]$TimeRange)\n{ \n # Initialize variables\n $rangeStart, $rangeEnd, $parsedDay = $null\n $currentTime = GetCurrentDate\n $midnight = $currentTime.AddDays(1).Date \n\n try\n {\n # Parse as range if contains '->'\n if($TimeRange -like '*->*')\n {\n $timeRangeComponents = $TimeRange -split '->' | foreach {$_.Trim()}\n if($timeRangeComponents.Count -eq 2)\n {\n $rangeStart = Get-Date $timeRangeComponents[0]\n $rangeEnd = Get-Date $timeRangeComponents[1]\n \n # Check for crossing midnight\n if($rangeStart -gt $rangeEnd)\n {\n # If current time is between the start of range and midnight tonight, interpret start time as earlier today and end time as tomorrow\n if($currentTime -ge $rangeStart -and $currentTime -lt $midnight)\n {\n $rangeEnd = $rangeEnd.AddDays(1)\n }\n # Otherwise interpret start time as yesterday and end time as today \n else\n {\n $rangeStart = $rangeStart.AddDays(-1)\n }\n }\n }\n else\n {\n Write-Output \"`tWARNING: Invalid time range format. Expects valid .Net DateTime-formatted start time and end time separated by '->'\" \n }\n }\n # Otherwise attempt to parse as a full day entry, e.g. 'Monday' or 'December 25' \n else\n {\n # If specified as day of week, check if today\n if([System.DayOfWeek].GetEnumValues() -contains $TimeRange)\n {\n if($TimeRange -eq (Get-Date).DayOfWeek)\n {\n $parsedDay = Get-Date '00:00'\n }\n else\n {\n # Skip detected day of week that isn't today\n }\n }\n # Otherwise attempt to parse as a date, e.g. 'December 25'\n else\n {\n $parsedDay = Get-Date $TimeRange\n }\n \n if($parsedDay -ne $null)\n {\n $rangeStart = $parsedDay # Defaults to midnight\n $rangeEnd = $parsedDay.AddHours(23).AddMinutes(59).AddSeconds(59) # End of the same day\n }\n }\n }\n catch\n {\n # Record any errors and return false by default\n Write-Output \"`tWARNING: Exception encountered while parsing time range. Details: $($_.Exception.Message). Check the syntax of entry, e.g. '<StartTime> -> <EndTime>', or days/dates like 'Sunday' and 'December 25'\"\n return $false\n }\n \n # Check if current time falls within range\n if($currentTime -ge $rangeStart -and $currentTime -le $rangeEnd)\n {\n return $true\n }\n else\n {\n return $false\n }\n \n} # End function Test-ScheduleEntry\n\n# Function to handle power state assertion for resources\nfunction Assert-ResourcePowerState\n{\n param(\n [Parameter(Mandatory=$true)]\n [object]$Resource,\n [Parameter(Mandatory=$true)]\n [string]$DesiredState,\n [bool]$Simulate\n )\n\n $processor = $ResourceProcessors | Where-Object ResourceType -eq $Resource.ResourceType\n if(-not $processor) {\n throw ('Unable to find a resource processor for type ''{0}''. Resource: {1}' -f $Resource.ResourceType, ($Resource | ConvertTo-Json -Depth 5000))\n }\n # If should be started and isn't, start resource\n $currentPowerState = & $processor.PowerStateAction -Resource $Resource -DesiredState $DesiredState\n if($DesiredState -eq 'Started' -and $currentPowerState -notmatch 'Started|Starting|running')\n {\n if($Simulate)\n {\n Write-Output \"`tSIMULATION -- Would have started resource. (No action taken)\"\n }\n else\n {\n Write-Output \"`tStarting resource\"\n & $processor.StartAction -ResourceId $Resource.ResourceId\n }\n }\n \n # If should be stopped and isn't, stop resource\n elseif($DesiredState -eq 'StoppedDeallocated' -and $currentPowerState -notmatch 'Stopped|deallocated')\n {\n if($Simulate)\n {\n Write-Output \"`tSIMULATION -- Would have stopped resource. (No action taken)\"\n }\n else\n {\n Write-Output \"`tStopping resource\"\n & $processor.DeallocateAction -ResourceId $Resource.ResourceId\n }\n }\n\n # Otherwise, current power state is correct\n else\n {\n Write-Output \"`tCurrent power state [$($currentPowerState)] is correct.\"\n }\n}\n\n# Main runbook content\ntry\n{\n $currentTime = GetCurrentDate\n Write-Output \"Runbook started. Version: $VERSION\"\n if($Simulate)\n {\n Write-Output '*** Running in SIMULATE mode. No power actions will be taken. ***'\n }\n else\n {\n Write-Output '*** Running in LIVE mode. Schedules will be enforced. ***'\n }\n Write-Output \"Current UTC/GMT time [$($currentTime.ToString('dddd, yyyy MMM dd HH:mm:ss'))] will be checked against schedules\"\n \n \n $Conn = Get-AutomationConnection -Name AzureRunAsConnection\n $resourceManagerContext = Connect-AzAccount -ServicePrincipal -Tenant $Conn.TenantID -ApplicationId $Conn.ApplicationID -CertificateThumbprint $Conn.CertificateThumbprint -Environment AzureUSGovernment\n \n $resourceList = @()\n # Get a list of all supported resources in subscription\n $ResourceProcessors | % {\n Write-Output ('Looking for resources of type {0}' -f $_.ResourceType)\n $resourceList += @(Get-AzResource -ResourceType $_.ResourceType)\n }\n\n $ResourceList | % { \n if($_.Tags -and $_.Tags.ContainsKey($autoShutdownOrderTagName) ) {\n $order = $_.Tags | % { if($_.ContainsKey($autoShutdownOrderTagName)) { $_.Item($autoShutdownOrderTagName) } }\n } else {\n $order = $defaultOrder\n }\n Add-Member -InputObject $_ -Name ProcessingOrder -MemberType NoteProperty -TypeName Integer -Value $order\n }\n\n $ResourceList | % { \n if($_.Tags -and $_.Tags.ContainsKey($autoShutdownDisabledTagName) ) {\n $disabled = $_.Tags | % { if($_.ContainsKey($autoShutdownDisabledTagName)) { $_.Item($autoShutdownDisabledTagName) } }\n } else {\n $disabled = '0'\n }\n Add-Member -InputObject $_ -Name ScheduleDisabled -MemberType NoteProperty -TypeName String -Value $disabled\n }\n\n # Get resource groups that are tagged for automatic shutdown of resources\n $taggedResourceGroups = Get-AzResourceGroup -Tag @{ \"AutoShutdownSchedule\" = $null }\n $taggedResourceGroupNames = @($taggedResourceGroups | select Name)\n \n Write-Output \"Found [$($taggedResourceGroupNames.Count)] schedule-tagged resource groups in subscription\" \n \n if($DefaultScheduleIfNotPresent) {\n Write-Output \"Default schedule was specified, all non tagged resources will inherit this schedule: $DefaultScheduleIfNotPresent\"\n }\n\n # For each resource, determine\n # - Is it directly tagged for shutdown or member of a tagged resource group\n # - Is the current time within the tagged schedule \n # Then assert its correct power state based on the assigned schedule (if present)\n Write-Output \"Processing [$($resourceList.Count)] resources found in subscription\"\n foreach($resource in $resourceList)\n {\n $schedule = $null\n\n if ($resource.ScheduleDisabled)\n {\n $disabledValue = $resource.ScheduleDisabled\n if ($disabledValue -eq \"1\" -or $disabledValue -eq \"Yes\"-or $disabledValue -eq \"True\")\n {\n Write-Output \"[$($resource.Name)]: `r`n`tIGNORED -- Found direct resource schedule with $autoShutdownDisabledTagName value: $disabledValue.\"\n continue\n }\n }\n\n # Check for direct tag or group-inherited tag\n if($resource.Tags.Count -gt 0 -and $resource.Tags.ContainsKey($autoShutdownTagName) -eq $true)\n {\n # Resource has direct tag (possible for resource manager deployment model resources). Prefer this tag schedule.\n $schedule = $resource.Tags.Item($autoShutdownTagName)\n Write-Output \"[$($resource.Name)]: `r`n`tADDING -- Found direct resource schedule tag with value: $schedule\"\n }\n elseif($taggedResourceGroupNames -contains $resource.ResourceGroupName)\n {\n # resource belongs to a tagged resource group. Use the group tag\n $parentGroup = $resourceGroups | Where-Object Name -eq $resource.ResourceGroupName\n $schedule = $parentGroup.Tags.Item($AUTOSHUTDOWNSCHEDULE_KEYWORD)\n Write-Output \"[$($resource.Name)]: `r`n`tADDING -- Found parent resource group schedule tag with value: $schedule\"\n }\n elseif($DefaultScheduleIfNotPresent)\n {\n $schedule = $DefaultScheduleIfNotPresent\n Write-Output \"[$($resource.Name)]: `r`n`tADDING -- Using default schedule: $schedule\"\n }\n else\n {\n # No direct or inherited tag. Skip this resource.\n Write-Output \"[$($resource.Name)]: `r`n`tIGNORED -- Not tagged for shutdown directly or via membership in a tagged resource group. Skipping this resource.\"\n continue\n }\n\n # Check that tag value was succesfully obtained\n if($schedule -eq $null)\n {\n Write-Output \"[$($resource.Name) `- $($resource.ProcessingOrder)]: `r`n`tIGNORED -- Failed to get tagged schedule for resource. Skipping this resource.\"\n continue\n }\n\n # Parse the ranges in the Tag value. Expects a string of comma-separated time ranges, or a single time range\n $timeRangeList = @($schedule -split ',' | foreach {$_.Trim()})\n \n # Check each range against the current time to see if any schedule is matched\n $scheduleMatched = $false\n $matchedSchedule = $null\n $neverStart = $false #if NeverStart is specified in range, do not wake-up machine\n foreach($entry in $timeRangeList)\n {\n if((Test-ScheduleEntry -TimeRange $entry) -eq $true)\n {\n $scheduleMatched = $true\n $matchedSchedule = $entry\n break\n }\n \n if ($entry -eq \"NeverStart\")\n {\n $neverStart = $true\n }\n }\n Add-Member -InputObject $resource -Name ScheduleMatched -MemberType NoteProperty -TypeName Boolean -Value $scheduleMatched\n Add-Member -InputObject $resource -Name MatchedSchedule -MemberType NoteProperty -TypeName Boolean -Value $matchedSchedule\n Add-Member -InputObject $resource -Name NeverStart -MemberType NoteProperty -TypeName Boolean -Value $neverStart\n }\n \n foreach($resource in $resourceList | Group-Object ScheduleMatched) {\n if($resource.Name -eq '') {continue}\n $sortedResourceList = @()\n if($resource.Name -eq $false) {\n # meaning we start resources, lower to higher\n $sortedResourceList += @($resource.Group | Sort ProcessingOrder)\n } else { \n $sortedResourceList += @($resource.Group | Sort ProcessingOrder -Descending)\n }\n\n foreach($resource in $sortedResourceList)\n { \n # Enforce desired state for group resources based on result. \n if($resource.ScheduleMatched)\n {\n # Schedule is matched. Shut down the resource if it is running. \n Write-Output \"[$($resource.Name) `- P$($resource.ProcessingOrder)]: `r`n`tASSERT -- Current time [$currentTime] falls within the scheduled shutdown range [$($resource.MatchedSchedule)]\"\n Add-Member -InputObject $resource -Name DesiredState -MemberType NoteProperty -TypeName String -Value 'StoppedDeallocated'\n\n }\n else\n {\n if ($resource.NeverStart)\n {\n Write-Output \"[$($resource.Name)]: `tIGNORED -- Resource marked with NeverStart. Keeping the resources stopped.\"\n Add-Member -InputObject $resource -Name DesiredState -MemberType NoteProperty -TypeName String -Value 'StoppedDeallocated'\n }\n else\n {\n # Schedule not matched. Start resource if stopped.\n Write-Output \"[$($resource.Name) `- P$($resource.ProcessingOrder)]: `r`n`tASSERT -- Current time falls outside of all scheduled shutdown ranges. Start resource.\"\n Add-Member -InputObject $resource -Name DesiredState -MemberType NoteProperty -TypeName Boolean -Value 'Started'\n } \n } \n Assert-ResourcePowerState -Resource $resource -DesiredState $resource.DesiredState -Simulate $Simulate\n }\n }\n\n Write-Output 'Finished processing resource schedules'\n}\ncatch\n{\n $errorMessage = $_.Exception.Message\n throw \"Unexpected exception: $errorMessage\"\n}\nfinally\n{\n Write-Output \"Runbook finished (Duration: $(('{0:hh\\:mm\\:ss}' -f ((GetCurrentDate) - $currentTime))))\"\n}\n<\/code><\/pre>\nHere is the error that I am receiving:\n\n<\/code><\/pre>\nRunbook started. Version: 3.3.0\n*** Running in LIVE mode. Schedules will be enforced. ***\nCurrent UTC/GMT time [Friday, 2019 Jul 12 12:03:29] will be checked against schedules\nLooking for resources of type Microsoft.ClassicCompute/virtualMachines\nGet-AzResource : 'this.Client.SubscriptionId' cannot be null.\nAt line:265 char:28\n+ ... $resourceList += @(Get-AzResource -ResourceType $_.ResourceType)\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n + CategoryInfo : CloseError: (:) [Get-AzResource], ValidationException\n + FullyQualifiedErrorId : Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.GetAzureResourceCmdlet\n \nLooking for resources of type Microsoft.Compute/virtualMachines\nGet-AzResource : 'this.Client.SubscriptionId' cannot be null.\nAt line:265 char:28\n+ ... $resourceList += @(Get-AzResource -ResourceType $_.ResourceType)\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n + CategoryInfo : CloseError: (:) [Get-AzResource], ValidationException\n + FullyQualifiedErrorId : Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.GetAzureResourceCmdlet\n \nLooking for resources of type Microsoft.Compute/virtualMachineScaleSets\nGet-AzResource : 'this.Client.SubscriptionId' cannot be null.\nAt line:265 char:28\n+ ... $resourceList += @(Get-AzResource -ResourceType $_.ResourceType)\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n + CategoryInfo : CloseError: (:) [Get-AzResource], ValidationException\n + FullyQualifiedErrorId : Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.GetAzureResourceCmdlet\n \nGet-AzResourceGroup : 'this.Client.SubscriptionId' cannot be null.\nAt line:287 char:29\n+ ... rceGroups = Get-AzResourceGroup -Tag @{ \"AutoShutdownSchedule\" = $nul ...\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n + CategoryInfo : CloseError: (:) [Get-AzResourceGroup], ValidationException\n + FullyQualifiedErrorId : \nMicrosoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.GetAzureResourceGroupCmdlet\n \nFound [0] schedule-tagged resource groups in subscription\nProcessing [0] resources found in subscription\nFinished processing resource schedules\nRunbook finished (Duration: 00:00:10)\n\n<\/code><\/pre>","upvoteCount":8,"answerCount":9,"datePublished":"2019-07-12T15:18:16.000Z","author":{"@type":"Person","name":"ralphaeljohnson","url":"https://community.spiceworks.com/u/ralphaeljohnson"},"suggestedAnswer":[{"@type":"Answer","text":"
Advertisement
I want to autoshutdown/startup virtual machines in azure. I have found this runbook but I have these last few errors that I cannot sort out. Any help would be greatly appreciated.<\/p>\n
Here is the code I am running:<\/p>\n
[CmdletBinding()]\nparam(\n [parameter(Mandatory=$false)]\n [bool]$Simulate = $false,\n [parameter(Mandatory=$false)]\n [string]$DefaultScheduleIfNotPresent,\n [parameter(Mandatory=$false)]\n [String] $Timezone = \"Eastern Standard Time\"\n)\n\n$VERSION = '3.3.0'\n$autoShutdownTagName = 'AutoShutdownSchedule'\n$autoShutdownOrderTagName = 'ProcessingOrder'\n$autoShutdownDisabledTagName = 'AutoShutdownDisabled'\n$defaultOrder = 1000\n\n$ResourceProcessors = @(\n @{\n ResourceType = 'Microsoft.ClassicCompute/virtualMachines'\n PowerStateAction = { param([object]$Resource, [string]$DesiredState) (Get-AzResource -ResourceId $Resource.ResourceId).Properties.InstanceView.PowerState }\n StartAction = { param([string]$ResourceId) Invoke-AzResourceAction -ResourceId $ResourceId -Action 'start' -Force } \n DeallocateAction = { param([string]$ResourceId) Invoke-AzResourceAction -ResourceId $ResourceId -Action 'shutdown' -Force } \n },\n @{\n ResourceType = 'Microsoft.Compute/virtualMachines'\n PowerStateAction = { \n param([object]$Resource, [string]$DesiredState)\n \n $vm = Get-AzVM -ResourceGroupName $Resource.ResourceGroupName -Name $Resource.Name -Status\n $currentStatus = $vm.Statuses | Where-Object Code -like 'PowerState*' \n $currentStatus.Code -replace 'PowerState/',''\n }\n StartAction = { param([string]$ResourceId) Invoke-AzResourceAction -ResourceId $ResourceId -Action 'start' -Force } \n DeallocateAction = { param([string]$ResourceId) Invoke-AzResourceAction -ResourceId $ResourceId -Action 'deallocate' -Force } \n },\n @{\n ResourceType = 'Microsoft.Compute/virtualMachineScaleSets'\n #since there is no way to get the status of a VMSS, we assume it is in the inverse state to force the action on the whole VMSS\n PowerStateAction = { param([object]$Resource, [string]$DesiredState) if($DesiredState -eq 'StoppedDeallocated') { 'Started' } else { 'StoppedDeallocated' } }\n StartAction = { param([string]$ResourceId) Invoke-AzResourceAction -ResourceId $ResourceId -Action 'start' -Parameters @{ instanceIds = @('*') } -Force } \n DeallocateAction = { param([string]$ResourceId) Invoke-AzResourceAction -ResourceId $ResourceId -Action 'deallocate' -Parameters @{ instanceIds = @('*') } -Force } \n }\n)\n\n# Define function to get current date using the TimeZone Paremeter\nfunction GetCurrentDate\n{\n return [system.timezoneinfo]::ConvertTime($(Get-Date),$([system.timezoneinfo]::GetSystemTimeZones() | ? id -eq $Timezone))\n}\n\n# Define function to check current time against specified range\nfunction Test-ScheduleEntry ([string]$TimeRange)\n{ \n # Initialize variables\n $rangeStart, $rangeEnd, $parsedDay = $null\n $currentTime = GetCurrentDate\n $midnight = $currentTime.AddDays(1).Date \n\n try\n {\n # Parse as range if contains '->'\n if($TimeRange -like '*->*')\n {\n $timeRangeComponents = $TimeRange -split '->' | foreach {$_.Trim()}\n if($timeRangeComponents.Count -eq 2)\n {\n $rangeStart = Get-Date $timeRangeComponents[0]\n $rangeEnd = Get-Date $timeRangeComponents[1]\n \n # Check for crossing midnight\n if($rangeStart -gt $rangeEnd)\n {\n # If current time is between the start of range and midnight tonight, interpret start time as earlier today and end time as tomorrow\n if($currentTime -ge $rangeStart -and $currentTime -lt $midnight)\n {\n $rangeEnd = $rangeEnd.AddDays(1)\n }\n # Otherwise interpret start time as yesterday and end time as today \n else\n {\n $rangeStart = $rangeStart.AddDays(-1)\n }\n }\n }\n else\n {\n Write-Output \"`tWARNING: Invalid time range format. Expects valid .Net DateTime-formatted start time and end time separated by '->'\" \n }\n }\n # Otherwise attempt to parse as a full day entry, e.g. 'Monday' or 'December 25' \n else\n {\n # If specified as day of week, check if today\n if([System.DayOfWeek].GetEnumValues() -contains $TimeRange)\n {\n if($TimeRange -eq (Get-Date).DayOfWeek)\n {\n $parsedDay = Get-Date '00:00'\n }\n else\n {\n # Skip detected day of week that isn't today\n }\n }\n # Otherwise attempt to parse as a date, e.g. 'December 25'\n else\n {\n $parsedDay = Get-Date $TimeRange\n }\n \n if($parsedDay -ne $null)\n {\n $rangeStart = $parsedDay # Defaults to midnight\n $rangeEnd = $parsedDay.AddHours(23).AddMinutes(59).AddSeconds(59) # End of the same day\n }\n }\n }\n catch\n {\n # Record any errors and return false by default\n Write-Output \"`tWARNING: Exception encountered while parsing time range. Details: $($_.Exception.Message). Check the syntax of entry, e.g. '<StartTime> -> <EndTime>', or days/dates like 'Sunday' and 'December 25'\"\n return $false\n }\n \n # Check if current time falls within range\n if($currentTime -ge $rangeStart -and $currentTime -le $rangeEnd)\n {\n return $true\n }\n else\n {\n return $false\n }\n \n} # End function Test-ScheduleEntry\n\n# Function to handle power state assertion for resources\nfunction Assert-ResourcePowerState\n{\n param(\n [Parameter(Mandatory=$true)]\n [object]$Resource,\n [Parameter(Mandatory=$true)]\n [string]$DesiredState,\n [bool]$Simulate\n )\n\n $processor = $ResourceProcessors | Where-Object ResourceType -eq $Resource.ResourceType\n if(-not $processor) {\n throw ('Unable to find a resource processor for type ''{0}''. Resource: {1}' -f $Resource.ResourceType, ($Resource | ConvertTo-Json -Depth 5000))\n }\n # If should be started and isn't, start resource\n $currentPowerState = & $processor.PowerStateAction -Resource $Resource -DesiredState $DesiredState\n if($DesiredState -eq 'Started' -and $currentPowerState -notmatch 'Started|Starting|running')\n {\n if($Simulate)\n {\n Write-Output \"`tSIMULATION -- Would have started resource. (No action taken)\"\n }\n else\n {\n Write-Output \"`tStarting resource\"\n & $processor.StartAction -ResourceId $Resource.ResourceId\n }\n }\n \n # If should be stopped and isn't, stop resource\n elseif($DesiredState -eq 'StoppedDeallocated' -and $currentPowerState -notmatch 'Stopped|deallocated')\n {\n if($Simulate)\n {\n Write-Output \"`tSIMULATION -- Would have stopped resource. (No action taken)\"\n }\n else\n {\n Write-Output \"`tStopping resource\"\n & $processor.DeallocateAction -ResourceId $Resource.ResourceId\n }\n }\n\n # Otherwise, current power state is correct\n else\n {\n Write-Output \"`tCurrent power state [$($currentPowerState)] is correct.\"\n }\n}\n\n# Main runbook content\ntry\n{\n $currentTime = GetCurrentDate\n Write-Output \"Runbook started. Version: $VERSION\"\n if($Simulate)\n {\n Write-Output '*** Running in SIMULATE mode. No power actions will be taken. ***'\n }\n else\n {\n Write-Output '*** Running in LIVE mode. Schedules will be enforced. ***'\n }\n Write-Output \"Current UTC/GMT time [$($currentTime.ToString('dddd, yyyy MMM dd HH:mm:ss'))] will be checked against schedules\"\n \n \n $Conn = Get-AutomationConnection -Name AzureRunAsConnection\n $resourceManagerContext = Connect-AzAccount -ServicePrincipal -Tenant $Conn.TenantID -ApplicationId $Conn.ApplicationID -CertificateThumbprint $Conn.CertificateThumbprint -Environment AzureUSGovernment\n \n $resourceList = @()\n # Get a list of all supported resources in subscription\n $ResourceProcessors | % {\n Write-Output ('Looking for resources of type {0}' -f $_.ResourceType)\n $resourceList += @(Get-AzResource -ResourceType $_.ResourceType)\n }\n\n $ResourceList | % { \n if($_.Tags -and $_.Tags.ContainsKey($autoShutdownOrderTagName) ) {\n $order = $_.Tags | % { if($_.ContainsKey($autoShutdownOrderTagName)) { $_.Item($autoShutdownOrderTagName) } }\n } else {\n $order = $defaultOrder\n }\n Add-Member -InputObject $_ -Name ProcessingOrder -MemberType NoteProperty -TypeName Integer -Value $order\n }\n\n $ResourceList | % { \n if($_.Tags -and $_.Tags.ContainsKey($autoShutdownDisabledTagName) ) {\n $disabled = $_.Tags | % { if($_.ContainsKey($autoShutdownDisabledTagName)) { $_.Item($autoShutdownDisabledTagName) } }\n } else {\n $disabled = '0'\n }\n Add-Member -InputObject $_ -Name ScheduleDisabled -MemberType NoteProperty -TypeName String -Value $disabled\n }\n\n # Get resource groups that are tagged for automatic shutdown of resources\n $taggedResourceGroups = Get-AzResourceGroup -Tag @{ \"AutoShutdownSchedule\" = $null }\n $taggedResourceGroupNames = @($taggedResourceGroups | select Name)\n \n Write-Output \"Found [$($taggedResourceGroupNames.Count)] schedule-tagged resource groups in subscription\" \n \n if($DefaultScheduleIfNotPresent) {\n Write-Output \"Default schedule was specified, all non tagged resources will inherit this schedule: $DefaultScheduleIfNotPresent\"\n }\n\n # For each resource, determine\n # - Is it directly tagged for shutdown or member of a tagged resource group\n # - Is the current time within the tagged schedule \n # Then assert its correct power state based on the assigned schedule (if present)\n Write-Output \"Processing [$($resourceList.Count)] resources found in subscription\"\n foreach($resource in $resourceList)\n {\n $schedule = $null\n\n if ($resource.ScheduleDisabled)\n {\n $disabledValue = $resource.ScheduleDisabled\n if ($disabledValue -eq \"1\" -or $disabledValue -eq \"Yes\"-or $disabledValue -eq \"True\")\n {\n Write-Output \"[$($resource.Name)]: `r`n`tIGNORED -- Found direct resource schedule with $autoShutdownDisabledTagName value: $disabledValue.\"\n continue\n }\n }\n\n # Check for direct tag or group-inherited tag\n if($resource.Tags.Count -gt 0 -and $resource.Tags.ContainsKey($autoShutdownTagName) -eq $true)\n {\n # Resource has direct tag (possible for resource manager deployment model resources). Prefer this tag schedule.\n $schedule = $resource.Tags.Item($autoShutdownTagName)\n Write-Output \"[$($resource.Name)]: `r`n`tADDING -- Found direct resource schedule tag with value: $schedule\"\n }\n elseif($taggedResourceGroupNames -contains $resource.ResourceGroupName)\n {\n # resource belongs to a tagged resource group. Use the group tag\n $parentGroup = $resourceGroups | Where-Object Name -eq $resource.ResourceGroupName\n $schedule = $parentGroup.Tags.Item($AUTOSHUTDOWNSCHEDULE_KEYWORD)\n Write-Output \"[$($resource.Name)]: `r`n`tADDING -- Found parent resource group schedule tag with value: $schedule\"\n }\n elseif($DefaultScheduleIfNotPresent)\n {\n $schedule = $DefaultScheduleIfNotPresent\n Write-Output \"[$($resource.Name)]: `r`n`tADDING -- Using default schedule: $schedule\"\n }\n else\n {\n # No direct or inherited tag. Skip this resource.\n Write-Output \"[$($resource.Name)]: `r`n`tIGNORED -- Not tagged for shutdown directly or via membership in a tagged resource group. Skipping this resource.\"\n continue\n }\n\n # Check that tag value was succesfully obtained\n if($schedule -eq $null)\n {\n Write-Output \"[$($resource.Name) `- $($resource.ProcessingOrder)]: `r`n`tIGNORED -- Failed to get tagged schedule for resource. Skipping this resource.\"\n continue\n }\n\n # Parse the ranges in the Tag value. Expects a string of comma-separated time ranges, or a single time range\n $timeRangeList = @($schedule -split ',' | foreach {$_.Trim()})\n \n # Check each range against the current time to see if any schedule is matched\n $scheduleMatched = $false\n $matchedSchedule = $null\n $neverStart = $false #if NeverStart is specified in range, do not wake-up machine\n foreach($entry in $timeRangeList)\n {\n if((Test-ScheduleEntry -TimeRange $entry) -eq $true)\n {\n $scheduleMatched = $true\n $matchedSchedule = $entry\n break\n }\n \n if ($entry -eq \"NeverStart\")\n {\n $neverStart = $true\n }\n }\n Add-Member -InputObject $resource -Name ScheduleMatched -MemberType NoteProperty -TypeName Boolean -Value $scheduleMatched\n Add-Member -InputObject $resource -Name MatchedSchedule -MemberType NoteProperty -TypeName Boolean -Value $matchedSchedule\n Add-Member -InputObject $resource -Name NeverStart -MemberType NoteProperty -TypeName Boolean -Value $neverStart\n }\n \n foreach($resource in $resourceList | Group-Object ScheduleMatched) {\n if($resource.Name -eq '') {continue}\n $sortedResourceList = @()\n if($resource.Name -eq $false) {\n # meaning we start resources, lower to higher\n $sortedResourceList += @($resource.Group | Sort ProcessingOrder)\n } else { \n $sortedResourceList += @($resource.Group | Sort ProcessingOrder -Descending)\n }\n\n foreach($resource in $sortedResourceList)\n { \n # Enforce desired state for group resources based on result. \n if($resource.ScheduleMatched)\n {\n # Schedule is matched. Shut down the resource if it is running. \n Write-Output \"[$($resource.Name) `- P$($resource.ProcessingOrder)]: `r`n`tASSERT -- Current time [$currentTime] falls within the scheduled shutdown range [$($resource.MatchedSchedule)]\"\n Add-Member -InputObject $resource -Name DesiredState -MemberType NoteProperty -TypeName String -Value 'StoppedDeallocated'\n\n }\n else\n {\n if ($resource.NeverStart)\n {\n Write-Output \"[$($resource.Name)]: `tIGNORED -- Resource marked with NeverStart. Keeping the resources stopped.\"\n Add-Member -InputObject $resource -Name DesiredState -MemberType NoteProperty -TypeName String -Value 'StoppedDeallocated'\n }\n else\n {\n # Schedule not matched. Start resource if stopped.\n Write-Output \"[$($resource.Name) `- P$($resource.ProcessingOrder)]: `r`n`tASSERT -- Current time falls outside of all scheduled shutdown ranges. Start resource.\"\n Add-Member -InputObject $resource -Name DesiredState -MemberType NoteProperty -TypeName Boolean -Value 'Started'\n } \n } \n Assert-ResourcePowerState -Resource $resource -DesiredState $resource.DesiredState -Simulate $Simulate\n }\n }\n\n Write-Output 'Finished processing resource schedules'\n}\ncatch\n{\n $errorMessage = $_.Exception.Message\n throw \"Unexpected exception: $errorMessage\"\n}\nfinally\n{\n Write-Output \"Runbook finished (Duration: $(('{0:hh\\:mm\\:ss}' -f ((GetCurrentDate) - $currentTime))))\"\n}\n<\/code><\/pre>\nHere is the error that I am receiving:\n\n<\/code><\/pre>\nRunbook started. Version: 3.3.0\n*** Running in LIVE mode. Schedules will be enforced. ***\nCurrent UTC/GMT time [Friday, 2019 Jul 12 12:03:29] will be checked against schedules\nLooking for resources of type Microsoft.ClassicCompute/virtualMachines\nGet-AzResource : 'this.Client.SubscriptionId' cannot be null.\nAt line:265 char:28\n+ ... $resourceList += @(Get-AzResource -ResourceType $_.ResourceType)\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n + CategoryInfo : CloseError: (:) [Get-AzResource], ValidationException\n + FullyQualifiedErrorId : Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.GetAzureResourceCmdlet\n \nLooking for resources of type Microsoft.Compute/virtualMachines\nGet-AzResource : 'this.Client.SubscriptionId' cannot be null.\nAt line:265 char:28\n+ ... $resourceList += @(Get-AzResource -ResourceType $_.ResourceType)\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n + CategoryInfo : CloseError: (:) [Get-AzResource], ValidationException\n + FullyQualifiedErrorId : Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.GetAzureResourceCmdlet\n \nLooking for resources of type Microsoft.Compute/virtualMachineScaleSets\nGet-AzResource : 'this.Client.SubscriptionId' cannot be null.\nAt line:265 char:28\n+ ... $resourceList += @(Get-AzResource -ResourceType $_.ResourceType)\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n + CategoryInfo : CloseError: (:) [Get-AzResource], ValidationException\n + FullyQualifiedErrorId : Microsoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.GetAzureResourceCmdlet\n \nGet-AzResourceGroup : 'this.Client.SubscriptionId' cannot be null.\nAt line:287 char:29\n+ ... rceGroups = Get-AzResourceGroup -Tag @{ \"AutoShutdownSchedule\" = $nul ...\n+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n + CategoryInfo : CloseError: (:) [Get-AzResourceGroup], ValidationException\n + FullyQualifiedErrorId : \nMicrosoft.Azure.Commands.ResourceManager.Cmdlets.Implementation.GetAzureResourceGroupCmdlet\n \nFound [0] schedule-tagged resource groups in subscription\nProcessing [0] resources found in subscription\nFinished processing resource schedules\nRunbook finished (Duration: 00:00:10)\n\n<\/code><\/pre>","upvoteCount":8,"datePublished":"2019-07-12T15:18:16.000Z","url":"https://community.spiceworks.com/t/can-someone-help-me-with-this-code/720634/1","author":{"@type":"Person","name":"ralphaeljohnson","url":"https://community.spiceworks.com/u/ralphaeljohnson"}},{"@type":"Answer","text":"If you post code, please use the ‘Insert Code’ button. Please and thank you!<\/p>\n
Also have a read here:<\/p>