Output variables in Octopus -Incorrectly Passing null value - azure

I want to pass a value in Octopus from one step to another of a project via output variable, the value is "VM running" or "VM deallocated".
There are two server, one of the server is down, another one is running so values should be passed accordingly. Now when I use the exact syntax of Output variable, it is passing Null value to next step.
Octopus deploy Project Step 1:
$RG = $RGName
$VM = "#{StepTemplate_VMName}"
$WarningPreference = 'SilentlyContinue'
$VMStats = (Get-AzureRmVM -Name $VM -ResourceGroupName $RG -Status).Statuses
$stats = ($VMStats | Where Code -Like 'PowerState/*')[0].DisplayStatus
Set-OctopusVariable -name "RunStatus" -value $stats
write-host $stats #value can either be "VM running" or "VM deallocated"
Octopus deploy Project Step 2:
$VM = "#{StepTemplate_VMName}"
$Runstatus = $OctopusParameters["Octopus.Action[Step1].Output[$VM].RunStatus"]
write-host $Runstatus
If I do not use [$VM] in the code of step 2, it give only 1 value to both the machine as "VM running"
As per the syntax given in Octopus website, we should use the VM name to pass machine specific different values.
so I used [$VM] but it gives null values to both of the machine
Edit: Event If I hardcode the value of $VM to any one VMName, it still gives me null.

Based on what I do in Octopus it should be:
$Runstatus = $OctopusParameters["Octopus.Action[Step1].Output.RunStatus"]
Even states that format on the help documentation. https://octopus.com/docs/projects/variables/output-variables

Is the step that's creating the output variable actually being run on the server that's down? When the output variable is created in a step that's run on multiple machines, multiple machine-scoped values for that variable will be created. When the subsequent step is run on the same machines, Octopus will pick the correct value based on that scope. So maybe a little off topic, but if both steps are run on both targets, specifying the $VM scope is redundant, and is only needed if you want a different machine's value.
Since hardcoding the target name here returns null it seems like the output variable creation step wasn't run on that target. I'd suggest debugging this by creating a variable called OctopusPrintEvaluatedVariables with value True in your project. That'll increase the task log verbosity and log how all variables are evaluated through each step of the deployment.

Related

Route Table Details

Is there a way to fetch all details of routing from each subscription with Route-Table Name, subscriptions, next hop type address prefix.
I have tried 'Get-AzRouteTable -ResourceGroupName "" -Name "prod" | Get-AzRouteConfig | Export-Csv azureroutetable2.csv' but getting only from specific environment, is there a better way to do the same?
Regards
Devbrat
According to the help for Get-AzRouteTable it doesn't require any value to be specified for ResourceGroupName so we should just be able to loop through your available contexts. If you did have a large Azure Network infrastructure and they were logically organised by Resource Group, you may want to consider looping through Resource Groups too.
That being said, you can do something like this. It will create a csv for each subscription in your current session. Change the . at the beginning of the path value to set a full path if you would like.
$Contexts = Get-AzContext -ListAvailable
foreach ($Context in $Contexts) {
[void](Set-AzContext -SubscriptionId $Context.Subscription.Id)
$RouteConfig = Get-AzRouteTable -Name "prod" | Get-AzRouteConfig
# If you are using PowerShell 5.1, add '-NoTypeInformation' to the end of this command.
$RouteConfig | Export-Csv -Path ".\$($Context.Subscription.Name)_Routes.csv" -Encoding utf8 -NoClobber
}

Azure YAML pipeline: find variable in group dynamically

I have a Powershell script that is executed in my YAML pipeline during the release stage.
I can reference group variables using $(myVar). However now I have a need to reference a variable dynamically given its name.
For instance, if I have the string 'myVar', I want to search the variable group for a variable named 'myVar' and get its value.
Is this possible to achieve ?
In the end I did it like this. First I installed the Devops module for powershell so I can get group variables through devops api :
Add-AzureDevOpsAccount -OrganisationName "myor" -ProjectName "myproj" -Token "mytoken"
Write-Host -ForegroundColor DarkGreen "Getting variables group based on name"
$group = Get-AzureDevOpsVariableGroup -SearchString "dev"
Then to get a variable based on its name :
$tokenValue = $group.variables | Select-Object -ExpandProperty $someName | Select-Object -ExpandProperty Value

