DEV Community

Cover image for Rotate storage account keys using Az Devops
Arindam Mitra
Arindam Mitra

Posted on • Edited on

Rotate storage account keys using Az Devops

Greetings to my fellow Technology Advocates and Specialists.

In this Session, I will demonstrate How to Rotate Storage Account Keys (Primary & Secondary) and Store it in Key Vault Using Azure DevOps.

I had the Privilege to talk on this topic in TWO Azure Communities:-

NAME OF THE AZURE COMMUNITY TYPE OF SPEAKER SESSION
Journey to the Cloud 9.0 Virtual
Festive Tech Calendar 2022 Virtual
LIVE RECORDED SESSION:-
LIVE DEMO was Recorded as part of my Presentation in JOURNEY TO THE CLOUD 9.0 Forum/Platform
Duration of My Demo = 55 Mins 42 Secs
LIVE DEMO was Recorded as part of my Presentation in FESTIVE TECH CALENDAR 2022 Forum/Platform
Duration of My Demo = 1 Hour 05 Mins 08 Secs
THIS IS HOW IT LOOKS:-
Image description
AUTOMATION OBJECTIVE:-
Validate If Resource Group Containing Key Vault Exists. If No Resource Group Found, Pipeline will FAIL.
Validate If Storage Account Exists inside the Specified Resource Group. If No Storage Account Found, Pipeline will FAIL.
Validate If Key Vault Exists inside the Specified Resource Group. If No Key Vault Found, Pipeline will FAIL.
If All of the above validation is SUCCESSFUL, Depending upon, which Key User wants to rotate (Primary or Secondary), Pipeline will then Rotate the Storage Account Key and Store it in the Key Vault.
IMPORTANT NOTE:-

The YAML Pipeline is tested on WINDOWS BUILD AGENT Only!!!

REQUIREMENTS:-
  1. Azure Subscription.
  2. Azure DevOps Organisation and Project.
  3. Service Principal with Required RBAC ( Contributor) applied on Subscription or Resource Group(s).
  4. Azure Resource Manager Service Connection in Azure DevOps.
CODE REPOSITORY:-

GitHub logo arindam0310018 / 29-Aug-2022-DevOps__Rotate-Storage-Account-Keys

ROTATE STORAGE ACCOUNT KEYS USING AZ DEVOPS

ROTATE STORAGE ACCOUNT KEYS USING AZ DEVOPS

Greetings to my fellow Technology Advocates and Specialists.

In this Session, I will demonstrate How to Rotate Storage Account Keys (Primary & Secondary) and Store it in Key Vault Using Azure DevOps.

I had the Privilege to talk on this topic in TWO Azure Communities:-

NAME OF THE AZURE COMMUNITY TYPE OF SPEAKER SESSION
Journey to the Cloud 9.0 Virtual
Festive Tech Calendar 2022 Virtual
LIVE RECORDED SESSION:-
LIVE DEMO was Recorded as part of my Presentation in JOURNEY TO THE CLOUD 9.0 Forum/Platform
Duration of My Demo = 55 Mins 42 Secs
IMAGE ALT TEXT HERE
LIVE DEMO was Recorded as part of my Presentation in FESTIVE TECH CALENDAR 2022 Forum/Platform
Duration of My Demo = 1 Hour 05 Mins 08 Secs
IMAGE ALT TEXT HERE
THIS IS HOW IT LOOKS:-
Image description
AUTOMATION OBJECTIVE:-
Validate If Resource Group Containing Key Vault Exists. If No Resource Group Found, Pipeline will FAIL
HOW DOES MY CODE PLACEHOLDER LOOKS LIKE:-
Image description
PIPELINE CODE SNIPPET:-
AZURE DEVOPS YAML PIPELINE (azure-pipelines-storage-account-key-rotation-v1.0.yml):-


trigger:
  none

######################
#DECLARE PARAMETERS:-
######################
parameters:
- name: SUBSCRIPTIONID
  displayName: Subscription ID Details Follow Below:-
  type: string
  default: 210e66cb-55cf-424e-8daa-6cad804ab604
  values:
  - 210e66cb-55cf-424e-8daa-6cad804ab604

- name: RGNAME
  displayName: Please Provide the Resource Group Name:-
  type: object
  default: 

- name: STORAGEACCOUNTNAME
  displayName: Please Provide the Storage Account Name:-
  type: object
  default:

- name: KVNAME
  displayName: Please Provide the Keyvault Name:-
  type: object
  default: 

- name: STORAGEACCOUNTKEYS
  displayName: Choose Storage Account Keys - Primary or Secondary:-
  type: string
  default: Primary
  values:
  - Primary
  - Secondary 

######################
#DECLARE VARIABLES:-
######################
variables:
  ServiceConnection: amcloud-cicd-service-connection
  BuildAgent: windows-latest

