Azure Runbook (workflow) output result to Table - powershell

I'm recreating an Azure Runbook to coincide with a Logic App functionality. Long story short, I want the Logic App to initiate the Runbook, grab the results from the Runbook and use them for the next steps in the Logic App.
Initially I wanted to grab the JSON output of starting some VMs, where I ended up with the output being like this:
{
"MyVM2": true
}
{
"MyVM1": true
}
I was then going to Parse the JSON to be used in the Runbook, but soon realising that I would not have a consisted number of results (maybe 2 VMs, or 20) I had found the Parse JSON schema to only Parse what I set the schema to be (2, in my case, so anything more would be missed).
Now I figure I could put my output to a table, then use that to allow the Logic App to look inside that table to pull my VM names and success results from. So, here is the Runbook that I've been mutilating:
workflow ShutDownStartByTagasdf
{
Param(
[Parameter(Mandatory=$true)]
[String]
$TagName,
[Parameter(Mandatory=$true)]
[String]
$TagValue,
[Parameter(Mandatory=$true)]
[Boolean]
$Shutdown
)
$connectionName = "AzureRunAsConnection";
try
{
# Get the connection "AzureRunAsConnection "
$servicePrincipalConnection=Get-AutomationConnection -Name $connectionName
# Logging in to Azure
$null = Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
}
catch {
if (!$servicePrincipalConnection)
{
$ErrorMessage = "Connection $connectionName not found."
throw $ErrorMessage
} else{
Write-Error -Message $_.Exception
throw $_.Exception
}
}
$vms = Find-AzureRmResource -TagName $TagName -TagValue $TagValue | where {$_.ResourceType -like "Microsoft.Compute/virtualMachines"}
Foreach -Parallel ($vm in $vms){
if($Shutdown){
$StopRtn = Stop-AzureRmVm -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -Force;
$objOut = [PSCustomObject]#{
VM = $vm.Name
Success = $StartRtn.IsSuccessStatusCode
}
}
else {
$StartRtn = Start-AzureRmVm -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName;
$objOut = New-Object psobject -Property #{
VM = $vm.Name
Success = $StartRtn.IsSuccessStatusCode
}
}
$outPut = InlineScript {
$Using:objOut | Format-Table Vm,Success
}
}
}
Ignore the $objOut = [PSCustomObject]#{ part, that's just history of my JSON messing about, and I'm leaving it there for now because I'm not using the $Shutdwon = $True, only $False, which is the very last part after else {
this bit:
else {
$StartRtn = Start-AzureRmVm -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName;
$objOut = New-Object psobject -Property #{
VM = $vm.Name
Success = $StartRtn.IsSuccessStatusCode
}
}
$outPut = InlineScript {
$Using:objOut | Format-Table Vm,Success
}
}
I was trying to implement something along the lines of what is already described here: Create Table with Variables in PowerShell
But there is no Output into the Azure Runbook console, but it does boot the VMs.
A very long winded explanation, but does anyone know how I could output to a Formatted Table inside a Workflow (Runbook) that will yield all my results in one table?

