Azure Automation : Adding modules via Powershell

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
    }
}

Advertisements

3 thoughts on “Azure Automation : Adding modules via Powershell

    1. @Adrian : I’ve updated the post with a code sniplet (obtained from Joe & tweaked a bit) to download the modules & import them automatically

      I guess that’ll put you on your way… 😉

    2. Never mind, figured it out. Turns out I need to upload dependencies manually beforehand. Helpful post though, thanks.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s