This guide walks you through deploying the CAPWATCHSyncPWSH Azure Function App from start to finish.
The deployment process involves several key steps:
- Infrastructure Deployment - Deploy Azure resources using Terraform
- PowerShell Module Setup - Upload required modules to Azure Storage ⭐ You are here
- Function App Configuration - Configure permissions and settings
- Function Deployment - Deploy the PowerShell code
- Testing & Validation - Verify everything works
After deploying the infrastructure with Terraform, you need to upload the required PowerShell modules to Azure Storage. This is a critical step that must be completed before deploying the function code.
The Azure Function App requires several PowerShell modules to operate:
Az.Accounts- Azure PowerShell authenticationAz.KeyVault- Access to Azure Key Vault secretsExchangeOnlineManagement- Exchange Online operationsMicrosoft.Graph.*- Microsoft Graph API operations
Due to Azure Functions deployment size limits (150MB), we cannot include these modules directly in the deployment package. Instead, we:
- Exclude modules from deployment using
.funcignore - Upload modules to Azure Storage for runtime access
- Load modules dynamically when the function starts
Before running the module upload script, ensure you have:
✅ Terraform infrastructure deployed - Storage account and other resources must exist
✅ PowerShell 7.6+ installed on your local machine
✅ Az PowerShell module installed locally:
Install-Module Az -Force -AllowClobber✅ Connected to Azure:
Connect-AzAccountFirst, download all required modules to your local ./Modules directory:
# Run from the project root directory
./Download_Modules.ps1This script will:
- Create a
./Modulesdirectory - Download the latest versions of all required modules
- Organize them in the correct structure for upload
Run the upload script with your Terraform-created storage account details:
# Run from the project root directory
./Upload-ModulesToStorage.ps1 -StorageAccountName "your-storage-account-name" -ResourceGroup "your-resource-group-name"Example using typical Terraform naming:
./Upload-ModulesToStorage.ps1 -StorageAccountName "capwatchsyncpwsh" -ResourceGroup "CAPWATCH_Sync_PWSH"The Upload-ModulesToStorage.ps1 script performs these actions:
- Validates Azure Connection - Ensures you're authenticated to Azure
- Locates Storage Account - Finds the storage account created by Terraform
- Creates Storage Container - Creates a "modules" container if it doesn't exist
- Compresses Modules - Zips each module directory for efficient transfer
- Uploads to Blob Storage - Uploads each module zip file to Azure Storage
- Cleans Up - Removes temporary zip files from your local machine
You should see output similar to this:
Storage account 'capwatchsyncpwsh' found
Storage container 'modules' created/verified
Processing module: Az.Accounts
- Compressing to: /tmp/Az.Accounts.zip
- Uploading Az.Accounts.zip... ✓
- Cleanup: removed /tmp/Az.Accounts.zip
Processing module: Az.KeyVault
- Compressing to: /tmp/Az.KeyVault.zip
- Uploading Az.KeyVault.zip... ✓
- Cleanup: removed /tmp/Az.KeyVault.zip
...
All modules uploaded successfully!
You can verify the modules were uploaded correctly by checking the Azure portal:
- Navigate to your storage account
- Go to Containers → modules
- You should see zip files for each module:
Az.Accounts.zipAz.KeyVault.zipExchangeOnlineManagement.zipMicrosoft.Graph.Authentication.zipMicrosoft.Graph.Groups.zipMicrosoft.Graph.Teams.zipMicrosoft.Graph.Users.zip
Once uploaded, the Azure Function will automatically:
- Check for modules during function startup (profile.ps1)
- Download from storage if modules aren't available locally
- Extract and import modules for use by function code
- Fall back to PowerShell Gallery if storage access fails
The module loading process is handled by:
shared/Load-Modules.ps1- Runtime module loading logicrequirements.psd1- Azure Functions managed dependencies (primary method)- Storage-based loading as fallback for reliability
Problem: "Storage account not found"
- Solution: Verify the storage account name and resource group are correct
- Check: Ensure Terraform deployment completed successfully
Problem: "Access denied" during upload
- Solution: Verify you're connected to Azure with
Get-AzContext - Check: Ensure your Azure account has Contributor access to the resource group
Problem: Upload is very slow
- Expected: Module uploads can take 5-10 minutes depending on connection speed
- Normal: Az.Accounts and Microsoft.Graph modules are particularly large
Problem: Function still can't find modules after upload
- Check: Verify the Function App has Storage Blob Data Reader permissions
- Solution: This is configured in the next deployment step
After successfully uploading modules to Azure Storage:
- Configure Function App Settings - Set environment variables and permissions
- Deploy Function Code - Use
func azure functionapp publish - Test Function Execution - Verify modules load correctly at runtime
Continue to Step 3: Function App Configuration in the deployment guide.
Az.Accounts (v4.x)
Az.KeyVault (v6.x)
ExchangeOnlineManagement (v3.x)
Microsoft.Graph.Authentication (v2.x)
Microsoft.Graph.Groups (v2.x)
Microsoft.Graph.Teams (v2.x)
Microsoft.Graph.Users (v2.x)
Storage Account: {your-storage-account}
├── Container: modules
├── Az.Accounts.zip
├── Az.KeyVault.zip
├── ExchangeOnlineManagement.zip
├── Microsoft.Graph.Authentication.zip
├── Microsoft.Graph.Groups.zip
├── Microsoft.Graph.Teams.zip
└── Microsoft.Graph.Users.zip
.funcignore- ExcludesModules/directory from deploymentrequirements.psd1- Specifies PowerShell dependencies for Azure Functionsshared/Load-Modules.ps1- Runtime module loading logicprofile.ps1- Function startup configuration