TFS/VSTS Custom variables that aren't string cannot be used - powershell

I have been using TFS to create some release variables from PowerShell. I can then use '$Env:Server' in subsequent tasks in TFS to reference this output, this seems great to start with! EG:
Task 1 returns a server name then creates the TFS Variable:
Write-Host "##vso[task.setvariable variable=Server]"MySevrer
Task 2 Uses this information:
Write-Output $env:Server
MyServer
Upon outputting something in another format like an array or a hash table this does not work. The variable that gets created is just string as the documentation states using "Write-Host".
Task1 returns:
##vso[task.setvariable variable=Server] System.Collections.DictionaryEntry
Task 2 can not use this:
Write-Output $env:Server
System.Collections.DictionaryEntry
Output that is created
I have tried outputting this as a string in the array format, EG:
[String]$Server = '#{MyServer=#("192.168.0.1")}'
Write-Host "##vso[task.setvariable variable=Server]"$Server
When i refer to this I try to convert this back to an array within PowerShell, however, I have had issues with doing this inside a script as it will see the string as being a single array object. (Not an array with a value)
Does anyone know if it posable to parse hashtables or arrays between Team Service's tasks/Task Task groups based on information output by a Powershell task?
I have currently got around this by writing a wrapper/orchestration function but this is not the ideal way for us. We are currently on Version 15.117.26714.0 but i cannot see anything in newer versions.

