The Hidden Risk: Secrets You Don’t Know About

Every Azure environment accumulates secrets over time. Developers create App Registration credentials for integrations. Infrastructure teams provision Key Vault secrets for connection strings. CI/CD pipelines store deployment credentials. Over months and years, these secrets multiply, and many of them have expiry dates that nobody is tracking.

The result? Unexpected outages. A midnight page because the SQL connection string in Key Vault expired. A failed deployment because the service principal credential lapsed. A broken SSO integration because the SAML certificate wasn’t renewed.

Microsoft’s data shows that credential expiry is one of the top causes of Azure application downtime. The risk scales linearly with the number of subscriptions you manage.

Method 1: Azure CLI (Quick and Dirty)

For a quick spot-check of a single Key Vault, the Azure CLI works well:

# List secrets with expiry dates in a specific vault
az keyvault secret list --vault-name myVault \
  --query "[].{name:name, expires:attributes.expires, enabled:attributes.enabled}" \
  -o table

# List certificates
az keyvault certificate list --vault-name myVault \
  --query "[].{name:name, expires:attributes.expires}" \
  -o table

# List keys
az keyvault key list --vault-name myVault \
  --query "[].{name:name, expires:attributes.expires}" \
  -o table

Limitations: This only queries one vault at a time. If you have 20 vaults across 5 subscriptions, you’re writing a loop. It also doesn’t check App Registration credentials, which live outside Key Vault.

Method 2: PowerShell with Az Module (Better Coverage)

PowerShell gives you more flexibility to scan across vaults and subscriptions:

# Get all Key Vaults across all subscriptions
$subscriptions = Get-AzSubscription
$allSecrets = @()

foreach ($sub in $subscriptions) {
    Set-AzContext -SubscriptionId $sub.Id | Out-Null
    $vaults = Get-AzKeyVault

    foreach ($vault in $vaults) {
        $secrets = Get-AzKeyVaultSecret -VaultName $vault.VaultName

        foreach ($secret in $secrets) {
            if ($secret.Expires) {
                $daysUntilExpiry = ($secret.Expires - (Get-Date)).Days
                $allSecrets += [PSCustomObject]@{
                    Subscription = $sub.Name
                    Vault = $vault.VaultName
                    Name = $secret.Name
                    Expires = $secret.Expires
                    DaysRemaining = $daysUntilExpiry
                    Status = if ($daysUntilExpiry -lt 0) { "EXPIRED" }
                             elseif ($daysUntilExpiry -lt 30) { "CRITICAL" }
                             elseif ($daysUntilExpiry -lt 90) { "WARNING" }
                             else { "OK" }
                }
            }
        }
    }
}

# Show secrets needing attention
$allSecrets | Where-Object { $_.Status -ne "OK" } |
    Sort-Object DaysRemaining |
    Format-Table -AutoSize

Limitations: Slow. Each Get-AzKeyVaultSecret call is a separate API request. For large environments this can take 30+ minutes. It also requires the operator to have Get permissions on every vault’s access policy or RBAC role, which isn’t always the case.

Method 3: Azure Resource Graph (Fastest for Scale)

Azure Resource Graph queries are fast and work across subscriptions without iterating:

resources
| where type == "microsoft.keyvault/vaults"
| project vaultName = name, subscriptionId, resourceGroup, location

However, Resource Graph has a significant limitation: it doesn’t expose secret-level details. You can find Key Vaults, but not the individual secrets inside them. For secret-level data, you still need the Key Vault data plane API.

For App Registration credentials, Microsoft Graph is the way to go:

# Find App Registrations with expiring credentials
az ad app list --all \
  --query "[].{appId:appId, displayName:displayName, \
    secrets:passwordCredentials[].{hint:hint, endDate:endDateTime}, \
    certs:keyCredentials[].{type:type, endDate:endDateTime}}" \
  -o json

This reveals a common blind spot: App Registration secrets that were created outside of Key Vault, have no expiry monitoring, and are only discovered when they break.

Method 4: Azure Policy (Preventive)

Azure Policy can enforce secret hygiene proactively:

  • Key Vault secrets should have an expiration date (built-in policy) prevents creating secrets without an expiry
  • Key Vault secrets should have more than N days before expiration flags non-compliant secrets
  • Certificates should have a maximum validity period prevents overly long certificate lifetimes

Policy is excellent for prevention but poor for remediation. It tells you what’s wrong but doesn’t fix it.

The Comprehensive Approach

Each method above solves a piece of the puzzle, but none provides the full picture. A comprehensive discovery solution needs to:

  1. Scan all Key Vaults across all subscriptions in a single pass
  2. Include App Registration credentials, both client secrets and certificates
  3. Check certificates including expiry, key size, and algorithm compliance
  4. Track secrets without expiry dates, which are the most dangerous because they never trigger alerts
  5. Provide a unified dashboard with one view showing every credential across your estate, sorted by urgency
  6. Run continuously, not just a one-time scan, but ongoing monitoring with alerting

What CertifyClouds Discovers

CertifyClouds runs discovery scans across all your Azure subscriptions and surfaces:

  • Key Vault secrets with expiry status, creation date, version history, and consuming applications
  • Key Vault certificates including chain validation, key size compliance, and renewal dates
  • Key Vault keys with rotation history and algorithm compliance
  • App Registration credentials including client secrets and certificates across all App Registrations you have access to

The scan results appear in a unified dashboard with compliance scoring, filterable by subscription, vault, expiry window, and secret type. You can configure email alerts for secrets entering your warning threshold (e.g., 30 days before expiry).

Building Your Discovery Practice

Whether you use scripts, policies, or dedicated tooling, the important thing is to start discovering. The secrets you don’t know about are the ones that will cause your next outage.

A pragmatic starting point:

  1. Run the PowerShell script above (or use the CLI method) to get a baseline inventory
  2. Enable Azure Policy to prevent new secrets without expiry dates
  3. Set up recurring scans, weekly at minimum, daily for production subscriptions
  4. Funnel results into your incident management workflow so expiring secrets become actionable tickets

Or, get your full picture in minutes: try CertifyClouds free and run your first cross-subscription discovery scan today. No agents to install, no scripts to maintain. Just connect your Azure subscriptions and see everything.


Further Reading