Pulumi Best Practices - Azure
This page lists all 30 policies in the Pulumi Best Practices pack for Azure, as published in pulumi-best-practices-azure version 1.1.1.
Policies by control
1. Least Privilege — Ensure all identities and services have only the minimum permissions required to perform their tasks.
2. Resource Encryption at Rest — Encrypt all stored data using approved encryption mechanisms to protect against unauthorized access.
- sql-database-customer-managed-keys
- storage-account-uses-customer-managed-keys
- vm-requires-managed-disks
3. Transport Layer Encryption — Require secure protocols (e.g., TLS) for all data in transit to prevent interception or tampering.
4. No Public Access — Prohibit direct public exposure of resources unless explicitly approved and required.
5. Tagging — Enforce standardized resource tags for ownership, environment, and compliance tracking.
6. Enforce Logging — Enable and retain audit logs for all security-relevant actions and events.
7. High Availability — Deploy resources in redundant, fault-tolerant configurations to ensure service continuity.
8. Require DLQ — Ensure all asynchronous messaging systems are configured with a dead-letter queue to handle failures.
9. Resource Availability — Define and enforce timeouts, quotas, and capacity limits to prevent resource exhaustion.
10. Approved Versions — Only allow deployment of approved, patched, and supported versions of runtimes, images, and dependencies.
11. Networking — Only allow required inbound and outbound traffic through network security groups, firewalls, or ACLs.
13. Data Backup and Recovery — Regularly back up critical data and systems, store backups securely, and test recovery procedures to ensure timely restoration after failures or disasters.
- cosmos-db-backup-policies
- key-vault-soft-delete
- sql-database-backup-retention
- storage-account-geo-replication
14. Key Management & Rotation — Manage encryption keys securely and enforce periodic key rotation to reduce the risk of compromise.
Policy details
application-gateway-has-health-probes
Severity: medium · Enforcement: advisory
Require Application Gateway to enable health probes
- 9. Resource Availability — Define and enforce timeouts, quotas, and capacity limits to prevent resource exhaustion.
Remediation
Fix: Configure Health Probes
const appGateway = new azurenative.network.ApplicationGateway("my-app-gateway", {
probes: [{
name: "health-probe",
protocol: "Https",
path: "/health",
interval: 30,
timeout: 30,
unhealthyThreshold: 3,
match: {
statusCodes: ["200-399"],
},
}],
backendHttpSettingsCollection: [{
name: "backend-settings",
probe: { id: healthProbe.id }, // Associate probe with backend settings
// ... other config
}],
// ... other config
});
application-gateway-multi-az
Severity: medium · Enforcement: advisory
Require Application Gateway to be configured across multiple availability zones
- 7. High Availability — Deploy resources in redundant, fault-tolerant configurations to ensure service continuity.
Remediation
Fix: Deploy Across Multiple Availability Zones
const appGateway = new azurenative.network.ApplicationGateway("my-app-gateway", {
zones: ["1", "2", "3"], // Deploy across at least 2 availability zones
// ... other config
});
application-gateway-tls
Severity: high · Enforcement: advisory
Require Application Gateway to have secure TLS configuration
- 3. Transport Layer Encryption — Require secure protocols (e.g., TLS) for all data in transit to prevent interception or tampering.
Remediation
Fix: Configure Secure TLS Policy
const appGateway = new azurenative.network.ApplicationGateway("my-app-gateway", {
sslPolicy: {
policyType: "Predefined",
policyName: "AppGwSslPolicy20220101", // Use modern policy with TLS 1.2+
},
// ... other config
});
cosmos-db-backup-policies
Severity: medium · Enforcement: advisory
Require Cosmos DB account to have backup policies configured
- 13. Data Backup and Recovery — Regularly back up critical data and systems, store backups securely, and test recovery procedures to ensure timely restoration after failures or disasters.
Remediation
Fix: Configure Cosmos DB Backup Policies
// Option 1: Continuous Backup (recommended)
const cosmosDbContinuous = new azurenative.cosmosdb.DatabaseAccount("my-cosmosdb", {
databaseAccountOfferType: "Standard",
backupPolicy: {
type: "Continuous", // Enable continuous backup
continuousModeProperties: {
tier: "Continuous30Days", // 30-day point-in-time restore
},
},
// ... other config
});
// Option 2: Periodic Backup
const cosmosDbPeriodic = new azurenative.cosmosdb.DatabaseAccount("my-cosmosdb", {
databaseAccountOfferType: "Standard",
backupPolicy: {
type: "Periodic", // Enable periodic backup
periodicModeProperties: {
backupIntervalInMinutes: 240, // 4 hours
backupRetentionIntervalInHours: 720, // 30 days
},
},
// ... other config
});
front-door-tls
Severity: high · Enforcement: advisory
Require Front Door custom domains to use secure TLS configuration
- 3. Transport Layer Encryption — Require secure protocols (e.g., TLS) for all data in transit to prevent interception or tampering.
Remediation
Fix: Configure Secure TLS Settings
const customDomain = new azurenative.cdn.AFDCustomDomain("my-custom-domain", {
tlsSettings: {
minimumTlsVersion: "TLS12", // Enforce minimum TLS 1.2
certificateType: "ManagedCertificate", // Use Azure-managed certificates
},
// ... other config
});
key-vault-key-configuration
Severity: high · Enforcement: advisory
Require proper Key Vault key creation and configuration
- 14. Key Management & Rotation — Manage encryption keys securely and enforce periodic key rotation to reduce the risk of compromise.
Remediation
Fix: Configure Key with Proper Type, Size, and Operations
const key = new azurenative.keyvault.Key("my-key", {
properties: {
kty: "RSA", // or "RSA-HSM" for hardware protection
keySize: 2048, // Minimum 2048 bits for RSA
keyOps: ["encrypt", "decrypt"], // Specify exact operations needed
attributes: {
exportable: false,
},
},
// ... other config
});
key-vault-key-lifecycle
Severity: high · Enforcement: advisory
Require proper Key Vault key deletion and lifecycle management
- 14. Key Management & Rotation — Manage encryption keys securely and enforce periodic key rotation to reduce the risk of compromise.
Remediation
Fix: Configure Key Vault Key Lifecycle Attributes
const key = new azurenative.keyvault.Key("my-key", {
properties: {
kty: "RSA",
attributes: {
exp: 1735689600, // Set expiration date (Unix epoch timestamp)
nbf: 1704067200, // Set not-before date (Unix epoch timestamp)
},
// ... other config
},
});
key-vault-key-rotation
Severity: high · Enforcement: advisory
Require Key Vault keys to have rotation policies configured
- 14. Key Management & Rotation — Manage encryption keys securely and enforce periodic key rotation to reduce the risk of compromise.
Remediation
Fix: Configure Key Vault Key Rotation Policy
const key = new azurenative.keyvault.Key("my-key", {
properties: {
kty: "RSA",
rotationPolicy: {
attributes: {
expiryTime: "P2Y", // Set expiry time for key versions
},
lifetimeActions: [{
action: { type: "rotate" },
trigger: { timeAfterCreate: "P90D" }, // Rotate 90 days after creation
}],
},
// ... other config
},
});
key-vault-soft-delete
Severity: high · Enforcement: advisory
Require Key Vault to have soft delete enabled with appropriate retention
- 13. Data Backup and Recovery — Regularly back up critical data and systems, store backups securely, and test recovery procedures to ensure timely restoration after failures or disasters.
Remediation
Fix: Enable Key Vault Soft Delete
const vault = new azurenative.keyvault.Vault("my-key-vault", {
properties: {
sku: { name: "standard" },
enableSoftDelete: true, // Enable soft delete
softDeleteRetentionInDays: 90, // Set retention period (minimum 90 days)
// ... other config
},
});
load-balancer-health-probes
Severity: medium · Enforcement: advisory
Require Load Balancer to enable health probes
- 9. Resource Availability — Define and enforce timeouts, quotas, and capacity limits to prevent resource exhaustion.
Remediation
Fix: Configure Load Balancer Health Probes
const loadBalancer = new azurenative.network.LoadBalancer("my-load-balancer", {
sku: { name: "Standard" },
probes: [{
name: "http-health-probe",
properties: {
protocol: "Http",
port: 80,
requestPath: "/health", // Configure health check endpoint
},
}],
loadBalancingRules: [{
name: "http-rule",
properties: {
probe: { id: probeId }, // Associate probe with rule
// ... other config
},
}],
});
load-balancer-multi-az
Severity: medium · Enforcement: advisory
Require Load Balancer to be configured across multiple availability zones
- 7. High Availability — Deploy resources in redundant, fault-tolerant configurations to ensure service continuity.
Remediation
Fix: Configure Load Balancer for Multiple Availability Zones
const loadBalancer = new azurenative.network.LoadBalancer("my-lb", {
sku: {
name: "Standard", // Standard SKU required for zone redundancy
},
frontendIPConfigurations: [{
zones: ["1", "2", "3"], // Deploy across multiple zones
// ... other config
}],
});
log-analytics-retention
Severity: medium · Enforcement: advisory
Require Log Analytics workspace to have appropriate retention policies
- 6. Enforce Logging — Enable and retain audit logs for all security-relevant actions and events.
Remediation
Fix: Configure Log Analytics Workspace Retention Policy
const workspace = new azurenative.operationalinsights.Workspace("my-workspace", {
sku: {
name: "PerGB2018",
},
retentionInDays: 90, // Set to meet compliance requirements (minimum 90 days)
// ... other config
});
network-interface-no-public-ip
Severity: critical · Enforcement: advisory
Require Network Interfaces to have no public IP address associations
- 4. No Public Access — Prohibit direct public exposure of resources unless explicitly approved and required.
Remediation
Fix: Remove Public IP Address from Network Interface
const nic = new azurenative.network.NetworkInterface("my-nic", {
ipConfigurations: [{
privateIPAllocationMethod: "Dynamic",
// Do NOT include publicIPAddress property
// ... other config
}],
});
nsg-disallow-public-internet-ingress
Severity: high · Enforcement: advisory
Require Network Security Groups to disallow public internet ingress
- 11. Networking — Only allow required inbound and outbound traffic through network security groups, firewalls, or ACLs.
Remediation
Fix: Remove Public Internet Ingress from Network Security Group
const nsg = new azurenative.network.NetworkSecurityGroup("secure-nsg", {
securityRules: [
{
protocol: "Tcp",
sourceAddressPrefix: "203.0.113.0/24", // Use specific IP ranges, not 0.0.0.0/0
access: "Allow",
direction: "Inbound",
// ... other config
},
],
});
nsg-strict-rules
Severity: high · Enforcement: advisory
Require strict Network Security Group rules with explicit allow/deny configuration
- 11. Networking — Only allow required inbound and outbound traffic through network security groups, firewalls, or ACLs.
Remediation
Fix: Configure Strict Network Security Group Rules
const nsg = new azurenative.network.NetworkSecurityGroup("strict-nsg", {
securityRules: [
{
protocol: "Tcp",
sourceAddressPrefix: "10.0.0.0/16", // Use specific IP ranges, not * or 0.0.0.0/0
destinationAddressPrefix: "10.0.1.0/24", // Use specific destinations
access: "Allow",
direction: "Inbound",
// ... other config
},
],
});
rbac-least-privilege
Severity: critical · Enforcement: advisory
Enforce least privilege access control by prohibiting overly broad RBAC role assignments
- 1. Least Privilege — Ensure all identities and services have only the minimum permissions required to perform their tasks.
Remediation
Fix: Use Specific Least-Privilege Roles
const roleAssignment = new azurenative.authorization.RoleAssignment("my-assignment", {
roleDefinitionId: "/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7", // Reader role instead of Owner/Contributor
principalId: servicePrincipalId,
scope: "/subscriptions/{sub-id}/resourceGroups/my-rg", // Scope to resource group instead of subscription
});
resources-change-tracking-tags
Severity: low · Enforcement: advisory
Require all Azure resources to have proper tagging for change tracking
- 5. Tagging — Enforce standardized resource tags for ownership, environment, and compliance tracking.
Remediation
Fix: Add Change Tracking Tags to Resources
const storageAccount = new azurenative.storage.StorageAccount("app-storage", {
sku: {
name: "Standard_LRS",
},
tags: {
"last-modified": "2025-10-07", // ISO 8601 date format (YYYY-MM-DD)
"modified-by": "jane.doe@company.com",
"change-reason": "Added storage account for new application deployment",
},
// ... other config
});
resources-environment-tags
Severity: low · Enforcement: advisory
Require all resources to have environment tags
- 5. Tagging — Enforce standardized resource tags for ownership, environment, and compliance tracking.
Remediation
Fix: Add Environment Tag
const resource = new azurenative.storage.StorageAccount("my-storage", {
tags: {
environment: "prod", // Use "dev", "test", "staging", or "prod"
},
// ... other config
});
service-bus-dead-letter-queue
Severity: medium · Enforcement: advisory
Validate Service Bus queues have proper dead letter queue configuration
- 8. Require DLQ — Ensure all asynchronous messaging systems are configured with a dead-letter queue to handle failures.
Remediation
Fix: Configure Service Bus Queue Dead Letter Settings
const queue = new azurenative.servicebus.Queue("my-queue", {
maxDeliveryCount: 10, // Maximum delivery attempts before dead lettering
deadLetteringOnMessageExpiration: true, // Enable dead lettering when messages expire
// ... other config
});
sql-database-backup-retention
Severity: medium · Enforcement: advisory
Require Azure SQL Database to have backup retention configured with redundant storage
- 13. Data Backup and Recovery — Regularly back up critical data and systems, store backups securely, and test recovery procedures to ensure timely restoration after failures or disasters.
Remediation
Fix: Enable SQL Database Backup Retention with Redundant Storage
const database = new azurenative.sql.Database("my-database", {
sku: {
name: "S0",
tier: "Standard",
},
requestedBackupStorageRedundancy: "Geo", // Use geo-redundant backup storage
// ... other config
});
sql-database-customer-managed-keys
Severity: high · Enforcement: advisory
Require Azure SQL databases to use customer-managed keys for transparent data encryption
- 2. Resource Encryption at Rest — Encrypt all stored data using approved encryption mechanisms to protect against unauthorized access.
Remediation
Fix: Enable SQL Database Customer-Managed Keys for TDE
const encryptionProtector = new azurenative.sql.EncryptionProtector("tde-protector", {
serverKeyType: "AzureKeyVault", // Use customer-managed key from Azure Key Vault
serverKeyName: serverKey.name,
autoRotationEnabled: true, // Enable auto-rotation when key version changes
// ... other config
});
sql-database-high-availability
Severity: medium · Enforcement: advisory
Require Azure SQL Database to have high availability configuration
- 7. High Availability — Deploy resources in redundant, fault-tolerant configurations to ensure service continuity.
Remediation
Fix: Enable SQL Database High Availability
const database = new azurenative.sql.Database("my-database", {
sku: {
name: "P1",
tier: "Premium",
},
zoneRedundant: true, // Enable zone redundancy for high availability
// ... other config
});
sql-server-audit-logging
Severity: medium · Enforcement: advisory
Require Azure SQL Server to have audit logging enabled
- 6. Enforce Logging — Enable and retain audit logs for all security-relevant actions and events.
Remediation
Fix: Enable SQL Server Audit Logging
const auditPolicy = new azurenative.sql.ServerBlobAuditingPolicy("audit-policy", {
state: "Enabled", // Enable audit logging
storageEndpoint: storageEndpoint,
retentionDays: 90, // Retain audit logs for 90 days
// ... other config
});
sql-server-disable-public-access
Severity: critical · Enforcement: advisory
Require Azure SQL Server to disable public network access
- 4. No Public Access — Prohibit direct public exposure of resources unless explicitly approved and required.
Remediation
Fix: Disable SQL Server Public Network Access
const sqlServer = new azurenative.sql.Server("my-sql-server", {
publicNetworkAccess: "Disabled", // Disable public network access
// ... other config
});
storage-account-geo-replication
Severity: medium · Enforcement: advisory
Require Storage Accounts to have geo-replication enabled for business continuity
- 13. Data Backup and Recovery — Regularly back up critical data and systems, store backups securely, and test recovery procedures to ensure timely restoration after failures or disasters.
Remediation
Fix: Enable Geo-Replication
const storageAccount = new azurenative.storage.StorageAccount("mystorageaccount", {
sku: { name: "Standard_GRS" }, // Use geo-replicated SKU for redundancy
kind: "StorageV2",
// ... other config
});
storage-account-https-only
Severity: high · Enforcement: advisory
Require Storage Accounts to enforce HTTPS-only traffic
- 3. Transport Layer Encryption — Require secure protocols (e.g., TLS) for all data in transit to prevent interception or tampering.
Remediation
Fix: Enable HTTPS-Only Traffic
const storageAccount = new azurenative.storage.StorageAccount("mystorageaccount", {
sku: { name: "Standard_LRS" },
kind: "StorageV2",
enableHttpsTrafficOnly: true, // Enforce HTTPS-only traffic
// ... other config
});
storage-account-public-access
Severity: critical · Enforcement: advisory
Require Storage Accounts to disable public blob access
- 4. No Public Access — Prohibit direct public exposure of resources unless explicitly approved and required.
Remediation
Fix: Disable Public Blob Access
const storageAccount = new azurenative.storage.StorageAccount("mystorageaccount", {
sku: { name: "Standard_LRS" },
kind: "StorageV2",
allowBlobPublicAccess: false, // Disable public blob access
// ... other config
});
storage-account-uses-customer-managed-keys
Severity: high · Enforcement: advisory
Require Storage Accounts to use customer-managed keys for encryption
- 2. Resource Encryption at Rest — Encrypt all stored data using approved encryption mechanisms to protect against unauthorized access.
Remediation
Fix: Configure Customer-Managed Keys
const storageAccount = new azurenative.storage.StorageAccount("mystorageaccount", {
sku: { name: "Standard_LRS" },
kind: "StorageV2",
encryption: {
keySource: "Microsoft.Keyvault", // Use customer-managed keys
keyVaultProperties: {
keyName: "my-encryption-key",
keyVaultUri: "https://my-keyvault.vault.azure.net/",
},
},
// ... other config
});
vm-approved-images
Severity: medium · Enforcement: advisory
Require pre-approved hardened VM images from trusted publishers
- 10. Approved Versions — Only allow deployment of approved, patched, and supported versions of runtimes, images, and dependencies.
Remediation
Fix: Use Approved VM Images
const vm = new azurenative.compute.VirtualMachine("my-vm", {
hardwareProfile: { vmSize: "Standard_D2s_v3" },
storageProfile: {
imageReference: {
publisher: "Canonical", // Use approved publisher
offer: "UbuntuServer", // Use approved offer
sku: "20.04-LTS", // Use approved SKU
version: "latest",
},
// ... other config
},
// ... other config
});
vm-requires-managed-disks
Severity: medium · Enforcement: advisory
Require VMs to use managed disks only
- 2. Resource Encryption at Rest — Encrypt all stored data using approved encryption mechanisms to protect against unauthorized access.
Remediation
Fix: Configure VM to Use Managed Disks
const vm = new azurenative.compute.VirtualMachine("my-vm", {
hardwareProfile: { vmSize: "Standard_D2s_v3" },
storageProfile: {
osDisk: {
createOption: "FromImage",
managedDisk: { // Use managed disk, not vhd property
storageAccountType: "Premium_LRS",
},
},
dataDisks: [{
lun: 0,
createOption: "Empty",
diskSizeGB: 128,
managedDisk: { // Use managed disk, not vhd property
storageAccountType: "Premium_LRS",
},
}],
// ... other config
},
// ... other config
});