I ended up using an array to capture the info:
workflow ShutDownStartByTagasdf
{
Param(
[Parameter(Mandatory=$true)]
[String]
$TagName,
[Parameter(Mandatory=$true)]
[String]
$TagValue,
[Parameter(Mandatory=$true)]
[Boolean]
$Shutdown
)
$connectionName = "AzureRunAsConnection";
try
{
# Get the connection "AzureRunAsConnection "
$servicePrincipalConnection=Get-AutomationConnection -Name $connectionName
# "Logging in to Azure..."
$null = Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
}
catch {
if (!$servicePrincipalConnection)
{
$ErrorMessage = "Connection $connectionName not found."
throw $ErrorMessage
} else{
Write-Error -Message $_.Exception
throw $_.Exception
}
}
$result_array = #()
$vms = Find-AzureRmResource -TagName $TagName -TagValue $TagValue | where {$_.ResourceType -like "Microsoft.Compute/virtualMachines"}
Foreach -Parallel ($vm in $vms) {
if($Shutdown){
# Write-Output "Stopping $($vm.Name)";
$StopRtn = Stop-AzureRmVm -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -Force;
$objOut = New-Object -TypeName psobject -Property #{
VM = $vm.Name
Success = $StopRtn.IsSuccessStatusCode
}
}
else {
# Write-Output "Starting $($vm.Name)";
$StartRtn = Start-AzureRmVm -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName;
$objOut = New-Object -TypeName psobject -Property #{
VM = $vm.Name
Success = $StartRtn.IsSuccessStatusCode
}
}
$workflow:result_array += $objOut
}
$result_array | ConvertTo-Json
}
The last bit $result_array | ConvertTo-Json allowed me to get a better output which I hope to use in the Logic App. The output:
[
{
"VM": "MyVM2",
"Success": true,
"PSComputerName": "localhost",
"PSShowComputerName": true,
"PSSourceJobInstanceId": "dadd87ad-1de1-432c-92b1-4c501c9a7ce8"
},
{
"VM": "MyVM1",
"Success": true,
"PSComputerName": "localhost",
"PSShowComputerName": true,
"PSSourceJobInstanceId": "dadd87ad-1de1-432c-92b1-4c501c9a7ce8"
}
]
I don't like how it's an array of objects, cos now I gotta figure out how to get some braces around it { } within the Logic App to utilise it better.

Related

Invoke-Pester -OutputFile and -OutputFormat are member of the legacy parameter set

