Azure API Management: Harnessing Bicep for Effortless User and Subscription Creation
Tags: azure, cloud, apim, bicep
Published: 2024-06-04 11:26:27

## Introduction
In the realm of cloud computing, the management of users and subscriptions is a fundamental task that can quickly become a bottleneck in operational efficiency. Clicking through interfaces to create each user and subscription individually is not only tedious but also prone to errors.

Fortunately, there's a better way: automation. By harnessing the power of scripting, we can streamline this process, making it both clickless and error-free. In this brief guide, we'll explore how to achieve this using a combination of Bicep and PowerShell.

By creating a single Bicep file and accompanying PowerShell script, we can automate the creation of multiple users and subscriptions effortlessly.

## Prerequisites
- An Azure account
- The latest [Azure Powershell](https://learn.microsoft.com/en-us/powershell/azure/install-azure-powershell?view=azps-12.0.0)
- The [Bicep extension](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/install)

## Scripts
**main.bicep**
The bicep file to create user and subscription.
```yml
@description('Name of Api Management')
param apimName string

@description('Name of the product')
param apimProductId string

@description('Array of users to create')
param users array

// Resource to add users to API Management
resource apimUser 'Microsoft.ApiManagement/service/users@2019-12-01' = [
  for user in users: {
    name: '${apimName}/${user}'
    properties: {
      firstName: '${user}'
      lastName: '${user}'
      email: '${user}@mail.be'
      password: 'myUserPassword'
      state: 'active'
    }
  }
]

// Resource to add subscriptions to API Management
resource apimSubscription 'Microsoft.ApiManagement/service/subscriptions@2019-12-01' = [
   for user in users: {
    name: '${apimName}/ProductAPIService'
    properties: {
      displayName: 'Product API Service' 
      state: 'active'
      scope: '/products/${apimProductId}'
      allowTracing: false
      ownerId: '/users/${user}'  
    }
  dependsOn: [
      apimUser
    ]
  }
]

// Output the name of the API Management instance
output addInApiName string = apimName

// Output the ID of the product in API Management
output addInProduct string = apimProductId

// Output the subscription keys for the created users
output souscriptionKey array = [for i in range(0, length(users)):  {
  user: last(split(apimSubscription[i].properties.ownerId, '/'))
  key: apimSubscription[i].listSecrets().primaryKey
}]
```
**main.dev.bicepparam**
The bicepparam to configure the `apimName`, `apimProductId` and `users`.
```properties
using 'main.bicep'

param apimName = 'myApimName'
param apimProductId = 'myProductID'
param users = [
  'user1'
  'user2'
]
```

**createUsersAndSubscriptions.ps1**
The powershell to launch the bicep file and returns the credential in text file.
```powershell
param(
    # Azure APIM Environment to deploy
    [string]$env
)

$date = Get-Date -Format "dd/MM/yyyy"
$logFolderPath = Join-Path -Path $PSScriptRoot -ChildPath "$env\logs"
$credentialsFolderPath = Join-Path -Path $PSScriptRoot -ChildPath "$env\$date\credentials"

# Function to create a directory if it doesn't exist
function Ensure-DirectoryExists {
    param (
        [string]$path
    )
    if (!(Test-Path -Path $path)) {
        New-Item -ItemType Directory -Path $path | Out-Null
        Write-Host "New folder $path created successfully!" -ForegroundColor Green
    } else {
        Write-Host "Folder $path already exists!"
    }
}

# Ensure necessary directories exist
Ensure-DirectoryExists -path $logFolderPath
Ensure-DirectoryExists -path $credentialsFolderPath

# Function to format validation output
function Format-ValidationOutput {
    param (
        $ValidationOutput, 
        [int]$Depth = 0
    )
    Set-StrictMode -Off
    $ValidationOutput | Where-Object { $_ -ne $null } | ForEach-Object {
        @('  ' * $Depth + ': ' + $_.Message) + @(Format-ValidationOutput -ValidationOutput $_.Details -Depth ($Depth + 1))
    }
}

# Function to return full environment name
function Return-FullEnvironmentName {
    param (
        [string]$environment
    )

    switch ($environment) {
        'dev' { 'Development' }
        'acc' { 'Acceptance' }
        'prd' { 'Production' }
        default { 'Unknown' }
    }
}

# Test Deployment
$ErrorMessages = Format-ValidationOutput (Test-AzResourceGroupDeployment -Verbose -ResourceGroupName "apim-$env-rg" -TemplateFile "main.bicep" -TemplateParameterFile "main.$env.bicepparam")

if ($ErrorMessages) {
    $errorMessage = 'Validation returned the following errors:' + [System.Environment]::NewLine + ($ErrorMessages -join [System.Environment]::NewLine) + [System.Environment]::NewLine + 'Template is invalid.'
    Write-Host $errorMessage
    Write-Output $errorMessage >> "$logFolderPath/logs.txt"
} else { 
    Write-Host "Start users and subscriptions creation on $env environment"

    $deploymentResult = New-AzResourceGroupDeployment `
        -Name "apiUserAndSubscriptionDeployment" `
        -Verbose `
        -ResourceGroupName "apim-$env-rg" `
        -TemplateFile "main.bicep" `
        -TemplateParameterFile "main.$env.bicepparam"

    $deploymentResult | Out-File -FilePath "$logFolderPath/logs.txt" -Append

    $apimName = $deploymentResult.Outputs.addInApiName.Value
    Add-Content -Path "$credentialsFolderPath/credentials.txt" -Value "Api Management : $apimName"

    $environmentFullName = Return-FullEnvironmentName -environment $env
    Add-Content -Path "$credentialsFolderPath/credentials.txt" -Value "Environment : $environmentFullName"

    $productName = $deploymentResult.Outputs.addInProduct.Value
    Add-Content -Path "$credentialsFolderPath/credentials.txt" -Value "Product : $productName"

    $subscriptionKeys = $deploymentResult.Outputs.subscriptionKey[0].Value | Select-Object
    Add-Content -Path "$credentialsFolderPath/credentials.txt" -Value "Subscriptions : $subscriptionKeys"
}
```

After creating those files, you can launch this command :
`createUsersAndSubscriptions.ps1 -env dev`

## Clean up the resources
You can clean up your resource using this command :
`Remove-AzResourceGroup -Name "apiUserAndSubscriptionDeploypment"`

##Go further
If you want to automate the creation you can add it to a pipeline following this link : 
https://learn.microsoft.com/en-us/training/modules/build-first-bicep-deployment-pipeline-using-azure-pipelines/

## Thank for reading

If you have any questions, feedback, or suggestions, please feel free to leave them in the comments below. I'm eager to hear from you and respond to your thoughts!