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 is 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 nobody renewed the SAML certificate. Every subscription you add is more places for one of these to hide.

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 needs list permission on every vault (or the Key Vault Reader role on RBAC vaults), which the person running it often doesn’t have everywhere.

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:

  • Secrets should have an expiration date blocks or flags secrets created without an expiry
  • Secrets should have more than the specified number of days before expiration flags secrets that are about to expire
  • [Preview]: Certificates should have the specified maximum validity period stops overly long certificate lifetimes

All three are built-in Key Vault policies, so there is no JSON to write.

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

What Full Discovery Needs

Each method above solves a piece of the puzzle, but none provides the full picture. Discovery that covers everything 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 with expiry, how each one renews (auto-renews, only emails its contacts, needs a manual renewal from an external CA, or will not renew at all), key size and weak signature checks, and a check that the CA’s intermediate certificates are valid
  • TLS certificates outside Key Vault: the certificate on each custom domain served by App Service, Application Gateway, API Management, Container Apps and Front Door, including ones uploaded by hand, with expiry alerts
  • Key Vault keys with expiry, version history and key strength checks
  • App Registration credentials including client secrets and certificates across all App Registrations you have access to, plus the federated credentials apps use to sign in without a secret
  • Enterprise Application credentials: SAML signing certificates, service principal secrets and other certificates, with owners. This is monitoring only. CertifyClouds alerts on these but doesn’t rotate them, and rollover SAML certificates are listed without triggering alerts

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