Generate a Service Bus SAS Token and Manage Token renewal using Azure Runbooks

Premier Developer

Developer

Rakhi Guha demonstrates how to use PowerShell and Azure Runbooks to automate Service Bus SAS Tokens.


This article provide a detailed walk-through of

  1. Generating an Azure Service Bus token using PowerShell
  2. Using an Azure Runbook for the token generation and renewal process
  3. Steps to incorporate a Runbook with a CI/CD pipeline to automate and manage the process.

The following code can be used to generate the SAS Token for Service Bus

[Reflection.Assembly]::LoadWithPartialName("System.Web")| out-null
$URI="<eventhubnamespace>.servicebus.windows.net"
$Access_Policy_Name="<PolicyName>"
$Access_Policy_Key="<Primary Access Name>"
##Token expires now+7,776,000(3 month)
$Expires=([DateTimeOffset]::Now.ToUnixTimeSeconds())+7776000
$SignatureString=[System.Web.HttpUtility]::UrlEncode($URI)+ "`n" + [string]$Expires
$HMAC = New-Object System.Security.Cryptography.HMACSHA256
$HMAC.key = [Text.Encoding]::ASCII.GetBytes($Access_Policy_Key)
$Signature = $HMAC.ComputeHash([Text.Encoding]::ASCII.GetBytes($SignatureString))
$Signature = [Convert]::ToBase64String($Signature)
$SASToken = "SharedAccessSignature sr=" + [System.Web.HttpUtility]::UrlEncode($URI) + "&sig=" + [System.Web.HttpUtility]::UrlEncode($Signature) + "&se=" + $Expires + "&skn=" + $Access_Policy_Name
$SASToken

We will be required to update the placeholder with appropriate values, specific to a use case.

The line below sets the generated token in AKV secrets for secured communication. Any application requires to use the AKV secrets to get the token and eventually use the same for accessing the service bus.

Set-AzureKeyVaultSecret -VaultName '<AKVNAME>' -Name '<SECRETNAME>' -SecretValue $Secret