Powershell script only starts first VM in list

I have an Azure powershell script to start a list of VMs. When it runs it sucessfully starts the first VM in the list then nothing. No errors are reported, it appears to never return from the start command
Code as follows:
$vmList = "VM1", "VM2", "VM3"
Write-Output "Starting VMs in '$($AzureResourceGroup)' resource group";
Foreach ($vm in $vmList)
{
Write-Output "Starting VM...$vm"
$result = Start-AzureRmVM -Name $vm -ResourceGroupName AzureResourceGroup
Write-Output "Result of Start VM...$result"
}
When this run it outputs the "Starting VM....VM1", it starts the VM then nothing...
It sounds like your Start-AzureVM call is simply waiting for the VM to finish starting up.
That is, Start-AzureVm is by default a blocking, synchronous operation - despite its name[1]
To make it asynchronous, use the -AsJob switch, which uses a background job to start the VM; the call ends once that job has been created and returns a job object that you can use to track start-up progress later, via the usual *-Job cmdlets, such as Receive-Job.
[1] Regarding the name Start-AzureVM:
It is a misnomer in that PowerShell's nomenclature calls for the Start verb to exhibit asynchronous behavior:
From https://learn.microsoft.com/en-us/powershell/developer/cmdlet/approved-verbs-for-windows-powershell-commands (emphasis added):
Invoke vs. Start
The Invoke verb is used to perform an operation that is generally a synchronous operation, such as running a command. The Start verb is used to begin an operation that is generally an asynchronous operation, such as starting a process.
Note that the core Start-Sleep cmdlet is similarly misnamed.
With synchronous-by-default behavior, there is no good naming solution, because the name Invoke-AzureVm would be confusing.
The better approach - which would obviously be a breaking change - would have been to make the cmdlet asynchronous by default and offer a -Wait switch for to opt-into synchronous operation.

How do I use ARM 'outputs' values another release task?