#########################
# Declare Build Agents:-
#########################
pool:
  vmImage: $(BuildAgent)

###################
# Declare Stages:-
###################

stages:

- stage: VALIDATE_RG_STORAGE_ACCOUNT_AND_KV 
  jobs:
  - job: VALIDATE_RG_STORAGE_ACCOUNT_AND_KV 
    displayName: VALIDATE RG STORAGE ACCOUNT & KV
    steps:
    - task: AzureCLI@2
      displayName: SET AZURE ACCOUNT
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |
          az --version
          az account set --subscription ${{ parameters.SUBSCRIPTIONID }}
          az account show  
    - task: AzureCLI@2
      displayName: VALIDATE RG STORAGE ACCOUNT & RG
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |      
          $i = az group exists -n ${{ parameters.RGNAME }}
            if ($i -eq "true") {
              echo "#####################################################"
              echo "Resource Group ${{ parameters.RGNAME }} exists!!!"
              echo "#####################################################"
              $j = az storage account check-name --name ${{ parameters.STORAGEACCOUNTNAME }} --query "reason" --out tsv
                if ($j -eq "AlreadyExists") {
                  echo "###################################################################"
                  echo "Storage Account ${{ parameters.STORAGEACCOUNTNAME }} exists!!!"
                  echo "###################################################################"
                  $k = az keyvault list --resource-group ${{ parameters.RGNAME }} --query [].name -o tsv        
                    if ($k -eq "${{ parameters.KVNAME }}") {
                      echo "###################################################################"
                      echo "Key Vault ${{ parameters.KVNAME }} exists!!!"
                      echo "###################################################################"
                    }
                  else {
                    echo "###################################################################################################"
                    echo "Key Vault ${{ parameters.KVNAME }} DOES NOT EXISTS in Resource Group ${{ parameters.RGNAME }}!!!"
                    echo "###################################################################################################"
                    exit 1
                  }
                }  
                else {
                  echo "#######################################################################################################################"
                  echo "Storage Account ${{ parameters.STORAGEACCOUNTNAME }} DOES NOT EXISTS in Resource Group ${{ parameters.RGNAME }}!!!"
                  echo "#######################################################################################################################"
                  exit 1
                }              
            }
            else {
              echo "#############################################################"
              echo "Resource Group ${{ parameters.RGNAME }} DOES NOT EXISTS!!!"
              echo "#############################################################"
              exit 1
            }

- stage: RENEW_STORAGE_ACCOUNT_PRIMARY_KEY
  condition: |
     and(succeeded(),
       eq('${{ parameters.STORAGEACCOUNTKEYS }}', 'Primary')
     ) 
  jobs:
  - job: RENEW_STORAGE_ACCOUNT_PRIMARY_KEY 
    displayName: ROTATE PRIMARY KEY & STORE IN KV
    steps:
    - task: AzureCLI@2
      displayName: ROTATE AND STORE
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |
          $saprimary = az storage account keys renew -g ${{ parameters.RGNAME }} -n ${{ parameters.STORAGEACCOUNTNAME }} --key ${{ parameters.STORAGEACCOUNTKEYS }} --query [0].value -o tsv
          az keyvault secret set --name "${{ parameters.STORAGEACCOUNTNAME }}-${{ parameters.STORAGEACCOUNTKEYS }}" --vault-name ${{ parameters.KVNAME }} --value $saprimary
          echo "#################################################################################################################################################################"
          echo "Storage Account Primary Key has been successfully Rotated and Stored in Key Vault ${{ parameters.KVNAME }} under the Resource Group ${{ parameters.RGNAME }}!!!"
          echo "#################################################################################################################################################################"

- stage: RENEW_STORAGE_ACCOUNT_SECONDARY_KEY
  condition: |
       eq('${{ parameters.STORAGEACCOUNTKEYS }}', 'Secondary')
  dependsOn: VALIDATE_RG_STORAGE_ACCOUNT_AND_KV   
  jobs:
  - job: RENEW_STORAGE_ACCOUNT_SECONDARY_KEY 
    displayName: ROTATE SECONDARY KEY & STORE IN KV
    steps:
    - task: AzureCLI@2
      displayName: ROTATE AND STORE
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |
          $sasecondary = az storage account keys renew -g ${{ parameters.RGNAME }} -n ${{ parameters.STORAGEACCOUNTNAME }} --key ${{ parameters.STORAGEACCOUNTKEYS }} --query [1].value -o tsv
          az keyvault secret set --name "${{ parameters.STORAGEACCOUNTNAME }}-${{ parameters.STORAGEACCOUNTKEYS }}" --vault-name ${{ parameters.KVNAME }} --value $sasecondary
          echo "#################################################################################################################################################################"
          echo "Storage Account Secondary Key has been successfully Rotated and Stored in Key Vault ${{ parameters.KVNAME }} under the Resource Group ${{ parameters.RGNAME }}!!!"
          echo "#################################################################################################################################################################"