ConvertTo-Json will do the trick, as mentioned by Daniel Mann.
Example:
In Azure Devops release pipeline, there is an Azure PowerShell task with following code:
$apps = Get-AzureRmResource -ResourceGroupName "$(ResourceGroupName)" -ResourceType Microsoft.Web/sites
$objectIdArray = New-Object System.Collections.ArrayList
foreach ($app in $apps) {
if (!$app.Identity) {
Write-Host "Enabling identity for $($app.Name) app service..."
$app = Set-AzureRmWebApp -Name $($app.Name) -ResourceGroupName "$(ResourceGroupName)" -AssignIdentity $true
} else {
Write-Host "$($app.Name) app service has $($app.Identity.PrincipalId) as the identity."
}
$objectIdArray.Add($($app.Identity.principalId))
}
$objectIdArrayJson = ($objectIdArray | ConvertTo-Json -Compress)
Write-Host "##vso[task.setvariable variable=objectIdArray;]$objectIdArrayJson"
Write-Host $env:objectIdArray
The following task is an Azure Resource Group Deployment task that has an overridden parameter:
-objectIdArray "$(objectIdArray)"
The release variable "$(objectIdArray)" has a value like:
["0512706a-0344-418a-9f25-5708d95e44aa","6047cbe6-c109-4aa7-8cfb-b473c088b1b1","68ee0d25-351f-44c8-aecf-cfc259f3cd97","44a6f3d6-23a3-443d-824b-445e0141f09c","805c3e6d-ab31-41f4-9d6c-8c9fc13ce
460","aa13b9db-200d-4c38-abf8-562a915ed8cd","8d1d7ec1-faa6-4af6-b732-331e51e86a90","02222b28-6370-4995-a633-29a1cdd08fd0","a48c21b1-b6ef-4582-b9a0-050965cb3614","9111421b-8535-4326-bbe9-1e891
33a0b56","5b1f6fca-599c-4895-ae4b-fabc0d3a4dd3","b12a935a-b1c3-4dec-b764-7c0a5307a766","8af7d615-c042-43b5-8ac0-736c6cf4ec3f","f0dd4dd9-e540-4e13-a8be-69ce08a6221c","b131e123-a87e-4faf-afed-4
37d6dbae4ab","af7f679b-1ac8-4991-b467-93ba4a16ec22","1bbb649c-b5e6-4f5c-a050-3a0cee0af985","4a7b728e-e8c6-49c0-bde2-54811708d5ab","3b190d28-c390-43c7-9269-1177afaf7b00","49f3777f-8668-4c72-82
60-753f65b933aa","727db5c4-ad56-457e-ad87-47b17c71e29b","801efff8-a852-4e7b-bc81-3d81d3bcfeb5","0947556e-7ece-4a36-a687-3c50f59e32f6"]

Pass them as JSON and use ConvertTo-Json and ConvertFrom-Json to convert them back and forth between JSON representations and PowerShell objects. When using ConvertTo-Json, be sure to use the -Compress flag.

Related

Unable to update Azure Cloud Service Extended Support via Azure Powershell Task

I am attempting to update Azure Cloud Service Extended Support using Azure PowerShell task in Azure DevOps Release pipeline. I receive the follow error when using Update-AzCloudService:
Specified api-version 2021-03-01 does not meet the minimum required api-version 2022-04-04 to set this property SlotType
Here is the code block used in the PowerShell task:
...
$cloudServiceIdentity = #{
CloudServiceName = $cloudServiceName
Location = $location
ResourceGroupName = $resourceGroupName
SubscriptionId = $subscription.Subscription.Id
}
Write-Host "-------------------------------"
Write-Host = "cloudServiceIdentity.CloudServiceName= " $cloudServiceIdentity.CloudServiceName
Write-Host = "cloudServiceIdentity.Location = " $cloudServiceIdentity.Location
Write-Host = "cloudServiceIdentity.ResourceGroupName = " $cloudServiceIdentity.ResourceGroupName
Write-Host = "cloudServiceIdentity.SubscriptionId = " $cloudServiceIdentity.SubscriptionId
Write-Host "-------------------------------"
$cloudService = #{
Location = $location
configurationUrl = $configSas
PackageUrl = $packageSas
OSProfile = $osProfile
}
Write-Host "-------------------------------"
Write-Host = "cloudService.Location= " $location
Write-Host = "cloudService.configurationUrl = " $configSas
Write-Host = "cloudService.PackageUrl = " $packageSas
Write-Host = "cloudService.OSProfile = " $osProfile
Write-Host "-------------------------------"
Write-Host "Starting Azure CSES Update"
Update-AzCloudService -InputObject $cloudServiceIdentity -Parameter $cloudService
Write-Host "Completed Azure CSES Update"
...
I have confirmed that Az.CloudService is running version is 1.1.0 (latest version)
Environment info:
| Environment | Value |
|:----------- |:------|
|Azure DevOps Version|18.181.31626.1 (Azure DevOps Server 2020 Update 1.1)|
|Azure DevOps Azure PowerShell Task |Version 5|
|Azure PowerShell version options - Preferred Azure PowerShell Version| 3.1.0|
|Az.CloudService | 1.1.0|
I have been struggling with this issue for several days and cannot seem to find any information on fixing the problem.
Any insights are greatly appreciated!
Given the error message you shared, I suppose that you could check this comment about api version error.
And run the command below to check the latest released api version.
((get-azresourceprovider -providernamespace Microsoft.Compute).resourcetypes | ?{$_.ResourceTypeName -eq "virtualmachines"}).apiversions
And remodify your api-version parameter in your json or bicep template.
The issue does appear to be related to the AzCloudService Powershell Module or the CloudService Resource by itself. When creating the resource, Azure does seem to select the 2021-03-01 API version even if you have a newer version selected in your ARM Template.
It could be that this tight versioning requirements with the way Powershell makes its queries to the resource doesn't seem to work well with the resource.
In my case, I was using the New-AzCloudService cmdlet and always encountered the issue (note, to update the CloudService resource, you may still need to use the New-AzCloudService cmdlet. If the resource exists, an update operation will be performed. If the resource doesn't exist, a new resource will be created).
I noticed though that if using Powershell, all the parameters need to be specified if using the New-AzCloudService cmdlet.
As specified here in the example Step 13:
https://learn.microsoft.com/en-us/azure/cloud-services-extended-support/deploy-powershell#create-cloud-service-using-profile-objects--sas-uris
you must have to pass in the other parameters before your query will be accepted (at least this is what worked for me). -Tag was not required.
So initially I was passing in my query as follows:
$cloudService = New-AzCloudService `
-Name “ContosoCS” `
-ResourceGroupName “ContosOrg” `
-Location “East US” `
-PackageUrl $cspkgUrl `
-ConfigurationUrl $cscfgUrl
because I only wanted to perform a release update and I always got the error:
Specified api-version 2021-03-01 does not meet the minimum required api-version 2022-04-04 to set this property SlotType
When I passed in all other parameters (I referenced the existing cloud service's parameters since I'm performing an in-place update), it got accepted.
$cloudService = New-AzCloudService `
-Name “ContosoCS” `
-ResourceGroupName “ContosOrg” `
-Location “East US” `
-PackageUrl $cspkgUrl `
-ConfigurationUrl $cscfgUrl `
-UpgradeMode 'Auto' `
-RoleProfile $roleProfile `
-NetworkProfile $networkProfile `
-ExtensionProfile $extensionProfile `
-OSProfile $osProfile
Hope this helps.

Register-Environment Failing

I am having trouble creating a release definition on Azure DevOps. I believe the Register-Environment function in PowerShellOnTargetMachines.ps1 is failing:
try
{
$connection = Get-VssConnection -TaskContext $distributedTaskContext
Write-Verbose "Starting Register-Environment cmdlet call for environment : $environmentName with filter $machineFilter"
$environment = Register-Environment -EnvironmentName $environmentName -EnvironmentSpecification $environmentName -UserName $adminUserName -Password $adminPassword -WinRmProtocol $protocol -TestCertificate ($testCertificate -eq "true") -Connection $connection -TaskContext $distributedTaskContext -ResourceFilter $machineFilter
Write-Verbose "Completed Register-Environment cmdlet call for environment : $environmentName"
Write-Verbose "Starting Get-EnvironmentResources cmdlet call on environment name: $environmentName"
$resources = Get-EnvironmentResources -Environment $environment
if ($resources.Count -eq 0)
{
Write-Telemetry "Input_Validation" "No machine exists for given environment"
throw (Get-LocalizedString -Key "No machine exists under environment: '{0}' for deployment" -ArgumentList $environmentName)
}
$resourcesPropertyBag = Get-ResourcesProperties -resources $resources
}
With the following error (I have omitted some of my organization's information, but it is there and looks right):
2019-09-04T12:34:55.6886629Z ##[debug]VssConnection created
2019-09-04T12:34:55.7518340Z ##[debug]Starting Register-Environment cmdlet call for environment : [machine] with filter [machine]
2019-09-04T12:34:55.7843531Z ##[debug]Begin Create-Environment cmdlet
2019-09-04T12:34:55.7872731Z ##[debug]UserName=[username]
2019-09-04T12:34:55.7878292Z ##[debug]WinRmProtocol=HTTP
2019-09-04T12:34:55.7878658Z ##[debug]TestCertificate=False
2019-09-04T12:34:55.7878965Z ##[debug]Unable to create a environment object for given json - Unexpected character encountered while parsing value: A. Path '', line 0, position 0.
2019-09-04T12:34:55.7879241Z ##[debug]projectName=[projectName]
2019-09-04T12:34:55.7879517Z ##[debug]Getting environment [machine] from DTL Service
2019-09-04T12:34:55.8485808Z ##[debug]Processed: ##vso[task.logissue type=error;code={"Task_Internal_Error":Page not found.};]
And I do not know what to do. Any ideas are appreciated. Thanks for taking the time to read my question.
Based on the details which from comment that Mike left, the issue should caused by the the task version you are using and its feature script we defined.
According to the ticket description you raised in DC ticket:
I am trying to use V2 of the following 'PowerShellOnTargetMachines'
PowerShell script for a release on azure devops, from
azure-pipeline-tasks.
the version of PowerShellOnTargetMachines you are using is 2.*.
We can get the Register-Environment cmdlet script with decompile tool follow the method I showed in this case.
You can see the parameters this cmdlet supported in 'PowerShellOnTargetMachines V2.*' does not include TaskContext and Connection. That's why you receive the error message "Unable to create a environment object for given json - Unexpected character encountered while parsing value". Because the parameter you input in the task does not match the configuration of Register-Environment cmdlet.
You can try with PowerShellOnTargetMachines V1.*. In 1.* we support the parameters TaskContext and Connection.

Retrieve something from different Azure subscriptions at the same time

I want to run a PowerShell Cmdlet against multiple Azure subscriptions, that's why I've thought about running it within a foreach loop but it did not work:
$Subscriptions = (Get-AzureRmSubscription).SubscriptionId
foreach ($sub in $Subscriptions)
{
Select-AzureRmSubscription -Subscription $sub
Do the task Cmdlet
}
Actually what it does is to run the task against the last subscription it was able to select.
Any better ways to workaround this?
Unfortunately the result cannot be exported to a csv file or a variable because it is displayed under the subscription info, as shown in the following figure.
Try using the Set-AzureRmContext cmdlet to set the subscription:
Get-AzureRmSubscription | ForEach-Object {
$_ | Set-AzureRmContext
# do your task
}

Get Status of Pipeline in Azure Data Factory

I am using Set-AzureRmDataFactoryPipelineActivePeriod to schedule the pipeline via PowerShell. However I am trying to get hold of Azure Commandlet that can give me the completion status of the pipeline once scheduled OR the code should run into some loop till the time the pipeline has not completed.
ADF v2
For v2 of Azure DataFactory, you can use Get-AzureRmDataFactoryV2PipelineRun cmdlet to get pipeline runs from some time period.
Parameters you need to set are ResourceGroupName, DataFactoryName, LastUpdatedAfter and -LastUpdatedBefore and optionally PipelineName.
For example, if you wanted to get pipeline runs from the last hour, for your pipeline my-pipeline in my-adf DataFactory in resource group my-rg, you would execute something like
Get-AzureRmDataFactoryV2PipelineRun -ResourceGroupName "my-rg" `
-DataFactoryName "my-adf" `
-PipelineName "my-pipeline" `
-LastUpdatedAfter (Get-Date).AddHours(-1) `
-LastUpdatedBefore (Get-Date).AddHours(1)
For more information, here's the cmdlet documentation: https://learn.microsoft.com/en-us/powershell/module/azurerm.datafactories/get-azurermdatafactoryv2pipelinerun?view=azurermps-4.4.1
ADF v1
If you are using ADF v1, you can invoke Get-AzureRmDataFactoryRun cmdlet to get pipeline status.However, since in DataFactory v1 output dataset slice is what runs the schedule for pipeline execution, you need to pass your output dataset name as well. Below is an example how you would invoke that cmdlet to get slices from the past hour
Get-AzureRmDataFactoryRun -ResourceGroupName "my-rg" `
-DataFactoryName "my-adf" `
-DatasetName "my-dataset" `
-StartDateTime (Get-Date).AddHours(-1)
For more information, here's the cmdlet documentation: https://learn.microsoft.com/en-us/powershell/module/azurerm.datafactories/get-azurermdatafactoryrun?view=azurermps-5.0.0
Got below code, but from where do we get the $Runid?
while ($True) {
$run = Get-AzureRmDataFactoryV2PipelineRun -ResourceGroupName $resourceGroupName -DataFactoryName $DataFactoryName -PipelineRunId $runId
if ($run) {
if ($run.Status -ne 'InProgress') {
Write-Host "Pipeline run finished. The status is: " $run.Status -foregroundcolor "Yellow"
$run
break
}
Write-Host "Pipeline is running...status: InProgress" -foregroundcolor "Yellow"
}
Start-Sleep -Seconds 30
}
The RunID comes from every pipeline run. With PowerShell, you can Invoke a pipeline and capture the RunID from that cmdlet:
https://learn.microsoft.com/en-us/powershell/module/azurerm.datafactoryv2/Invoke-AzureRmDataFactoryV2Pipeline?view=azurermps-4.4.1
There is an example of setting $RunID in this PS script: https://learn.microsoft.com/en-us/azure/data-factory/scripts/incremental-copy-powershell?toc=%2fpowershell%2fmodule%2ftoc.json

Azure Powershell Runbook - Get-AzureRMWebAppSlot SiteConfig.ConnectionStrings[0] erroring cannot index into a null array

I'm trying to execute below command in a PowerShell Workflow Runbook. I'm getting the error "cannot index into a null array.", which is not true as the same script which ran perfectly on my local machine is not executing while in the Azure portal as a PowerShell Workflow Runbook.
Can anyone please look into this?
$webApp = Get-AzureRMWebAppSlot -ResourceGroupName $ResourceGroupName -Name $WebSiteName -Slot $WebSiteSlot
$webApp
"Printing Website ConncectionString"
$webApp.SiteConfig.ConnectionStrings.ConnectionString[0]
Some types do not serialize/deserialize correctly, and in PowerShell Workflow that is a problem because PowerShell Workflow relies on object serialization/deserialization (that's how PSWF is able to checkpoint, suspend, and resume -- it converts all objects to a string form when checkpointing/suspending, and restores back to full objects from those strings when resuming).
It would appear Get-AzureRMWebAppSlot's output object is one of those types that does not serialize/deserialize correctly. From your screenshot I can see that the SiteConfig property of $webApp is a string containing Microsoft.Azure.Management.WebSites.Model.SiteConfig rather than an object as you're expecting. Clearly, the object is not deserializing correctly back to its original form, where SiteConfig is a complex object.
The way to work around this is to only interact with the object in PowerShell script context, rather than workflow context. For example:
workflow foo {
$ResourceGroupName = "RG"
$WebSiteName = "WS"
$WebSiteSlot = "Slot"
$ConnectionString = InlineScript {
$webApp = Get-AzureRMWebAppSlot -ResourceGroupName $using:ResourceGroupName -Name $using:WebSiteName -Slot $using:WebSiteSlot
$webApp.SiteConfig.ConnectionStrings.ConnectionString[0]
}
"Printing Website ConnectionString"
$ConnectionString
}