How to install a PowerShell module in an Azure Function

13,865

Solution 1

Azure Functions support for PowerShell scripting is currently in an experimental stage. The following scenarios are supported:

  1. Azure Functions will be able to support customers bringing their own modules. These modules will reside in a folder named modules that is located in the same directory where the PowerShell script resides. In the Kudu console for a sample Function, the directory structure would look like the following,

enter image description here

  1. We will not support customers installing their own modules using the Install-Module cmdlet, however, customers may upload their modules into the modules folder.

  2. All modules in the modules folder will be loaded automatically, so customers will not have to explicitly use the Import-Module cmdlet.

  3. We will support script, binary and manifest modules. These modules will reside in a flat structure within the modules folder. An example layout is as follows:

    enter image description here

In the context of what you are trying to achieve, here are some suggested steps to make sure that the AWSPowerShell module is loaded.

  1. Install AWSPowerShell locally on your development machine. You will need to upload all the contents under \AWSPowerShell\3.3.5.0

  2. Using the Kudu interface, upload the installed dependencies of AWSPowerShell to a modules folder residing in your Function's directory. To do so, open the the Portal UI for your Function App and click on the Function App Settings button.

  3. Next, click on the Go to Kudu button to launch the Kudu console. You should see a snapshot similar to the following,

    enter image description here

  4. In the cmd console prompt, navigate to your Function's folder, create a modules directory and upload all the contents from \AWSPowerShell\3.3.5.0 to the modules directory.

You should end up with a modules folder that has a list of files similar to the snapshot below:

enter image description here

  1. Run your Function. For instance, given the following script for my Function,

    if (-not (Get-Module -Name "AWSPowerShell")) { Write-Output "AWSPowerShell not installed"; } else { Write-Output "AWSPowerShell installed"; }

when executed, the log output is as follows,

2016-10-11T18:26:01.486 Function started (Id=582b69aa-6236-436d-81c5-c08ada8ae674)
2016-10-11T18:26:03.267 Loaded modules:
/AWSPowerShell/modules/AWSPowerShell.psd1
/AWSPowerShell/modules/AWSPowerShell.dll
/AWSPowerShell/modules/AWSSDK.APIGateway.dll
/AWSPowerShell/modules/AWSSDK.ApplicationAutoScaling.dll
/AWSPowerShell/modules/AWSSDK.ApplicationDiscoveryService.dll
/AWSPowerShell/modules/AWSSDK.AutoScaling.dll
/AWSPowerShell/modules/AWSSDK.AWSMarketplaceCommerceAnalytics.dll
/AWSPowerShell/modules/AWSSDK.AWSMarketplaceMetering.dll
/AWSPowerShell/modules/AWSSDK.AWSSupport.dll
/AWSPowerShell/modules/AWSSDK.CertificateManager.dll
/AWSPowerShell/modules/AWSSDK.CloudFormation.dll
/AWSPowerShell/modules/AWSSDK.CloudFront.dll
/AWSPowerShell/modules/AWSSDK.CloudHSM.dll
/AWSPowerShell/modules/AWSSDK.CloudSearch.dll
/AWSPowerShell/modules/AWSSDK.CloudSearchDomain.dll
/AWSPowerShell/modules/AWSSDK.CloudTrail.dll
/AWSPowerShell/modules/AWSSDK.CloudWatch.dll
/AWSPowerShell/modules/AWSSDK.CloudWatchEvents.dll
/AWSPowerShell/modules/AWSSDK.CloudWatchLogs.dll
/AWSPowerShell/modules/AWSSDK.CodeCommit.dll
/AWSPowerShell/modules/AWSSDK.CodeDeploy.dll
/AWSPowerShell/modules/AWSSDK.CodePipeline.dll
/AWSPowerShell/modules/AWSSDK.CognitoIdentity.dll
/AWSPowerShell/modules/AWSSDK.CognitoIdentityProvider.dll
/AWSPowerShell/modules/AWSSDK.ConfigService.dll
/AWSPowerShell/modules/AWSSDK.Core.dll
/AWSPowerShell/modules/AWSSDK.DatabaseMigrationService.dll
/AWSPowerShell/modules/AWSSDK.DataPipeline.dll
/AWSPowerShell/modules/AWSSDK.DeviceFarm.dll
/AWSPowerShell/modules/AWSSDK.DirectConnect.dll
/AWSPowerShell/modules/AWSSDK.DirectoryService.dll
/AWSPowerShell/modules/AWSSDK.DynamoDBv2.dll
/AWSPowerShell/modules/AWSSDK.EC2.dll
/AWSPowerShell/modules/AWSSDK.ECR.dll
/AWSPowerShell/modules/AWSSDK.ECS.dll
/AWSPowerShell/modules/AWSSDK.ElastiCache.dll
/AWSPowerShell/modules/AWSSDK.ElasticBeanstalk.dll
/AWSPowerShell/modules/AWSSDK.ElasticFileSystem.dll
/AWSPowerShell/modules/AWSSDK.ElasticLoadBalancing.dll
/AWSPowerShell/modules/AWSSDK.ElasticLoadBalancingV2.dll
/AWSPowerShell/modules/AWSSDK.ElasticMapReduce.dll
/AWSPowerShell/modules/AWSSDK.Elasticsearch.dll
/AWSPowerShell/modules/AWSSDK.ElasticTranscoder.dll
/AWSPowerShell/modules/AWSSDK.GameLift.dll
/AWSPowerShell/modules/AWSSDK.IdentityManagement.dll
/AWSPowerShell/modules/AWSSDK.ImportExport.dll
/AWSPowerShell/modules/AWSSDK.Inspector.dll
/AWSPowerShell/modules/AWSSDK.IoT.dll
/AWSPowerShell/modules/AWSSDK.IotData.dll
/AWSPowerShell/modules/AWSSDK.KeyManagementService.dll
/AWSPowerShell/modules/AWSSDK.Kinesis.dll
/AWSPowerShell/modules/AWSSDK.KinesisAnalytics.dll
/AWSPowerShell/modules/AWSSDK.KinesisFirehose.dll
/AWSPowerShell/modules/AWSSDK.Lambda.dll
/AWSPowerShell/modules/AWSSDK.MachineLearning.dll
/AWSPowerShell/modules/AWSSDK.MobileAnalytics.dll
/AWSPowerShell/modules/AWSSDK.OpsWorks.dll
/AWSPowerShell/modules/AWSSDK.RDS.dll
/AWSPowerShell/modules/AWSSDK.Redshift.dll
/AWSPowerShell/modules/AWSSDK.Route53.dll
/AWSPowerShell/modules/AWSSDK.Route53Domains.dll
/AWSPowerShell/modules/AWSSDK.S3.dll
/AWSPowerShell/modules/AWSSDK.SecurityToken.dll
/AWSPowerShell/modules/AWSSDK.ServiceCatalog.dll
/AWSPowerShell/modules/AWSSDK.SimpleEmail.dll
/AWSPowerShell/modules/AWSSDK.SimpleNotificationService.dll
/AWSPowerShell/modules/AWSSDK.SimpleSystemsManagement.dll
/AWSPowerShell/modules/AWSSDK.SimpleWorkflow.dll
/AWSPowerShell/modules/AWSSDK.Snowball.dll
/AWSPowerShell/modules/AWSSDK.SQS.dll
/AWSPowerShell/modules/AWSSDK.StorageGateway.dll
/AWSPowerShell/modules/AWSSDK.WAF.dll
/AWSPowerShell/modules/AWSSDK.WorkSpaces.dll
/AWSPowerShell/modules/log4net.dll
/AWSPowerShell/modules/AWSPowerShellCompleters.psm1
2016-10-11T18:27:21.265 AWSPowerShell installed
2016-10-11T18:27:21.464 Function completed (Success, Id=582b69aa-6236-436d-81c5-c08ada8ae674)