Enter fullscreen mode Exit fullscreen mode

Now, let me explain each part of YAML Pipeline for better understanding.

PART #1:-
BELOW FOLLOWS PIPELINE RUNTIME VARIABLES CODE SNIPPET:-


######################
#DECLARE PARAMETERS:-
######################
parameters:
- name: SUBSCRIPTIONID
  displayName: Subscription ID Details Follow Below:-
  type: string
  default: 210e66cb-55cf-424e-8daa-6cad804ab604
  values:
  - 210e66cb-55cf-424e-8daa-6cad804ab604

- name: RGNAME
  displayName: Please Provide the Resource Group Name:-
  type: object
  default: 

- name: STORAGEACCOUNTNAME
  displayName: Please Provide the Storage Account Name:-
  type: object
  default:

- name: KVNAME
  displayName: Please Provide the Keyvault Name:-
  type: object
  default: 

- name: STORAGEACCOUNTKEYS
  displayName: Choose Storage Account Keys - Primary or Secondary:-
  type: string
  default: Primary
  values:
  - Primary
  - Secondary 



Enter fullscreen mode Exit fullscreen mode
PART #2:-
BELOW FOLLOWS PIPELINE VARIABLES CODE SNIPPET:-


######################
#DECLARE VARIABLES:-
######################
variables:
  ServiceConnection: amcloud-cicd-service-connection
  BuildAgent: windows-latest



Enter fullscreen mode Exit fullscreen mode
NOTE:-
Please change the values of the variables accordingly.
The entire YAML pipeline is build using Runtime Parameters and Variables. No Values are Hardcoded.
PART #3:-
This is a 3 Stage Pipeline:-
STAGE #1 - VALIDATE_RG_STORAGE_ACCOUNT_AND_KV:-
In this Stage, Pipeline will validate Resource Group, Storage Account and Key Vault. If any one of the Azure Resource is Not Available, Pipeline will FAIL and the other 2 Stages gets SKIPPED.


- stage: VALIDATE_RG_STORAGE_ACCOUNT_AND_KV 
  jobs:
  - job: VALIDATE_RG_STORAGE_ACCOUNT_AND_KV 
    displayName: VALIDATE RG STORAGE ACCOUNT & KV
    steps:
    - task: AzureCLI@2
      displayName: SET AZURE ACCOUNT
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |
          az --version
          az account set --subscription ${{ parameters.SUBSCRIPTIONID }}
          az account show  
    - task: AzureCLI@2
      displayName: VALIDATE RG STORAGE ACCOUNT & RG
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |      
          $i = az group exists -n ${{ parameters.RGNAME }}
            if ($i -eq "true") {
              echo "#####################################################"
              echo "Resource Group ${{ parameters.RGNAME }} exists!!!"
              echo "#####################################################"
              $j = az storage account check-name --name ${{ parameters.STORAGEACCOUNTNAME }} --query "reason" --out tsv
                if ($j -eq "AlreadyExists") {
                  echo "###################################################################"
                  echo "Storage Account ${{ parameters.STORAGEACCOUNTNAME }} exists!!!"
                  echo "###################################################################"
                  $k = az keyvault list --resource-group ${{ parameters.RGNAME }} --query [].name -o tsv        
                    if ($k -eq "${{ parameters.KVNAME }}") {
                      echo "###################################################################"
                      echo "Key Vault ${{ parameters.KVNAME }} exists!!!"
                      echo "###################################################################"
                    }
                  else {
                    echo "###################################################################################################"
                    echo "Key Vault ${{ parameters.KVNAME }} DOES NOT EXISTS in Resource Group ${{ parameters.RGNAME }}!!!"
                    echo "###################################################################################################"
                    exit 1
                  }
                }  
                else {
                  echo "#######################################################################################################################"
                  echo "Storage Account ${{ parameters.STORAGEACCOUNTNAME }} DOES NOT EXISTS in Resource Group ${{ parameters.RGNAME }}!!!"
                  echo "#######################################################################################################################"
                  exit 1
                }              
            }
            else {
              echo "#############################################################"
              echo "Resource Group ${{ parameters.RGNAME }} DOES NOT EXISTS!!!"
              echo "#############################################################"
              exit 1
            }


Enter fullscreen mode Exit fullscreen mode
STAGE #2 - RENEW_STORAGE_ACCOUNT_PRIMARY_KEY:-
In this Stage, Pipeline has Conditions in Place.
Condition #1: The Previous Stage has to be Successful.
Condition #2: The User should Select option "Primary".


