Azure Key Vault: Complete Secrets Management Guide

Tyler Maginnis | January 20, 2024

AzureKey VaultSecuritySecrets ManagementCertificates

Need Professional Azure Services?

Get expert assistance with your azure services implementation and management. Tyler on Tech Louisville provides priority support for Louisville businesses.

Same-day service available for Louisville area

Azure Key Vault: Complete Secrets Management Guide

Azure Key Vault provides secure storage and management of secrets, keys, and certificates. This comprehensive guide covers everything from basic setup to advanced security configurations for small business environments.

Understanding Azure Key Vault

Key Vault Objects

  • Secrets: Application secrets, passwords, connection strings
  • Keys: Cryptographic keys for encryption and signing
  • Certificates: SSL/TLS certificates and PKI management
  • Storage Account Keys: Automated key rotation

Pricing Tiers

  • Standard: Software-protected keys and secrets
  • Premium: Hardware Security Module (HSM) protected keys
  • Managed HSM: Dedicated HSM instances

Creating and Configuring Key Vault

PowerShell Setup

# Install Azure PowerShell module
Install-Module -Name Az -Force

# Connect to Azure
Connect-AzAccount

# Create resource group
New-AzResourceGroup -Name "KeyVault-RG" -Location "East US"

# Create Key Vault
$vault = New-AzKeyVault `
    -ResourceGroupName "KeyVault-RG" `
    -VaultName "BusinessKeyVault001" `
    -Location "East US" `
    -EnabledForDeployment `
    -EnabledForTemplateDeployment `
    -EnabledForDiskEncryption `
    -Sku "Standard"

# Enable soft delete and purge protection
Update-AzKeyVault `
    -ResourceGroupName "KeyVault-RG" `
    -VaultName "BusinessKeyVault001" `
    -EnableSoftDelete `
    -EnablePurgeProtection

Azure CLI Setup

# Login to Azure
az login

# Create resource group
az group create --name "KeyVault-RG" --location "eastus"

# Create Key Vault
az keyvault create \
    --name "BusinessKeyVault001" \
    --resource-group "KeyVault-RG" \
    --location "eastus" \
    --enable-soft-delete \
    --enable-purge-protection \
    --enabled-for-deployment \
    --enabled-for-template-deployment \
    --enabled-for-disk-encryption

Access Control and Security

Access Policies

# Set access policy for user
Set-AzKeyVaultAccessPolicy `
    -VaultName "BusinessKeyVault001" `
    -UserPrincipalName "admin@company.com" `
    -PermissionsToSecrets get,list,set,delete `
    -PermissionsToKeys get,list,create,delete,update `
    -PermissionsToCertificates get,list,create,delete,update

# Set access policy for service principal
Set-AzKeyVaultAccessPolicy `
    -VaultName "BusinessKeyVault001" `
    -ServicePrincipalName "12345678-1234-1234-1234-123456789012" `
    -PermissionsToSecrets get,list `
    -PermissionsToKeys get,list

RBAC Integration

# Enable RBAC for Key Vault
Update-AzKeyVault `
    -ResourceGroupName "KeyVault-RG" `
    -VaultName "BusinessKeyVault001" `
    -EnableRbacAuthorization

# Assign RBAC roles
New-AzRoleAssignment `
    -SignInName "admin@company.com" `
    -RoleDefinitionName "Key Vault Secrets Officer" `
    -Scope "/subscriptions/subscription-id/resourceGroups/KeyVault-RG/providers/Microsoft.KeyVault/vaults/BusinessKeyVault001"

New-AzRoleAssignment `
    -SignInName "developer@company.com" `
    -RoleDefinitionName "Key Vault Secrets User" `
    -Scope "/subscriptions/subscription-id/resourceGroups/KeyVault-RG/providers/Microsoft.KeyVault/vaults/BusinessKeyVault001"

Network Security

# Configure network access rules
Update-AzKeyVault `
    -ResourceGroupName "KeyVault-RG" `
    -VaultName "BusinessKeyVault001" `
    -DefaultAction "Deny" `
    -NetworkRuleSet @{
        "ipRules" = @(
            @{
                "ipAddressOrRange" = "203.0.113.0/24"
                "action" = "Allow"
            }
        )
        "virtualNetworkRules" = @(
            @{
                "virtualNetworkResourceId" = "/subscriptions/subscription-id/resourceGroups/Network-RG/providers/Microsoft.Network/virtualNetworks/Business-VNet/subnets/KeyVault-Subnet"
                "action" = "Allow"
            }
        )
    }

Secrets Management

Creating and Managing Secrets

# Create secret
Set-AzKeyVaultSecret `
    -VaultName "BusinessKeyVault001" `
    -Name "DatabaseConnectionString" `
    -SecretValue (ConvertTo-SecureString "Server=businesssqlserver001.database.windows.net;Database=BusinessDB;User Id=sqladmin;Password=SecurePassword123!;" -AsPlainText -Force)