In Pester 4 the commandlet offers the possibility to explicitly specify the OutputPath.
Invoke-Pester -Script $testFile -PassThru -Verbose -OutputFile $tr `
-OutputFormat NUnitXml -CodeCoverage "$tmp/*-*.ps1" `
-CodeCoverageOutputFile $cc -Show All
In version 5, this option is declared as a legacy parameter set and issues a corresponding warning.
WARNING: You are using Legacy parameter set that adapts Pester 5 syntax to Pester 4 syntax. This parameter set is deprecated, and does not work 100%. The -Strict and -PesterOption parameters are ignored, and providing advanced configuration to -Path (-Script), and -CodeCoverage via a hash table does not work. Please refer to https://github.com/pester/Pester/releases/tag/5.0.1#legacy-parameter-set for more information.
Which implementation is planned for the following versions? If the parameter is no longer available, should the test results be extracted from the result object?
There is pretty good documentation written for the new Pester 5 which you can find here: https://github.com/pester/Pester#simple-and-advanced-interface.
That link should take you to the specific section that you're looking for.
Essentially, they moved the configuration to an assembly class [PesterConfiguration]. You can access the defaults by using [PesterConfigruation]::Default or more likely cast it to a new object that you will configure with your specific settings and output path. You could do that like this as an example:
$configuration = [PesterConfiguration]#{
Run = #{
Path = $testFile
}
Output = #{
Verbosity = 'Detailed'
}
Filter = #{
Tag = 'Acceptance'
ExcludeTag = 'WindowsOnly'
}
Should = #{
ErrorAction = 'Continue'
}
CodeCoverage = #{
Enable = $true
OutputPath = $yourPath
}
}
You then pass that configuration object to Invoke-Pester. i.e. Invoke-Pester -Configuration $configuration
You can still use some of the parameters in that 'legacy' style, Pester will just yell at you so that you aren't too surprised when it gets deprecated.
As a side note- I do not see a nunit format for the test output so I don't know if they discontinued that. The only one I see is 'JaCoCo'.
I use Pester 5.1.1 to test Azure Resources after their deployment.
In Azure DevOps Services I execute a PowerShell task within a pipeline that triggers one script (Start-Pester.ps1) which in turn invokes tests from another script (PostDeployment.Tests.ps1) while passing the necessary parameter values.
Start-Pester.ps1
param(
[string]$SubscriptionId,
[string]$TenantId,
[string]$Username,
[string]$Password,
[string]$ResourceGroupName,
[string]$FunctionAppName,
[string]$EventHubNamespaceName,
[string]$EventHubNamespaceAuthorizationRuleName,
[string]$EventHubName,
[string]$EventHubAuthorizationRuleName,
[string]$EventHubAuthorizationRuleName1,
[string]$ModulePath,
[switch]$Publish,
[string]$ResultsPath
)
[string]$SubscriptionId = (Get-Item env:SubscriptionId).value
[string]$TenantId = (Get-Item env:TenantId).value
[string]$Username = (Get-Item env:Username).value
[string]$Password = (Get-Item env:Password).value
[string]$ResourceGroupName = (Get-Item env:ResourceGroupName).value
[string]$FunctionAppName = (Get-Item env:FunctionAppName).value
[string]$EventHubNamespaceName = (Get-Item env:EventHubNamespaceName).value
[string]$EventHubNamespaceAuthorizationRuleName = (Get-Item env:EventHubNamespaceAuthorizationRuleName).value
[string]$EventHubName = (Get-Item env:EventHubName).value
[string]$EventHubAuthorizationRuleName = (Get-Item env:EventHubAuthorizationRuleName).value
[string]$EventHubAuthorizationRuleName1 = (Get-Item env:EventHubAuthorizationRuleName1).value
$WarningPreference = "SilentlyContinue"
Set-Item Env:\SuppressAzurePowerShellBreakingChangeWarnings "true"
[array]$ModuleName = #("Az.Accounts", "Az.Resources", "Az.EventHub", "Az.Functions")
foreach ($Module in $ModuleName) {
Install-Module $Module -Scope CurrentUser -Force -SkipPublisherCheck -confirm:$false -AllowClobber
Import-Module -Name $Module
Get-InstalledModule -Name $Module -AllVersions | Select-Object Name, Version
}
# Authentication
$Credentials = New-Object System.Management.Automation.PSCredential ($Username, $(ConvertTo-SecureString $Password -AsPlainText -Force))
Connect-AzAccount -Credential $Credentials -ServicePrincipal -Tenant $TenantId
# Subscription
Set-AzContext -Subscription $SubscriptionId
$PesterModule = Get-Module -Name Pester -ListAvailable | Where-Object { $_.Version -like '5.*' }
if (!$PesterModule) {
try {
Install-Module -Name Pester -Scope CurrentUser -Force -SkipPublisherCheck -MinimumVersion "5.0" -Repository PSGallery
$PesterModule = Get-Module -Name Pester -ListAvailable | Where-Object { $_.Version -like '5.*' }
}
catch {
Write-Error "Failed to install the Pester module."
}
}
Write-Host "Pester version: $($PesterModule.Version.Major).$($PesterModule.Version.Minor).$($PesterModule.Version.Build)"
$PesterModule | Import-Module
if ($Publish) {
if (!(Test-Path -Path $ResultsPath)) {
New-Item -Path $ResultsPath -ItemType Directory -Force | Out-Null
}
}
$Tests = (Get-ChildItem -Path $($ModulePath) -Recurse | Where-Object { $_.Name -like "*Tests.ps1" }).FullName
$Params = [ordered]#{
Path = $Tests;
Data = #{
ResourceGroupName = $ResourceGroupName;
FunctionAppName = $FunctionAppName;
EventHubNamespaceName = $EventHubNamespaceName;
EventHubNamespaceAuthorizationRuleName = $EventHubNamespaceAuthorizationRuleName;
EventHubName = $EventHubName;
EventHubAuthorizationRuleName = $EventHubAuthorizationRuleName;
EventHubAuthorizationRuleName1 = $EventHubAuthorizationRuleName1;
}
}
$Container = New-PesterContainer #Params
$Configuration = [PesterConfiguration]#{
Run = #{
Container = $Container
}
Output = #{
Verbosity = 'Detailed'
}
TestResult = #{
Enabled = $true
OutputFormat = "NUnitXml"
OutputPath = "$($ResultsPath)\Test-Pester.xml"
}
CodeCoverage = #{
Enabled = $true
Path = $Tests
OutputFormat = "JaCoCo"
OutputPath = "$($ResultsPath)\Pester-Coverage.xml"
}
}
if ($Publish) {
Invoke-Pester -Configuration $Configuration
}
else {
Invoke-Pester -Container $Container -Output Detailed
}
PostDeployment.Tests.ps1
param(
[string]$ResourceGroupName,
[string]$FunctionAppName,
[string]$EventHubNamespaceName,
[string]$EventHubNamespaceAuthorizationRuleName,
[string]$EventHubName,
[string]$EventHubAuthorizationRuleName,
[string]$EventHubAuthorizationRuleName1
)
Describe "Structure Tests" {
BeforeAll {
if ($ResourceGroupName.Length -gt 0) {
$ResourceGroupData = Get-AzResourceGroup -Name $ResourceGroupName
}
if ($EventHubNamespaceName.Length -gt 0) {
$EventHubNamespaceData = Get-AzEventHubNamespace -ResourceGroupName $ResourceGroupName -Name $EventHubNamespaceName
$EventHubNamespaceAuthorizationRuleData = Get-AzEventHubAuthorizationRule -ResourceGroupName $ResourceGroupName -NamespaceName $EventHubNamespaceName -Name $EventHubNamespaceAuthorizationRuleName
}
if ($EventHubName.Length -gt 0) {
$EventHubData = Get-AzEventHub -ResourceGroupName $ResourceGroupName -NamespaceName $EventHubNamespaceName -EventHubName $EventHubName
$EventHubAuthorizationRuleData = Get-AzEventHubAuthorizationRule -ResourceGroupName $ResourceGroupName -NamespaceName $EventHubNamespaceName -EventHubName $EventHubName -Name $EventHubAuthorizationRuleName
$EventHubAuthorizationRuleData1 = Get-AzEventHubAuthorizationRule -ResourceGroupName $ResourceGroupName -NamespaceName $EventHubNamespaceName -EventHubName $EventHubName -Name $EventHubAuthorizationRuleName1
}
if ($FunctionAppName.Length -gt 0) {
$FunctionAppData = Get-AzFunctionApp -Name $FunctionAppName -ResourceGroupName $ResourceGroupName
}
}
# Resource Group
Context -Name "Resource Group" {
It -Name "Passed Resource Group existence check" -Test {
$ResourceGroupData | Should -Not -Be $null
}
}
# Event Hub Namespace
Context -Name "Event Hub Namespace" {
It -Name "Passed Event Hub Namespace existence check" -Test {
$EventHubNamespaceData | Should -Not -Be $null
}
It -Name "Passed Event Hub Namespace tier check" -Test {
$EventHubNamespaceData.Sku.Tier | Should -Be "Standard"
}
It -Name "Passed Event Hub Namespace TU check" -Test {
$EventHubNamespaceData.Sku.Capacity | Should -Be 1
}
It -Name "Passed Event Hub Namespace auto-inflate check" -Test {
$EventHubNamespaceData.IsAutoInflateEnabled | Should -Be $true
}
It -Name "Passed Event Hub Namespace maximum TU check" -Test {
$EventHubNamespaceData.MaximumThroughputUnits | Should -Be 2
}
It -Name "Passed Event Hub Namespace shared access policies check" -Test {
$EventHubNamespaceAuthorizationRuleData.Rights.Count | Should -Be 3
}
}
# Event Hub
Context -Name "Event Hub" {
It -Name "Passed Event Hub existence check" -Test {
$EventHubData | Should -Not -Be $null
}
It -Name "Passed Event Hub 'Listen' shared access policies check" -Test {
$EventHubAuthorizationRuleData.Rights | Should -Be "Listen"
}
It -Name "Passed Event Hub 'Send' shared access policies check" -Test {
$EventHubAuthorizationRuleData1.Rights | Should -Be "Send"
}
}
# Function App
Context -Name "Function App" {
It -Name "Passed Function App existence check" -Test {
$FunctionAppData | Should -Not -Be $null
}
It -Name "Passed Function App AppSettings configuration existence check" -Test {
$FunctionAppData.ApplicationSettings | Should -Not -Be $null
}
It -Name "Passed Function App APPINSIGHTS_INSTRUMENTATIONKEY existence check" -Test {
$FunctionAppData.ApplicationSettings.APPINSIGHTS_INSTRUMENTATIONKEY | Should -Not -Be $null
}
It -Name "Passed Function App FUNCTIONS_WORKER_RUNTIME value check" -Test {
$FunctionAppData.ApplicationSettings.FUNCTIONS_WORKER_RUNTIME | Should -Be "dotnet"
}
}
}
As you can see I am overwriting [PesterConfigruation]::Default with my configuration.
And yes, TestResult block with NUnitXml works as well.
Just add Publish Test Results task at the end of the pipeline. It will pick up the test results and publish them.
Hope this will help someone in the future.
They moved many settings to a new Configuration object.
Described here:
https://pester-docs.netlify.app/docs/commands/New-PesterConfiguration
Old:
Invoke-Pester -Script $testFile -PassThru -Verbose -OutputFile $tr `
-OutputFormat NUnitXml -CodeCoverage "$tmp/*-*.ps1" `
-CodeCoverageOutputFile $cc -Show All
New:
$configuration = [PesterConfiguration]#{
PassThru = $true
Run = #{
Path = $testFile
}
Output = #{
Verbosity = 'Detailed'
}
TestResult = #{
Enabled = $true
OutputFormat = "NUnitXml"
OutputPath = $tr
}
CodeCoverage = #{
Enabled = $true
Path = "$tmp/*-*.ps1"
OutputPath = $cc
}
}
Invoke-Pester -Configuration $configuration

