Why We Built a Free Scanner

The Azure teams we speak to tell the same story: they know secrets are expiring somewhere, but they don’t have visibility across all their subscriptions and Key Vaults. They’ve cobbled together PowerShell scripts, Azure Policy assignments, and calendar reminders, but nothing gives them a single, clear picture.

So we built one. cc-scan is a free command-line tool that audits every Key Vault you have access to and tells you what’s expiring and what’s misconfigured. It runs on your own machine with your existing az login session, and it costs nothing.

What It Does

cc-scan performs a read-only audit of your Azure Key Vaults. It inventories secrets, certificates, and cryptographic keys across all accessible subscriptions, then evaluates them against 14 security checks covering expiry, network security, access control, and data protection.

This is real scanner output from a demo subscription, trimmed for length, with the names changed:

Scanning Azure subscriptions...

contoso-demo (00000000-0000-0000-0000-000000000000)
├── kv-contoso-prod (uksouth): 22 secrets, 9 certificates, 13 keys
├── kv-contoso-staging (uksouth): 9 secrets, 4 certificates, 8 keys
├── kv-contoso-dev (uksouth): 29 secrets, 18 certificates, 20 keys
└── kv-contoso-dr (uksouth): 40 secrets, 10 certificates, 11 keys

─────────────────────────────── SUMMARY ───────────────────────────────
  Subscriptions: 1    Vaults: 7    Total assets: 206
  Secrets: 107    Certificates: 43    Keys: 56

EXPIRY WARNINGS
  EXPIRED     19 secrets, 3 certificates, 6 keys
  < 30 days   4 certificates, 4 keys
  < 90 days   4 secrets, 1 key

SECURITY FINDINGS
  CRITICAL: 28    MEDIUM: 45    INFO: 8

Severity    Finding                         Resource
CRITICAL    Expired secret still enabled    kv-contoso-prod/GitHub-PAT-CI
CRITICAL    Expired secret still enabled    kv-contoso-prod/Twilio-Auth-Token
CRITICAL    Expired key still enabled       kv-contoso-prod/PII-Encryption-Key
  ... and 66 more findings

That demo subscription is seeded with problems on purpose. Your numbers will be different, but the layout is the same.

No secret values are ever read. The scanner accesses metadata only: names, expiry dates, enabled status, vault configuration. Your credentials stay where they are.

The 14 Security Checks

Each finding has a severity so you know what to fix first:

Severity Rule What It Catches
CRITICAL SEC-001, SEC-002, SEC-012 Expired secrets (SEC-001), certificates (SEC-002) and keys (SEC-012) that are still enabled. Anything still using them is about to fail, or already has
HIGH SEC-003 Key Vaults with public network access enabled
HIGH SEC-004, SEC-005 Missing soft-delete or purge protection, meaning an accidental or malicious delete is permanent
MEDIUM SEC-006, SEC-007, SEC-013 Secrets (SEC-006), certificates (SEC-007) and keys (SEC-013) with no expiry date set, invisible to any expiry-based alerting
MEDIUM SEC-008 RBAC not enforced, relying on legacy access policies instead of Azure’s recommended model
LOW SEC-009 Standard SKU vaults without HSM backing for key operations
INFO SEC-010, SEC-011, SEC-014 Secrets (SEC-010), certificates (SEC-011) and keys (SEC-014) expiring within 30 days

The checks cover the same ground as the Key Vault section of the CIS Microsoft Azure Foundations Benchmark: expiry dates set, soft-delete and purge protection on, public access restricted, RBAC in use. If an auditor asks how you manage key material, a dated cc-scan report is a concrete answer.

Install cc-scan

pip (Python 3.11+):

pip install certifyclouds

Homebrew (macOS/Linux):

brew tap certifyclouds/certifyclouds
brew install certifyclouds

First run asks for your email once and gives you a free API key (no expiry), saved to ~/.certifyclouds/config.json. After that:

az login
cc-scan

That’s it. The scanner discovers all subscriptions your account can access and scans every Key Vault within them.

Output Formats

The default output is a rich terminal table, but cc-scan supports multiple formats for different workflows:

  • --format json for piping into jq, scripts, or SIEM ingestion
  • --format csv for spreadsheet analysis and sharing with non-technical stakeholders
  • --format html for a standalone report with charts that you can open in any browser or attach to an email