# Create secret with expiration
Set-AzKeyVaultSecret `
    -VaultName "BusinessKeyVault001" `
    -Name "APIKey" `
    -SecretValue (ConvertTo-SecureString "api-key-value" -AsPlainText -Force) `
    -Expires (Get-Date).AddYears(1) `
    -ContentType "text/plain"

# Get secret
$secret = Get-AzKeyVaultSecret `
    -VaultName "BusinessKeyVault001" `
    -Name "DatabaseConnectionString"

# Get secret value
$secretValue = Get-AzKeyVaultSecret `
    -VaultName "BusinessKeyVault001" `
    -Name "DatabaseConnectionString" `
    -AsPlainText

Secret Versioning

# Update secret (creates new version)
Set-AzKeyVaultSecret `
    -VaultName "BusinessKeyVault001" `
    -Name "DatabaseConnectionString" `
    -SecretValue (ConvertTo-SecureString "Server=businesssqlserver001.database.windows.net;Database=BusinessDB;User Id=sqladmin;Password=NewSecurePassword123!;" -AsPlainText -Force)

# List secret versions
Get-AzKeyVaultSecret `
    -VaultName "BusinessKeyVault001" `
    -Name "DatabaseConnectionString" `
    -IncludeVersions

# Get specific version
Get-AzKeyVaultSecret `
    -VaultName "BusinessKeyVault001" `
    -Name "DatabaseConnectionString" `
    -Version "12345678123456781234567812345678" `
    -AsPlainText

Bulk Secret Operations

# Import secrets from JSON file
$secrets = @{
    "SMTPPassword" = "smtp-password"
    "StorageAccountKey" = "storage-account-key"
    "ThirdPartyAPIKey" = "third-party-api-key"
}

