{"id":11,"date":"2017-04-05T15:00:21","date_gmt":"2017-04-05T21:00:21","guid":{"rendered":"http:\/\/peterod.azurewebsites.net\/?p=11"},"modified":"2017-04-05T15:00:21","modified_gmt":"2017-04-05T21:00:21","slug":"programmatically-adding-elements-to-an-azure-arm-template-using-powershell","status":"publish","type":"post","link":"http:\/\/www.perktime.org\/index.php\/2017\/04\/05\/programmatically-adding-elements-to-an-azure-arm-template-using-powershell\/","title":{"rendered":"Programmatically adding elements to an Azure ARM template using PowerShell"},"content":{"rendered":"<p><span style=\"font-size: small;\">One of the great features of Azure ARM templates is that you can do template linking so that you can split your deployment into smaller components. This promotes better reuse as well as less unwieldy template files. However, one limitation today is that it is not easily possible to do conditional logic within your ARM template (see this blob post for one possible method using arrays: <\/span><a title=\"https:\/\/jodygblog.wordpress.com\/2016\/05\/02\/conditional-parameters-for-arm-templates\/\" href=\"https:\/\/jodygblog.wordpress.com\/2016\/05\/02\/conditional-parameters-for-arm-templates\/\"><span style=\"font-size: small;\">https:\/\/jodygblog.wordpress.com\/2016\/05\/02\/conditional-parameters-for-arm-templates\/<\/span><\/a><span style=\"font-size: small;\">). However, this technique does not work in all situations. For example, if you wanted a base Windows\/Linux template and wanted to have one base template that utilizes an availability set and one that did not, you would need to create two versions of the base template. This is definitely not ideal because now you have to make changes to two base templates instead of one. <\/span><br \/>\n<span style=\"font-size: small;\">A solution to this is to use PowerShell\u2019s ability to load a JSON file, make changes as required in memory and then emit the JSON file back to disk. You still have two base template files but only one of them will need to be edited manually and the other will be a generated file. <\/span><br \/>\n<span style=\"font-size: small;\">Using our example of having two base Windows templates with and without an availability set, this section demonstrates how the additional JSON can be added using PowerShell. We would first need to have variables for the additional JSON for elements like parameters, resources, and properties:<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">param<br \/>\n(<br \/>\n[string]$InputJsonFilePath,<br \/>\n[string]$OutputJsonFilePath,<br \/>\n[bool]$IsManagedAvailabilitySet #if this is a managed availability set, set this to true otherwise false<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">)<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">$availabilitySetParam =@&#8221;<br \/>\n{<br \/>\n&#8220;type&#8221;:&#8221;string&#8221;,<br \/>\n&#8220;metadata&#8221;:<br \/>\n{<br \/>\n&#8220;description&#8221;: &#8220;Name of the availability set&#8221;<br \/>\n}<br \/>\n}<br \/>\n&#8220;@<br \/>\n$faultDomainParam = @&#8221;<br \/>\n{<br \/>\n&#8220;type&#8221;: &#8220;int&#8221;,<br \/>\n&#8220;defaultValue&#8221;: 3,<br \/>\n&#8220;metadata&#8221;: {<br \/>\n&#8220;description&#8221;: &#8220;Fault Domain Count&#8221;<br \/>\n}<br \/>\n}<br \/>\n&#8220;@<br \/>\n$updateDomainParam = @&#8221;<br \/>\n{<br \/>\n&#8220;type&#8221;: &#8220;int&#8221;,<br \/>\n&#8220;defaultValue&#8221;: 5,<br \/>\n&#8220;metadata&#8221;: {<br \/>\n&#8220;description&#8221;: &#8220;Update Domain Count&#8221;<br \/>\n}<br \/>\n}<br \/>\n&#8220;@<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">$availabilitySetResourceJson = @&#8221;<br \/>\n{<br \/>\n&#8220;type&#8221;: &#8220;Microsoft.Compute\/availabilitySets&#8221;,<br \/>\n&#8220;name&#8221;: &#8220;[parameters(&#8216;availabilitySetName&#8217;)]&#8221;,<br \/>\n&#8220;apiVersion&#8221;: &#8220;2016-04-30-preview&#8221;,<br \/>\n&#8220;location&#8221;: &#8220;[resourceGroup().location]&#8221;,<br \/>\n&#8220;tags&#8221;: {<br \/>\n&#8220;displayName&#8221;: &#8220;Availability Set&#8221;<br \/>\n},<br \/>\n&#8220;properties&#8221;: {<br \/>\n&#8220;platformFaultDomainCount&#8221;: &#8220;[parameters(&#8216;faultDomainCount&#8217;)]&#8221;,<br \/>\n&#8220;platformUpdateDomainCount&#8221;: &#8220;[parameters(&#8216;updateDomainCount&#8217;)]&#8221;,<br \/>\n&#8220;managed&#8221;: false<br \/>\n}<br \/>\n}<br \/>\n&#8220;@<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">$availabilitySetVMPropertiesJson = @&#8221;<br \/>\n{<br \/>\n&#8220;id&#8221;: &#8220;[resourceId(&#8216;Microsoft.Compute\/availabilitySets&#8217; , parameters(&#8216;availabilitySetName&#8217;))]&#8221;<br \/>\n}<br \/>\n&#8220;@<\/span><br \/>\n&nbsp;<br \/>\n<span style=\"font-size: small;\">Next we would load our JSON file into memory and convert each of the JSON variables into JSON objects:<\/span><br \/>\n&nbsp;<br \/>\n<span style=\"font-family: Courier New; font-size: small;\">$JsonFile = Get-Content $InputJsonFilePath |Out-String<br \/>\n$JsonFile = ConvertFrom-Json $JsonFile<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">$availabilitySet = ConvertFrom-Json -InputObject $availabilitySetParam<br \/>\n$faultDomain = ConvertFrom-Json -InputObject $faultDomainParam<br \/>\n$updateDomain = ConvertFrom-Json -InputObject $updateDomainParam<br \/>\n$availabilitySetResource = ConvertFrom-Json -InputObject $availabilitySetResourceJson<br \/>\n$availabilitySetProperty = ConvertFrom-Json -InputObject $availabilitySetVMPropertiesJson<\/span><br \/>\n&nbsp;<br \/>\n<span style=\"font-size: small;\">Now this is where the real magic happens. <img decoding=\"async\" class=\"wlEmoticon wlEmoticon-smile\" src=\"https:\/\/msdnshared.blob.core.windows.net\/media\/2017\/04\/wlEmoticon-smile1.png\" alt=\"Smile\" \/> We use <strong>Add-Member<\/strong> to add the new parameters:<\/span><br \/>\n&nbsp;<br \/>\n<span style=\"font-family: Courier New; font-size: small;\">$JsonFile.parameters |Add-Member -Name &#8220;availabilitySetName&#8221; -MemberType NoteProperty -Value $availabilitySet<br \/>\n$JsonFile.parameters |Add-Member -Name &#8220;faultDomainCount&#8221; -MemberType NoteProperty -Value $faultDomain<br \/>\n$JsonFile.parameters |Add-Member -Name &#8220;updateDomainCount&#8221; -MemberType NoteProperty -Value $updateDomain<\/span><br \/>\n&nbsp;<br \/>\n<span style=\"font-size: small;\">For elements in the JSON file that are arrays, we use \u2018+\u2019 to add an element to the array:<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">$JsonFile.resources = $JsonFile.Resources + $availabilitySetResource<\/span><br \/>\n&nbsp;<br \/>\n<span style=\"font-size: small;\">We now add a \u201cDependsOn\u201d as well as an AvailabilitySet property:<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">foreach ($vm in $JsonFile.Resources)<br \/>\n{<br \/>\nif ($vm.type -eq &#8216;Microsoft.Compute\/virtualMachines&#8217;)<br \/>\n{<br \/>\n$vm.dependsOn = $vm.dependsOn + &#8220;[resourceId(&#8216;Microsoft.Compute\/availabilitySets&#8217;, parameters(&#8216;availabilitySetName&#8217;))]&#8221;<br \/>\n$vm.properties |Add-Member -Name &#8220;availabilitySet&#8221; -MemberType NoteProperty -Value $availabilitySetProperty<br \/>\n}<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">\u00a0\u00a0\u00a0 if ($vm.type -eq &#8216;Microsoft.Compute\/availabilitySets&#8217;)<br \/>\n{<br \/>\nif ($IsManagedAvailabilitySet)<br \/>\n{<br \/>\n$vm.properties.managed = $true<br \/>\n}<br \/>\nelse<br \/>\n{<br \/>\n$vm.properties.managed = $false<br \/>\n}<br \/>\n}<br \/>\n}<\/span><br \/>\n<span style=\"font-size: small;\">Lastly we write the JSON file back to disk. Note that by default ConvertTo-JSON has a depth of 2 which will likely not be sufficient. Also, it will escape characters by default. <\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">ConvertTo-Json -Depth 10 -InputObject $JsonFile |Out-File $OutputJsonFilePath \u2013Force<\/span><br \/>\n<span style=\"font-size: small;\">To remove the escape characters (which is purely for readability), you can reload the file and do a string replacement:<\/span><br \/>\n&nbsp;<br \/>\n<span style=\"font-size: small;\">$jsonFile = $jsonFile.Replace(&#8216;\\u0027&#8217;, &#8220;&#8216;&#8221;)<br \/>\n$jsonFile |Out-File -FilePath $OutputJsonFilePath -force<\/span><br \/>\n&nbsp;<br \/>\n<span style=\"font-size: small;\">Well, that\u2019s all there is to it. You could use this same technique in other ways such as having base template with or without managed disks, copying parameters to multiple files, etc. <\/span><br \/>\n&nbsp;<br \/>\n<span style=\"font-size: small;\"><strong>Updated: Corrected API version in availability set resource JSON to 2016-04-30-preview. <\/strong><\/span><br \/>\n&nbsp;<br \/>\n&nbsp;<br \/>\n<span style=\"font-size: small;\">Here is the complete PowerShell for your convenience:<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">param<br \/>\n(<br \/>\n[string]$InputJsonFilePath,<br \/>\n[string]$OutputJsonFilePath,<br \/>\n[bool]$IsManagedAvailabilitySet #if this is a managed availability set, set this to true otherwise false<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">)<br \/>\n$availabilitySetParam =@&#8221;<br \/>\n{<br \/>\n&#8220;type&#8221;:&#8221;string&#8221;,<br \/>\n&#8220;metadata&#8221;:<br \/>\n{<br \/>\n&#8220;description&#8221;: &#8220;Name of the availability set&#8221;<br \/>\n}<br \/>\n}<br \/>\n&#8220;@<br \/>\n$faultDomainParam = @&#8221;<br \/>\n{<br \/>\n&#8220;type&#8221;: &#8220;int&#8221;,<br \/>\n&#8220;defaultValue&#8221;: 3,<br \/>\n&#8220;metadata&#8221;: {<br \/>\n&#8220;description&#8221;: &#8220;Fault Domain Count&#8221;<br \/>\n}<br \/>\n}<br \/>\n&#8220;@<br \/>\n$updateDomainParam = @&#8221;<br \/>\n{<br \/>\n&#8220;type&#8221;: &#8220;int&#8221;,<br \/>\n&#8220;defaultValue&#8221;: 5,<br \/>\n&#8220;metadata&#8221;: {<br \/>\n&#8220;description&#8221;: &#8220;Update Domain Count&#8221;<br \/>\n}<br \/>\n}<br \/>\n&#8220;@<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">$availabilitySetResourceJson = @&#8221;<br \/>\n{<br \/>\n&#8220;type&#8221;: &#8220;Microsoft.Compute\/availabilitySets&#8221;,<br \/>\n&#8220;name&#8221;: &#8220;[parameters(&#8216;availabilitySetName&#8217;)]&#8221;,<br \/>\n&#8220;apiVersion&#8221;: &#8220;2016-04-30-preview&#8221;,<br \/>\n&#8220;location&#8221;: &#8220;[resourceGroup().location]&#8221;,<br \/>\n&#8220;tags&#8221;: {<br \/>\n&#8220;displayName&#8221;: &#8220;Availability Set&#8221;<br \/>\n},<br \/>\n&#8220;properties&#8221;: {<br \/>\n&#8220;platformFaultDomainCount&#8221;: &#8220;[parameters(&#8216;faultDomainCount&#8217;)]&#8221;,<br \/>\n&#8220;platformUpdateDomainCount&#8221;: &#8220;[parameters(&#8216;updateDomainCount&#8217;)]&#8221;,<br \/>\n&#8220;managed&#8221;: false<br \/>\n}<br \/>\n}<br \/>\n&#8220;@<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">$availabilitySetVMPropertiesJson = @&#8221;<br \/>\n{<br \/>\n&#8220;id&#8221;: &#8220;[resourceId(&#8216;Microsoft.Compute\/availabilitySets&#8217; , parameters(&#8216;availabilitySetName&#8217;))]&#8221;<br \/>\n}<br \/>\n&#8220;@<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">$JsonFile = Get-Content $InputJsonFilePath |Out-String<br \/>\n$JsonFile = ConvertFrom-Json $JsonFile<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">$availabilitySet = ConvertFrom-Json -InputObject $availabilitySetParam<br \/>\n$faultDomain = ConvertFrom-Json -InputObject $faultDomainParam<br \/>\n$updateDomain = ConvertFrom-Json -InputObject $updateDomainParam<br \/>\n$availabilitySetResource = ConvertFrom-Json -InputObject $availabilitySetResourceJson<br \/>\n$availabilitySetProperty = ConvertFrom-Json -InputObject $availabilitySetVMPropertiesJson<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">$JsonFile.parameters |Add-Member -Name &#8220;availabilitySetName&#8221; -MemberType NoteProperty -Value $availabilitySet<br \/>\n$JsonFile.parameters |Add-Member -Name &#8220;faultDomainCount&#8221; -MemberType NoteProperty -Value $faultDomain<br \/>\n$JsonFile.parameters |Add-Member -Name &#8220;updateDomainCount&#8221; -MemberType NoteProperty -Value $updateDomain<br \/>\n$JsonFile.resources = $JsonFile.Resources + $availabilitySetResource<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">foreach ($vm in $JsonFile.Resources)<br \/>\n{<br \/>\nif ($vm.type -eq &#8216;Microsoft.Compute\/virtualMachines&#8217;)<br \/>\n{<br \/>\n$vm.dependsOn = $vm.dependsOn + &#8220;[resourceId(&#8216;Microsoft.Compute\/availabilitySets&#8217;, parameters(&#8216;availabilitySetName&#8217;))]&#8221;<br \/>\n$vm.properties |Add-Member -Name &#8220;availabilitySet&#8221; -MemberType NoteProperty -Value $availabilitySetProperty<br \/>\n}<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">\u00a0\u00a0\u00a0 if ($vm.type -eq &#8216;Microsoft.Compute\/availabilitySets&#8217;)<br \/>\n{<br \/>\nif ($IsManagedAvailabilitySet)<br \/>\n{<br \/>\n$vm.properties.managed = true<br \/>\n}<br \/>\nelse<br \/>\n{<br \/>\n$vm.properties.managed = false<br \/>\n}<br \/>\n}<br \/>\n}<\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">ConvertTo-Json -Depth 10 -InputObject $JsonFile |Out-File $OutputJsonFilePath -Force\u00a0 <\/span><br \/>\n<span style=\"font-family: Courier New; font-size: small;\">$JsonFile = Get-Content $OutputJsonFilePath |Out-String<br \/>\n$jsonFile = $jsonFile.Replace(&#8216;\\u0027&#8217;, &#8220;&#8216;&#8221;)<br \/>\n$jsonFile |Out-File -FilePath $OutputJsonFilePath -force<\/span><br \/>\n&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>One of the great features of Azure ARM templates is that you can do template linking so that you can split your deployment into smaller components. This promotes better reuse as well as less unwieldy template files. However, one limitation today is that it is not easily possible to do conditional logic within your ARM &hellip; <\/p>\n<p class=\"link-more\"><a href=\"http:\/\/www.perktime.org\/index.php\/2017\/04\/05\/programmatically-adding-elements-to-an-azure-arm-template-using-powershell\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Programmatically adding elements to an Azure ARM template using PowerShell&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-11","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"http:\/\/www.perktime.org\/index.php\/wp-json\/wp\/v2\/posts\/11","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.perktime.org\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.perktime.org\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.perktime.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.perktime.org\/index.php\/wp-json\/wp\/v2\/comments?post=11"}],"version-history":[{"count":1,"href":"http:\/\/www.perktime.org\/index.php\/wp-json\/wp\/v2\/posts\/11\/revisions"}],"predecessor-version":[{"id":23,"href":"http:\/\/www.perktime.org\/index.php\/wp-json\/wp\/v2\/posts\/11\/revisions\/23"}],"wp:attachment":[{"href":"http:\/\/www.perktime.org\/index.php\/wp-json\/wp\/v2\/media?parent=11"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.perktime.org\/index.php\/wp-json\/wp\/v2\/categories?post=11"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.perktime.org\/index.php\/wp-json\/wp\/v2\/tags?post=11"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}