Note: The Function takes a while to complete due to the fact that all the modules are loaded at runtime.

It is important to bear in mind that unlike most IaaS setup, an Azure Function is executing in a multi-tenant environment. As such, there remains the following known caveats:

  1. Our infrastructure guards against any Function executing low-level APIs that we deem as security risks (e.g. interactive modes, host credentials access, registry edits, and etc.). If the PowerShell script or module you are using calls any of those blocked APIs, you will not be able to execute those workloads in Functions. Nonetheless, our goal is to support as many scenarios as possible, so we will prioritize unblocking scenarios based on the demand volume from our customers.

  2. We currently have PowerShell version 4.0 and Azure PowerShell 1.4 installed in our infrastructure. We will upgrade these versions soon. As we add more support for PowerShell in Azure Functions, module suites may be upgraded or added over time. There is a remote possibility that these pre-installed modules may conflict with your existing modules.

Solution 2

You need to make sure you look for all modules, not just the loaded modules by adding -listavailable to the get-module call.

You may need to bootstrap nuget for install-module to work in non-interactive environments. The command is: Get-PackageProvider -Name nuget -ForceBootstrap

If the repository you are installing from is not trusted, you may need to force the install-module command.

Example

[Console]::WriteLine("Powershell Timer trigger function executed at:$(get-date)");

if (-not (Get-Module -listavailable -Name "AWSPowerShell")) {
    [Console]::WriteLine("AWSPowerShell not installed");
    Install-Module -Name AWSPowerShell -Scope CurrentUser -ErrorAction Continue -Verbose -force
}

if (-not (Get-Module -listavailable -Name "AWSPowerShell")){
    [Console]::WriteLine("AWSPowerShell install step failed");
}

FYI: [Console]::WriteLine is not considered a good practice in PowerShell for automated scripts. Try to stick to Write-Verbose you can force it like this Write-Verbose -message 'my message' -verbose

Solution 3

What about updating the azurerm powershell modules, there are a lot of modules to get to the latest version 4.1.0 which if we upload will be a issue to put in a flat directory.

C:\Program Files (x86)\Microsoft SDKs\Azure\PowerShell\ResourceManager\AzureResourceManager