foreach ($secretName in $secrets.Keys) {
    Set-AzKeyVaultSecret `
        -VaultName "BusinessKeyVault001" `
        -Name $secretName `
        -SecretValue (ConvertTo-SecureString $secrets[$secretName] -AsPlainText -Force)
}

# Export secrets (for backup/migration)
$allSecrets = Get-AzKeyVaultSecret -VaultName "BusinessKeyVault001"
$secretsBackup = @{}
foreach ($secret in $allSecrets) {
    $secretValue = Get-AzKeyVaultSecret -VaultName "BusinessKeyVault001" -Name $secret.Name -AsPlainText
    $secretsBackup[$secret.Name] = $secretValue
}
$secretsBackup | ConvertTo-Json | Out-File "secrets-backup.json"

Key Management

Creating Cryptographic Keys

# Create RSA key
Add-AzKeyVaultKey `
    -VaultName "BusinessKeyVault001" `
    -Name "EncryptionKey" `
    -KeyType "RSA" `
    -KeySize 2048 `
    -KeyUsage "Encrypt", "Decrypt"

# Create EC key
Add-AzKeyVaultKey `
    -VaultName "BusinessKeyVault001" `
    -Name "SigningKey" `
    -KeyType "EC" `
    -CurveName "P-256" `
    -KeyUsage "Sign", "Verify"

# Create HSM-protected key (Premium tier)
Add-AzKeyVaultKey `
    -VaultName "BusinessKeyVault001" `
    -Name "HSMKey" `
    -KeyType "RSA-HSM" `
    -KeySize 2048 `
    -KeyUsage "Encrypt", "Decrypt"

Key Operations

# Get key
$key = Get-AzKeyVaultKey `
    -VaultName "BusinessKeyVault001" `
    -Name "EncryptionKey"

# Encrypt data
$plaintext = "sensitive data"
$plaintextBytes = [System.Text.Encoding]::UTF8.GetBytes($plaintext)
$encryptedData = Invoke-AzKeyVaultKeyOperation `
    -VaultName "BusinessKeyVault001" `
    -KeyName "EncryptionKey" `
    -Operation "Encrypt" `
    -Algorithm "RSA-OAEP" `
    -Value $plaintextBytes

# Decrypt data
$decryptedData = Invoke-AzKeyVaultKeyOperation `
    -VaultName "BusinessKeyVault001" `
    -KeyName "EncryptionKey" `
    -Operation "Decrypt" `
    -Algorithm "RSA-OAEP" `
    -Value $encryptedData.Result

$decryptedText = [System.Text.Encoding]::UTF8.GetString($decryptedData.Result)

Key Rotation

# Create new key version
Add-AzKeyVaultKey `
    -VaultName "BusinessKeyVault001" `
    -Name "EncryptionKey" `
    -KeyType "RSA" `
    -KeySize 2048 `
    -KeyUsage "Encrypt", "Decrypt"

# Set key expiration
Update-AzKeyVaultKey `
    -VaultName "BusinessKeyVault001" `
    -Name "EncryptionKey" `
    -Expires (Get-Date).AddMonths(6)

# Disable old key version
Update-AzKeyVaultKey `
    -VaultName "BusinessKeyVault001" `
    -Name "EncryptionKey" `
    -Version "old-version-id" `
    -Enable $false

Certificate Management

Creating Self-Signed Certificates

# Create certificate policy
$policy = New-AzKeyVaultCertificatePolicy `
    -SubjectName "CN=company.com" `
    -IssuerName "Self" `
    -ValidityInMonths 12 `
    -RenewAtNumberOfDaysBeforeExpiry 30

# Create certificate
Add-AzKeyVaultCertificate `
    -VaultName "BusinessKeyVault001" `
    -Name "CompanyCertificate" `
    -CertificatePolicy $policy

Importing Certificates

# Import PFX certificate
Import-AzKeyVaultCertificate `
    -VaultName "BusinessKeyVault001" `
    -Name "SSLCertificate" `
    -FilePath "C:\Certificates\ssl-certificate.pfx" `
    -Password (ConvertTo-SecureString "certificate-password" -AsPlainText -Force)

# Import certificate from file
$cert = Import-AzKeyVaultCertificate `
    -VaultName "BusinessKeyVault001" `
    -Name "CACertificate" `
    -FilePath "C:\Certificates\ca-certificate.cer"

Certificate Automation

# Set up Let's Encrypt certificate
$policy = New-AzKeyVaultCertificatePolicy `
    -SubjectName "CN=www.company.com" `
    -IssuerName "Let's Encrypt" `
    -ValidityInMonths 3 `
    -RenewAtNumberOfDaysBeforeExpiry 30 `
    -SubjectAlternativeNames @("company.com", "api.company.com")

Add-AzKeyVaultCertificate `
    -VaultName "BusinessKeyVault001" `
    -Name "LetsEncryptCertificate" `
    -CertificatePolicy $policy

Application Integration

.NET Integration

// Install NuGet packages:
// Azure.Security.KeyVault.Secrets
// Azure.Identity

using Azure.Identity;
using Azure.Security.KeyVault.Secrets;

public class KeyVaultService
{
    private readonly SecretClient _secretClient;

    public KeyVaultService()
    {
        var credential = new DefaultAzureCredential();
        _secretClient = new SecretClient(
            new Uri("https://businesskeyvault001.vault.azure.net/"),
            credential);
    }

    public async Task<string> GetSecretAsync(string secretName)
    {
        try
        {
            var secret = await _secretClient.GetSecretAsync(secretName);
            return secret.Value.Value;
        }
        catch (Exception ex)
        {
            // Handle exception
            throw;
        }
    }

    public async Task SetSecretAsync(string secretName, string secretValue)
    {
        await _secretClient.SetSecretAsync(secretName, secretValue);
    }
}

PowerShell Integration

# Function to get secret
function Get-BusinessSecret {
    param(
        [string]$SecretName,
        [string]$VaultName = "BusinessKeyVault001"
    )

    try {
        $secret = Get-AzKeyVaultSecret -VaultName $VaultName -Name $SecretName -AsPlainText
        return $secret
    }
    catch {
        Write-Error "Failed to retrieve secret: $($_.Exception.Message)"
        return $null
    }
}

# Function to set secret
function Set-BusinessSecret {
    param(
        [string]$SecretName,
        [string]$SecretValue,
        [string]$VaultName = "BusinessKeyVault001"
    )

    try {
        Set-AzKeyVaultSecret -VaultName $VaultName -Name $SecretName -SecretValue (ConvertTo-SecureString $SecretValue -AsPlainText -Force)
        Write-Host "Secret '$SecretName' set successfully"
    }
    catch {
        Write-Error "Failed to set secret: $($_.Exception.Message)"
    }
}

ARM Template Integration

{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
        "keyVaultName": {
            "type": "string",
            "defaultValue": "BusinessKeyVault001"
        }
    },
    "variables": {},
    "resources": [
        {
            "type": "Microsoft.Web/sites",
            "apiVersion": "2021-02-01",
            "name": "businesswebapp001",
            "properties": {
                "siteConfig": {
                    "appSettings": [
                        {
                            "name": "DatabaseConnectionString",
                            "value": "[concat('@Microsoft.KeyVault(SecretUri=https://', parameters('keyVaultName'), '.vault.azure.net/secrets/DatabaseConnectionString/)')]"
                        }
                    ]
                }
            }
        }
    ]
}

