The last days I was troubleshooting an issue where I was unable to deploy modules to Azure Automation via Powershell… What did I want to do? Add the xActiveDirectory module to my Automation Account.
So let’s look at the documentation of the “New-AzureRmAutomationModule” cmdlet ;
-ContentLink<Uri>
Specifies the URL of the .zip file that contains a module that this cmdlet imports.
Which would give something like…
$dscActiveDirectoryLink = "<a class="linkified" href="https://github.com/PowerShell/xActiveDirectory/archive/2.10.0.0-PSGallery.zip" target="_blank" rel="nofollow noreferrer">https://github.com/PowerShell/xActiveDirectory/archive/2.10.0.0-PSGallery.zip</a>" New-AzureRmAutomationModule -ResourceGroupName $ResourceGroupNameAutomationAccount -AutomationAccountName $automationAccountName -Name xActiveDirectory -ContentLink $dscActiveDirectoryLink
So what was my logic here? I went to the project website and took the latest release (bundled as a zip file). Sounds great? It failed… Everytime I got the following error ;
Error extracting the activities from module xActiveDirectory-2.10.0.0-PSGallery. Extraction failed with the following error: Orchestrator.Shared.AsyncModuleImport.ModuleImportException: Cannot import the module of name xActiveDirectory-2.10.0.0-PSGallery, as the module structure was invalid.
After a hint by Joe Levy, it struck me… The command was expecting a nuget package! Underneath, this is also a zipfile. So when obtaining that package and using it for the -ContentLink, all went smooth!
Update with Code Sniplet (with help from Joe Levy!) ;
# Requires that authentication to Azure is already established before running param( [Parameter(Mandatory=$true)] [String] $ResourceGroupName, [Parameter(Mandatory=$true)] [String] $AutomationAccountName, [Parameter(Mandatory=$true)] [String] $ModuleName, # if not specified latest version will be imported [Parameter(Mandatory=$false)] [String] $ModuleVersion ) $Url = "https://www.powershellgallery.com/api/v2/Search()?`$filter=IsLatestVersion&searchTerm=%27$ModuleName $ModuleVersion%27&targetFramework=%27%27&includePrerelease=false&`$skip=0&`$top=40" $SearchResult = Invoke-RestMethod -Method Get -Uri $Url if(!$SearchResult) { Write-Error "Could not find module '$ModuleName' on PowerShell Gallery." } elseif($SearchResult.C -and $SearchResult.Length -gt 1) { Write-Error "Module name '$ModuleName' returned multiple results. Please specify an exact module name." } else { $PackageDetails = Invoke-RestMethod -Method Get -Uri $SearchResult.id if(!$ModuleVersion) { $ModuleVersion = $PackageDetails.entry.properties.version } $ModuleContentUrl = "https://www.powershellgallery.com/api/v2/package/$ModuleName/$ModuleVersion" # Test if the module/version combination exists try { Invoke-RestMethod $ModuleContentUrl -ErrorAction Stop | Out-Null $Stop = $False } catch { Write-Error "Module with name '$ModuleName' of version '$ModuleVersion' does not exist. Are you sure the version specified is correct?" $Stop = $True } if(!$Stop) { # Find the actual blob storage location of the module do { $ActualUrl = $ModuleContentUrl $ModuleContentUrl = (Invoke-WebRequest -Uri $ModuleContentUrl -MaximumRedirection 0 -ErrorAction Ignore).Headers.Location } while($ModuleContentUrl -ne $Null) New-AzureRmAutomationModule ` -ResourceGroupName $ResourceGroupName ` -AutomationAccountName $AutomationAccountName ` -Name $ModuleName ` -ContentLink $ActualUrl } }