I have an ARM template that has and outputs section like the following:
"outputs": {
"sqlServerFqdn": {
"type": "string",
"value": "[reference(concat('Microsoft.Sql/servers/', variables('sqlserverName'))).fullyQualifiedDomainName]"
},
"primaryConnectionString": {
"type": "string",
"value": "[concat('Data Source=tcp:', reference(concat('Microsoft.Sql/servers/', variables('sqlserverName'))).fullyQualifiedDomainName, ',1433;Initial Catalog=', variables('databaseName'), ';User Id=', parameters('administratorLogin'), '#', variables('sqlserverName'), ';Password=', parameters('administratorLoginPassword'), ';')]"
},
"envResourceGroup": {
"type": "string",
"value": "[parameters('hostingPlanName')]"
}
}
I have a Azure Resource Group Deployment task that uses the template. I then want to use the variable $(sqlServerFqdn) in the next task for configuration. The variable doesn't seem to just populate and I cannot find anywhere that tells me how to use 'outputs' values on release.
What do I need to do to get the variable to populate for use in configuring tasks after this ARM template runs? An example would be in the parameters to a powershell script task or another ARM template.
VSTS Azure Resource Group Deployment task has outputs section now (since January 2018). So you can set variable name in Deployment outputs of Azure Resource Group Deployment task to, for example, ResourceGroupDeploymentOutputs and add PowerShell Script task with the following inline script:
# Make outputs from resource group deployment available to subsequent tasks
$outputs = ConvertFrom-Json $($env:ResourceGroupDeploymentOutputs)
foreach ($output in $outputs.PSObject.Properties) {
Write-Host "##vso[task.setvariable variable=RGDO_$($output.Name)]$($output.Value.value)"
}
And in subsequent tasks you can use your template variables. So, for example, if you have sqlServerFqdn variable in your template it will be available as $(RGDO_sqlServerFqdn) after PowerShell Script task is completed.
Capturing this answer because I always end up at this question when searching for the solution.
There is a marketplace task which makes ARM template output parameters available further down the pipeline. But in some cases you don't have permission to purchase marketplace items for your subscription, so the following PowerShell will do the same thing. To use it you add it as a powershell script step immediately following the ARM template resource group deployment step. It will look at the last deployment and pull the output variables into pipeline variables.
param(
[string] $resourceGroupName
)
$lastDeployment = Get-AzureRmResourceGroupDeployment -ResourceGroupName $resourceGroupName | Sort Timestamp -Descending | Select -First 1
if(!$lastDeployment) {
throw "Deployment could not be found for Resource Group '$resourceGroupName'."
}
if(!$lastDeployment.Outputs) {
throw "No output parameters could be found for the last deployment of Resource Group '$resourceGroupName'."
}
foreach ($key in $lastDeployment.Outputs.Keys){
$type = $lastDeployment.Outputs.Item($key).Type
$value = $lastDeployment.Outputs.Item($key).Value
if ($type -eq "SecureString") {
Write-Host "##vso[task.setvariable variable=$key;issecret=true]$value"
}
else {
Write-Host "##vso[task.setvariable variable=$key;]$value"
}
}
Note that the environmental variables won't be available in the context of this script, but will in subsequent tasks.
The output value shown on the UI for the Visual Studio Team Services task for Azure Resource Group Deployment only seems to work for the scenario described in Eddie's answer, which is for VMs. In fact, if your deployment doesn't include VMs, you will get an error something like:
No VMs found in resource group: 'MY-RESOURCE-GROUP-NAME'. Could not
register environment in the output variable: 'myVariableName'.
For non-VM examples, I created a powershell script that runs after the RG deployment. This script, as an example, takes input variables for resource group $resourceGroupName and the name of the output variable you need $rgDeploymentOutputParameterName. You could customize and use something similar:
#get the most recent deployment for the resource group
$lastRgDeployment = Get-AzureRmResourceGroupDeployment -ResourceGroupName $resourceGroupName |
Sort Timestamp -Descending |
Select -First 1
if(!$lastRgDeployment)
{
throw "Resource Group Deployment could not be found for '$resourceGroupName'."
}
$deploymentOutputParameters = $lastRgDeployment.Outputs
if(!$deploymentOutputParameters)
{
throw "No output parameters could be found for the last deployment of '$resourceGroupName'."
}
$outputParameter = $deploymentOutputParameters.Item($rgDeploymentOutputParameterName)
if(!$outputParameter)
{
throw "No output parameter could be found with the name of '$rgDeploymentOutputParameterName'."
}
$outputParameterValue = $outputParameter.Value
# From here, use $outputParameterValue, for example:
Write-Host "##vso[task.setvariable variable=$rgDeploymentOutputParameterName;]$outputParameterValue"
In Nov 2020, after this commit - https://github.com/microsoft/azure-pipelines-tasks/commit/1173324604c3f61ce52cdcc999f6d4d7ea9ab8f9 , the variables could directly be used in the subsequent tasks in the pipeline (No powershell scripts required!!)
This is what the steps look like -
In the ARM template deployment task, give any reference name to the Deployment Outputs section under Advanced drop down. In my case I have given armOutputVariable.
See image for visual description
Now to use the value of sqlServerFqdn in the subsequent tasks, simply use it in this manner $(armOutputVariable.sqlServerFqdn.value)
For example, let's say I want to use it to override a parameter in my test task which follows the deployment so I can use it in the following manner -
Example image
To summarize all the outputs in the ARM could be used in the further steps directly in this manner (make sure you assign a reference name in the ARM template deployment step) -
$(armOutputVariable.sqlServerFqdn.value)
$(armOutputVariable.sqlServerFqdn.type)
$(armOutputVariable.primaryConnectionString.value)
$(armOutputVariable.primaryConnectionString.type)
$(armOutputVariable.envResourceGroup.value)
$(armOutputVariable.envResourceGroup.type)
First you define the Azure Resource Deployment Task and in this context the Deployment outputs
In the next step you create a PowerShell Task that takes the Deployment outputs defined above as input arguments
The PowerShell script looks as follows and assigns for each output defined in the ARM template a separate VSTS environment variable with the same name as defined in the ARM template output section. These variables can then be used in subsequent tasks.
param (
[Parameter(Mandatory=$true)]
[string]
$armOutputString
)
Write-Host $armOutputString
$armOutputObj = $armOutputString | convertfrom-json
Write-Host $armOutputObj
$armOutputObj.PSObject.Properties | ForEach-Object {
$type = ($_.value.type).ToLower()
$key = $_.name
$value = $_.value.value
if ($type -eq "securestring") {
Write-Host "##vso[task.setvariable variable=$key;issecret=true]$value"
Write-Host "Create VSTS variable with key '$key' and value '$value' of type '$type'!"
} elseif ($type -eq "string") {
Write-Host "##vso[task.setvariable variable=$key]$value"
Write-Host "Create VSTS variable with key '$key' and value '$value' of type '$type'!"
} else {
Throw "Type '$type' not supported!"
}
}
In a subsequent task you can access the environment variables either by passing them as argument via '$(varName)' (this works for SecureString too) or e.g. in a PowerShell script via $env:varName (this does not work for SecureString)
VSTS allows setting variables in powershell scripts which you can use in other tasks.
The syntax is
Write-Host "##vso[task.setvariable variable=myvariable;]myvalue"
You can have an inline Powershell script which can set the required variable to consume in yet to be executed tasks.You can access it like $(myvariable).
You may need to system.debug variable to true to use this.
Read more details here.
You just need to add an output variable name for "Azure Resource Group Deployment" task like following:
And then use the variable in "PowerShell on Target Machines" task:
"PowerShell on Target Machines" task will use the resource configured in "Azure Resource Group Deployment" task:
Output variables:
Create/update action of the Azure Resource Group task now produces an
output variable during execution. The output variable can be used to
refer to the resource group object in the subsequent tasks. For
example "PowerShell on Target Machine" task can now refer to resource
group output variable as '$(variableName)' so that it can execute the
powershell script on the resource group VM targets.
Limitation: Output variable produced during execution will have
details about VM hostname(s) and (public) ports, if any. Credentials
to connect to the VM host(s) are to be provided explicitly in the
subsequent tasks.
Refer to this link for more details: Azure Resource Group Deployment Task