Monitoring and Auditing

Diagnostic Settings

# Enable diagnostic logging
$workspaceId = "/subscriptions/subscription-id/resourceGroups/Monitoring-RG/providers/Microsoft.OperationalInsights/workspaces/BusinessLogAnalytics"

Set-AzDiagnosticSetting `
    -ResourceId "/subscriptions/subscription-id/resourceGroups/KeyVault-RG/providers/Microsoft.KeyVault/vaults/BusinessKeyVault001" `
    -Name "KeyVaultDiagnostics" `
    -Enabled $true `
    -WorkspaceId $workspaceId `
    -Log @(
        @{
            "category" = "AuditEvent"
            "enabled" = $true
            "retentionPolicy" = @{
                "enabled" = $true
                "days" = 90
            }
        }
    )

Azure Monitor Alerts

# Create action group
$actionGroup = New-AzActionGroup `
    -ResourceGroupName "KeyVault-RG" `
    -Name "keyvault-alerts" `
    -ShortName "kvAlerts" `
    -EmailReceiver @{
        "name" = "admin"
        "emailAddress" = "admin@company.com"
    }

# Create alert rule for failed access attempts
New-AzMetricAlertRule `
    -ResourceGroupName "KeyVault-RG" `
    -Name "failed-key-vault-access" `
    -TargetResourceId "/subscriptions/subscription-id/resourceGroups/KeyVault-RG/providers/Microsoft.KeyVault/vaults/BusinessKeyVault001" `
    -MetricName "ServiceApiResult" `
    -Operator "GreaterThan" `
    -Threshold 5 `
    -WindowSize "00:15:00" `
    -TimeAggregationOperator "Total" `
    -ActionGroupId $actionGroup.Id

Log Analytics Queries

// Key Vault access logs
KeyVaultLogs
| where TimeGenerated > ago(24h)
| where ResultType != "Success"
| summarize count() by CallerIPAddress, OperationName
| order by count_ desc

// Secret access patterns
KeyVaultLogs
| where TimeGenerated > ago(7d)
| where OperationName == "SecretGet"
| summarize count() by SecretName = tostring(parse_json(Properties).secretName)
| order by count_ desc

// Certificate expiration monitoring
KeyVaultLogs
| where TimeGenerated > ago(30d)
| where OperationName == "CertificateGet"
| extend CertName = tostring(parse_json(Properties).certificateName)
| summarize LastAccessed = max(TimeGenerated) by CertName

Backup and Recovery

Backup Strategies

# Backup all secrets
$secrets = Get-AzKeyVaultSecret -VaultName "BusinessKeyVault001"
$backup = @{}

foreach ($secret in $secrets) {
    $secretValue = Get-AzKeyVaultSecret -VaultName "BusinessKeyVault001" -Name $secret.Name -AsPlainText
    $backup[$secret.Name] = @{
        "value" = $secretValue
        "contentType" = $secret.ContentType
        "expires" = $secret.Expires
        "notBefore" = $secret.NotBefore
    }
}

# Save backup
$backup | ConvertTo-Json -Depth 3 | Out-File "keyvault-backup-$(Get-Date -Format 'yyyyMMdd-HHmmss').json"

# Backup individual secret
$secretBackup = Backup-AzKeyVaultSecret -VaultName "BusinessKeyVault001" -Name "DatabaseConnectionString" -OutputFile "secret-backup.blob"