Sorting contents with a PS script

Goal of this post:
Sort Name column with csv filter -contains "-POS-"
Only pull back the top Bitlocker key from AzureAD and place that one key into the bitlockerKeys column.
This is a script from - https://gitlab.com/Lieben/assortedFunctions/blob/master/get-bitlockerEscrowStatusForAzureADDevices.ps1
This is not my script, but I need it to work like this for a project I am doing. Did I mention that I am a complete PS noob here? Take it easy on me please lol.
function get-bitlockerEscrowStatusForAzureADDevices{
#Requires -Modules ImportExcel
<#
.SYNOPSIS
Retrieves bitlocker key upload status for all azure ad devices
.DESCRIPTION
Use this report to determine which of your devices have backed up their bitlocker key to AzureAD (and find those that haven't and are at risk of data loss!).
Report will be stored in current folder.
.EXAMPLE
get-bitlockerEscrowStatusForAzureADDevices
.PARAMETER Credential
Optional, pass a credential object to automatically sign in to Azure AD. Global Admin permissions required
.PARAMETER showBitlockerKeysInReport
Switch, is supplied, will show the actual recovery keys in the report. Be careful where you distribute the report to if you use this
.PARAMETER showAllOSTypesInReport
By default, only the Windows OS is reported on, if for some reason you like the additional information this report gives you about devices in general, you can add this switch to show all OS types
.NOTES
filename: get-bitlockerEscrowStatusForAzureADDevices.ps1
author: Jos Lieben
blog: www.lieben.nu
created: 9/4/2019
#>
[cmdletbinding()]
Param(
$Credential,
[Switch]$showBitlockerKeysInReport,
[Switch]$showAllOSTypesInReport
)
Import-Module AzureRM.Profile
if (Get-Module -Name "AzureADPreview" -ListAvailable) {
Import-Module AzureADPreview
} elseif (Get-Module -Name "AzureAD" -ListAvailable) {
Import-Module AzureAD
}
if ($Credential) {
Try {
Connect-AzureAD -Credential $Credential -ErrorAction Stop | Out-Null
} Catch {
Write-Warning "Couldn't connect to Azure AD non-interactively, trying interactively."
Connect-AzureAD -TenantId $(($Credential.UserName.Split("#"))[1]) -ErrorAction Stop | Out-Null
}
Try {
Login-AzureRmAccount -Credential $Credential -ErrorAction Stop | Out-Null
} Catch {
Write-Warning "Couldn't connect to Azure RM non-interactively, trying interactively."
Login-AzureRmAccount -TenantId $(($Credential.UserName.Split("#"))[1]) -ErrorAction Stop | Out-Null
}
} else {
Login-AzureRmAccount -ErrorAction Stop | Out-Null
}
$context = Get-AzureRmContext
$tenantId = $context.Tenant.Id
$refreshToken = #($context.TokenCache.ReadItems() | where {$_.tenantId -eq $tenantId -and $_.ExpiresOn -gt (Get-Date)})[0].RefreshToken
$body = "grant_type=refresh_token&refresh_token=$($refreshToken)&resource=74658136-14ec-4630-ad9b-26e160ff0fc6"
$apiToken = Invoke-RestMethod "https://login.windows.net/$tenantId/oauth2/token" -Method POST -Body $body -ContentType 'application/x-www-form-urlencoded'
$restHeader = #{
'Authorization' = 'Bearer ' + $apiToken.access_token
'X-Requested-With'= 'XMLHttpRequest'
'x-ms-client-request-id'= [guid]::NewGuid()
'x-ms-correlation-id' = [guid]::NewGuid()
}
Write-Verbose "Connected, retrieving devices..."
$restResult = Invoke-RestMethod -Method GET -UseBasicParsing -Uri "https://main.iam.ad.ext.azure.com/api/Devices?nextLink=&queryParams=%7B%22searchText%22%3A%22%22%7D&top=15" -Headers $restHeader
$allDevices = #()
$allDevices += $restResult.value
while($restResult.nextLink){
$restResult = Invoke-RestMethod -Method GET -UseBasicParsing -Uri "https://main.iam.ad.ext.azure.com/api/Devices?nextLink=$([System.Web.HttpUtility]::UrlEncode($restResult.nextLink))&queryParams=%7B%22searchText%22%3A%22%22%7D&top=15" -Headers $restHeader
$allDevices += $restResult.value
}
Write-Verbose "Retrieved $($allDevices.Count) devices from AzureAD, processing information..."
$csvEntries = #()
foreach($device in $allDevices){
if(!$showAllOSTypesInReport -and $device.deviceOSType -notlike "Windows*"){
Continue
}
$keysKnownToAzure = $False
$osDriveEncrypted = $False
$lastKeyUploadDate = $Null
if($device.deviceOSType -eq "Windows" -and $device.bitLockerKey.Count -gt 0){
$keysKnownToAzure = $True
$keys = $device.bitLockerKey | Sort-Object -Property creationTime -Descending
if($keys.driveType -contains "Operating system drive"){
$osDriveEncrypted = $True
}
$lastKeyUploadDate = $keys[0].creationTime
if($showBitlockerKeysInReport){
$bitlockerKeys = ""
foreach($key in $device.bitlockerKey){
$bitlockerKeys += "$($key.creationTime)|$($key.driveType)|$($key.recoveryKey)|"
}
}else{
$bitlockerKeys = "HIDDEN FROM REPORT: READ INSTRUCTIONS TO REVEAL KEYS"
}
}else{
$bitlockerKeys = "NOT UPLOADED YET OR N/A"
}
$csvEntries += [PSCustomObject]#{"Name"=$device.displayName;"bitlockerKeys"=$bitlockerKeys}
}
$csvEntries | Export-Excel -workSheetName "BitlockerKeyReport" -path "C:\BitLockerKeyReport.xlsx" -ClearSheet -TableName "BitlockerKeyReport" -AutoSize -Verbose
}
get-bitlockerEscrowStatusForAzureADDevices -showBitlockerKeysInReport