contains 46 folders.

Share:
13,865

Related videos on Youtube

Steph Locke
Author by

Steph Locke

Data Scientist, Author turned Entrepreneur, Steph Locke is passionate about helping business use their data more effectively through AI. She believes the adoption of low/no-code tools are helping many more businesses digitally transform and investing in the right tooling is a much bigger performance enabler than many businesses think. Locke runs Nightingale HQ and is focused on helping manufacturers to automate repetitive processes across their operations. Their GoSmarter.ai platform offers AI-powered tools that are easy to deploy and support manufacturers to do more with less, so employees don’t need as much time per task. It’s a low-risk way to increase productivity and employees tend to support it. Steph is a contributor on the Focus Group Artificial Intelligence (AI) run by the European DIGITAL SME Alliance, and she sits on several industry steering groups and boards. She is also one of 4 people in the world to be recognised by Microsoft as a MVPs (Most Valued Professional) in both Data Platform and Artificial Intelligence.

Updated on September 15, 2022

Comments

  • Steph Locke
    Steph Locke over 1 year

    I need the AWS module, which is available from the PS-Gallery but when I try to run the install step inside an Azure Function, it doesn't work. The -verbose argument flag isn't writing anything to the console.

    What is the right way to get and use additional PowerShell modules within an Azure function?

    Function code

    [Console]::WriteLine("PowerShell Timer trigger function executed at:$(get-date)");
    if (-not (Get-Module -Name "AWSPowerShell")) {
    [Console]::WriteLine("AWSPowerShell not installed");
    Install-Module -Name AWSPowerShell -Scope CurrentUser -ErrorAction Continue -Verbose
    }
    if (-not (Get-Module -Name "AWSPowerShell")){
    [Console]::WriteLine("AWSPowerShell install step failed");
    }
    

    Function log

    2016-06-09T11:24:31.108 Function started (Id=e09be687-2e13-4754-942e-eab75c8516e5)
    2016-06-09T11:24:32.788 Powershell Timer trigger function executed at:06/09/2016 11:24:32
    AWSPowerShell not installed
    AWSPowerShell install step failed
    2016-06-09T11:24:32.788 Function completed (Success, Id=e09be687-2e13-4754-942e-eab75c8516e5)
    

    Update

    Per @travis' answer I added the suggested code, including the nuget package manager line. This still didn't work. I added another debug line and found that there were no module providers, even after trying to add nuget!

    Revised code

    [Console]::WriteLine("Powershell Timer trigger function executed at:$(get-date)");
    
    Get-PackageProvider -Name nuget -ForceBootstrap
    
    $pkg=Get-PackageProvider -ListAvailable
    if ($pkg.Count -lt 1){ [Console]::WriteLine("No providers")}
    
    if (-not (Get-Module -listavailable -Name "AWSPowerShell")) {
        [Console]::WriteLine("AWSPowerShell not installed");
        Install-Module -Name AWSPowerShell -Scope CurrentUser -ErrorAction Continue -Verbose -force
    }
    
    if (-not (Get-Module -listavailable -Name "AWSPowerShell")){
        [Console]::WriteLine("AWSPowerShell install step failed");
    }
    
    Import-Module AWSPowerShell
    

    Revised log

    2016-06-09T17:54:03.859 Powershell Timer trigger function executed at:06/09/2016 17:54:02
    No providers
    AWSPowerShell not installed
    AWSPowerShell install step failed
    2016-06-09T17:54:03.859 Function completed (Success, Id=80efb9fc-5e91-45f9-ab58-3b71fcd764af)
    
  • Steph Locke
    Steph Locke almost 8 years
    I was using write-host initially but the WriteLine is what Microsoft wrote so I went with what they did, assuming best practice/correct route for solution. I guess the adage about assuming comes into play here! ;)
  • TravisEz13
    TravisEz13 almost 8 years
    I don't know where this is being used. Console.WriteHost may be used for a specific reason there, but if it is. I'd like to understand better. My twitter information is in my profile. We should stay on topic here.
  • Steph Locke
    Steph Locke almost 8 years
    It looks like I may not be able to add package providers inside the context of powershell for Azure Functions :-/
  • user1600801
    user1600801 about 7 years
    This is helpful to get the initial code setup. However it makes local development a pain. When running ./run.ps1 locally, it doesn't have the DLL's or my script module loaded locally. So I have to figure out some defensive way to run the script in local development... haven't figure that out yet.
  • Ling Toh
    Ling Toh about 7 years
    The "modules" folder is just a convenience feature. You can rename it to "Modules" or any other name and put your DLLs in there. Then use the Import-Module cmdlet in your run.ps1 script.
  • Ling Toh
    Ling Toh about 7 years
    Additionally, you may also move your custom modules directory up the tree so that the same path can be shared between Functions within the same Function App.
  • user1600801
    user1600801 about 7 years
    I do see now that I can indeed check for even a script module by using if (-not (Get-Module -Name "ScriptName")). I'm still working through trying to then optionally load the DLL's via Add-Type in the module.