AxAzureBlueprint.psm1
function Connect-AzureBlueprint { [CmdletBinding()] param ( [parameter(mandatory=$true)] [string]$ManagementGroupName, [switch]$Force ) begin { $Script:AzureContext = Get-AzContext if (!$Script:AzureContext){ Login-AzAccount $Script:AzureContext = Get-AzContext } if (!$Script:AzureContext){ Write-Warning "Could not connect to Azure" Continue } } process { $ManagementGroups = Get-AzManagementGroup if ($ManagementGroups.Name -notcontains $ManagementGroupName){ if ($Force){ New-AzManagementGroup -GroupName $ManagementGroupName } else { Write-Warning "$ManagementGroupName not found. Use the Force switch if you want to create it" continue } } $Script:ManagementGroupName = $ManagementGroupName $Script:AzureProfile = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile $Script:AzureProfileClient = New-Object -TypeName Microsoft.Azure.Commands.ResourceManager.Common.RMProfileClient -ArgumentList ($Script:AzureProfile) $Script:BlueprintPrefix = 'https://management.azure.com/providers/Microsoft.Management/managementGroups/{0}/providers/Microsoft.Blueprint/blueprints' -f $Script:ManagementGroupName $Script:APIversion = '?api-version=2017-11-11-preview' Write-Verbose "Connected to $ManagementGroupName" } end { } } function Get-AzureBlueprint { [CmdletBinding()] param ( [Parameter (ParameterSetName = 'Specific', Mandatory = $True)] [string]$Blueprint, [Parameter (ParameterSetName = 'All')] [Switch]$ListAll, [switch]$AsObject ) begin { if (!$Script:ManagementGroupName){Connect-AzureBlueprint} Get-Header $ParamHash = @{ Uri = '' Method = 'Get' Headers = $Script:Header UseBasicParsing = $True } } process { if ($ListAll){ $ParamHash.Uri = '{0}{2}' -f $Script:BlueprintPrefix,$Blueprint,$Script:APIversion } else { $ParamHash.Uri = '{0}/{1}{2}' -f $Script:BlueprintPrefix,$Blueprint,$Script:APIversion } try { $Blueprint = Invoke-WebRequest @ParamHash | Select-Object -ExpandProperty Content } catch { Write-Warning "$Blueprint not found!" continue } } end { if ($AsObject){ $Blueprint | ConvertFrom-Json } else { $Blueprint } } } function Get-AzureBlueprintArtifact { [CmdletBinding()] param ( [string]$Blueprint, [string[]]$Artifact, [switch]$ListAllArtifacts, [switch]$AsObject ) begin { if (!$Script:ManagementGroupName){Connect-AzureBlueprint} Get-Header $ParamHash = @{ Uri = '' Method = 'GET' Headers = $Script:Header UseBasicParsing = $True } } process { $ArtifactJson = @() if ($Artifact){ $ArtifactJson += foreach ($a in $Artifact){ $ParamHash.Uri = '{0}/{1}/artifacts/{2}{3}' -f $Script:BlueprintPrefix,$Blueprint,$a,$Script:APIversion Invoke-WebRequest @ParamHash | Select-Object -ExpandProperty Content } } elseif ($ListAllArtifacts){ $ParamHash.Uri = '{0}/{1}/artifacts{2}' -f $Script:BlueprintPrefix,$Blueprint,$Script:APIversion $ArtifactJson += Invoke-WebRequest @ParamHash| Select-Object -ExpandProperty Content } else { Write-warning "Please provide specific artifact names or the -ListAllArtifacts switch" continue } } end { if ($AsObject){ if ($ListAllArtifacts){ $ArtifactJson | ConvertFrom-Json | Select-Object -ExpandProperty value } else { $ArtifactJson | ConvertFrom-Json } } else {$ArtifactJson} } } function Get-AzureBlueprintImportParameters { [CmdletBinding()] param ( [ValidateScript( {$_.exists})] [System.IO.DirectoryInfo]$ARMTemplateDirectory, [switch]$UseFilenameAsArtifactname ) begin { } process { $Files = Get-ChildItem $ARMTemplateDirectory -filter '*json' $Objects = foreach ($File in $Files){ if (!$UseFilenameAsArtifactname){ $ArtifactName = Read-Host -Prompt "ArtifactName for $($File.name)" } else { $ArtifactName = $File.Basename } $ResourceGroupName = Read-Host -Prompt "Resource Group name for $($File.name)" [PSCustomObject]@{ ARMTemplateJson = $File.FullName ArtifactName = $ArtifactName ResourceGroup = $ResourceGroupName } } } end { $Objects } } function Import-AzureBlueprintArtifact { [CmdletBinding(DefaultParameterSetName = 'SingleFile')] param ( [parameter(ValueFromPipelineByPropertyName=$true, ParameterSetName = 'SingleFile')] [ValidateScript( {$_.exists})] [System.IO.FileInfo]$ARMTemplateJson, [parameter(ParameterSetName = 'Directory')] [ValidateScript( {$_.exists})] [System.IO.DirectoryInfo]$ARMTemplateDirectory, [ValidateScript( {$_.exists})] [System.Io.DirectoryInfo]$TargetDirectory, [parameter(ValueFromPipelineByPropertyName=$true, mandatory = $true)] [string]$ResourceGroup, [parameter(ValueFromPipelineByPropertyName=$true, ParameterSetName = 'SingleFile', mandatory = $true)] [string]$ArtifactName, [string]$NewBlueprintName = 'blueprint' ) begin { } process { if ($PSCmdlet.ParameterSetName -eq 'Directory') { $ARMTemplateJsons = Get-ChildItem $ARMTemplateDirectory -Filter '*json' } else { $ARMTemplateJsons = $ARMTemplateJson } foreach ($ARMTemplateJson in $ARMTemplateJsons) { if ($PSCmdlet.ParameterSetName -eq 'Directory') { $ArtifactName = $ARMTemplateJson.BaseName } $JsonObjects = Get-JsonObject -BlueprintFolder $TargetDirectory.FullName $ARMTemplate = ConvertFrom-Json -InputObject $(Get-Content $ARMTemplateJson.Fullname -Raw) $CurrentParameters = $ARMTemplate.parameters if (!$CurrentParameters ) { $ARMTemplate continue } $MarkedParameters = Set-ArtifactParameter -ParameterObject $CurrentParameters -ResourceGroup $ResourceGroup Write-Verbose "Blueprint parameters has been calculated" $Blueprint = $JsonObjects | Where-Object {!$_.kind} if ($Blueprint) { Write-Verbose "Updating $($Blueprint.Filepath)" $Blueprint = Get-JsonObject -BlueprintFile $Blueprint.Filepath $Blueprint.JsonObject = Add-BlueprintParameter -BlueprintJsonObject $Blueprint.JsonObject -ArtifactParameters $MarkedParameters -ResourceGroup $ResourceGroup } else { $Blueprintfile = '{0}\blueprint.json' -f $TargetDirectory.FullName Write-Warning "No blueprint found - creating $Blueprintfile" $Blueprint = [PSCustomObject]@{ Filepath = $Blueprintfile Content = '' Kind = $Null BaseName = $NewBlueprintName Name = '{0}.json' -f $NewBlueprintName Blueprint = $TargetDirectory.Name JsonObject = New-BlueprintJsonObject -Parameters $MarkedParameters -ResourceGroup $ResourceGroup } } $Blueprint.Content = Convertto-Json -InputObject $Blueprint.JsonObject -Depth 99 $Blueprint.Content | Out-file $Blueprint.Filepath $PairHash = Set-ArtifactParameter -ParameterObject $CurrentParameters -ResourceGroup $ResourceGroup -AsPairHash $PropParams = New-Object -TypeName PSCustomObject $TemplParams = New-Object -TypeName PSCustomObject foreach ($key in $PairHash.Keys) { $ParamHash = @{ InputObject = $PropParams NotePropertyName = $key NotePropertyValue = [PsCustomObject]@{value = "[parameters('{0}')]" -f $PairHash[$key]} } Add-Member @ParamHash $ParamHash = @{ InputObject = $TemplParams NotePropertyName = $key NotePropertyValue = [PsCustomObject]@{type = $CurrentParameters.$key.type} } Add-Member @ParamHash } $ARMTemplate.parameters = $TemplParams $Artifact = [PSCustomObject]@{ kind = 'template' properties = [PSCustomObject]@{ template = $ARMTemplate resourceGroup = $ResourceGroup parameters = $PropParams } } $ArtifactFile = '{0}\{1}.json' -f $TargetDirectory.FullName, $ArtifactName Convertto-Json -InputObject $Artifact -Depth 99 | Out-File $ArtifactFile } } end { } } function Remove-AzureBlueprint { [CmdletBinding()] param ( [string]$Blueprint, [string[]]$Artifact, [switch]$Recurse ) begin { $Ids = @() if ($Recurse){ $Ids = Get-AzureBlueprint -Blueprint $Blueprint -AsObject | Select-Object -ExpandProperty Id } else { $Ids += Get-AzureBlueprintArtifact -Blueprint $Blueprint -Artifact $Artifact -AsObject | Select-Object -ExpandProperty Id } } process { foreach ($Id in $Ids){ Get-Header $ParamHash = @{ Uri = 'https://management.azure.com{0}{1}' -f $Id,$Script:APIversion Method = 'DELETE' Headers = $Script:Header UseBasicParsing = $True } $Name = $Id -split '/' | Select-Object -last 1 try { $Req = Invoke-WebRequest @ParamHash Write-Verbose "$Name has been deleted" } catch { Write-Warning "$Name could not be delete" } } } end { } } function Set-AzureBlueprint { [CmdletBinding()] param ( [Parameter(Mandatory=$true)] [ValidateScript({$_.exists})] [System.IO.DirectoryInfo]$BlueprintFolder, [switch]$Passthru ) begin { if (!$Script:ManagementGroupName){Connect-AzureBlueprint} } process { $JsonObjects = Get-JsonObject -BlueprintFolder $BlueprintFolder.FullName Get-Header foreach ($JsonObject in $($JsonObjects | Sort-Object kind)){ $Name = '{0}{1}' -f $JsonObject.Blueprint,$(if ($JsonObject.kind){'/artifacts/{0}' -f $JsonObject.BaseName} else {''}) $ParamHash = @{ Uri = '{0}/{1}{2}' -f $Script:BlueprintPrefix,$Name,$Script:APIversion Method = 'PUT' Headers = $Script:Header Body = $JsonObject.Content UseBasicParsing = $True } Write-Verbose "Uri: $($ParamHash['Uri'])" try { $Put = Invoke-WebRequest @ParamHash -ErrorVariable Fail if ($Passthru){$Put.Content} } catch { Write-Warning "Could not set $($JsonObject.Name)" if ($Fail.message){ $Message = try { $fail.message | ConvertFrom-Json | Select-Object -ExpandProperty error | Select-Object -ExpandProperty message | Out-String } catch { $Fail.message } } else { $Message = $fail } Write-Warning $Message } } } end { } } function Add-BlueprintParameter { [CmdletBinding()] param ( $BlueprintJsonObject, $ArtifactParameters, $ResourceGroupName ) begin { } process { $AddMemberParams = @() if (-not $BlueprintJsonObject.properties.resourceGroups.$ResourceGroupName) { Write-Verbose "Adding ResourceGroup $ResourceGroupName to the blueprint" $AddMemberParams += @{ InputObject = $BlueprintJsonObject.properties.resourceGroups NotePropertyName = $ResourceGroupName NotePropertyValue = @{} ErrorAction = 'Stop' } } $ArtifactNames = Get-Member -MemberType NoteProperty -InputObject $ArtifactParameters | Select-Object -expand Name foreach ($ArtifactName in $ArtifactNames) { $AddMemberParams += @{ InputObject = $BlueprintJsonObject.properties.parameters NotePropertyName = $ArtifactName NotePropertyValue = $ArtifactParameters.$ArtifactName ErrorAction = 'Stop' } } foreach ($Set in $AddMemberParams) { try { Add-Member @Set } catch { Write-Verbose "$($Set.NotePropertyName) is already in the Blueprint" } } } end { $BlueprintJsonObject } } function Get-Header { [CmdletBinding()] param ( [switch]$Passthru ) begin { } process { $Token = $Script:AzureProfileClient.AcquireAccessToken($Script:AzureContext.Subscription.TenantId) Write-Verbose "Accesstoken: $($Token.AccessToken)" $Script:Header = @{ 'Content-Type'='application/json' 'Authorization'='Bearer ' + $Token.AccessToken } } end { if ($Passthru){ $Script:Header } } } function Get-JsonObject { [CmdletBinding(DefaultParameterSetName='Directory')] param ( [Parameter(ParameterSetName='Directory')] [ValidateScript({$_.exists})] [System.IO.DirectoryInfo]$BlueprintFolder, [Parameter(ParameterSetName='Files')] [ValidateScript({$_.exists})] [System.IO.FileInfo[]]$BlueprintFile ) begin { If ($PSCmdlet.ParameterSetName -eq 'Directory'){ if ($JsonFiles = Get-ChildItem $BlueprintFolder.Fullname -Filter *.json){ } else { Write-Warning "No json files found in $BlueprintFolder" } } else { $JsonFiles = $BlueprintFile } Write-Verbose $("Doing these files",$JsonFiles.Name | Out-String) } process { foreach ($File in $JsonFiles) { $FileContent = Get-Content $File.Fullname -Raw try { $Object = ConvertFrom-Json -InputObject $FileContent [PSCustomObject]@{ Filepath = $File.Fullname Content = $FileContent Kind = $Object.kind Name = $File.Name BaseName = $File.BaseName Blueprint = $BlueprintFolder.Name JsonObject = $Object } } catch { Write-Warning "$($File.Fullname) is not a valid JSON" } } } end { } } function New-BlueprintJsonObject { [CmdletBinding()] param ( $Parameters, $Description = 'Autogenerated Blueprint', $TargetScope = 'subscription', $ResourceGroup ) begin { } process { [PSCustomObject]@{ properties = @{ description = $Description targetScope = $TargetScope parameters = $Parameters resourceGroups = @{ $ResourceGroup = @{} } } } } end { } } function Set-ArtifactParameter { [CmdletBinding()] param ( [PSCustomObject]$ParameterObject, $ResourceGroupName, $NameStyle = 'ResourceGroupName_ParameterName', $DisplayNameStyle = 'ParameterName (ResourceGroupName)', [switch]$AsPairHash ) begin { $NameStyle = $NameStyle -replace 'ParameterName', '{0}' -replace 'ResourceGroupName', '{1}' $DisplayNameStyle = $DisplayNameStyle -replace 'ParameterName', '{0}' -replace 'ResourceGroupName', '{1}' $CurrentNPs = Get-Member -InputObject $ParameterObject -MemberType NoteProperty | Select-Object -ExpandProperty Name } process { if ($AsPairHash) { $PairHash = @{} foreach ($CurrentNP in $CurrentNPs) { $PairHash.Add($CurrentNP, $($NameStyle -f $CurrentNP, $ResourceGroupName)) } $PairHash } else { $NewObject = New-Object -TypeName PSCustomObject foreach ($CurrentNP in $CurrentNPs) { $NewNPName = $NameStyle -f $CurrentNP, $ResourceGroupName $NewNPdisplayName = $DisplayNameStyle -f $CurrentNP, $ResourceGroupName Add-Member -InputObject $NewObject -NotePropertyName $NewNPName -NotePropertyValue $ParameterObject.$CurrentNP If (-not $NewObject.$NewNPName.metadata){ Add-Member -InputObject $NewObject.$NewNPName -NotePropertyName metadata -NotePropertyValue @{} } if (-not $NewObject.$NewNPName.metadata.displayName){ Add-Member -InputObject $NewObject.$NewNPName.metadata -NotePropertyName displayName -NotePropertyValue $NewNPdisplayName } } $NewObject } } end { } } |