Start-AzureRmAutomationRunbook is not accepting System.String parameter

I am trying to automate server reboot for one of our environment in Azure through azure automation.
I have created one runbook to know how many servers online.
Then trying pass the output as a parameter to another runbook and getting the
error.
below is the code.
$connectionName = "AzureRunAsConnection"
try
{
# Get the connection "AzureRunAsConnection "
$servicePrincipalConnection=Get-AutomationConnection -Name $connectionName
"Logging in to Azure..."
Add-AzureRmAccount `
-ServicePrincipal `
-TenantId $servicePrincipalConnection.TenantId `
-ApplicationId $servicePrincipalConnection.ApplicationId `
-CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint
}
catch {
if (!$servicePrincipalConnection)
{
$ErrorMessage = "Connection $connectionName not found."
throw $ErrorMessage
} else{
Write-Error -Message $_.Exception
throw $_.Exception
}
}
$Output = #()
$Running_XenApps = #()
$Output = .\Test.ps1
Foreach ($out in $Output)
{
$Running_XenApps += $out.Name | Out-String
}
Start-AzureRmAutomationRunbook –AutomationAccountName 'acm2eo-azure- automation' -Name 'Server-Reboot' -ResourceGroupName 'acm2eo-automation' -Parameters $Running_Xenapps -Runon acm2eo-hybrid-group1
I am getting below error.
Start-AzureRmAutomationRunbook : Cannot convert 'System.Object[]' to the type 'System.Collections.IDictionary' requiredby parameter 'Parameters'. Specified method is not supported.
Please let me know how to convert this.
Thanks in Advance.
You have to pass a hashtable / dictionary to the -Parameter param:
$params = #{
"YourParameterName" = $Running_XenApps
}
Start-AzureRmAutomationRunbook –AutomationAccountName 'acm2eo-azure- automation' -Name 'Server-Reboot' -ResourceGroupName 'acm2eo-automation' -Parameters $params -Runon acm2eo-hybrid-group1
As documented here: Start-AzureRmAutomationRunbook cmdlet.