With the above code, the renewal of a SAS token will be required to re-execute the code once the token is expired in order to ensure the AKV contains the valid SAS token. In order to automate the entire process we can build up a process as described below

  1. Develop a run book with the below code.
    [Reflection.Assembly]::LoadWithPartialName("System.Web")| out-null
    $serviceBusNameSpace=Get-AutomationVariable -Name 'ServiceBusNameSpace'
    $serviceBusAccessPolicyName=Get-AutomationVariable -Name 'ServiceBusAccessPolicyName'
    $serviceBusAccessPolicyKey=Get-AutomationVariable -Name 'ServiceBusAccessPolicyKey'
    $akvName=Get-AutomationVariable -Name 'AKVName'
    $sasTokenSecretName=Get-AutomationVariable -Name 'SASTokenSecretName'
    
    $URI=$serviceBusNameSpace+".servicebus.windows.net"
    write-host 'Generate SAS token for 3 month'
    #Token expires now+7,776,000(3 month)
    
    $Expires=([DateTimeOffset]::Now.ToUnixTimeSeconds())+7776000
    $SignatureString=[System.Web.HttpUtility]::UrlEncode($URI)+ "`n" + [string]$Expires
    $HMAC = New-Object System.Security.Cryptography.HMACSHA256
    $HMAC.key = [Text.Encoding]::ASCII.GetBytes($serviceBusAccessPolicyKey)
    $Expires
    $Signature = $HMAC.ComputeHash([Text.Encoding]::ASCII.GetBytes($SignatureString))
    $Signature = [Convert]::ToBase64String($Signature)
    $SASToken = "SharedAccessSignature sr=" + [System.Web.HttpUtility]::UrlEncode($URI) + "&sig=" + [System.Web.HttpUtility]::UrlEncode($Signature) + "&se=" + $Expires + "&skn=" + $serviceBusAccessPolicyName
    $SASToken
    $SecretToken = ConvertTo-SecureString -String $SASToken -AsPlainText -Force
    
    $connectionName = "AzureRunAsConnection"
    try
    {
        # Get the connection "AzureRunAsConnection "
        $servicePrincipalConnection=Get-AutomationConnection -Name $connectionName         
    
        "Logging in to Azure..."
        Add-AzureRmAccount `
            -ServicePrincipal `
            -TenantId $servicePrincipalConnection.TenantId `
            -ApplicationId $servicePrincipalConnection.ApplicationId `
            -CertificateThumbprint $servicePrincipalConnection.CertificateThumbprint 
    }
    catch {
        if (!$servicePrincipalConnection)
        {
            $ErrorMessage = "Connection $connectionName not found."
            throw $ErrorMessage
        } else{
            Write-Error -Message $_.Exception
            throw $_.Exception
        }
    }
    write-host 'Update SAS token in AKV'
    Set-AzureKeyVaultSecret -VaultName $akvName -Name $sasTokenSecretName -SecretValue $SecretToken
    write-host 'Execution completed'
    
  2. Deploy the runbook integrated with the CI/CD pipeline. In a CI pipeline package, the PowerShell script is described above. Once the build is generated with the package artifacts, follow the below steps
    1. Create a CD pipeline and add an azure PowerShell task
    2. In inline scripting option provide the following script.
      $serviceBusNamespaceKey=Get-AzureRmServiceBusKey -Namespace $(ServiceBusNameSpace)  -AuthorizationRuleName $(ServiceBusAccessPolicyName) -ResourceGroup $(PlantConnectorRG)
      $serviceBusNamespaceKey.PrimaryKey
      $SecretToken = ConvertTo-SecureString -String $serviceBusNamespaceKey.PrimaryKey -AsPlainText -Force
      Set-AzureKeyVaultSecret -VaultName $(KeyVaultName) -Name 'ServiceBusAccessPolicyKey' -SecretValue $SecretToken
      
      
      Install-Module AzureRM.Automation -Scope CurrentUser -AllowClobber -Force
      
      Import-AzureRMAutomationRunbook -Name 'RenewSASToken' -Path <Path of the Runbook PS script from build package>  -ResourceGroupName $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Type PowerShell -Force -Published
      try{
       $alreadyPresentVar = Get-AzureRmAutomationVariable -ResourceGroupName $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Name 'ServiceBusNameSpace' -ErrorAction Stop
       Set-AzureRmAutomationVariable -ResourceGroupName  $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Name 'ServiceBusNameSpace' -Value $(ServiceBusNameSpace) -Encrypted $false
      
      }
      catch{
      New-AzureRmAutomationVariable -ResourceGroupName  $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Name 'ServiceBusNameSpace' -Value $(ServiceBusNameSpace) -Encrypted $false
      }
      try{
       $alreadyPresentVar = Get-AzureRmAutomationVariable -ResourceGroupName $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Name 'ServiceBusAccessPolicyName' -ErrorAction Stop
       Set-AzureRmAutomationVariable -ResourceGroupName  $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Name 'ServiceBusAccessPolicyName' -Value $(ServiceBusAccessPolicyName) -Encrypted $false
      
      }
      catch{
      New-AzureRmAutomationVariable -ResourceGroupName  $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Name 'ServiceBusAccessPolicyName' -Value $(ServiceBusAccessPolicyName) -Encrypted $false
      }
      
      try{
       $alreadyPresentVar = Get-AzureRmAutomationVariable -ResourceGroupName $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Name 'SASTokenSecretName' -ErrorAction Stop
       Set-AzureRmAutomationVariable -ResourceGroupName  $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Name 'SASTokenSecretName' -Value $(SASTokenSecretName) -Encrypted $false
      
      }
      catch{
      New-AzureRmAutomationVariable -ResourceGroupName  $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Name 'SASTokenSecretName' -Value $(SASTokenSecretName) -Encrypted $false
      }
      try{
       $alreadyPresentVar = Get-AzureRmAutomationVariable -ResourceGroupName $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Name 'AKVNAme' -ErrorAction Stop
       Set-AzureRmAutomationVariable -ResourceGroupName  $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Name 'AKVNAme' -Value $(KeyVaultName) -Encrypted $false
      
      }
      catch{
      New-AzureRmAutomationVariable -ResourceGroupName  $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Name 'AKVNAme' -Value $(KeyVaultName) -Encrypted $false
      }
      
      try{
       $alreadyPresentVar = Get-AzureRmAutomationVariable -ResourceGroupName $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Name 'ServiceBusAccessPolicyKey' -ErrorAction Stop
       Set-AzureRmAutomationVariable -ResourceGroupName  $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Name 'ServiceBusAccessPolicyKey' -Value $serviceBusNamespaceKey.PrimaryKey -Encrypted $true
      
      }
      catch{
      New-AzureRmAutomationVariable -ResourceGroupName  $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Name 'ServiceBusAccessPolicyKey' -Value $serviceBusNamespaceKey.PrimaryKey -Encrypted $true
      } 
      
    3. Add a another Azure PowerShell task
    4. Provide the following inline script to execute the runbook post deployment using above step and schedule the same with a renewal interval. Here, a 3 month duration has been considered for next run.
      try{
      Remove-AzAutomationSchedule -Name 'SASTokenRenewal' -ResourceGroupName $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -Force
      }
      catch{
      write-host 'SASTokenRenewal schedule is not pre existing, will be configured.'
      }
      $StartTime = Get-Date
      $StartTime 
      $StartTime =$StartTime.AddMinutes(15) 
      $StartTime 
      New-AzAutomationSchedule  -Name 'SASTokenRenewal' -ResourceGroupName $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -StartTime $StartTime  -DayInterval 90 
      
      Register-AzAutomationScheduledRunbook -Name 'RenewSASToken' -ResourceGroupName $(AutomationRG) -AutomationAccountName $(AutomationAccountName) -ScheduleName 'SASTokenRenewal'
      
      Start-AzAutomationRunbook -AutomationAccountName $(AutomationAccountName) -Name "RenewSASToken" -ResourceGroupName $(AutomationRG)
      
    5. Provide the appropriate variables in CD pipelines which will be used within both the scripts.

NOTE:

  • The DeploySASTokenRenewalRunbook will deploy the Runbook with all the environment variables require for executing the Runbook. The Run book Update Schedule step will trigger the runbook post deployment. It will also schedule the runbook for consecutive executions.
  • The automation account needs to have the following module preinstalled for successful execution of the runbook.
    • AzureRM.Automation
    • AzureRM.Keyvault

 

  • The automation account should be able to perform read/write operation in Key Vault. Provide (Get,List,Set,Delete) access to ApplicationId of “AzureRunAsConnection” listed in “Connections” for the automation account.

0 comments

Leave a comment