Logo

dev-resources.site

for different kinds of informations.

Automate Azure VM Password Rotation with PowerShell and Azure DevOps

Published at
11/7/2023
Categories
azuredevops
powershell
azure
automation
Author
txchi
Author
5 person written this
txchi
open
Automate Azure VM Password Rotation with PowerShell and Azure DevOps

Virtual Machines are ideal for hosting applications and sensitive databases. Regularly changing passwords for virtual machines is vital to prevent unauthorized access. However, manually updating passwords across multiple VMs can be tedious and error-prone. Automating password rotation using PowerShell and Azure DevOps improves efficiency.

Why the drama?

Imagine this: you have lots of VMs hosting sensitive data, and for some reason, you have a disgruntled ex-employee who still has access to some of the VMs. Having a script that generates and sets new random passwords at intervals could save you lots of mishaps. Other reasons are:

  • Mitigating Security Risks: Password rotation shortens the attacker’s window. Even if a password is compromised, its limited validity minimizes potential damage.
  • Compliance Requirements: Some industry regulations demand password rotation for security. Non-compliance risks severe penalties.
  • Proactive Security: By automating password rotation, you can ensure a proactive approach to security, rather than a reactive one that responds only after a security breach.

Less time wasted on repetitive manual processes means more time for higher value work that only humans can do.

Tools to achieve this task:

  • PowerShell. To generate and set random passwords for Azure VMs.
  • Azure DevOps. Pipeline to run the PowerShell script on a schedule.
  • Azure KeyVault. To store the generated password so it can be accessed.

Flow of the solution:

flow of the solution

I assume you already have an Azure subscription and Azure DevOps account set up, so lets get to it straight away…

Step 1: Set up App Registration

App Registration is required to setup service connection for authentication from Azure DevOps to the subscription(s) hosting our VMs and KeyVault.

  • Create a new one or use an existing registration.
  • In your portal, visit Microsoft Entra ID >> App Registrations.
  • Once created, note the Tenant ID and Application ID.
  • In Certificates and Secrets, create a client secret and note the value.

App and Tenant ID

client secret

Step 2: Set up Service Connection on Azure DevOps

Using the details we retrieved in step 1, we would set up a service connection in Azure DevOps.

  • In your Azure DevOps, create or navigate to your existing organization
  • Create a new project >> select project settings at the bottom left corner
  • In the pipelines section, select service connection and click create new
  • Select Azure Resource Manager >> select Service Principal (manual)
  • Fill the fields using details from step 1 and your subscription details (id and name),
  • Name the service connection >> click verify to validate the details.
  • Once verified, check the ‘grant access to all pipelines” box >> click Verify and save. It should be similar to the below:

Azure DevOps Service Connection Creation

You can add as many connections for other VMs that may be located in other subscriptions.

Step 3: Set Up Azure Key Vault

We use the KeyVault to store the newly generated password so we can access it securely.

In your portal, navigate to KeyVault, create a new or use an existing one. In ‘Secrets,’ click ‘Generate/Import.’ Provide a name (preferably your VM name) and add a placeholder password to the secret field, the script would modify it. Click create.

Create secret in key vault

In the overview page, choose ‘Access Policies.’ Under ‘Secret permissions,’ select all. In ‘Principal,’ find and select the earlier created App Registration. Review and create. This grants ‘KeyVault permission’ to your app.

Grant access to app registration in key vault

Step 4: The PowerShell Script

The script simplifies steps for easy understanding with enough comments for clarity.

It has three parts…

  • A primary script called by the pipeline, it fetches the VMs in a subscription.
  • The script then calls a function that generates the random password, updates the Azure KeyVault and VM passwords.
  • A JSON file stores VM details for accurate KeyVault secret matching.

Let’s dive in, beginning with the JSON file containing KeyVault details:

