
Azure Infra Engineer Skill
- 126 installs
- 404kidwiz/claude-supercode-skills
Manage Azure infrastructure, deployment, and cloud operations.
About
Skill for managing Azure cloud infrastructure and deployments. Operations teams and platform engineers use this to architect, deploy, and maintain cloud-based systems at scale.
- Azure cloud
- Infrastructure management
- Cloud ops
Azure Infra Engineer by the numbers
- 126 all-time installs (skills.sh)
- Ranked #536 of 1,048 Cloud & Infrastructure skills by installs in the Skillselion catalog
- Data as of Aug 11, 2026 (Skillselion catalog sync)
npx skills add https://github.com/404kidwiz/claude-supercode-skills --skill azure-infra-engineerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 126 |
|---|---|
| Repository | 404kidwiz/claude-supercode-skills ↗ |
What it does
Manage Azure infrastructure, deployment, and cloud operations.
Files
Azure Infrastructure Engineer
Purpose
Provides Microsoft Azure cloud expertise specializing in Bicep/ARM templates, Enterprise Landing Zones, and Cloud Adoption Framework (CAF) implementations. Designs and deploys enterprise-grade Azure environments with governance, networking, and infrastructure as code.
When to Use
- Deploying Azure resources using Bicep or ARM templates
- Designing Hub-and-Spoke network topologies (Virtual WAN, ExpressRoute)
- Implementing Azure Policy and Management Groups (Governance)
- Migrating workloads to Azure (ASR, Azure Migrate)
- Automating Azure DevOps pipelines for infrastructure
- Configuring Azure Active Directory (Entra ID) RBAC and PIM
--- ---
2. Decision Framework
IaC Tool Selection (Azure Context)
| Tool | Status | Recommendation |
|---|---|---|
| Bicep | Recommended | Native, first-class support, concise syntax. |
| Terraform | Alternative | Best for multi-cloud strategies. |
| ARM Templates | Legacy | Verbose JSON. Avoid for new projects (compile Bicep instead). |
| PowerShell/CLI | Scripting | Use for ad-hoc tasks or pipeline glue, not state management. |
Networking Architecture
What is the connectivity need?
│
├─ **Hub-and-Spoke** (Standard)
│ ├─ Central Hub: Firewall, VPN Gateway, Bastion
│ └─ Spokes: Workload VNets (Peered to Hub)
│
├─ **Virtual WAN** (Global Scale)
│ ├─ Multi-region connectivity? → **Yes**
│ └─ Branch-to-Branch (SD-WAN)? → **Yes**
│
└─ **Private Access**
├─ PaaS Services? → **Private Link / Private Endpoints**
└─ Service Endpoints? → Legacy (Use Private Link where possible)Governance Strategy (CAF)
1. Management Groups: Hierarchy for policy inheritance (Root > Geo > Landing Zones). 2. Azure Policy: "Deny" non-compliant resources (e.g., only East US region). 3. RBAC: Least privilege access via Entra ID Groups. 4. Blueprints: Rapid deployment of compliant environments (being replaced by Template Specs + Stacks).
Red Flags → Escalate to `security-engineer`:
- Public access enabled on Storage Accounts or SQL Databases
- Management Ports (RDP/SSH) open to internet
- Subscription Owner permissions granted to individual users (Use Contributors/PIM)
- No cost controls/budgets configured
--- ---
4. Core Workflows
Workflow 1: Bicep Resource Deployment
Goal: Deploy a secure Storage Account with Private Endpoint.
Steps:
1. Define Bicep Module (`storage.bicep`)
param location string = resourceGroup().location
param name string
resource stg 'Microsoft.Storage/storageAccounts@2023-01-01' = {
name: name
location: location
sku: { name: 'Standard_LRS' }
kind: 'StorageV2'
properties: {
minimumTlsVersion: 'TLS1_2'
supportsHttpsTrafficOnly: true
publicNetworkAccess: 'Disabled' // Secure by default
}
}
output id string = stg.id2. Main Deployment (`main.bicep`)
module storage './modules/storage.bicep' = {
name: 'deployStorage'
params: {
name: 'stappprod001'
}
}3. Deploy via CLI
az deployment group create --resource-group rg-prod --template-file main.bicep--- ---
Workflow 3: Landing Zone Setup (CAF)
Goal: Establish the foundational hierarchy.
Steps:
1. Create Management Groups
-
MG-Root -
MG-Platform(Identity, Connectivity, Management) -
MG-LandingZones(Online, Corp) -
MG-Sandbox(Playground)
2. Assign Policies
- Assign "Allowed Locations" to
MG-Root. - Assign "Enable Azure Monitor" to
MG-LandingZones.
3. Deploy Hub Network
- Deploy VNet in connectivity subscription.
- Deploy Azure Firewall and VPN Gateway.
--- ---
5. Anti-Patterns & Gotchas
❌ Anti-Pattern 1: "ClickOps"
What it looks like:
- Creating resources manually in the Azure Portal.
Why it fails:
- Unrepeatable.
- Configuration drift.
- Disaster recovery is impossible (no code to redeploy).
Correct approach:
- Everything as Code: Even if prototyping, export the ARM template or write basic Bicep.
❌ Anti-Pattern 2: One Giant Resource Group
What it looks like:
-
rg-productioncontains VNets, VMs, Databases, and Web Apps for 5 different projects.
Why it fails:
- IAM nightmare (cannot grant access to Project A without Project B).
- Tagging and cost analysis becomes difficult.
- Risk of accidental deletion.
Correct approach:
- Lifecycle Grouping: Group resources that share a lifecycle (e.g.,
rg-network,rg-app1-prod,rg-app1-dev).
❌ Anti-Pattern 3: Ignoring Naming Conventions
What it looks like:
-
myvm1,test-storage,sql-server.
Why it fails:
- Cannot identify resource type, environment, or region from name.
- Name collisions (Storage accounts must be globally unique).
Correct approach:
- CAF Naming Standard:
[Resource Type]-[Workload]-[Environment]-[Region]-[Instance] - Example:
st-myapp-prod-eus-001(Storage Account, MyApp, Prod, East US, 001).
--- ---
7. Quality Checklist
Governance:
- [ ] Naming: Resources follow CAF naming conventions.
- [ ] Tagging: Resources tagged with
CostCenter,Environment,Owner. - [ ] Policies: Azure Policy enforces compliance (e.g., allowed SKUs).
Security:
- [ ] Network: No public IPs on backend resources (VMs, DBs).
- [ ] Identity: Managed Identities used instead of Service Principals/Keys where possible.
- [ ] Encryption: CMK (Customer Managed Keys) enabled for sensitive data.
Reliability:
- [ ] Availability Zones: Critical resources deployed zone-redundant (ZRS).
- [ ] Backup: Azure Backup enabled for VMs and SQL.
- [ ] Locks: Resource Locks (
CanNotDelete) on critical production resources.
Cost:
- [ ] Sizing: Resources right-sized based on metrics.
- [ ] Reservations: Reserved Instances purchased for steady workloads.
- [ ] Cleanup: Unused resources (orphaned disks/NICs) deleted.
Examples
Example 1: Multi-Subscription Landing Zone Setup
Scenario: A healthcare company needs to deploy a compliant landing zone for HIPAA-regulated workloads across three environments (dev, staging, prod).
Architecture: 1. Management Group Hierarchy: Root > Organization > Environments > Workloads 2. Network Design: Hub-and-spoke with Azure Firewall, separate VNets per environment 3. Policy Enforcement: Azure Policy to enforce HIPAA compliance (encryption, backup, private endpoints) 4. CI/CD Pipeline: Azure DevOps pipeline with approval gates for prod deployments
Key Components:
- Azure Firewall Manager for centralized policy
- Private DNS Zones for app-internal resolution
- Azure Backup with immutable vaults for compliance
- Cost Management tags for departmental chargebacks
Example 2: Zero-Trust Network Architecture
Scenario: A financial services firm needs to replace their VPN-based access with a Zero Trust architecture using Azure Private Link and Conditional Access.
Implementation: 1. Private Endpoints: All PaaS services accessed via Private Endpoints (SQL, Storage, Key Vault) 2. Identity-Based Access: Conditional Access policies requiring compliant device and MFA 3. Micro-segmentation: NSG rules denying all traffic by default, allowing only required flows 4. Monitoring: Azure Sentinel for security analytics and anomaly detection
Security Controls:
- Azure AD Conditional Access with device compliance
- Just-In-Time VM access for administration
- Azure Defender for Cloud threat protection
- Comprehensive audit logging to Log Analytics
Example 3: Cost-Optimized Dev/Test Environment
Scenario: A software company wants to reduce their Azure dev/test environment costs by 60% while maintaining developer productivity.
Optimization Strategy: 1. Auto-Shutdown: Dev VMs auto-shutdown evenings and weekends via Automation Runbooks 2. Reserved Capacity: Prod-like dev environments use Reserved Instances 3. Dev-Optimized SKUs: Development uses Dev/Test SKUs where available 4. Tagging and Governance: Required tags for cost allocation, orphaned resource cleanup
Cost Savings Results:
- 65% reduction in dev/test compute costs
- Automated cleanup of unused resources saving $2K/month
- Reserved Instance savings for stable environments
- Developer productivity maintained with auto-start capabilities
Best Practices
Infrastructure as Code
- Everything as Code: Every resource defined in Bicep, never manual portal changes
- Module Library: Create reusable Bicep modules for common patterns
- Parameter Files: Separate parameter files per environment (dev, staging, prod)
- GitOps Workflow: Infrastructure changes via PR and approval process
- State Management: Use AzDO stateful pipelines or Terraform backend
Networking Excellence
- Hub-and-Spoke Default: Standard architecture for most workloads
- Private by Default: All PaaS access via Private Endpoints
- DNS Planning: Private DNS Zones with VNet links, avoid host file modifications
- Firewall Integration: Centralized threat protection with Azure Firewall
- Hybrid Connectivity: ExpressRoute for production, VPN for secondary
Security Hardening
- Least Privilege: RBAC with specific roles, avoid Subscription Owner
- Managed Identities: Prefer over Service Principals with secrets
- Secrets Management: Key Vault for all secrets, never environment variables
- Encryption Everywhere: CMK for sensitive data, TLS 1.2+ everywhere
- Network Isolation: NSG rules denying by default, allow-listing required traffic
Cost Management
- Right-Sizing: Regular review of actual utilization vs allocated size
- Reservation Planning: Identify stable workloads for Reserved Instances
- Auto-Shutdown: Dev/test resources off during off-hours
- Tagging Strategy: Required tags for cost center, environment, owner
- Budget Alerts: Budget thresholds with alerts at 50%, 75%, 90%
Governance and Compliance
- Policy as Guardrails: Azure Policy for prevention, not just detection
- Management Groups: Hierarchy reflecting organizational structure
- Blueprint Usage: Azure Blueprints for standard compliant environments
- Monitoring Strategy: Centralized logging to Log Analytics workspace
- Automation: Runbooks for routine operational tasks
Azure Infrastructure Patterns
Common patterns and best practices for Azure infrastructure deployment and management.
Infrastructure as Code Patterns
Modular Bicep Templates
// main.bicep
param location string = resourceGroup().location
module vnetModule 'modules/vnet.bicep' = {
name: 'vnet-deployment'
params: {
location: location
vnetName: 'main-vnet'
addressSpace: '10.0.0.0/16'
}
}
module vmModule 'modules/vm.bicep' = {
name: 'vm-deployment'
params: {
location: location
subnetId: vnetModule.outputs.subnetId
vmName: 'main-vm'
}
dependsOn: [
vnetModule
]
}Parameterized Templates
const parameters = {
environment: 'production',
location: 'eastus',
vmCount: 3,
databaseTier: 'Premium'
};
const config = {
parameters,
// ... other config
};Network Patterns
Hub and Spoke Topology
const hubVNetConfig = {
name: 'hub-vnet',
addressSpace: ['10.0.0.0/16'],
subnets: [
{ name: 'GatewaySubnet', addressPrefix: '10.0.0.0/24' },
{ name: 'AzureFirewallSubnet', addressPrefix: '10.0.1.0/24' },
{ name: 'SharedServicesSubnet', addressPrefix: '10.0.2.0/24' }
]
};
const spokeVNetConfig = {
name: 'spoke-vnet',
addressSpace: ['10.1.0.0/16'],
subnets: [
{ name: 'WorkloadSubnet', addressPrefix: '10.1.1.0/24' }
]
};VNet Peering Configuration
// Peer VNets for connectivity
await deployVNetPeering({
peeringName: 'hub-to-spoke',
sourceVNet: hubVNetId,
targetVNet: spokeVNetId,
allowForwardedTraffic: true,
allowGatewayTransit: false
});NSG Rule Patterns
const commonRules: NSGRule[] = [
{
name: 'AllowHTTP',
priority: 100,
direction: 'Inbound',
access: 'Allow',
protocol: 'Tcp',
sourceAddressPrefix: '*',
sourcePortRange: '*',
destinationAddressPrefix: '*',
destinationPortRange: '80'
},
{
name: 'AllowHTTPS',
priority: 110,
direction: 'Inbound',
access: 'Allow',
protocol: 'Tcp',
sourceAddressPrefix: '*',
sourcePortRange: '*',
destinationAddressPrefix: '*',
destinationPortRange: '443'
},
{
name: 'DenyAll',
priority: 4096,
direction: 'Inbound',
access: 'Deny',
protocol: '*',
sourceAddressPrefix: '*',
sourcePortRange: '*',
destinationAddressPrefix: '*',
destinationPortRange: '*'
}
];Security Patterns
Managed Identity Pattern
// Use Managed Identity for service-to-service authentication
const vmConfig = {
identity: {
type: 'UserAssigned',
userAssignedIdentities: {
[managedIdentityId]: {}
}
}
};RBAC Assignment Pattern
await assignRole({
roleDefinitionId: '/providers/Microsoft.Authorization/roleDefinitions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
principalId: servicePrincipalObjectId,
scope: resourceGroupScope,
principalType: 'ServicePrincipal'
});Azure Policy Pattern
const policyDefinition = {
policyRule: {
if: {
field: 'type',
equals: 'Microsoft.Compute/virtualMachines'
},
then: {
effect: 'deny',
details: {
type: 'Microsoft.Authorization/policyDefinitions',
resourceActions: ['Microsoft.Compute/virtualMachines/write']
}
}
}
};Monitoring Patterns
Multi-Metric Alert Pattern
const multiMetricAlert = {
criteria: {
'odata.type': 'Microsoft.Azure.Monitor.MultipleResourceMultipleMetricCriteria',
allOf: [
{
threshold: 80,
name: 'CPUHigh',
metricName: 'Percentage CPU',
operator: 'GreaterThan',
timeAggregation: 'Average'
},
{
threshold: 90,
name: 'MemoryHigh',
metricName: 'Available Memory',
operator: 'LessThan',
timeAggregation: 'Average'
}
]
}
};Log Analytics Query Pattern
const query = `
AzureActivity
| where OperationName == 'Microsoft.Compute/virtualMachines/write'
| project TimeGenerated, Caller, OperationName, ActivityStatusValue
| sort by TimeGenerated desc
`;Deployment Patterns
Blue-Green Deployment
// Deploy new infrastructure alongside existing
const greenConfig = { ...blueConfig, name: 'app-green' };
await deployBicepTemplate(greenConfig);
// Test green environment
await runIntegrationTests('app-green');
// Switch traffic
await updateTrafficManager({
profileName: 'app-profile',
endpoints: [
{ name: 'green', target: 'app-green', weight: 100 },
{ name: 'blue', target: 'app-blue', weight: 0 }
]
});
// Clean up blue after validation
await deleteResourceGroup('app-blue');Rolling Update Pattern
for (let i = 0; i < vmCount; i++) {
await updateVM(vmNames[i], newImageVersion);
await healthCheck(vmNames[i]);
}Cost Optimization Patterns
Auto-Scale Configuration
const autoScaleConfig = {
profile: {
capacity: {
minimum: '1',
maximum: '5',
default: '2'
},
rules: [
{
metricTrigger: {
metricName: 'Percentage CPU',
metricResourceUri: vmResourceId,
timeGrain: 'PT1M',
statistic: 'Average',
threshold: 75,
operator: 'GreaterThan'
},
scaleAction: {
direction: 'Increase',
type: 'ChangeCount',
value: '1',
cooldown: 'PT5M'
}
}
]
}
};Reserved Instance Pattern
const reservationConfig = {
sku: {
name: 'Standard_D2s_v3'
},
location: 'eastus',
reservedResourceType: 'VirtualMachines',
billingScopeId: subscriptionId,
quantity: 3,
term: 'P1Y'
};Error Handling Patterns
Retry Pattern
async function retryWithBackoff<T>(
operation: () => Promise<T>,
maxRetries = 3,
delayMs = 1000
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await operation();
} catch (error: any) {
if (i === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, delayMs * Math.pow(2, i)));
}
}
throw new Error('Max retries exceeded');
}Circuit Breaker Pattern
class CircuitBreaker {
private failures = 0;
private lastFailureTime = 0;
private readonly threshold = 3;
private readonly timeout = 60000;
async execute<T>(operation: () => Promise<T>): Promise<T> {
if (this.isOpen()) {
throw new Error('Circuit breaker is open');
}
try {
const result = await operation();
this.reset();
return result;
} catch (error) {
this.recordFailure();
throw error;
}
}
private isOpen(): boolean {
return this.failures >= this.threshold &&
Date.now() - this.lastFailureTime < this.timeout;
}
}Tagging Strategy
const standardTags = {
'CostCenter': 'IT-001',
'Environment': environment,
'Owner': 'DevOps Team',
'Project': project,
'CreatedBy': userName,
'CreatedDate': new Date().toISOString()
};
const resourceConfig = {
// ... other config
tags: standardTags
};Resource Naming Conventions
function getResourceName(resourceType: string, environment: string, appName: string, instance?: string): string {
const suffixes = {
'VirtualNetwork': 'vnet',
'NetworkSecurityGroup': 'nsg',
'VirtualMachine': 'vm',
'AppServicePlan': 'asp',
'WebApp': 'app'
};
const parts = [appName, environment, suffixes[resourceType] || 'res'];
if (instance) parts.push(instance);
return parts.join('-').toLowerCase();
}
// Examples:
// getResourceName('VirtualMachine', 'prod', 'myapp', '01') => 'myapp-prod-vm-01'
// getResourceName('WebApp', 'dev', 'api') => 'api-dev-app'Azure Infrastructure Engineer - Quick Start Guide
This guide helps you get started with the Azure infrastructure engineer skill's scripts and tools.
Prerequisites
- Node.js 16+ installed
- Azure CLI installed and configured (
az login) - Azure subscription with appropriate permissions
- TypeScript installed globally
Installation
npm install @azure/arm-resources @azure/arm-network @azure/arm-monitor @azure/identity
npm install -D typescript @types/nodeAuthentication
The scripts use Azure DefaultAzureCredential which supports multiple authentication methods:
1. Azure CLI (recommended for local development):
az login2. Service Principal (for CI/CD):
export AZURE_CLIENT_ID=<client-id>
export AZURE_CLIENT_SECRET=<client-secret>
export AZURE_TENANT_ID=<tenant-id>3. Managed Identity (for Azure resources):
- Automatically uses system-assigned or user-assigned managed identity
Quick Examples
Deploy a Virtual Network
import { deployVNet } from './scripts/deploy_azure_resources';
const config = {
subscriptionId: 'your-subscription-id',
name: 'my-vnet',
addressSpace: ['10.0.0.0/16'],
subnets: [
{
name: 'subnet-1',
addressPrefix: '10.0.1.0/24'
},
{
name: 'subnet-2',
addressPrefix: '10.0.2.0/24'
}
],
location: 'eastus',
resourceGroupName: 'my-resource-group'
};
const result = await deployVNet(config);
if (result.success) {
console.log(`VNet deployed: ${result.vnetId}`);
} else {
console.error(`Errors: ${result.errors?.join(', ')}`);
}Deploy a Bicep Template
import { deployBicepTemplate } from './scripts/configure_bicep_template';
const config = {
subscriptionId: 'your-subscription-id',
resourceGroupName: 'my-resource-group',
deploymentName: 'app-deployment',
templatePath: './templates/main.bicep',
parameters: {
location: 'eastus',
vmSize: 'Standard_DS2_v2'
},
location: 'eastus'
};
const result = await deployBicepTemplate(config);
if (result.success) {
console.log(`Deployment successful: ${result.deploymentId}`);
console.log(`Outputs: ${JSON.stringify(result.outputs, null, 2)}`);
}Set Up Monitoring
import { createActionGroup, createMetricAlert } from './scripts/setup_monitoring';
const actionGroupConfig = {
subscriptionId: 'your-subscription-id',
resourceGroupName: 'my-resource-group',
name: 'devops-alerts',
location: 'eastus',
emailReceivers: [
{
name: 'DevOps Team',
emailAddress: 'devops@example.com'
}
]
};
const actionGroupId = await createActionGroup(actionGroupConfig);
if (actionGroupId) {
const alertConfig = {
subscriptionId: 'your-subscription-id',
resourceGroupName: 'my-resource-group',
name: 'cpu-alert',
targetResourceId: '/subscriptions/.../resourceGroups/.../providers/Microsoft.Compute/virtualMachines/my-vm',
criteria: {
metricName: 'Percentage CPU',
threshold: 80,
operator: 'GreaterThan',
timeAggregation: 'Average',
windowSize: 'PT5M',
evaluationFrequency: 'PT1M'
},
actionGroups: [actionGroupId]
};
const alertCreated = await createMetricAlert(alertConfig);
console.log(`Alert created: ${alertCreated}`);
}Common Patterns
Validating Address Prefixes
import { validateAddressPrefix } from './scripts/deploy_azure_resources';
const isValid = validateAddressPrefix('10.0.1.0/24');
console.log(`Valid CIDR: ${isValid}`);Pre-flight Validation
import { validateDeployment } from './scripts/configure_bicep_template';
const isValid = await validateDeployment(config);
if (isValid) {
console.log('Template validation passed');
} else {
console.log('Template validation failed');
process.exit(1);
}Best Practices
1. Always validate before deploying - Use validateDeployment() before running deployments 2. Use what-if - Run whatIfDeployment() to preview changes 3. Implement proper error handling - Check result.errors for detailed error messages 4. Use naming conventions - Follow Azure naming conventions for resources 5. Tag resources - Add tags for cost tracking and resource organization 6. Monitor deployments - Set up alerts for critical resources 7. Use resource groups - Group related resources together 8. Implement RBAC - Grant least privilege access to resources
Troubleshooting
Authentication Errors
Error: DefaultAzureCredential: Authentication failedSolution: Run az login to authenticate with Azure CLI
Permission Errors
Error: AuthorizationFailed: The client has permission to perform actionSolution: Ensure your account has Contributor or Owner role on the resource group
Template Validation Failures
Error: Template validation failedSolution: 1. Check the Bicep template syntax 2. Verify all required parameters are provided 3. Use az bicep build to compile Bicep to JSON first 4. Review the detailed error message
Resource Not Found Errors
Error: ResourceNotFound: The resource with id could not be foundSolution: 1. Verify the resource ID is correct 2. Check if the resource exists 3. Ensure you're using the correct subscription
Network Deployment Timeouts
Error: Deployment operation timed outSolution: 1. Increase timeout values in the deployment configuration 2. Check Azure service health 3. Verify network connectivity to Azure endpoints
Additional Resources
Azure Infrastructure Troubleshooting
Common issues and solutions for Azure infrastructure deployments and management.
Authentication Issues
DefaultAzureCredential Authentication Failed
Symptoms:
Error: DefaultAzureCredential: Authentication failed. Attempted 4 credential typesSolutions:
1. Authenticate with Azure CLI:
az login
az account set --subscription <subscription-id>2. Set environment variables for Service Principal:
export AZURE_CLIENT_ID=<client-id>
export AZURE_CLIENT_SECRET=<client-secret>
export AZURE_TENANT_ID=<tenant-id>3. Check token expiration:
az account get-access-token4. Verify subscription access:
az account list --output tableInsufficient Permissions
Symptoms:
Error: AuthorizationFailed: The client 'xxx' with object id 'yyy' does not have authorization to perform actionSolutions:
1. Check current role assignments:
az role assignment list --assignee $(az ad signed-in-user show --query objectId -o tsv)2. Assign Contributor role:
az role assignment create --assignee <user-or-service-principal> \
--role Contributor \
--scope /subscriptions/<subscription-id>/resourceGroups/<resource-group>3. Use Azure Portal to verify permissions:
- Navigate to Resource Group → Access Control (IAM)
- Check if you have necessary roles
Deployment Issues
Bicep Template Validation Failed
Symptoms:
Error: Template validation failed: The template is not validSolutions:
1. Compile Bicep to JSON for better error messages:
az bicep build main.bicep2. Check for syntax errors:
- Verify all parameters are defined
- Check for missing commas
- Ensure proper indentation
3. Validate parameters:
if (!config.parameters || Object.keys(config.parameters).length === 0) {
throw new Error('Parameters object is empty');
}4. Use what-if to preview changes:
await whatIfDeployment(config);Deployment Timeout
Symptoms:
Error: Deployment operation timed out after 30 minutesSolutions:
1. Increase timeout in poller configuration:
const poller = await client.virtualNetworks.beginCreateOrUpdateAndWait(
resourceGroupName,
vnetName,
params,
{
abortSignal: AbortSignal.timeout(60 * 60 * 1000) // 1 hour timeout
}
);2. Check Azure service health:
az service-health list-events --output table3. Verify network connectivity:
ping login.microsoftonline.com4. Review deployment status:
az deployment group show \
--resource-group <rg-name> \
--name <deployment-name>Resource Already Exists Error
Symptoms:
Error: The Resource 'Microsoft.Network/virtualNetworks/my-vnet' under resource group 'my-rg' already existsSolutions:
1. Check if resource exists:
az resource show \
--resource-group <rg-name> \
--name <resource-name> \
--resource-type Microsoft.Network/virtualNetworks2. Update existing resource instead of creating new:
const existingVNet = await client.virtualNetworks.get(resourceGroupName, vnetName);
if (existingVNet) {
// Update existing resource
await client.virtualNetworks.beginCreateOrUpdateAndWait(/* ... */);
}3. Use unique deployment names:
const deploymentName = `deploy-${Date.now()}`;Network Issues
VNet Deployment Fails
Symptoms:
Error: Virtual network creation failed with code 'VnetSizeTooSmall'Solutions:
1. Validate address prefix format:
const isValid = validateAddressPrefix('10.0.0.0/16');
if (!isValid) {
throw new Error('Invalid CIDR format');
}2. Check for overlapping address spaces:
az network vnet list --query "[].addressSpace.addressPrefixes" --output json3. Ensure subnet prefixes don't overlap:
// Subnets must be within VNet address space
const vnetPrefix = '10.0.0.0/16';
const subnetPrefix = '10.0.1.0/24';
// Verify subnet is within VNet rangeNSG Rule Conflicts
Symptoms:
Error: NSG rule priority conflictSolutions:
1. Check for duplicate priorities:
const priorities = config.rules.map(r => r.priority);
const duplicates = priorities.filter((p, i) => priorities.indexOf(p) !== i);
if (duplicates.length > 0) {
throw new Error(`Duplicate priorities: ${duplicates.join(', ')}`);
}2. Use priority ranges by rule type:
const priorityRanges = {
'allow': 100-1000,
'deny': 3000-4000,
'system': 5000-4096
};3. Order rules from specific to general:
const sortedRules = config.rules.sort((a, b) => a.priority - b.priority);Monitoring Issues
Action Group Creation Fails
Symptoms:
Error: Action group validation failedSolutions:
1. Validate email addresses:
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(config.emailAddress)) {
throw new Error('Invalid email address');
}2. Validate phone numbers for SMS:
if (!config.phoneNumber || !config.countryCode) {
throw new Error('Phone number and country code are required');
}3. Check action group short name length:
if (config.groupShortName.length > 12) {
throw new Error('Group short name must be 12 characters or less');
}Alert Rule Not Triggering
Symptoms:
Alert configured but not firing when conditions are metSolutions:
1. Check if alert is enabled:
const alert = await monitorClient.metricAlerts.get(rgName, alertName);
if (!alert.enabled) {
await monitorClient.metricAlerts.createOrUpdate(rgName, alertName, { enabled: true });
}2. Verify metric is available:
az monitor metrics list-definitions \
--resource <resource-id> \
--query "[].name.value"3. Check threshold and aggregation:
// Ensure threshold is realistic
if (config.criteria.threshold < 0 || config.criteria.threshold > 100) {
console.warn('Threshold may be outside expected range');
}4. Test with manual metric data:
const metricData = await monitorClient.metrics.list(resourceId, {
timespan: 'PT1H',
interval: 'PT5M',
metricnames: config.criteria.metricName
});Cost Issues
Unexpected Costs
Symptoms:
Azure bill higher than expectedSolutions:
1. Enable cost alerts:
await createBudget({
name: 'monthly-budget',
amount: 1000,
timeGrain: 'Monthly',
notification: {
emailContacts: ['finance@example.com'],
threshold: 80
}
});2. Review resource usage:
az consumption usage list --top 100 --output table3. Check for idle resources:
az vm list --show-details --query "[?powerState!='VM running']"4. Implement auto-shutdown:
await setAutoShutdown({
vmId: vmResourceId,
schedule: '22:00',
timezone: 'Eastern Standard Time'
});Performance Issues
Slow Deployment Performance
Symptoms:
Deployments taking longer than expectedSolutions:
1. Check Azure region availability:
az account list-locations --query "[].name" --output table2. Use parallel deployments:
await Promise.all([
deployResource(resource1),
deployResource(resource2),
deployResource(resource3)
]);3. Optimize Bicep templates:
- Use modules for reusable components
- Minimize dependencies
- Use deployment scripts sparingly
4. Monitor deployment performance:
const startTime = Date.now();
await deployBicepTemplate(config);
const duration = Date.now() - startTime;
console.log(`Deployment took ${duration}ms`);Debugging Tips
Enable Debug Logging
import { setLogLevel } from '@azure/logger';
setLogLevel('verbose');Use Azure CLI for Troubleshooting
# Show detailed error information
az deployment group show \
--resource-group <rg-name> \
--name <deployment-name> \
--output json | jq '.error'Check Deployment History
az deployment operation group list \
--resource-group <rg-name> \
--name <deployment-name> \
--output tableExport Logs for Analysis
az monitor activity-log list \
--resource-group <rg-name> \
--start-time 2024-01-01T00:00:00Z \
--output json > activity-logs.jsonGetting Help
- Azure Status - Check Azure service health
- Azure Portal Diagnostics - Interactive troubleshooting
- Azure Documentation - Official documentation
- Azure Support - Contact Azure support
Common Error Codes
| Error Code | Description | Solution |
|---|---|---|
AuthorizationFailed | Insufficient permissions | Assign appropriate RBAC role |
ResourceNotFound | Resource doesn't exist | Verify resource ID and existence |
SubscriptionNotFound | Subscription doesn't exist | Check subscription ID and access |
InvalidTemplate | Template validation failed | Fix template syntax and parameters |
DeploymentActive | Deployment already in progress | Wait for deployment to complete or cancel |
QuotaExceeded | Resource quota exceeded | Request quota increase or use different tier |
import * as resources from '@azure/arm-resources';
import * as fs from 'fs/promises';
import { DefaultAzureCredential } from '@azure/identity';
export interface BicepDeploymentConfig {
subscriptionId: string;
resourceGroupName: string;
deploymentName: string;
templatePath: string;
parameters?: Record<string, any>;
location?: string;
}
export interface DeploymentResult {
success: boolean;
deploymentId?: string;
outputs?: Record<string, any>;
errors?: string[];
}
export async function deployBicepTemplate(config: BicepDeploymentConfig): Promise<DeploymentResult> {
const result: DeploymentResult = { success: false, errors: [] };
try {
if (!config.subscriptionId || !config.resourceGroupName || !config.templatePath) {
throw new Error('subscriptionId, resourceGroupName, and templatePath are required');
}
await fs.access(config.templatePath);
const credential = new DefaultAzureCredential();
const resourceClient = new resources.ResourceManagementClient(credential, config.subscriptionId);
const templateContent = await fs.readFile(config.templatePath, 'utf-8');
const deploymentParams: resources.Deployment = {
properties: {
template: JSON.parse(templateContent),
parameters: config.parameters || {},
mode: resources.DeploymentMode.Incremental
},
location: config.location
};
const poller = await resourceClient.deployments.beginCreateOrUpdateAndWait(
config.resourceGroupName,
config.deploymentName,
deploymentParams
);
result.success = true;
result.deploymentId = poller.id;
result.outputs = poller.properties?.outputs;
} catch (error: any) {
if (error.code === 'ENOENT') {
result.errors?.push(`Template file not found: ${config.templatePath}`);
} else {
result.errors?.push(`Bicep deployment failed: ${error.message}`);
}
}
return result;
}
export async function validateDeployment(config: BicepDeploymentConfig): Promise<boolean> {
try {
if (!config.subscriptionId || !config.resourceGroupName || !config.templatePath) {
throw new Error('Missing required configuration');
}
await fs.access(config.templatePath);
const credential = new DefaultAzureCredential();
const resourceClient = new resources.ResourceManagementClient(credential, config.subscriptionId);
const templateContent = await fs.readFile(config.templatePath, 'utf-8');
const template = JSON.parse(templateContent);
const validateParams: resources.DeploymentsValidateOptionalParams = {
properties: {
template: template,
parameters: config.parameters || {},
mode: resources.DeploymentMode.Incremental
}
};
await resourceClient.deployments.validate(
config.resourceGroupName,
config.deploymentName,
validateParams
);
return true;
} catch (error: any) {
console.error(`Validation failed: ${error.message}`);
return false;
}
}
export async function whatIfDeployment(config: BicepDeploymentConfig): Promise<any> {
const result: DeploymentResult = { success: false, errors: [] };
try {
const credential = new DefaultAzureCredential();
const resourceClient = new resources.ResourceManagementClient(credential, config.subscriptionId);
const templateContent = await fs.readFile(config.templatePath, 'utf-8');
const template = JSON.parse(templateContent);
const whatIfParams: resources.DeploymentsWhatIfOptionalParams = {
properties: {
template: template,
parameters: config.parameters || {},
mode: resources.DeploymentMode.Incremental
}
};
const response = await resourceClient.deployments.beginCreateOrUpdateAndWait(
config.resourceGroupName,
config.deploymentName,
whatIfParams as any
);
result.success = true;
} catch (error: any) {
result.errors?.push(`What-if operation failed: ${error.message}`);
}
return result;
}
import * as azure from '@azure/arm-resources';
import * as network from '@azure/arm-network';
import { DefaultAzureCredential } from '@azure/identity';
export interface VNetConfig {
name: string;
addressSpace: string[];
subnets: SubnetConfig[];
location: string;
resourceGroupName: string;
}
export interface SubnetConfig {
name: string;
addressPrefix: string;
nsgRules?: NSGRule[];
}
export interface NSGRule {
name: string;
priority: number;
direction: 'Inbound' | 'Outbound';
access: 'Allow' | 'Deny';
protocol: 'Tcp' | 'Udp' | 'Icmp' | '*';
sourceAddressPrefix: string;
sourcePortRange: string;
destinationAddressPrefix: string;
destinationPortRange: string;
}
export interface DeploymentResult {
success: boolean;
vnetId?: string;
subnets?: string[];
errors?: string[];
}
export async function deployVNet(config: VNetConfig): Promise<DeploymentResult> {
const result: DeploymentResult = { success: false, errors: [] };
try {
if (!config.name || !config.addressSpace || config.addressSpace.length === 0) {
throw new Error('Invalid VNet configuration: name and addressSpace are required');
}
if (!config.resourceGroupName) {
throw new Error('resourceGroupName is required');
}
const credential = new DefaultAzureCredential();
const networkClient = new network.NetworkManagementClient(credential, config.subscriptionId);
const vnetParams: network.VirtualNetwork = {
location: config.location,
addressSpace: { addressPrefixes: config.addressSpace },
subnets: config.subnets.map(s => ({
name: s.name,
addressPrefix: s.addressPrefix
}))
};
const poller = await networkClient.virtualNetworks.beginCreateOrUpdateAndWait(
config.resourceGroupName,
config.name,
vnetParams
);
result.success = true;
result.vnetId = poller.id;
result.subnets = config.subnets.map(s => s.name);
} catch (error: any) {
result.errors?.push(`VNet deployment failed: ${error.message}`);
}
return result;
}
export async function deployNSG(config: NSGConfig): Promise<DeploymentResult> {
const result: DeploymentResult = { success: false, errors: [] };
try {
if (!config.name || !config.resourceGroupName) {
throw new Error('NSG name and resource group are required');
}
const credential = new DefaultAzureCredential();
const networkClient = new network.NetworkManagementClient(credential, config.subscriptionId);
const nsgParams: network.NetworkSecurityGroup = {
location: config.location,
securityRules: config.rules.map(r => ({
name: r.name,
priority: r.priority,
direction: r.direction,
access: r.access,
protocol: r.protocol,
sourceAddressPrefix: r.sourceAddressPrefix,
sourcePortRange: r.sourcePortRange,
destinationAddressPrefix: r.destinationAddressPrefix,
destinationPortRange: r.destinationPortRange
}))
};
const poller = await networkClient.networkSecurityGroups.beginCreateOrUpdateAndWait(
config.resourceGroupName,
config.name,
nsgParams
);
result.success = true;
result.vnetId = poller.id;
} catch (error: any) {
result.errors?.push(`NSG deployment failed: ${error.message}`);
}
return result;
}
export interface NSGConfig {
subscriptionId: string;
name: string;
location: string;
resourceGroupName: string;
rules: NSGRule[];
}
export async function validateAddressPrefix(addressPrefix: string): boolean {
const cidrPattern = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\/(\d{1,2})$/;
const match = addressPrefix.match(cidrPattern);
if (!match) return false;
const [, octet1, octet2, octet3, octet4, prefix] = match.map(Number);
if (octet1 > 255 || octet2 > 255 || octet3 > 255 || octet4 > 255) return false;
if (prefix < 0 || prefix > 32) return false;
return true;
}
import * as monitor from '@azure/arm-monitor';
import * as resources from '@azure/arm-resources';
import { DefaultAzureCredential } from '@azure/identity';
export interface AlertRuleConfig {
name: string;
resourceGroupName: string;
subscriptionId: string;
targetResourceId: string;
criteria: AlertCriteria;
actionGroups?: string[];
}
export interface AlertCriteria {
metricName: string;
threshold: number;
operator: 'GreaterThan' | 'LessThan' | 'GreaterThanOrEqual' | 'LessThanOrEqual';
timeAggregation: 'Average' | 'Minimum' | 'Maximum' | 'Total';
windowSize: string;
evaluationFrequency: string;
}
export interface ActionGroupConfig {
name: string;
resourceGroupName: string;
subscriptionId: string;
location: string;
emailReceivers?: EmailReceiver[];
smsReceivers?: SmsReceiver[];
}
export interface EmailReceiver {
name: string;
emailAddress: string;
}
export interface SmsReceiver {
name: string;
countryCode: string;
phoneNumber: string;
}
export async function createActionGroup(config: ActionGroupConfig): Promise<string | null> {
try {
if (!config.name || !config.resourceGroupName || !config.subscriptionId) {
throw new Error('Action group name, resource group, and subscription ID are required');
}
const credential = new DefaultAzureCredential();
const monitorClient = new monitor.MonitorManagementClient(credential, config.subscriptionId);
const actionGroupParams: monitor.ActionGroupResource = {
location: config.location,
groupShortName: config.name.substring(0, 12),
enabled: true,
emailReceivers: config.emailReceivers?.map(e => ({
name: e.name,
emailAddress: e.emailAddress
})),
smsReceivers: config.smsReceivers?.map(s => ({
name: s.name,
countryCode: s.countryCode,
phoneNumber: s.phoneNumber
}))
};
const poller = await monitorClient.actionGroups.beginCreateOrUpdateAndWait(
config.resourceGroupName,
config.name,
actionGroupParams
);
return poller.id;
} catch (error: any) {
console.error(`Failed to create action group: ${error.message}`);
return null;
}
}
export async function createMetricAlert(config: AlertRuleConfig): Promise<boolean> {
try {
if (!config.name || !config.targetResourceId || !config.criteria) {
throw new Error('Alert name, target resource ID, and criteria are required');
}
const credential = new DefaultAzureCredential();
const monitorClient = new monitor.MonitorManagementClient(credential, config.subscriptionId);
const alertParams: monitor.MetricAlertResource = {
location: 'global',
description: `Alert for ${config.criteria.metricName}`,
severity: 3,
enabled: true,
scopes: [config.targetResourceId],
evaluationFrequency: { duration: config.criteria.evaluationFrequency },
windowSize: { duration: config.criteria.windowSize },
criteria: {
'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria',
allOf: [{
threshold: config.criteria.threshold,
name: `${config.criteria.metricName}_threshold`,
metricName: config.criteria.metricName,
metricNamespace: 'Microsoft.Compute/virtualMachines',
dimensions: [],
operator: config.criteria.operator,
timeAggregation: config.criteria.timeAggregation,
skipMetricValidation: false
}]
},
actions: config.actionGroups?.map(ag => ({
actionGroupId: ag
}))
};
await monitorClient.metricAlerts.createOrUpdate(
config.resourceGroupName,
config.name,
alertParams
);
return true;
} catch (error: any) {
console.error(`Failed to create metric alert: ${error.message}`);
return false;
}
}
export async function setupLogAnalytics(
subscriptionId: string,
resourceGroupName: string,
workspaceName: string,
location: string
): Promise<string | null> {
try {
const credential = new DefaultAzureCredential();
const monitorClient = new monitor.MonitorManagementClient(credential, subscriptionId);
const workspaceParams: monitor.Workspace = {
location: location,
sku: { name: 'PerGB2018' },
retentionInDays: 30
};
const poller = await monitorClient.workspaces.beginCreateOrUpdateAndWait(
resourceGroupName,
workspaceName,
workspaceParams
);
return poller.id;
} catch (error: any) {
console.error(`Failed to create Log Analytics workspace: ${error.message}`);
return null;
}
}
export async function enableDiagnostics(
subscriptionId: string,
resourceGroupName: string,
resourceId: string,
logAnalyticsWorkspaceId: string
): Promise<boolean> {
try {
const credential = new DefaultAzureCredential();
const monitorClient = new monitor.MonitorManagementClient(credential, subscriptionId);
const diagnosticSettings: monitor.DiagnosticSettingsResource = {
location: 'global',
logs: [
{ category: 'AllMetrics', enabled: true, retentionPolicy: { days: 30, enabled: true } }
],
metrics: [
{ category: 'AllMetrics', enabled: true, retentionPolicy: { days: 30, enabled: true } }
],
workspaceId: logAnalyticsWorkspaceId
};
await monitorClient.diagnosticSettings.createOrUpdate(
resourceId,
`${resourceGroupName}-diagnostics`,
diagnosticSettings
);
return true;
} catch (error: any) {
console.error(`Failed to enable diagnostics: ${error.message}`);
return false;
}
}