AzureAutomationPack.psm1
######################################################################################### # # Copyright (c) Microsoft Corporation. All rights reserved. # # AzureAutomationPack Module # ######################################################################################### Microsoft.PowerShell.Core\Set-StrictMode -Version Latest Microsoft.PowerShell.Utility\Import-LocalizedData LocalizedData -filename AzureAutomationPack.Resource.psd1 #region *-AzureAutomationPack cmdlets function Publish-AzureAutomationPack { <# .ExternalHelp AzureAutomationPack.psm1-help.xml #> [CmdletBinding(SupportsShouldProcess=$true, PositionalBinding=$false, DefaultParameterSetName="DefaultAutomationPackParameterSet")] Param ( [Parameter(Mandatory=$true, Position=0, ParameterSetName='DefaultAutomationPackParameterSet', ValueFromPipelineByPropertyName=$true)] [Parameter(Mandatory=$true, Position=0, ParameterSetName='ValidateAutomationPackParameterSet', ValueFromPipelineByPropertyName=$true)] [ValidateNotNullOrEmpty()] [string] $Path, [Parameter()] [ValidateNotNullOrEmpty()] [string] $NuGetApiKey, [Parameter()] [ValidateNotNullOrEmpty()] [string] $Repository, [Parameter(Mandatory=$true, ParameterSetName='ValidateAutomationPackParameterSet')] [ValidateNotNullOrEmpty()] [string] $SubscriptionId, [Parameter(Mandatory=$true, ParameterSetName='ValidateAutomationPackParameterSet')] [ValidateNotNullOrEmpty()] [string] $ResourceGroupName, [Parameter(Mandatory=$true, ParameterSetName='ValidateAutomationPackParameterSet')] [ValidateNotNullOrEmpty()] [string] $Description, [Parameter(Mandatory=$true, ParameterSetName='ValidateAutomationPackParameterSet')] [ValidateNotNullOrEmpty()] [Version] $Version, [Parameter(Mandatory=$true, ParameterSetName='ValidateAutomationPackParameterSet')] [ValidateNotNullOrEmpty()] [string] $Author, [Parameter(ParameterSetName='ValidateAutomationPackParameterSet')] [ValidateNotNullOrEmpty()] [Guid] $Guid, [Parameter(ParameterSetName='ValidateAutomationPackParameterSet')] [ValidateNotNullOrEmpty()] [String] $CompanyName, [Parameter(ParameterSetName='ValidateAutomationPackParameterSet')] [ValidateNotNullOrEmpty()] [string] $Copyright, [Parameter(ParameterSetName='ValidateAutomationPackParameterSet')] [ValidateNotNullOrEmpty()] [string[]] $Tags, [Parameter(ParameterSetName='ValidateAutomationPackParameterSet')] [ValidateNotNullOrEmpty()] [Uri] $ProjectUri, [Parameter(ParameterSetName='ValidateAutomationPackParameterSet')] [ValidateNotNullOrEmpty()] [Uri] $LicenseUri, [Parameter(ParameterSetName='ValidateAutomationPackParameterSet')] [ValidateNotNullOrEmpty()] [Uri] $IconUri, [Parameter(ParameterSetName='ValidateAutomationPackParameterSet')] [string] $ReleaseNotes ) Process { <# Publishes the specified automation pack to the target repository. Following operations are performed by the Publish-AzureAutomationPack cmdlet: - Checks the existence of root template and its parameters.json file - Validates the root template using ARM publish API and gets the compiled json contents. Azure subscription ID and Resource group name are required for validating the root template with its parameters json file. - Compiled json file will be placed under the automation pack base - Creates the PowerShell module manifest file using specified metadata parameters and resource details from the compiled json file. - Validates the existence of specified repository name. NuGetApiKey is required for a web-based repository. - Automation pack folder will be published to the specified repository #> $ev = $null $repo = PowerShellGet\Get-PSRepository -Name $Repository -ErrorVariable ev if($ev) { return } if($SubscriptionId -and (Validate-AzureAutomationPackFolder -Path $Path -CallerPSCmdlet $PSCmdlet)) { if($Repository) { $null = $PSBoundParameters.Remove('Repository') } if($NuGetApiKey) { $null = $PSBoundParameters.Remove('NuGetApiKey') } $ev = $null New-AzureAutomationPackManifest @PSBoundParameters -ErrorVariable ev if($ev) { return } } $publishParameters = @{Path = $Path} if($Repository) { $publishParameters['Repository'] = $Repository } if($NuGetApiKey) { $publishParameters['NuGetApiKey'] = $NuGetApiKey } if(Validate-AzureAutomationPackFolder -Path $Path -ValidateManifest -CallerPSCmdlet $PSCmdlet) { PowerShellGet\Publish-Module @publishParameters ` -Verbose:$VerbosePreference ` -Debug:$DebugPreference ` -WarningAction $WarningPreference ` -ErrorAction $ErrorActionPreference } } } function Find-AzureAutomationPack { <# .ExternalHelp AzureAutomationPack.psm1-help.xml #> [CmdletBinding()] [outputtype("PSCustomObject[]")] Param ( [Parameter(ValueFromPipelineByPropertyName=$true, Position=0)] [ValidateNotNullOrEmpty()] [string[]] $Name, [Parameter()] [ValidateNotNull()] [Version] $MinimumVersion, [Parameter()] [ValidateNotNull()] [Version] $MaximumVersion, [Parameter()] [ValidateNotNull()] [Version] $RequiredVersion, [Parameter()] [switch] $AllVersions, [Parameter()] [ValidateNotNull()] [string] $Filter, [Parameter()] [ValidateNotNull()] [string[]] $Tag, [Parameter()] [ValidateNotNullOrEmpty()] [string[]] $Repository ) Begin { } Process { $Tag += 'AzureAutomationPack' $PSBoundParameters['Tag'] = $Tag PowerShellGet\Find-Module @PSBoundParameters | Where-Object {$_.Tags -contains 'AzureAutomationPack'} } } function Save-AzureAutomationPack { <# .ExternalHelp AzureAutomationPack.psm1-help.xml #> [CmdletBinding(DefaultParameterSetName='NameAndPathParameterSet', SupportsShouldProcess=$true)] Param ( [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true, Position=0, ParameterSetName='NameAndPathParameterSet')] [ValidateNotNullOrEmpty()] [string[]] $Name, [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, Position=0, ParameterSetName='InputOjectAndPathParameterSet')] [ValidateNotNull()] [PSCustomObject[]] $InputObject, [Parameter(ValueFromPipelineByPropertyName=$true, ParameterSetName='NameAndPathParameterSet')] [ValidateNotNull()] [Version] $MinimumVersion, [Parameter(ValueFromPipelineByPropertyName=$true, ParameterSetName='NameAndPathParameterSet')] [ValidateNotNull()] [Version] $MaximumVersion, [Parameter(ValueFromPipelineByPropertyName=$true, ParameterSetName='NameAndPathParameterSet')] [ValidateNotNull()] [Version] $RequiredVersion, [Parameter(ValueFromPipelineByPropertyName=$true, ParameterSetName='NameAndPathParameterSet')] [ValidateNotNullOrEmpty()] [string[]] $Repository, [Parameter(Mandatory=$true, ParameterSetName='NameAndPathParameterSet')] [Parameter(Mandatory=$true, ParameterSetName='InputOjectAndPathParameterSet')] [string] $Path, [Parameter()] [switch] $Force ) Process { PowerShellGet\Save-Module @PSBoundParameters } } #endregion *-AzureAutomationPack cmdlets function Validate-RootTemplate { [CmdletBinding()] Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $Path, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $TemplateFilePath, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $TemplateParameterFilePath, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $SubscriptionId, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $ResourceGroupName ) Process { if(-not (Microsoft.PowerShell.Management\Test-Path -Path $TemplateFilePath -PathType Leaf)) { $message = $LocalizedData.PathNotFound -f ($TemplateFilePath) ThrowError -ExceptionName 'System.ArgumentException' ` -ExceptionMessage $message ` -ErrorId 'TemplateFilePathNotFound' ` -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidArgument ` -ExceptionObject $TemplateFilePath return } if(-not (Microsoft.PowerShell.Management\Test-Path -Path $TemplateParameterFilePath -PathType Leaf)) { $message = $LocalizedData.PathNotFound -f ($TemplateParameterFilePath) ThrowError -ExceptionName 'System.ArgumentException' ` -ExceptionMessage $message ` -ErrorId 'TemplateParameterFilePathNotFound' ` -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidArgument ` -ExceptionObject $TemplateParameterFilePath return } $authenticationContext = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("https://login.windows.net/common") $resource = "https://management.core.windows.net/" $clientId = "1950a258-227b-4e31-a9cf-717495945fc2" $redirectUri = New-Object Uri("urn:ietf:wg:oauth:2.0:oob") $promptBehavior = [Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior]::Auto Write-Progress $LocalizedData.AcquiringAccessToken $result = $authenticationContext.AcquireToken($resource, $clientId, $redirectUri, $promptBehavior) Write-Progress $LocalizedData.AcquiringAccessToken -Completed if (-not $result) { $errorMessage = $LocalizedData.FailedToAcquireJWTToken ThrowError -ExceptionName 'System.InvalidOperationException' ` -ExceptionMessage $errorMessage ` -ErrorId 'FailedToAcquireJWTToken' ` -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidOperation return } $DeploymentName = "$(Get-Random)_DeploymentName" $ApiVersion = "2015-01-01" $Headers = @{ 'Host' = 'management.azure.com' 'Content-Length' = '0' 'Authorization' = $result.CreateAuthorizationHeader() } $uri = "https://management.azure.com/subscriptions/$SubscriptionId/resourcegroups/$ResourceGroupName/providers/microsoft.resources/deployments/$DeploymentName/validate?api-version=$ApiVersion" $mainTemplateContents = Get-Content -Path $TemplateFilePath -Raw $mainTemplateParameterContents = Get-Content -Path $TemplateParameterFilePath -Raw $bodyContent = @" { "properties": { "template": $mainTemplateContents, "mode": "Complete", "parameters": $mainTemplateParameterContents } } "@ Write-Progress ($LocalizedData.VatidatingTheRootTemplate -f $TemplateFilePath) $response = Invoke-WebRequest -Method POST ` -Uri $uri ` -ContentType application/json ` -Body $bodyContent ` -Headers $Headers ` -UseBasicParsing Write-Progress ($LocalizedData.VatidatingTheRootTemplate -f $TemplateFilePath) -Completed if(-not $response -or $response.StatusDescription -ne 'OK') { $errorMessage = $LocalizedData.UpdateToValidateTheTemplateFile ThrowError -ExceptionName 'System.InvalidOperationException' ` -ExceptionMessage $errorMessage ` -ErrorId 'AzureAccountIsNotLoggedIn' ` -CallerPSCmdlet $PSCmdlet ` -ErrorCategory InvalidOperation return } # Copy the validated json contents to "$($AutomationPackName)-Compiled.json" file $CompiledRootTemplateFilePath = Join-Path -Path $Path ` -ChildPath "$(Split-Path -Path $Path -Leaf)-Compiled.json" Microsoft.PowerShell.Management\Set-Content -Path $CompiledRootTemplateFilePath -Value $response.Content -Force $compiledRootTemplate = ConvertFrom-Json -InputObject $response.Content $Resources = @{} $supportedResourceTypes = @( 'runbooks' 'Configurations' 'schedules' 'certificates' 'modules' 'credentials' 'variables' 'connections' 'Webhooks' ) $compiledRootTemplate.properties.dependencies | ForEach-Object { $resourceType = $_.resourceType -split '/' | Select-Object -Last 1 $resourceName = $_.resourceName -split '/' | Select-Object -Last 1 if($resourceType -and $resourceName -and ($supportedResourceTypes -contains $resourceType)) { $Resources["$($resourceType)_$($resourceName)"] = @{ResourceName = $resourceName; ResourceType = $resourceType; } } } $Tags = @() $Resources.Values | ForEach-Object {$Tags += "AutoPackResource_$($_.ResourceType)_$($_.ResourceName)" } return $Tags } } function Validate-AzureAutomationPackFolder { [CmdletBinding()] Param ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $Path, [Parameter()] [switch] $ValidateManifest, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCmdlet] $CallerPSCmdlet ) Process { $AutoPackPath = Resolve-Path -Path $Path if(-not $AutoPackPath -or -not (Microsoft.PowerShell.Management\Test-Path -Path $AutoPackPath -PathType Container)) { $AutoPackPath = Resolve-Path -LiteralPath $Path if(-not $AutoPackPath -or -not (Microsoft.PowerShell.Management\Test-Path -LiteralPath $AutoPackPath -PathType Container)) { $message = $LocalizedData.PathNotFound -f ($Path) ThrowError -ExceptionName 'System.ArgumentException' ` -ExceptionMessage $message ` -ErrorId 'AutomationPackPathNotFound' ` -CallerPSCmdlet $CallerPSCmdlet ` -ErrorCategory InvalidArgument ` -ExceptionObject $Path return } } $ModuleBasePath = $AutoPackPath.ProviderPath $AutomationPackName = Split-Path -Path $ModuleBasePath -Leaf $RootTemplateFileName = "$AutomationPackName.json" $TemplateParameterFileName = "$AutomationPackName-parameters.json" $ManifestFileName = "$AutomationPackName.psd1" $TemplateFilePath = Join-Path -Path $ModuleBasePath -ChildPath $RootTemplateFileName $TemplateParameterFilePath = Join-Path -Path $ModuleBasePath -ChildPath $TemplateParameterFileName $ManifestFilePath = Join-Path -Path $ModuleBasePath -ChildPath $ManifestFileName if(-not (Microsoft.PowerShell.Management\Test-Path -Path $TemplateFilePath -PathType Leaf)) { $message = $LocalizedData.TemplateFileIsMissing -f ($TemplateFilePath) ThrowError -ExceptionName 'System.ArgumentException' ` -ExceptionMessage $message ` -ErrorId 'TemplateFileIsMissing' ` -CallerPSCmdlet $CallerPSCmdlet ` -ErrorCategory InvalidArgument ` -ExceptionObject $TemplateFilePath return } if(-not (Microsoft.PowerShell.Management\Test-Path -Path $TemplateParameterFilePath -PathType Leaf)) { $message = $LocalizedData.TemplateParameterFileIsMissing -f ($TemplateParameterFileName) ThrowError -ExceptionName 'System.ArgumentException' ` -ExceptionMessage $message ` -ErrorId 'TemplateParameterFilePathNotFound' ` -CallerPSCmdlet $CallerPSCmdlet ` -ErrorCategory InvalidArgument ` -ExceptionObject $TemplateParameterFileName return } if($ValidateManifest -and -not (Microsoft.PowerShell.Management\Test-Path -Path $ManifestFilePath -PathType Leaf)) { $message = $LocalizedData.MissingAutomationPackManifestFile -f ($ManifestFilePath) ThrowError -ExceptionName 'System.InvalidOperationException' ` -ExceptionMessage $message ` -ErrorId 'MissingAutomationPackManifestFile' ` -CallerPSCmdlet $CallerPSCmdlet ` -ErrorCategory InvalidOperation ` -ExceptionObject $ManifestFilePath return } return $ModuleBasePath } } function New-AzureAutomationPackManifest { <# .ExternalHelp AzureAutomationPack.psm1-help.xml #> [CmdletBinding(SupportsShouldProcess=$true, PositionalBinding=$false)] Param ( [Parameter(Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)] [ValidateNotNullOrEmpty()] [string] $Path, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $SubscriptionId, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $ResourceGroupName, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $Description, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [Version] $Version, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $Author, [ValidateNotNullOrEmpty()] [Guid] $Guid, [Parameter()] [ValidateNotNullOrEmpty()] [String] $CompanyName, [Parameter()] [ValidateNotNullOrEmpty()] [string] $Copyright, [Parameter()] [ValidateNotNullOrEmpty()] [string[]] $Tags, [Parameter()] [ValidateNotNullOrEmpty()] [Uri] $ProjectUri, [Parameter()] [ValidateNotNullOrEmpty()] [Uri] $LicenseUri, [Parameter()] [ValidateNotNullOrEmpty()] [Uri] $IconUri, [Parameter()] [string] $ReleaseNotes ) Process { $null = $PSBoundParameters.Remove('SubscriptionId') $null = $PSBoundParameters.Remove('ResourceGroupName') if($Version) { $PSBoundParameters['ModuleVersion'] = $Version $null = $PSBoundParameters.Remove('Version') } $ModuleBasePath = Validate-AzureAutomationPackFolder -Path $Path -CallerPSCmdlet $PSCmdlet if(-not $ModuleBasePath) { return } $AutomationPackName = Split-Path -Path $ModuleBasePath -Leaf $RootTemplateFileName = "$AutomationPackName.json" $TemplateParameterFileName = "$AutomationPackName-parameters.json" $ManifestFileName = "$AutomationPackName.psd1" $TemplateFilePath = Join-Path -Path $ModuleBasePath -ChildPath $RootTemplateFileName $TemplateParameterFilePath = Join-Path -Path $ModuleBasePath -ChildPath $TemplateParameterFileName $ManifestFilePath = Join-Path -Path $ModuleBasePath -ChildPath $ManifestFileName $PSBoundParameters['FileList'] = @($RootTemplateFileName, $TemplateParameterFileName) $PSBoundParameters['Path'] = $ManifestFilePath $resourceTags = Validate-RootTemplate -Path $Path ` -TemplateFilePath $TemplateFilePath ` -TemplateParameterFilePath $TemplateParameterFilePath ` -SubscriptionId $SubscriptionId ` -ResourceGroupName $ResourceGroupName if(-not $resourceTags) { return } $Tags += @('AzureAutomationPack', 'Azure', 'Automation') $Tags += $resourceTags $PSBoundParameters['Tags'] = $Tags Microsoft.PowerShell.Core\New-ModuleManifest @PSBoundParameters } } function Update-AzureAutomationPackManifest { <# .ExternalHelp AzureAutomationPack.psm1-help.xml #> [CmdletBinding(SupportsShouldProcess=$true, PositionalBinding=$false)] Param ( [Parameter(Mandatory=$true, Position=0, ValueFromPipelineByPropertyName=$true)] [ValidateNotNullOrEmpty()] [string] $Path, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $SubscriptionId, [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $ResourceGroupName, [ValidateNotNullOrEmpty()] [Guid] $Guid, [Parameter()] [ValidateNotNullOrEmpty()] [string] $Author, [Parameter()] [ValidateNotNullOrEmpty()] [String] $CompanyName, [Parameter()] [ValidateNotNullOrEmpty()] [string] $Copyright, [Parameter()] [ValidateNotNullOrEmpty()] [Version] $Version, [Parameter()] [ValidateNotNullOrEmpty()] [string] $Description, [Parameter()] [ValidateNotNullOrEmpty()] [string[]] $Tags, [Parameter()] [ValidateNotNullOrEmpty()] [Uri] $ProjectUri, [Parameter()] [ValidateNotNullOrEmpty()] [Uri] $LicenseUri, [Parameter()] [ValidateNotNullOrEmpty()] [Uri] $IconUri, [Parameter()] [string] $ReleaseNotes ) Process { $null = $PSBoundParameters.Remove('SubscriptionId') $null = $PSBoundParameters.Remove('ResourceGroupName') if($Version) { $PSBoundParameters['ModuleVersion'] = $Version $null = $PSBoundParameters.Remove('Version') } $ModuleBasePath = Validate-AzureAutomationPackFolder -Path $Path -ValidateManifest -CallerPSCmdlet $PSCmdlet if(-not $ModuleBasePath) { return } $AutomationPackName = Split-Path -Path $ModuleBasePath -Leaf $RootTemplateFileName = "$AutomationPackName.json" $TemplateParameterFileName = "$AutomationPackName-parameters.json" $ManifestFileName = "$AutomationPackName.psd1" $TemplateFilePath = Join-Path -Path $ModuleBasePath -ChildPath $RootTemplateFileName $TemplateParameterFilePath = Join-Path -Path $ModuleBasePath -ChildPath $TemplateParameterFileName $ManifestFilePath = Join-Path -Path $ModuleBasePath -ChildPath $ManifestFileName $PSBoundParameters['FileList'] = @($RootTemplateFileName, $TemplateParameterFileName) $PSBoundParameters['Path'] = $ManifestFilePath if(-not $Tags) { $moduleInfo = Microsoft.PowerShell.Core\Test-ModuleManifest -Path $ManifestFilePath # Remove the existing Automation pack resource tags, so that updated resource tags will be added later if($moduleInfo -and $moduleInfo.Tags) { $Tags = $moduleInfo.Tags | Where-Object {-not ($_.StartsWith('AutoPackResource_', [System.StringComparison]::OrdinalIgnoreCase))} } } else { $Tags += @('AzureAutomationPack', 'Azure', 'Automation') } $resourceTags = Validate-RootTemplate -Path $Path ` -TemplateFilePath $TemplateFilePath ` -TemplateParameterFilePath $TemplateParameterFilePath ` -SubscriptionId $SubscriptionId ` -ResourceGroupName $ResourceGroupName if(-not $resourceTags) { return } $Tags += $resourceTags $PSBoundParameters['Tags'] = $Tags PowerShellGet\Update-ModuleManifest @PSBoundParameters } } #region Common functions # Utility to throw an errorrecord function ThrowError { param ( [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.Management.Automation.PSCmdlet] $CallerPSCmdlet, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $ExceptionName, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $ExceptionMessage, [System.Object] $ExceptionObject, [parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [System.String] $ErrorId, [parameter(Mandatory = $true)] [ValidateNotNull()] [System.Management.Automation.ErrorCategory] $ErrorCategory ) $exception = New-Object $ExceptionName $ExceptionMessage; $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $ErrorId, $ErrorCategory, $ExceptionObject $CallerPSCmdlet.ThrowTerminatingError($errorRecord) } #endregion Export-ModuleMember -Function 'Publish-AzureAutomationPack', 'Find-AzureAutomationPack', 'Save-AzureAutomationPack', 'New-AzureAutomationPackManifest', 'Update-AzureAutomationPackManifest' |