Recovery Procedures

# Restore from backup file
Restore-AzKeyVaultSecret -VaultName "BusinessKeyVault001" -InputFile "secret-backup.blob"

# Restore from JSON backup
$backupData = Get-Content "keyvault-backup.json" | ConvertFrom-Json
foreach ($secretName in $backupData.PSObject.Properties.Name) {
    $secretInfo = $backupData.$secretName
    Set-AzKeyVaultSecret `
        -VaultName "BusinessKeyVault001" `
        -Name $secretName `
        -SecretValue (ConvertTo-SecureString $secretInfo.value -AsPlainText -Force) `
        -ContentType $secretInfo.contentType `
        -Expires $secretInfo.expires
}

Best Practices

Security

  • Use RBAC instead of access policies when possible
  • Enable soft delete and purge protection
  • Implement network restrictions for production vaults
  • Regular access reviews and permission audits

Operations

  • Use managed identities for application access
  • Implement secret rotation policies
  • Monitor access patterns with Azure Monitor
  • Backup critical secrets regularly

Development

  • Use different vaults for different environments
  • Implement proper error handling in applications
  • Use Azure Key Vault references in ARM templates
  • Test disaster recovery procedures

Cost Optimization

Usage Monitoring

# Monitor Key Vault operations
Get-AzMetric `
    -ResourceId "/subscriptions/subscription-id/resourceGroups/KeyVault-RG/providers/Microsoft.KeyVault/vaults/BusinessKeyVault001" `
    -MetricName "ServiceApiHit" `
    -TimeGrain 01:00:00 `
    -StartTime (Get-Date).AddDays(-30) `
    -EndTime (Get-Date)

# Calculate monthly costs
$operations = 10000 # Example operations per month
$standardCost = [math]::Ceiling($operations / 10000) * 0.03
$premiumCost = [math]::Ceiling($operations / 10000) * 0.15
Write-Host "Standard tier monthly cost: $${standardCost}"
Write-Host "Premium tier monthly cost: $${premiumCost}"

Optimization Strategies

# Cleanup old secret versions
$secrets = Get-AzKeyVaultSecret -VaultName "BusinessKeyVault001"
foreach ($secret in $secrets) {
    $versions = Get-AzKeyVaultSecret -VaultName "BusinessKeyVault001" -Name $secret.Name -IncludeVersions
    $oldVersions = $versions | Where-Object { $_.Enabled -eq $false -and $_.Created -lt (Get-Date).AddDays(-30) }

    foreach ($oldVersion in $oldVersions) {
        Remove-AzKeyVaultSecret -VaultName "BusinessKeyVault001" -Name $secret.Name -Version $oldVersion.Version -Force
    }
}

Troubleshooting

Common Issues

# Check vault status
Get-AzKeyVault -VaultName "BusinessKeyVault001"

# Test connectivity
Test-AzKeyVaultKeyOperation -VaultName "BusinessKeyVault001" -KeyName "TestKey"

# Check access permissions
Get-AzKeyVaultAccessPolicy -VaultName "BusinessKeyVault001"

# Verify network access
$context = Get-AzContext
Invoke-AzRestMethod -Uri "https://businesskeyvault001.vault.azure.net/secrets?api-version=7.3" -Method GET

Error Resolution

# Access denied errors
# Check RBAC assignments
Get-AzRoleAssignment -Scope "/subscriptions/subscription-id/resourceGroups/KeyVault-RG/providers/Microsoft.KeyVault/vaults/BusinessKeyVault001"

# Network access errors
# Check firewall rules
Get-AzKeyVault -VaultName "BusinessKeyVault001" | Select-Object -ExpandProperty NetworkRuleSet

# Soft delete recovery
# List deleted secrets
Get-AzKeyVaultSecret -VaultName "BusinessKeyVault001" -InRemovedState

# Recover deleted secret
Undo-AzKeyVaultSecretRemoval -VaultName "BusinessKeyVault001" -Name "DeletedSecret"

Conclusion

Azure Key Vault provides enterprise-grade security for managing secrets, keys, and certificates. Proper implementation of access controls, monitoring, and backup strategies ensures secure and reliable secret management for your business applications.

For professional Azure Key Vault implementation and security consulting services in Louisville, contact Tyler on Tech Louisville for expert assistance with your secrets management strategy.