{
    "KeyVaultConfigurations": [
        {
        "VMName": "vm1", //your virtual machine name
        "KeyVaultName": "my-secretss", // your key vault name
        "SecretName": "vm1-secret" // name of the secret used to store your password
        },
        {
        "VMName": "vm2",
        "KeyVaultName": "my-secretss",
        "SecretName": "vm2-secret"
        }   
    ]
}
Enter fullscreen mode Exit fullscreen mode

The JSON file is named ‘configuration.json’.

Next, we create the script called by the pipeline that fetches the VMs from the subscription and calls the function, lets call it “secret-rotate.ps1”:

# Get all VMs in the current subscription context
$virtualMachinesDetails = Get-AzVM | Where-Object { $_.StorageProfile.OSDisk.OSType -eq "Windows" }

# Dot-source the function script to load the function
. ".\vm-kv-updatefunc.ps1"

# Call the function with your parameters
Update-VMPasswords -VirtualMachinesDetails $virtualMachinesDetails
Enter fullscreen mode Exit fullscreen mode

Next, we create the function to generate a strong password and update the VM password and KeyVault, lets call it “vm-kv-updatefunc.ps1”:

# Stage 1: Function to generate password
function Set-RandomPassword {
    param (
        [int]$length = 16
    )

    $lowerCase = "abcdefghijklmnopqrstuvwxyz"
    $upperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    $numbers = "0123456789"
    $symbols = "!@#$%^&*()_-+=<>?/[]{}|"

    $charPool = $lowerCase + $upperCase + $numbers + $symbols
    $password = -join ($charPool.ToCharArray() | Get-Random -Count $length)

    return $password
}

