Format-AzTemplate.ps1
function Format-AzTemplate { <# .Synopsis Formats a resource manager template in the desired order. .Description Sorts the content in a resource manager template. .Link https://github.com/Azure/azure-quickstart-templates/blob/master/1-CONTRIBUTION-GUIDE/best-practices.md #> param( # The path to a file [Parameter(Mandatory=$true,ParameterSetName='FilePath',ValueFromPipelineByPropertyName=$true,Position=0)] [Alias('Fullname')] [string]$FilePath, # The path to a file [Parameter(Mandatory=$true,ParameterSetName='TemplateObject',ValueFromPipelineByPropertyName=$true,Position=0)] [PSObject]$TemplateObject) begin { $topLevelPropertyOrder = '$schema','contentVersion', 'apiProfile', 'parameters','functions','variables', 'resources', 'outputs' $resourceOrder = 'comments', 'condition', 'type', 'apiVersion', 'name', 'location', 'sku', 'kind', 'dependsOn', 'tags', 'copy' $sortProperties = { param([Parameter(ValueFromPipeline=$true)]$in, [string[]]$order,[string[]]$LastOrder, [switch]$Recurse) process { $newObject = [PSObject]::new() # create a new object to output. if (-not $in) { return $null } if ([string], [int], [float], [bool] -contains $in.GetType()) { return $in } if ($Recurse) { $recurseSplat = @{} + $PSBoundParameters $recurseSplat.Remove('In') } foreach ($propName in $order) { # Walk thru the properties in the preferred order. if ($in.$propName) { # If the object had that property $newPropValue = if ($Recurse -and $in.$propName -is [Array]) { @($in.$propName | & $MyInvocation.MyCommand.ScriptBlock @recurseSplat) } else { $in.$propName } $newProp = [Management.Automation.PSNoteProperty]::new($propName, $newPropValue) $newObject.psobject.properties.add($newProp) # add it to the new object $in.psobject.properties.remove($propName) # and remove it from the original object. } } if (@($in.psobject.properties).Count) { # If the template object had any properties left foreach ($prop in $in.psobject.properties) { # add them to the new object in the order they were found. if ($LastOrder -contains $prop.Name) { continue } $newProp = [Management.Automation.PSNoteProperty]::new($prop.Name, $in.($prop.Name)) $newObject.psobject.properties.add($newProp) } } if ($LastOrder) { foreach ($propName in $LastOrder) { if ($in.$propName) { # If the object had that property $newPropValue = if ($Recurse -and $in.$propName -is [Array]) { $in.$propName | & $MyInvocation.MyCommand.ScriptBlock @recurseSplat } else { $in.$propName } $newProp = [Management.Automation.PSNoteProperty]::new($propName, $newPropValue) $newObject.psobject.properties.add($newProp) # add it to the new object $in.psobject.properties.remove($propName) # and remove it from the original object. } } } $newObject } } } process { if ($PSCmdlet.ParameterSetName -eq 'FilePath') { # If we're provided the path to a file $templateObject = Import-Json -FilePath $FilePath if (-not $templateObject) { return } # If it was null, return. Format-AzTemplate -TemplateObject $TemplateObject # Call ourself, passing in the contents of the file. return } if ($PSCmdlet.ParameterSetName -eq 'TemplateObject') { # If we're provided a template object if ($templateObject -is [Collections.IDictionary]) { $templateObject = [PSCustomObject]$templateObject } $templateObjectCopy = @{} foreach ($prop in $templateObject.psobject.properties) { $templateObjectCopy[$prop.Name] = $prop.Value } $newTemplate = [PSCustomObject]$TemplateObjectCopy | & $sortProperties -Order $topLevelPropertyOrder # sort the top-level properties. if ($newTemplate.resources) { # If the template had resources, sort them $newTemplate.resources =@( $newTemplate.resources | & $sortProperties -Order $resourceOrder -LastOrder 'properties', 'resources' -Recurse ) } return $newTemplate # then return the newly formatted object. } } } |