Detect configuration drift between deployed Azure resources and stored deployment state. Compare actual Azure configuration against desired state in .azure/deployments/, identify differences, and guide user through reconciliation options. Use when checking for manual changes, policy remediations, or unauthorized modifications.
npx skills add https://github.com/Azure/git-ape --skill azure-drift-detector
Detect when Azure resources have been modified outside of Git-Ape workflows (manual portal changes, Azure Policy remediations, external tools) and guide users through reconciliation.
Triggers:
Ask user which deployment to check for drift:
Which deployment do you want to check for drift?
Recent deployments:
- deploy-20260218-143022 (func-api-dev-eastus) - Deployed 2 hours ago
- deploy-20260217-091530 (app-webapp-prod-eastus) - Deployed 1 day ago
- deploy-20260216-153045 (sql-data-prod-eastus) - Deployed 2 days ago
Or enter deployment ID:
Load deployment state:
DEPLOYMENT_ID="deploy-20260218-143022"
DEPLOYMENT_PATH=".azure/deployments/$DEPLOYMENT_ID"
# Load stored state
REQUIREMENTS=$(cat "$DEPLOYMENT_PATH/requirements.json")
TEMPLATE=$(cat "$DEPLOYMENT_PATH/template.json")
METADATA=$(cat "$DEPLOYMENT_PATH/metadata.json")
# Extract resource IDs
RESOURCE_IDS=$(jq -r '.resources[].id' "$DEPLOYMENT_PATH/metadata.json")
For each resource in the deployment, fetch current configuration:
# Use Azure CLI to get current resource properties
for RESOURCE_ID in $RESOURCE_IDS; do
CURRENT_STATE=$(az resource show --ids "$RESOURCE_ID" --output json)
# Store for comparison
echo "$CURRENT_STATE" > "$DEPLOYMENT_PATH/drift-analysis/current-state-$(basename $RESOURCE_ID).json"
done
Alternative: Use Azure Resource Graph
# Query multiple resources efficiently
az graph query -q "
Resources
| where id in ('$RESOURCE_ID_1', '$RESOURCE_ID_2')
| project id, name, type, location, properties, tags
" --output json > "$DEPLOYMENT_PATH/drift-analysis/current-state.json"
Use the drift detection script to identify differences:
# Run drift detection
.github/skills/azure-drift-detector/scripts/detect-drift.sh \
--deployment-id "$DEPLOYMENT_ID" \
--output-format "markdown"
Script analyzes:
Drift categories:
Show user a comprehensive drift analysis:
## Drift Detection Report
**Deployment:** deploy-20260218-143022
**Checked:** 2026-02-18 19:30:00 UTC
**Resources Analyzed:** 5
### Summary
- 🔴 Critical Drift: 1 resource
- 🟡 Warning Drift: 2 resources
- ✅ No Drift: 2 resources
---
### 🔴 CRITICAL: Function App (func-api-dev-eastus)
**Property:** `properties.httpsOnly`
- Expected (deployment state): `true`
- Current (Azure): `false`
- **Impact:** Security vulnerability - HTTP traffic allowed
- **Changed:** ~30 minutes ago (2026-02-18 19:00:00 UTC)
- **Likely Cause:** Manual portal change or policy remediation
**Property:** `properties.siteConfig.appSettings.FUNCTIONS_WORKER_RUNTIME`
- Expected: `python`
- Current: `node`
- **Impact:** Runtime mismatch - application may fail
- **Changed:** ~30 minutes ago
---
### 🟡 WARNING: Storage Account (stfuncdeveastus8k3m)
**Property:** `tags.Environment`
- Expected: `dev`
- Current: `development`
- **Impact:** Tag inconsistency - reporting/billing may be affected
**Property:** `properties.minimumTlsVersion`
- Expected: `TLS1_2`
- Current: `TLS1_0`
- **Impact:** Weak TLS version - security concern
---
### ✅ NO DRIFT: Application Insights (appi-api-dev-eastus)
All properties match deployment state.
Present reconciliation options:
## Drift Reconciliation Options
You have 3 options for handling this drift:
### A. **Accept Drift** (Update IaC to match Azure)
Update deployment state files to reflect current Azure configuration.
- Updates: requirements.json, template.json, metadata.json
- Next deployment will use new values as baseline
- **Use when:** The Azure changes are intentional and should be preserved
### B. **Revert Drift** (Restore desired state from IaC)
Redeploy resources to enforce original configuration.
- Reverts: httpsOnly → true, runtime → python, tags → original
- Creates new deployment: deploy-20260218-193000-revert
- **Use when:** Azure changes were unauthorized or incorrect
### C. **Selective Reconciliation** (Choose per property)
Review each drift and decide individually:
- Keep some Azure changes
- Revert others to deployment state
- **Use when:** Some changes are valid, others are not
### D. **Mark as Known Drift** (Ignore for now)
Document the drift but don't reconcile yet.
- Adds to: drift-analysis/known-drift.json
- Future checks won't alert on these specific changes
- **Use when:** Investigating before deciding action
---
**Which option would you like?** (Type A, B, C, or D)
Option A: Accept Drift
# Update deployment state to match Azure
.github/skills/azure-drift-detector/scripts/accept-drift.sh \
--deployment-id "$DEPLOYMENT_ID"
# Actions:
# 1. Fetch current Azure state
# 2. Update requirements.json with new values
# 3. Regenerate ARM template to match
# 4. Update metadata.json with drift acceptance record
# 5. Create git commit (if in version control)
# Log acceptance
cat >> "$DEPLOYMENT_PATH/drift-analysis/drift-log.jsonl" <<EOF
{
"timestamp": "2026-02-18T19:30:00Z",
"action": "accept",
"user": "$(az account show --query user.name -o tsv)",
"changes": {
"httpsOnly": {"from": true, "to": false},
"runtime": {"from": "python", "to": "node"}
},
"reason": "Intentional runtime change to Node.js"
}
EOF
Option B: Revert Drift
# Redeploy to enforce desired state
.github/skills/azure-drift-detector/scripts/revert-drift.sh \
--deployment-id "$DEPLOYMENT_ID" \
--confirm
# Actions:
# 1. Load original template.json and parameters.json
# 2. Create revert deployment: deploy-{timestamp}-revert
# 3. Deploy with mode: Incremental (only changes)
# 4. Monitor deployment
# 5. Verify drift resolved
# 6. Update metadata with revert record
# Confirmation prompt:
echo "⚠️ This will revert the following changes:"
echo " - httpsOnly: false → true"
echo " - runtime: node → python"
echo ""
echo "Type 'confirm revert' to proceed:"
read CONFIRMATION
Option C: Selective Reconciliation
# Present each drift individually
echo "Drift 1/4: httpsOnly changed from true to false"
echo ""
echo "What should we do?"
echo " A. Keep Azure value (false) - Update IaC"
echo " B. Revert to IaC value (true) - Redeploy"
echo " S. Skip this property for now"
echo ""
echo "Your choice (A/B/S):"
read CHOICE_1
# Repeat for each drift
# Build reconciliation plan
# Execute combined update + revert
Option D: Mark as Known Drift
# Document known drift
cat >> "$DEPLOYMENT_PATH/drift-analysis/known-drift.json" <<EOF
{
"resource": "func-api-dev-eastus",
"properties": {
"httpsOnly": {
"expected": true,
"actual": false,
"markedBy": "[email protected]",
"markedAt": "2026-02-18T19:30:00Z",
"reason": "Investigating runtime issue, temporarily allowing HTTP",
"reviewBy": "2026-02-19T09:00:00Z"
}
}
}
EOF
# Future drift checks will skip these properties
Scheduled Drift Detection:
# Add to cron or Azure DevOps pipeline
# Run daily drift check on all active deployments
.github/scripts/drift-check-all.sh
# Output:
# Checking 15 active deployments...
# ✅ 12 deployments: No drift detected
# 🟡 2 deployments: Warning drift (tags only)
# 🔴 1 deployment: Critical drift (security settings)
#
# Details in: .azure/drift-reports/2026-02-18.md
Pre-Deployment Drift Check:
User: @git-ape deploy changes to func-api-dev-eastus
Agent: Before deploying, let me check for configuration drift...
🔴 DRIFT DETECTED
The current Azure state differs from your last deployment.
Changes detected:
- httpsOnly: true → false (in Azure)
- tags.Environment: dev → development (in Azure)
What would you like to do?
A. Accept drift first, then deploy your changes on top
B. Revert drift, then deploy (restore original baseline)
C. Cancel deployment and investigate drift manually
This ensures we don't lose Azure changes or create conflicts.
Azure Policy: "All Function Apps must use managed identity"
Policy remediation adds:
- identity.type: SystemAssigned
Drift detection shows:
🟡 WARNING: Managed identity added by Azure Policy
Recommendation: **Accept drift** - This is a policy-enforced improvement.
Update IaC to include managed identity in future deployments.
Production incident: Function App scale limit reached
Manual portal change:
- properties.siteConfig.alwaysOn: true
- App Service Plan: Scaled from S1 to S2
Drift detection shows:
🔴 CRITICAL: SKU changed, Always On enabled
Recommendation: **Accept drift + Document** - Emergency change required.
Update IaC and create proper change request for next deployment.
Unknown change detected:
- properties.httpsOnly: true → false
- Changed: 2 days ago
- No change ticket or approved request
Drift detection shows:
🔴 CRITICAL: HTTPS enforcement disabled
Recommendation: **Revert drift immediately** - Security violation.
Investigate who made the change and why. Restore secure state.
Billing team added cost center tags:
- tags.CostCenter: "CC-12345"
- tags.BillingOwner: "[email protected]"
Drift detection shows:
🟡 WARNING: New tags added
Recommendation: **Accept drift** - Valid tags for financial tracking.
Update IaC baseline to preserve these tags in future deployments.
GitHub Action Example:
# .github/workflows/drift-detection.yml
name: Azure Drift Detection
on:
schedule:
- cron: '0 */6 * * *' # Every 6 hours
workflow_dispatch: # Manual trigger
jobs:
detect-drift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Azure Login
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
- name: Run Drift Detection
run: |
.github/scripts/drift-check-all.sh --format github-annotation
- name: Create Issue if Critical Drift
if: failure()
uses: actions/github-script@v6
with:
script: |
github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: '🔴 Critical Configuration Drift Detected',
body: 'See workflow logs for details',
labels: ['drift', 'security']
})
Save drift analysis to:
.azure/deployments/{deployment-id}/drift-analysis/
├── current-state.json # Azure resource state at check time
├── drift-report.md # Human-readable report
├── drift-details.json # Machine-readable diff
├── known-drift.json # Acknowledged drift to ignore
└── drift-log.jsonl # Audit log of reconciliation actions
Assess Kubernetes workloads and cluster configuration for AKS Automatic compatibility. Identifies incompatibilities, generates fixes, and guides migration from AKS Standard to AKS Automatic. WHEN: migrate to AKS Automatic, check AKS Automatic readiness, validate manifests for Automatic, assess cluster for Automatic compatibility, fix deployment for Automatic compatibility, identify AKS Automatic migration blockers, is my cluster ready for AKS Automatic.
Discovers available Azure OpenAI model capacity across regions and projects. Analyzes quota limits, compares availability, and recommends optimal deployment locations based on capacity requirements. USE FOR: find capacity, check quota, where can I deploy, capacity discovery, best region for capacity, multi-project capacity search, quota analysis, model availability, region comparison, check TPM availability. DO NOT USE FOR: actual deployment (hand off to preset or customize after discovery), quota increase requests (direct user to Azure Portal), listing existing deployments.
Interactive guided deployment flow for Azure OpenAI models with full customization control. Step-by-step selection of model version, SKU (GlobalStandard/Standard/ProvisionedManaged), capacity, RAI policy (content filter), and advanced options (dynamic quota, priority processing, spillover). USE FOR: custom deployment, customize model deployment, choose version, select SKU, set capacity, configure content filter, RAI policy, deployment options, detailed deployment, advanced deployment, PTU deployment, provisioned throughput. DO NOT USE FOR: quick deployment to optimal region (use preset).
Unified Azure OpenAI model deployment skill with intelligent intent-based routing. Handles quick preset deployments, fully customized deployments (version/SKU/capacity/RAI policy), and capacity discovery across regions and projects. USE FOR: deploy model, deploy gpt, create deployment, model deployment, deploy openai model, set up model, provision model, find capacity, check model availability, where can I deploy, best region for model, capacity analysis. DO NOT USE FOR: listing existing deployments (use foundry_models_deployments_list MCP tool), deleting deployments, agent creation (use agent/create), project creation (use project/create).
Intelligently deploys Azure OpenAI models to optimal regions by analyzing capacity across all available regions. Automatically checks current region first and shows alternatives if needed. USE FOR: quick deployment, optimal region, best region, automatic region selection, fast setup, multi-region capacity check, high availability deployment, deploy to best location. DO NOT USE FOR: custom SKU selection (use customize), specific version selection (use customize), custom capacity configuration (use customize), PTU deployments (use customize).
This skill should be used when working with LaminDB, an open-source data framework for biology that makes data queryable, traceable, reproducible, and FAIR. Use when managing biological datasets (scRNA-seq, spatial, flow cytometry, etc.), tracking computational workflows, curating and validating data with biological ontologies, building data lakehouses, or ensuring data lineage and reproducibility in biological research. Covers data management, annotation, ontologies (genes, cell types, diseases, tissues), schema validation, integrations with workflow managers (Nextflow, Snakemake) and MLOps platforms (W&B, MLflow), and deployment strategies.
Latch platform for bioinformatics workflows. Build pipelines with Latch SDK, @workflow/@task decorators, deploy serverless workflows, LatchFile/LatchDir, Nextflow/Snakemake integration.
Run Python code in the cloud with serverless containers, GPUs, and autoscaling. Use when deploying ML models, running batch processing jobs, scheduling compute-intensive tasks, or serving APIs that require GPU acceleration or dynamic scaling.
Take azure/azure-drift-detector from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.