How to set IIS ApplicationPool "Private Memory Limit" value using PowerShell

I'm using PowerShell for auto-deploying website, and recently found AppPool setting which cannot be set with PS. or at least I did not manage to find out how to do it.
$appPool = $serverManager.ApplicationPools.Add($sitename)...
I need to set "Private Memory Limit" to some value, but it looks like there is no such property at ApplicationPool or ApplicationPoolRecycling object.
Does anybode know workaround for this issue?
This script uses Get-Webconfiguration and Set-WebConfiguration to get the value for private memory for all app pools. You can set each individually or set the application pool defaults for them to inherit. I have commented out the line which actually does the set.
import-module webadministration
$applicationPoolsPath = "/system.applicationHost/applicationPools"
$applicationPools = Get-WebConfiguration $applicationPoolsPath
foreach ($appPool in $applicationPools.Collection)
{
$appPoolPath = "$applicationPoolsPath/add[#name='$($appPool.Name)']"
Get-WebConfiguration "$appPoolPath/recycling/periodicRestart/#privateMemory"
# Set-WebConfiguration "$appPoolPath/recycling/periodicRestart/#privateMemory" -Value 1000
}
I am adding an answer because I was having trouble using the existing.
import-module webadministration
$applicationPools = Get-ChildItem IIS:\AppPools
foreach ($appPool in $applicationPools){
Set-ItemProperty IIS:\AppPools\$appPool.name `
-Name recycling.periodicrestart.privateMemory -Value 7000000
}
Your responses helped me come up with a solution to a problem that I was having with my WSUS servers. I knew that it was the application pool size for the WsusPool that was giving me problems so I made the following PS script and applied it to the root OU for my WSUS servers (I have 3) I was getting a connection error and my option to Reset Server Node didn't help. The event viewer had Event IDs 12002, 12012, 12032, 12022, 12042, 12052, and 12072.
Set-WebConfiguration "/system.applicationHost/applicationPools/add[#name='WsusPool']/recycling/periodicRestart/#privateMemory" -Value 0

Resources