function Update-VMPasswords {
    param (
        [array]$VirtualMachinesDetails
    )

    # Stage 2: Loop through each VM, update keyvault and VM password
    $VirtualMachinesDetails | ForEach-Object {

        # Generate new password
        $generatedPassword = Set-RandomPassword

        $vmName = $_.Name

        # Load VM configurations from the JSON file
        $configurations = Get-Content -Raw -Path "./configurations.json" | ConvertFrom-Json
        $vmConfigurations = $configurations.KeyVaultConfigurations

        # Find the corresponding configuration for the VM
        $vmConfiguration = $vmConfigurations | Where-Object { $_.VMName -eq $vmName }

        # Check if keyvault details exisit for the vm
        if ($null -eq $vmConfiguration) {
            Write-Host "No keyvault configuration found for this VM: $vmName"
            continue
        }
        # Retrieve key vault details
        $keyVaultName = $vmConfiguration.KeyVaultName
        $secretName = $vmConfiguration.SecretName

        # Update the VM and keyvault with the new password
        try {
            $securePassword = ConvertTo-SecureString -String $generatedPassword -AsPlainText -Force
            Set-AzKeyVaultSecret -VaultName $keyVaultName -Name $secretName -SecretValue $securePassword

            Write-Host "Password for user $userName on VM $vmName successfully updated in Key Vault."

            $extensionParams = @{
                'ResourceGroupName' = $_.ResourceGroupName
                'Location' = $_.Location
                'VMName' = $_.Name
                'Name' = 'VMAccessAgent'
                'TypeHandlerVersion' = '2.0'
                'Credential' = New-Object System.Management.Automation.PSCredential ($vmName, $securePassword)
                'ForceRerun' = $true
            }

            Set-AzVMAccessExtension @extensionParams

            Write-Host "Password for VM: $vmName updated successfully."
            Write-Host -ForegroundColor Cyan ".........................................................................................\n"
        } catch {
            Write-Host "An error occurred while updating the Virtual Machine Password: $($_.Exception.Message)"
        }            
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Set Up Azure Pipeline

The pipeline yaml file to run the script at intervals via the Azure DevOps pipeline:

schedules:
- cron: '0 0 * * 7'  # Run every every week on a Sunday
  displayName: 'Every 1 minute schedule'
  branches:
    include:
    - <replace with the branch you want to trigger>

pr:
- '*'

variables:
- name: azureSubscription 
  value: '<replace with the name of your service connection>'

pool: 
  vmImage: 'windows-latest'

stages:
- stage: VirtualMachineSecretUpdate

  jobs:
  - job: VMSecretUpdate

    steps:
    - task: AzurePowerShell@5
      inputs:
        azureSubscription: $(azureSubscription)
        scriptType: filePath
        scriptPath: ./secret-rotate-scripts/secret-rotate.ps1 #replace with the path to your script
        azurePowerShellVersion: latestVersion
        pwsh: true
Enter fullscreen mode Exit fullscreen mode

Put the script and JSON file and script inside a folder. Let your folder structure look similar to this in Azure DevOps:

Folder structure of the solution

Final Step: Confirm Setup Works

I already have two virtual machines and their passwords are stored in a key vault. You can create and connect to yours using: Create Azure VM

Azure VMs

Once the pipeline triggers, it first updates the key vault and then update the VM password. You should get an output similar to the image below:

Azure pipeline successfully triggered

With the new password updated in the key vault (current version) I can now login into the VM.

Key vault updated password

Once you click the “current version” you would see the new password.

Conclusion

Automation makes easy work of complex and laborious tasks. By leveraging PowerShell and Azure DevOps to rotate credentials automatically, organizations can painlessly enforce strict password policies across their environments.

Next, I will cover updating each users in a VM (a VM can have multiple users). Until then, keep automating…

azuredevops Article's
30 articles in total
Favicon
Creating SBOM with sbom-tool and CycloneDX on Azure DevOps
Favicon
Public IP Address in Azure | Understanding Public IP Address in Azure VM
Favicon
Terraform - Mastering Idempotency Violations - Handling Resource Conflicts and Failures in Azure
Favicon
Azure Devops Converting Classic Pipelines to Yaml Based Pipeline
Favicon
Register Azure DevOps Agents with Service Principal Secret !
Favicon
The Ultimate Guide to Azure DevOps: Key Benefits for Software Development Projects
Favicon
Azure DevOps Zero to Hero Series
Favicon
Azure DevOps Series - Azure Boards
Favicon
cp: cannot stat 'bhfonlineshop': No such file or directory. Azure devops pipeline error
Favicon
How to Maintain Issue Hierarchy Between Jira and Azure DevOps
Favicon
Azure DevOps | Using Terraform
Favicon
Azure DevOps | Installing Postman and Newman using npm
Favicon
Azure DevOps | Running JMeter Test Collection using JMeter Docker Image
Favicon
Azure DevOps | Running a Postman Collection using Newman Docker Image
Favicon
Azure DevOps | Deploy Postman Tests in Azure DevOps Test Plans
Favicon
Exploring Microsoft Azure AI Capabilities Using React, Github Actions, Azure Static Apps and Azure AI
Favicon
Azure DevOps Zero-to-Hero
Favicon
Azure pipelines - Passing variables across stages
Favicon
Terraform - Keep dependencies up to date with Dependabot (Azure DevOps version)
Favicon
Automate Azure VM Password Rotation with PowerShell and Azure DevOps
Favicon
Investigating az-cli performance on the hosted Azure Pipelines and GitHub Runners
Favicon
Azure DevOps Pipeline deployments to Azure App Services with Access Restrictions
Favicon
Step by step for build , test and deploy using azuredevops pipeline
Favicon
Step by Step instruction hosting on aks cluster for begineers
Favicon
Deploying to Azure from Azure DevOps without secrets
Favicon
VSBuild task fails on self-hosted Azure Pipelines Agent
Favicon
Azure devops pipeline for dotnet with cicd
Favicon
Bicep modules with PSRule – Testing, documentation, CI Pipeline & examples
Favicon
Conectando o VS Community ao Azure DevOps
Favicon
Como clonar um projeto no Azure DevOps

Featured ones: