InstallAzModule.ps1
<#PSScriptInfo
.VERSION 1.2 .GUID 70e6f41b-5941-4ec7-b797-60b96a301319 .AUTHOR Ted Sdoukos .COMPANYNAME .COPYRIGHT .TAGS AzureAutomation,Runbook .LICENSEURI .PROJECTURI .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES .PRIVATEDATA #> <# .SYNOPSIS Installs Az modules to automation account. .DESCRIPTION This Azure Automation runbook installs the Az modules selected into an Azure Automation account with the module versions published to the PowerShell Gallery. Prerequisite: an Azure Automation account with an Azure Run As account credential. .PARAMETER ResourceGroupName The Azure resource group name. .PARAMETER AutomationAccountName The Azure Automation account name. .PARAMETER All This will install the Az module and all dependancies .NOTES Credit to: https://stackoverflow.com/questions/60847861/how-to-import-modules-into-azure-automation-account-using-powershell Credit to: https://github.com/microsoft/AzureAutomation-Account-Modules-Update #> [CmdletBinding()] Param( [Parameter(Mandatory)]$ResourceGroupName, [Parameter(Mandatory)]$AutomationAccountName, [string[]]$AzModule, [string]$ModuleVersion, [bool]$All, [bool]$Wait, $PsGalleryApiUrl = 'https://www.powershellgallery.com/api/v2' ) #region Functions Function Get-AzModuleInfo { [CmdletBinding()] Param($ModuleName) $ModuleUrlFormat = "$PsGalleryApiUrl/Search()?`$filter={1}&searchTerm=%27{0}%27&targetFramework=%27%27&includePrerelease=false&`$skip=0&`$top=40" $CurrentModuleURL = $ModuleUrlFormat -f $ModuleName, 'IsLatestVersion' $SearchID = Invoke-RestMethod -Method Get -Uri $CurrentModuleURL -UseBasicParsing | Where-Object -FilterScript { $_.Title.InnerText -eq $moduleName } $packageDetails = Invoke-RestMethod -Method Get -UseBasicParsing -Uri $SearchID.id $packageDetails } Function Get-AzModuleDependancy { [CmdletBinding()] Param($ModuleName) $output = $(Get-AZModuleInfo -ModuleName $ModuleName).entry.properties.Dependencies $output -replace ':\[\d+\.\d+\.\d, \d*\.*\d*\.*\d*\]*\)*:' -split '\|' } Function Install-AzModuleDependancy { [CmdletBinding()] Param( $ModuleName, $ModuleVersion ) foreach ($M in $ModuleName) { Write-Verbose -Message "Getting information for $M" $module = (Get-AzModuleInfo -ModuleName $M).Entry.Properties If ($ModuleVersion) { $link = "$PsGalleryApiUrl/package/$($module.id)/$($module.Version)" } else { $link = "$PsGalleryApiUrl/package/$($module.id)" } # Find the actual blob storage location of the module do { $Link = (Invoke-WebRequest -Uri $link -MaximumRedirection 0 -UseBasicParsing -ErrorAction Ignore).Headers.Location } until ($link.Contains('.nupkg')) $status = Get-AzureRmAutomationModule -AutomationAccountName $AutomationAccountName -ResourceGroupName $ResourceGroupName -Name $module.id -ErrorAction SilentlyContinue If ( (-Not($status)) -or ($status.Version -ne $module.Version)) { Write-Verbose -Message "Currently installing the $($module.id) - Version-$($module.version) dependancy" $null = New-AzureRmAutomationModule -AutomationAccountName $automationAccountName -Name $module.id -ContentLink $link -ResourceGroupName $resourceGroupName Do { $State = Get-AzureRmAutomationModule -ResourceGroupName $resourceGroupName -AutomationAccountName $automationAccountName -Name $module.id Start-Sleep -Seconds 1 Write-Verbose -Message "Waiting on install of $($module.id)" Write-Progress -Activity "Installing $($module.id)" -Status "Current status is: $($state.ProvisioningState)" } While ($state.ProvisioningState -eq 'Creating') } If ($state.ProvisioningState -eq 'Failed') { Throw "Unable to install $($module.id)" } While ($state.ProvisioningState -ne 'Succeeded') { $State = Get-AzureRmAutomationModule -ResourceGroupName $ResourceGroupName -AutomationAccountName $AutomationAccountName -Name $module.id Start-Sleep -Seconds 1 } If ($state.ProvisioningState -eq 'Succeeded') { Write-Progress -Activity "Installing $($module.id)" -Status "Current status is: $($state.ProvisioningState)" Write-Verbose -Message "Installation of $($module.id) successful" } } } Function Install-AzAutomationModule { <# .Synopsis Installs Az modules in your azure automation account .DESCRIPTION Long description .EXAMPLE Install-AzAutomationModule -ResourceGroupName 'ContosoResourceGroup' -AutomationAccountName 'ContosoAutomationAccount' -All .EXAMPLE I'll do more examples later .NOTES Author: Ted Sdoukos Credit to: https://stackoverflow.com/questions/60847861/how-to-import-modules-into-azure-automation-account-using-powershell Credit to: https://github.com/microsoft/AzureAutomation-Account-Modules-Update REQUIEMENTS: Az.Automation module, PowerShell v5 or better #> [CmdletBinding(DefaultParameterSetName = 'All')] Param( ) If ($All) { $AzModule = 'Az' } $DepList = New-Object -TypeName System.Collections.ArrayList $List = $AzModule | ForEach-Object { Get-AzModuleDependancy -ModuleName $_ } foreach ($item in $List) { Get-AzModuleDependancy -ModuleName $item | ForEach-Object { If (($_ -ne '') -and ($DepList -notcontains $_)) { $null = $DepList.Add($_) } } } Write-Verbose -Message "Dependant list:`n$DepList" If ($DepList) { Install-AzModuleDependancy -ModuleName $DepList } $PsGalleryApiUrl = 'https://www.powershellgallery.com/api/v2' If ($All) { $AzModule = Get-AzModuleDependancy -ModuleName 'Az' } $AzModule | ForEach-Object { if (($_) -notin $DepList) { $InstallState = Get-AzureRmAutomationModule -AutomationAccountName $automationAccountName -ResourceGroupName $resourceGroupName ` -Name $_ -ErrorAction SilentlyContinue If (($InstallState.ProvisioningState -ne 'Succeeded') -or (-not($InstallState))) { $module = (Get-AzModuleInfo -ModuleName $_).Entry.Properties If ($ModuleVersion) { $link = "$PsGalleryApiUrl/package/$($_)/$($module.Version)" } else { $link = "$PsGalleryApiUrl/package/$($_)" } do { $TryCount = 0 $Link = (Invoke-WebRequest -Uri $link -MaximumRedirection 0 -UseBasicParsing -ErrorAction Ignore).Headers.Location $TryCount++ } until ($link.Contains('.nupkg') -or $TryCount -gt 10) $ModName = $Module.Id Write-Verbose -Message "Currently installing $ModName" Write-Progress -Activity "Installing $ModName" $null = New-AzureRmAutomationModule -AutomationAccountName $automationAccountName -Name $ModName -ContentLink $link -ResourceGroupName $resourceGroupName If ($Wait) { Do { $Status = Get-AzureRmAutomationModule -ResourceGroupName $ResourceGroupName -AutomationAccountName $AutomationAccountName -Name $_ Start-Sleep -Seconds 1 } While ($Status.ProvisioningState -eq 'Creating') Write-Verbose -Message "Provisioning of $_ is complete. Current status is $($Status.ProvisioningState)" } #Added a sleep in here to alleviate errors # Most common error: Index was out of range. Must be non-negative and less than the size of the collection. Start-Sleep -Seconds 2 } } } $AzModule | ForEach-Object { Get-AzureRmAutomationModule -ResourceGroupName $ResourceGroupName -AutomationAccountName $AutomationAccountName -Name $_ | Select-Object -Property Name, ProvisioningState } } function Connect-AzureAutomation { try { $RunAsConnection = Get-AutomationConnection -Name 'AzureRunAsConnection' $RunAsConnection | Select-Object -Property * Write-Output "Logging in to Azure ($AzureEnvironment)..." if (!$RunAsConnection.ApplicationId) { $ErrorMessage = "Connection 'AzureRunAsConnection' is incompatible type." throw $ErrorMessage } Add-AzureRmAccount -ServicePrincipal -TenantId $RunAsConnection.TenantId -ApplicationId $RunAsConnection.ApplicationId ` -CertificateThumbprint $RunAsConnection.CertificateThumbprint Select-AzureRmSubscription -SubscriptionId $RunAsConnection.SubscriptionID | Write-Verbose } catch { if (!$RunAsConnection) { $_.Exception $ErrorMessage = "Connection 'AzureRunAsConnection' not found." throw $ErrorMessage } throw $_.Exception } } #EndRegion Functions #region main script Connect-AzureAutomation Install-AzAutomationModule $failCheck = Get-AzureRmAutomationModule -ResourceGroupName $ResourceGroupName -AutomationAccountName $AutomationAccountName | Where-Object -FilterScript { $_.ProvisioningState -eq 'Failed' } If ($failCheck) { Write-Warning -Message "The following modules failed to install: $($failCheck.Name | ForEach-Object {"`n$_"})" Write-Warning -Message ` "Type the following to retry: `nInstall-AzAutomationModule -ResourceGroupName $ResourceGroupName -AutomationAccountName $AutomationAccountName -AzModule $($failCheck.name -join ', ') -Wait" } #endRegion |