Workflow error "unexpected Token"

I have the workflow below and I ge the error whenever I try to put it inside a workflow.
If I take it out of a workflow it works, I was wondering if there's some incorrect syntax I'm missing or even if this is not allowed n a workflow.
Apologies but my knowledge on workflows is limited (as you can probably tell). I'm ultimately trying to get the VMs to boot up in parallel.
workflow Set-AzureRmTags-and-Start {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[string]$VmRG
)
$Start = Get-Date
Write-Output "Time is" $Start
$VmRGs = Get-AzureRmResourceGroup | Where-Object { $_.ResourceGroupName -like "*$VmRG*" }
foreach ($VmRG in $VmRGs) {
$VMs = Get-AzureRmVM -ResourceGroupName $VmRG.ResourceGroupName
ForEach ($vm in $vms) {
$tags2 = $_.Tags
$tags2 ['ShutdownSchedule_AllowStop'] = "$False";
}
Set-AzureRmResource -ResourceName $vm.Name -ResourceGroupName $vm.ResourceGroupName -ResourceType "Microsoft.Compute/virtualMachines" -Tag $tags2 -Force -Verbose
}
$Middle = Get-Date
Write-Output "Time Taken To Assign Tags" ($Middle - $Start).Minutes "Minutes"
ForEach -Parallel ($vm in $vms) {
Start-AzureRmVM -Name $vm.Name -ResourceGroupName $vm.ResourceGroupName -Verbose -Confirm:$false
}
$End = Get-Date
Write-Output "Time Taken To Start VMs" ($End - $Middle).Minutes "Minutes"
Write-Output "Total Time Taken" ($End - $Start).Minutes "Minutes"
}
The error is around ['ShutdownSchedule_AllowStop'] = "$False"; } Error "Unexpected Token in Expression or Statement".
Any ideas how I could correct this?
Thanks in advance.
$VmRGs = Get-AzureRmResourceGroup | Where-Object { $_.ResourceGroupName -like "*$VmRG*" }
foreach ($VmRG in $VmRGs) {
$VMs = Get-AzureRmVM -ResourceGroupName $VmRG.ResourceGroupName
ForEach ($vm in $vms) {
$tags2 = $_.Tags
$tags2 ['ShutdownSchedule_AllowStop'] = "$False";
}
Set-AzureRmResource -ResourceName $vm.Name -ResourceGroupName $vm.ResourceGroupName -ResourceType "Microsoft.Compute/virtualMachines" -Tag $tags2 -Force -Verbose
}
The line $tags2 = $_.Tags does not makes sense. There is no $_ at this point.
I don't have any experience in Azure tags, but the documentation here contains sample code that your code should more closely emulate. Setting tags in their samples always seems to use a HashTable construct, like #{ShutdownSchedule_AllowStop=$false}
This section seems relevant to what you're trying to accomplish:
To add tags to a resource group that has existing tags, retrieve the
existing tags, add the new tag, and reapply the tags:
$tags = (Get-AzureRmResourceGroup -Name examplegroup).Tags
$tags += #{Status="Approved"}
Set-AzureRmResourceGroup -Tag $tags -Name examplegroup
Up until now, I've never looked into workflows. After a little testing and a bit of research, it seems the 'unexpected Token' error is due the code existing in a workflow:
function Test-ArraySubscriptFn {
$tags2 = #{}
$tags2['asdf'] = $false
}
workflow Test-ArraySubscriptWf {
$tags2 = #{}
$tags2['asdf'] = $false # error: Only variable names may be used as the target of an assignment statement
$tags2.Add('asdf',$false) # error: Method invocation is not supported in a Windows Powershell Workflow...
$tags2 += #{'asdf'=$false} # no error
}
This is consistent with google search results. I guess we both need to do a bit of research on how workflows differ from 'regular' powershell scripts.