- stage: RENEW_STORAGE_ACCOUNT_PRIMARY_KEY
  condition: |
     and(succeeded(),
       eq('${{ parameters.STORAGEACCOUNTKEYS }}', 'Primary')
     ) 


Enter fullscreen mode Exit fullscreen mode
BELOW FOLLOWS THE LOGIC DEFINED TO RENEW/ROTATE STORAGE ACCOUNT PRIMARY KEY AND STORE IN THE MENTIONED KEYVAULT:-


jobs:
  - job: RENEW_STORAGE_ACCOUNT_PRIMARY_KEY 
    displayName: ROTATE PRIMARY KEY & STORE IN KV
    steps:
    - task: AzureCLI@2
      displayName: ROTATE AND STORE
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |
          $saprimary = az storage account keys renew -g ${{ parameters.RGNAME }} -n ${{ parameters.STORAGEACCOUNTNAME }} --key ${{ parameters.STORAGEACCOUNTKEYS }} --query [0].value -o tsv
          az keyvault secret set --name "${{ parameters.STORAGEACCOUNTNAME }}-${{ parameters.STORAGEACCOUNTKEYS }}" --vault-name ${{ parameters.KVNAME }} --value $saprimary
          echo "#################################################################################################################################################################"
          echo "Storage Account Primary Key has been successfully Rotated and Stored in Key Vault ${{ parameters.KVNAME }} under the Resource Group ${{ parameters.RGNAME }}!!!"
          echo "#################################################################################################################################################################"



Enter fullscreen mode Exit fullscreen mode
STAGE #3 - RENEW_STORAGE_ACCOUNT_SECONDARY_KEY:-
In this Stage, Pipeline has Conditions in Place.
Condition #1: The User should Select option "Secondary".
Condition #2: Depends upon Stage #1: VALIDATE_RG_STORAGE_ACCOUNT_AND_KV .


- stage: RENEW_STORAGE_ACCOUNT_SECONDARY_KEY
  condition: |
       eq('${{ parameters.STORAGEACCOUNTKEYS }}', 'Secondary')
  dependsOn: VALIDATE_RG_STORAGE_ACCOUNT_AND_KV  



Enter fullscreen mode Exit fullscreen mode
BELOW FOLLOWS THE LOGIC DEFINED TO RENEW/ROTATE STORAGE ACCOUNT SECONDARY KEY AND STORE IN THE MENTIONED KEYVAULT:-


jobs:
  - job: RENEW_STORAGE_ACCOUNT_SECONDARY_KEY 
    displayName: ROTATE SECONDARY KEY & STORE IN KV
    steps:
    - task: AzureCLI@2
      displayName: ROTATE AND STORE
      inputs:
        azureSubscription: $(ServiceConnection)
        scriptType: ps
        scriptLocation: inlineScript
        inlineScript: |
          $sasecondary = az storage account keys renew -g ${{ parameters.RGNAME }} -n ${{ parameters.STORAGEACCOUNTNAME }} --key ${{ parameters.STORAGEACCOUNTKEYS }} --query [1].value -o tsv
          az keyvault secret set --name "${{ parameters.STORAGEACCOUNTNAME }}-${{ parameters.STORAGEACCOUNTKEYS }}" --vault-name ${{ parameters.KVNAME }} --value $sasecondary
          echo "#################################################################################################################################################################"
          echo "Storage Account Secondary Key has been successfully Rotated and Stored in Key Vault ${{ parameters.KVNAME }} under the Resource Group ${{ parameters.RGNAME }}!!!"
          echo "#################################################################################################################################################################"



Enter fullscreen mode Exit fullscreen mode

NOW ITS TIME TO TEST !!!...

TEST CASES:-
TEST CASE #1: VALIDATE RESOURCE GROUP, STORAGE ACCOUNT AND KEY VAULT:-
DESIRED OUTPUT: PIPELINE FAILS WHEN RESOURCE GROUP DOES NOT EXISTS.
Image description
Image description
DESIRED OUTPUT: PIPELINE FAILS WHEN STORAGE ACCOUNT DOES NOT EXISTS.
Image description
Image description
DESIRED OUTPUT: PIPELINE FAILS WHEN KEY VAULT DOES NOT EXISTS.
Image description
Image description
TEST CASE #2: ROTATE/RENEW STORAGE ACCOUNT PRIMARY KEY AND STORE IN KEY VAULT:-
Image description
Image description
Image description
Image description
Image description
TEST CASE #3: ROTATE/RENEW STORAGE ACCOUNT SECONDARY KEY AND STORE IN KEY VAULT:-
Image description
Image description
Image description
Image description
Image description

Hope You Enjoyed the Session!!!

Stay Safe | Keep Learning | Spread Knowledge

Top comments (0)