Executes custom bash commands before and after tool runs plus on every user prompt submission.
npx claudepluginhub charris-msft/azure-pluginDefined in hooks/hooks.json
{
"PreToolUse": [
{
"hooks": [
{
"type": "prompt",
"prompt": "CRITICAL: Check the Bash command for two issues:\n\n## ISSUE 1: az deployment commands that should use azd\n\nBLOCK these az deployment commands:\n- 'az containerapp create' or 'az containerapp up'\n- 'az webapp create' or 'az webapp up' or 'az webapp deploy'\n- 'az functionapp create' or 'az functionapp deployment'\n- 'az group create' followed by resource creation\n- 'az acr build' (when part of deployment workflow)\n\nALLOW these az commands (queries/management only):\n- 'az containerapp show', 'az containerapp list', 'az containerapp logs'\n- 'az webapp show', 'az webapp list'\n- 'az acr login', 'az acr list', 'az acr show'\n- 'az account', 'az group list', 'az group show'\n- 'az containerapp registry set', 'az containerapp update'\n- Any 'az' command the USER explicitly requested\n\n## ISSUE 2: Large output commands without filters\n\nWARN (but allow) if these commands lack --query or -o table:\n- 'az account list' without '--query'\n- 'az resource list' without '--query' or '-g'\n- 'az group list' without '--query'\n\nIf az DEPLOYMENT command, return:\n{\"decision\": \"block\", \"reason\": \"STOP: Use azd instead of az for deployments. azd is faster (parallel provisioning) and handles ACR integration automatically. Use 'azd up' to deploy.\"}\n\nIf LARGE OUTPUT risk (no filter), return:\n{\"decision\": \"block\", \"reason\": \"WARNING: This command may produce large output (100K+ chars). Add '--query [].{Name:name,ID:id} -o table' to filter results and prevent context overflow.\"}\n\nIf command is OK, return {}.\n\nOutput ONLY valid JSON."
}
],
"matcher": "Bash"
}
],
"PostToolUse": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Check the Bash output for these error patterns:\n\n1. DOCKER ERRORS: 'docker daemon is not running', 'Cannot connect to the Docker daemon', 'failed to connect to the docker API', 'dockerDesktopLinuxEngine', 'The system cannot find the file specified' (docker context), 'Is the docker daemon running'.\n\n2. AZD SUBSCRIPTION ERRORS: 'is not an allowed choice. allowed choices:', 'Select an Azure Subscription', 'reading subscription id'.\n\n3. AZD INTERACTIVE/ENVIRONMENT ERRORS: 'environment name' AND 'is invalid', 'Enter a unique environment name', 'Enter a new environment name'.\n\n4. ACR TASKS DISABLED: 'ACR Tasks is not supported', 'TasksOperationsNotAllowed', 'Feature not available'.\n\n5. CLI NOT FOUND: \"'az' is not recognized\", \"az: command not found\", \"'azd' is not recognized\", \"azd: command not found\", \"'docker' is not recognized\", \"docker: command not found\", \"'func' is not recognized\", \"func: command not found\".\n\n6. ACR/IMAGE PULL ERRORS: 'unauthorized: authentication required', 'ImagePullBackOff', 'ErrImagePull', 'manifest unknown'.\n\n7. NAMING ERRORS: 'must be between 3 and 24 characters', 'name is invalid', 'can only contain lowercase letters and numbers', 'already in use', 'must be alphanumeric', 'exceeds maximum length', 'must be globally unique', 'invalid storage account name'.\n\nIf DOCKER error: {\"decision\": \"block\", \"reason\": \"DOCKER ERROR: Use AskUserQuestion to ask 'Docker Desktop isn't running. Would you like me to start it?' If yes, run platform command to start Docker, wait for ready, retry.\"}\n\nIf AZD SUBSCRIPTION error: {\"decision\": \"block\", \"reason\": \"AZD SUBSCRIPTION ERROR: 1) Run 'az account list --query [].{Name:name,ID:id} -o table', 2) AskUserQuestion for which subscription, 3) 'azd env set AZURE_SUBSCRIPTION_ID <id>', 4) Retry with --no-prompt.\"}\n\nIf AZD INTERACTIVE error: {\"decision\": \"block\", \"reason\": \"AZD INTERACTIVE ERROR: 1) AskUserQuestion for environment name, 2) 'azd env new <name> --no-prompt', 3) Set AZURE_SUBSCRIPTION_ID and AZURE_LOCATION, 4) Retry with --no-prompt.\"}\n\nIf ACR TASKS DISABLED: {\"decision\": \"block\", \"reason\": \"ACR TASKS DISABLED: FALLBACK to local build: 1) 'docker build -t ACR.azurecr.io/app:tag .', 2) 'az acr login --name ACR', 3) 'docker push ACR.azurecr.io/app:tag'. Common on free subscriptions.\"}\n\nIf CLI NOT FOUND: {\"decision\": \"block\", \"reason\": \"CLI TOOL MISSING: AskUserQuestion 'I detected [TOOL] is not installed. Install it?' If yes, use winget/brew. Verify after.\"}\n\nIf ACR/IMAGE PULL error: {\"decision\": \"block\", \"reason\": \"ACR CREDENTIAL ERROR: Run 'az containerapp registry set --name APP -g RG --server ACR.azurecr.io --identity system'.\"}\n\nIf NAMING error: {\"decision\": \"block\", \"reason\": \"NAMING ERROR: Resource name invalid. RULES: Storage/KeyVault max 24 chars, Storage=lowercase+numbers only, ACR=alphanumerics only. Suggest shorter name with abbreviations (prod, stor, kv, acr).\"}\n\nIf no match: {}. Output ONLY valid JSON."
}
],
"matcher": "Bash"
}
],
"UserPromptSubmit": [
{
"hooks": [
{
"type": "prompt",
"prompt": "The user's prompt mentions Azure services. Check if Azure MCP tools (like azure_subscription_list) are available. If Azure MCP tools are NOT available, add a brief systemMessage reminding the user: 'Tip: Enable the Azure MCP server via /mcp for direct Azure access, or run /azure:setup for guided configuration.' If Azure MCP tools ARE available, return an empty JSON object {}. Output ONLY valid JSON with optional systemMessage field, nothing else."
}
],
"matcher": "(?i)(azure|storage account|blob|cosmos|keyvault|key vault|app service|aks|kubernetes|resource group|subscription|arm template|bicep|azure sql|redis cache|ai search|cognitive|container app|deploy)"
}
]
}{
"riskFlags": {
"touchesBash": true,
"matchAllTools": false,
"touchesFileWrites": false
},
"typeStats": {
"prompt": 3
},
"eventStats": {
"PreToolUse": 1,
"PostToolUse": 1,
"UserPromptSubmit": 1
},
"originCounts": {
"absolutePaths": 0,
"pluginScripts": 0,
"projectScripts": 0
},
"timeoutStats": {
"commandsWithoutTimeout": 0
}
}