Runspaces and Jobs in VSTS hosted agent release Azure Powershell script

I have a VSTS setup with hosted agents and I'm trying to run an Azure Powershell script to resize classic cloud services using a Runspaces async method. This script works fine when I run it on my local machine.
[hashtable]$myServices = #{}
$myServices.Add('serviceA',3)
$myServices.Add('serviceB',1)
$myServices.Add('serviceC',2)
$myServices.Add('serviceD',1)
$myServices.Add('serviceE',3)
$Throttle = 5 #threads
$ScriptBlock = {
Param (
[string]$serviceName,
[int]$instanceCount
)
Try{
$roles = Get-AzureRole -ServiceName $serviceName -Slot Production -ErrorAction Stop
foreach($role in $roles){
Write-Output 'Currently on ' + $role.RoleName
$result = Set-AzureRole -ServiceName $serviceName -Slot Production -RoleName $role.RoleName -Count $instanceCount -ErrorAction Stop
$thisRunResult = New-Object PSObject -Property #{
Service = $serviceName
Role = $role.RoleName
Description = $result.OperationDescription
Status = $result.OperationStatus
}
$RunResult += $thisRunResult
}
Return $RunResult
} Catch {
write-output "An error occurred in the script block."
write-output $_.Exception.Message
}
}
Try{
$ErrorActionPreference = "Stop"
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, $Throttle)
$RunspacePool.Open()
$Jobs = #()
} Catch {
write-output "An error occurred while instantiating Runspaces."
write-output $_.Exception.Message
} Finally {
$ErrorActionPreference = "Continue"
}
foreach ($service in $qaServices.GetEnumerator()){
Write-Output 'Currently on ' + $service.Key
Try{
$ErrorActionPreference = "Stop"
$Job = [powershell]::Create().AddScript($ScriptBlock).AddArgument($service.Key).AddArgument($service.Value)
write-output "Line after the Job is created gets executed."
$Job.RunspacePool = $RunspacePool
$Jobs += New-Object PSObject -Property #{
RunNum = $service.Key
Pipe = $Job
Result = $Job.BeginInvoke()
}
} Catch {
write-output "An error occurred creating job."
write-output $_.Exception.Message
} Finally {
$ErrorActionPreference = "Continue"
}
}
Write-Host "Waiting.." -NoNewline
Do {
Write-Host "." -NoNewline
Start-Sleep -Seconds 1
} While ( $Jobs.Result.IsCompleted -contains $false)
Write-Host "All jobs completed!"
$Results = #()
ForEach ($Job in $Jobs)
{ $Results += $Job.Pipe.EndInvoke($Job.Result)
}
$Results | Select-Object -Property Service,Role,Description,Status | Sort-Object -Property Service | Out-Host
None of my Try/Catch stuff outputs anything in the log. When I run this locally, I get the resulting output like I expect with a new "." every second until it's complete:
Waiting.........................All jobs completed!
Locally it's a few hundred ...'s because it takes a few minutes to scale things. When run in VSTS, it's about eight dots returned immediately. So it looks like nothing is actually happening (specifically that the Jobs are not initializing), but there's no error to tell me what the problem is. Thanks in advance.
(untested)
try this:
try {
$ErrorActionPreference = "Stop"
# Your code
} catch{
Write-Host $_.Exception
}finally{
$ErrorActionPreference = "Continue"
}
by default the $ErrorActionPreference is set to Continue so by setting it to Stop you are saying that all error are "terminating" thus the exception is catched by try/catch block.
or second option is to use common parameter -ErrorAction Stop
try {
$result = Set-AzureRole -ServiceName $serviceName -Slot Production -RoleName $role.RoleName -Count $instanceCount -ErrorAction Stop
} catch{
Write-Host $_.Exception
}
I had the same issue with AD(active directory) commandlets.