# Generate an HTML report
cc-scan --format html -o audit-report.html

# JSON for automation
cc-scan --format json -o results.json

# Scan specific subscriptions only
cc-scan --subscription "sub-id-1" --subscription "sub-id-2"

CI/CD Integration

cc-scan is built for pipelines. When several exit codes apply, the first one listed wins:

  • Exit 2: The scan could not run or read nothing, including a sign-in error or timeout
  • Exit 3: No key and no terminal to register in, or registration failed
  • Exit 1: A finding at or above --fail-on (default: high)
  • Exit 4: The scan finished with coverage gaps, such as unreadable vaults or item types
  • Exit 0: None of the above

A pipeline has no one to answer the email prompt, so set CC_SCAN_KEY as a secret. For GitHub Actions, sign in with OIDC through azure/login, then use the Azure CLI session with --auth cli. Give the pipeline identity Reader on each subscription and the vault permissions listed on the scanner page.

# GitHub Actions job: grant id-token: write in the workflow permissions
- name: Sign in to Azure
  uses: azure/login@v3
  with:
    client-id: ${{ secrets.AZURE_CLIENT_ID }}
    tenant-id: ${{ secrets.AZURE_TENANT_ID }}
    subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Audit Key Vaults
  env:
    CC_SCAN_KEY: ${{ secrets.CC_SCAN_KEY }}
  run: |
    pip install certifyclouds
    cc-scan --auth cli --format json -o scan-results.json
# Azure DevOps (variables come from a variable group)
- script: |
    pip install certifyclouds
    cc-scan --auth default --format json -o scan-results.json
  displayName: 'Key Vault Security Audit'
  env:
    AZURE_CLIENT_ID: $(AZURE_CLIENT_ID)
    AZURE_CLIENT_SECRET: $(AZURE_CLIENT_SECRET)
    AZURE_TENANT_ID: $(AZURE_TENANT_ID)
    CC_SCAN_KEY: $(CC_SCAN_KEY)

The CI/CD guide has the full workflows, the service principal setup and a GitLab example.

A failed gate means someone created a Key Vault without purge protection, or a secret expired and nobody rotated it. You want to hear about either before it reaches production.

Privacy and Offline Mode

We designed cc-scan with a minimal data footprint. After registration it sends counts only: how many subscriptions, vaults, secrets, certificates and keys were scanned, and how many were expired or expiring soon. It never sends names, values or subscription IDs.

If even that is too much for your environment, use --offline so nothing is sent to CertifyClouds after the initial registration:

cc-scan --offline

With --offline nothing is sent to CertifyClouds. The scan still talks to the Azure APIs, because that is where your vaults are.

From Scanner to Platform

cc-scan gives you a point-in-time snapshot, which is enough for an ad-hoc audit, a CI/CD gate or a first inventory. Credentials keep expiring after the scan finishes, though, and new ones appear every week.

When you’re ready to move from periodic scans to continuous lifecycle management, CertifyClouds picks up where cc-scan leaves off:

  • Continuous discovery with scheduled scans and real-time alerting
  • Certificates outside Key Vault, the ones your App Services, Application Gateways, API Management, Container Apps and Front Door serve on their custom domains, which a Key Vault scan never sees
  • Automated rotation of App Registration secrets and certificates with zero-downtime overlap windows
  • Dependency mapping showing which App Services, Functions, and Container Apps consume each secret
  • Multi-cloud sync to keep credentials consistent across Azure, AWS, and GCP
  • Compliance scoring against the CIS Microsoft Azure Foundations Benchmark, SOC 2, ISO 27001, NIST 800-53, the Microsoft cloud security benchmark, HIPAA and PCI DSS

Try CertifyClouds free with a 30-day trial. Starter or Pro depending on what you need. Starter includes discovery, compliance scoring, alerts, and 3 manual rotations per month.

Get Started

Install cc-scan, sign in to Azure and run your first scan:

pip install certifyclouds
az login
cc-scan

Full documentation: docs.certifyclouds.com/scanner

If the first scan turns up expired items you didn’t know about, start with the CRITICAL rows. Finding them now beats finding them at 3 AM, when a production integration fails because a certificate expired three weeks ago.


Further Reading