CIS 8.1 - Azure
This page lists all 104 policies in the CIS 8.1 pack for Azure, as published in cis-azure version 1.0.2.
Policies by control
1.1 — Establish and maintain an accurate, detailed, and up-to-date inventory of all enterprise assets with the potential to store or process data, to include: end-user devices (including portable and mobile), network devices, non-computing/IoT devices, and servers. Ensure the inventory records the network address (if static), hardware address, machine name, enterprise asset owner, department for each asset, and whether the asset has been approved to connect to the network. For mobile end-user devices, MDM type tools can support this process, where appropriate. This inventory includes assets connected to the infrastructure physically, virtually, remotely, and those within cloud environments. Additionally, it includes assets that are regularly connected to the enterprise’s network infrastructure, even if they are not under control of the enterprise. Review and update the inventory of all enterprise assets bi-annually, or more frequently.
1.2 — Ensure that a process exists to address unauthorized assets on a weekly basis. The enterprise may choose to remove the asset from the network, deny the asset from connecting remotely to the network, or quarantine the asset.
2.2 — Ensure that only currently supported software is designated as authorized in the software inventory for enterprise assets. If software is unsupported, yet necessary for the fulfillment of the enterprise’s mission, document an exception detailing mitigating controls and residual risk acceptance. For any unsupported software without an exception documentation, designate as unauthorized. Review the software list to verify software support at least monthly, or more frequently.
3.1 — Establish and maintain a documented data management process. In the process, address data sensitivity, data owner, handling of data, data retention limits, and disposal requirements, based on sensitivity and retention standards for the enterprise. Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
- application-gateway-https-redirection
- application-gateway-tls
- load-balancer-https-listeners
- storage-account-secure-transfer
3.3 — Establish and maintain a data management process. In the process, address data sensitivity, data owner, handling of data, data retention limits, and disposal requirements, based on sensitivity and retention standards for the enterprise. Review and update documentation annually, or when significant enterprise changes occur that could impact this safeguard.
- aad-custom-roles
- aad-direct-user-roles
- aad-role-assignments
- aks-cluster-public-access
- container-instance-privileged-mode
- dms-instance-public-access
- function-public-access
- function-vnet-integration
- hdinsight-cluster-public-access
- hdinsight-kerberos-enabled
- managed-disk-snapshot-restrict-network-access
- managed-disk-snapshot-restrict-public-access
- ml-compute-internet-access
- search-service-vnet-only
- sql-server-disable-public-access
- storage-account-level-public-access
- storage-account-policy-grantee
- storage-account-public-access
- storage-account-public-write
- subnet-auto-assign-public-ip
- synapse-cluster-public-access
- vm-in-vnet
- vm-managed-identity
- vm-not-publicly-accessible
- vm-scale-set-public-ip
3.8 — Document data flows. Data flow documentation includes service provider data flows and should be based on the enterprise’s data management process. Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
- activity-log-monitoring
- function-scaling-check
- log-analytics-workspace-retention
- no-unrestricted-route-to-internet-gateway
- nsg-ssh-rdp-restriction
- vm-detailed-monitoring
3.11 — Encrypt sensitive data at rest on servers, applications, and databases. Storage-layer encryption, also known as server-side encryption, meets the minimum requirement of this Safeguard. Additional encryption methods may include application-layer encryption, also known as client-side encryption, where access to the data storage device(s) does not permit access to the plain-text data.
- cosmos-db-customer-key-encryption
- key-vault-key-not-scheduled-for-deletion
- key-vault-using-cmk
- log-analytics-encrypted
- managed-disk-encryption
- ml-compute-instance-cmk-configured
- ml-workspace-cmk-configured
- search-service-encrypted-at-rest
- service-bus-encrypted-cmk
- sql-database-tde-enabled
- storage-account-default-encryption-cmk
- storage-account-server-side-encryption
- storage-file-encrypted-check
3.14 — Log sensitive data access, including modification and disposal.
- activity-log-monitor-integration
- activity-log-storage-dataevents
- api-management-diagnostic-settings-exist
- diagnostic-setting-configuration
- load-balancer-logging
- search-service-logs-monitor
- synapse-configuration-logging
- waf-logging-enabled
4.1 — Establish and maintain a documented secure configuration process for enterprise assets (end-user devices, including portable and mobile, non-computing/IoT devices, and servers) and software (operating systems and applications). Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
4.6 — Securely manage enterprise assets and software. Example implementations include managing configuration through version-controlled Infrastructure-as-Code (IaC) and accessing administrative interfaces over secure network protocols, such as Secure Shell (SSH) and Hypertext Transfer Protocol Secure (HTTPS). Do not use insecure management protocols, such as Telnet (Teletype Network) and HTTP, unless operationally essential.
- activity-log-encryption-enabled
- activity-log-file-integrity-enabled
- activity-log-multi-region-trail-enabled
- key-vault-key-rotation-enabled
- managed-disk-attached-encryption-enabled
- nsg-restrict-ingress-ssh-all
- storage-account-default-encryption-enabled
- storage-account-public-read-prohibited
- storage-account-public-write-prohibited
- storage-account-replication-enabled
- vmss-load-balancer-healthcheck-required
4.7 — Manage default accounts on enterprise assets and software, such as root, administrator, and other pre-configured vendor accounts. Example implementations can include: disabling default accounts or making them unusable.
5.4 — Restrict administrator privileges to dedicated administrator accounts on enterprise assets. Conduct general computing activities, such as internet browsing, email, and productivity suite use, from the user’s primary, non-privileged account.
7.1 — Establish and maintain a documented vulnerability management process for enterprise assets. Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
7.3 — Perform operating system updates on enterprise assets through automated patch management on a monthly, or more frequent, basis.
8.2 — Collect audit logs. Ensure that logging, per the enterprise’s audit log management process, has been enabled across enterprise assets.
- activity-log-trail-enabled
- cdn-distribution-logging-enabled
- sql-server-audit-logging
- vnet-flow-logs-enabled
- wafv2-logging-enabled
11.2 — Perform automated backups of in-scope enterprise assets. Run backups weekly, or more frequently, based on the sensitivity of the data.
- azure-database-backup-enabled
- backup-instance-configuration
- cosmos-db-in-backup-plan
- cosmos-db-pitr-enabled
- managed-disk-backup-protection-exists
- snapshot-configuration
- storage-account-cross-region-replication-enabled
- storage-account-versioning-enabled
- synapse-backup-enabled
12.2 — Establish and maintain a secure network architecture. A secure network architecture must address segmentation, least privilege, and availability, at a minimum. Example implementations may include documentation, policy, and design components.
- azure-database-deletion-protection-enabled
- azure-database-multi-region-support
- cosmos-db-autoscaling-enabled
- key-vault-certificate-expiration-check
- load-balancer-cross-zone-enabled
- load-balancer-deletion-protection
- synapse-enhanced-vnet-routing-enabled
- vm-no-public-ip
13.1 — Centralize security event alerting across enterprise assets for log correlation and analysis. Best practice implementation requires the use of a SIEM, which includes vendor-defined event correlation alerts. A log analytics platform configured with security-relevant correlation alerts also satisfies this Safeguard.
Policy details
aad-custom-roles
Severity: high · Enforcement: advisory
Prevent Azure AD custom roles and Azure Native custom role definitions with broad permissions.
- 3.3 — Establish and maintain a data management process. In the process, address data sensitivity, data owner, handling of data, data retention limits, and disposal requirements, based on sensitivity and retention standards for the enterprise. Review and update documentation annually, or when significant enterprise changes occur that could impact this safeguard.
Remediation
Fix: Remove Broad Permissions from Custom Roles
To pass this policy:
- Replace wildcard (*) actions with specific permissions
- Remove forbidden actions (Microsoft.Authorization/, Microsoft.Resources/)
- Keep total actions under 50 (or configured limit)
Fix for Azure Native Custom Roles
new azure.authorization.RoleDefinition("custom-role", {
roleName: "Storage Manager",
roleType: "CustomRole",
assignableScopes: [`/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}`],
permissions: [{
actions: [
"Microsoft.Storage/storageAccounts/read",
"Microsoft.Storage/storageAccounts/write",
"Microsoft.Storage/storageAccounts/listKeys/action",
], // Specific actions only, no wildcards
notActions: [],
}],
});
Fix for Azure AD Custom Roles
new azuread.CustomDirectoryRole("custom-aad-role", {
displayName: "User Support Specialist",
enabled: true,
permissions: [{
allowedResourceActions: [
"microsoft.directory/users/password/update",
"microsoft.directory/users/usageLocation/update",
"microsoft.directory/users/userPrincipalName/update",
], // Specific actions only, no wildcards
}],
});
aad-direct-user-roles
Severity: high · Enforcement: advisory
Prevent Azure AD users from having direct role assignments.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Use Group-Based Role Assignments
To pass this policy:
- Assign roles to Azure AD groups, not individual users
- Set principalType to “Group” instead of “User”
// Create a security group
const devGroup = new azuread.Group("dev-team", {
displayName: "Development Team",
securityEnabled: true,
});
// Assign role to the group (not to users directly)
new azure.authorization.RoleAssignment("group-role", {
principalId: devGroup.id,
principalType: "Group", // Must be Group, not User
roleDefinitionId: contributorRoleId,
scope: resourceGroupScope,
});
// Add users as group members
new azuread.GroupMember("dev-member", {
groupObjectId: devGroup.id,
memberObjectId: userObjectId,
});
aad-global-admin-no-permanent-access
Severity: high · Enforcement: advisory
Manage default global administrator accounts to prevent permanent privileged access.
- 4.7 — Manage default accounts on enterprise assets and software, such as root, administrator, and other pre-configured vendor accounts. Example implementations can include: disabling default accounts or making them unusable.
Remediation
Fix: Remove Permanent Global Administrator Access
To pass this policy:
- Add conditions to Global Admin assignments (condition & conditionVersion properties)
- Never assign Global Admin to Service Principals
// BAD: Permanent Global Administrator assignment without conditions
new azure.authorization.RoleAssignment("permanent-global-admin", {
principalId: userId,
principalType: "User",
roleDefinitionId: "/providers/Microsoft.Authorization/roleDefinitions/62e90394-69f5-4237-9190-012177145e10",
scope: "/",
});
// GOOD: Conditional assignment
new azure.authorization.RoleAssignment("conditional-global-admin", {
principalId: groupId,
principalType: "Group", // Use groups, not individual users
roleDefinitionId: "/providers/Microsoft.Authorization/roleDefinitions/62e90394-69f5-4237-9190-012177145e10",
scope: "/subscriptions/${subscriptionId}", // Limit scope when possible
condition: "@Resource[Microsoft.Authorization/roleAssignments:principalId] StringEquals '${groupId}'",
conditionVersion: "2.0",
});
Use Azure Native PIM Resources (When Available)
Configure time-bounded eligible role assignments:
// Create PIM Role Eligibility Schedule
new azure.authorization.RoleEligibilitySchedule("global-admin-eligible", {
scope: "/subscriptions/${subscriptionId}",
roleDefinitionId: "/providers/Microsoft.Authorization/roleDefinitions/62e90394-69f5-4237-9190-012177145e10",
principalId: groupId,
principalType: "Group",
expirationDate: "2024-12-31T23:59:59Z", // Must have expiration
startDate: "2024-01-01T00:00:00Z",
});
Use Azure AD Privileged Access Groups
Configure privileged access through Azure AD groups with eligibility schedules:
// Create a privileged access group
const privilegedAdminGroup = new azuread.Group("privileged-admins", {
displayName: "Privileged Global Administrators",
securityEnabled: true,
});
// Create eligibility schedule for the group
new azuread.PrivilegedAccessGroupEligibilitySchedule("admin-eligibility", {
groupId: privilegedAdminGroup.objectId,
principalId: userId,
assignmentType: "member",
duration: "PT8H", // 8-hour eligibility window
justification: "Required for quarterly security audit",
startDate: "2024-01-01T00:00:00Z",
permanentAssignment: false, // Never permanent
});
Implement Break-Glass Accounts with Monitoring
Create dedicated emergency access accounts with proper alerts:
// Create a break-glass security group
const breakGlassGroup = new azuread.Group("break-glass-admins", {
displayName: "Emergency Break-Glass Administrators",
securityEnabled: true,
});
// Assign Global Admin to break-glass group (permanent but monitored)
new azure.authorization.RoleAssignment("break-glass-admin", {
principalId: breakGlassGroup.objectId,
principalType: "Group",
roleDefinitionId: "/providers/Microsoft.Authorization/roleDefinitions/62e90394-69f5-4237-9190-012177145e10",
scope: "/",
});
// Create activity log alert for break-glass usage
new azure.insights.ActivityLogAlert("break-glass-usage-alert", {
resourceGroupName: securityResourceGroup.name,
location: "Global",
enabled: true,
condition: {
allOf: [{
field: "category",
equals: "Administrative",
}, {
field: "caller",
contains: breakGlassGroup.objectId.apply(id => id),
}],
},
actions: [{
actionGroupId: securityAlertActionGroup.id,
}],
});
Use Least Privilege Alternatives
Consider using more specific admin roles instead of Global Administrator:
// Instead of Global Administrator, use specific roles
const specificAdminRoles = {
userAdmin: "fe930be7-5e62-47db-91af-98c3a49a38b1",
securityAdmin: "194ae4cb-b126-40b2-bd5b-6091b380977d",
cloudAppAdmin: "158c047a-c907-4556-b7ef-446551a6b5f7",
conditionalAccessAdmin: "b1be1c3e-b65d-4f19-8427-f6fa0d97feb9",
};
// Assign only the specific role needed
new azure.authorization.RoleAssignment("security-admin-limited", {
principalId: securityTeamGroupId,
principalType: "Group",
roleDefinitionId: `/providers/Microsoft.Authorization/roleDefinitions/${specificAdminRoles.securityAdmin}`,
scope: "/subscriptions/${subscriptionId}",
});
Service Principals Should Never Have Global Admin
// BAD: Service Principal with Global Admin
new azure.authorization.RoleAssignment("sp-global-admin", {
principalId: servicePrincipalId,
principalType: "ServicePrincipal",
roleDefinitionId: "/providers/Microsoft.Authorization/roleDefinitions/62e90394-69f5-4237-9190-012177145e10",
scope: "/",
});
// GOOD: Service Principal with specific, scoped permissions
new azure.authorization.RoleAssignment("sp-contributor", {
principalId: servicePrincipalId,
principalType: "ServicePrincipal",
roleDefinitionId: "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c", // Contributor
scope: "/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}",
});
aad-role-assignments
Severity: high · Enforcement: advisory
Prevent Azure AD and Azure Native role assignments with excessive privileges.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Remove Excessive Role Assignment Privileges
- Avoid forbidden roles (Owner, User Access Administrator)
- Don’t use overly broad scopes (root “/” or all subscriptions)
- Assign roles to groups instead of individual users
// GOOD: Specific role, scoped to resource group, assigned to group
new azure.authorization.RoleAssignment("good-assignment", {
principalId: securityGroupId,
principalType: "Group",
roleDefinitionId: "/subscriptions/${subscriptionId}/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c", // Contributor
scope: `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}`,
});
aad-role-no-wildcard-assignments
Severity: high · Enforcement: advisory
Restrict administrator privileges - no wildcard role assignments.
- 5.4 — Restrict administrator privileges to dedicated administrator accounts on enterprise assets. Conduct general computing activities, such as internet browsing, email, and productivity suite use, from the user’s primary, non-privileged account.
Remediation
Fix: Remove Wildcard or Overly Broad Role Assignments
To pass this policy:
- Avoid root “/” or “/subscriptions” scopes
- Don’t use wildcards (*) or “all” in scopes
- Scope assignments to resource groups or specific resources
- Service Principals must have narrow scopes (resource-level)
- Add conditions for subscription-level assignments
// GOOD: Resource group scoped
new azure.authorization.RoleAssignment("specific-scope", {
principalId: groupId,
principalType: "Group",
roleDefinitionId: contributorRoleId,
scope: `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}`,
});
// GOOD: Subscription-level with conditions
new azure.authorization.RoleAssignment("conditional-broad", {
principalId: groupId,
roleDefinitionId: readerRoleId,
scope: `/subscriptions/${subscriptionId}`,
condition: "@Resource[Microsoft.Storage/storageAccounts:name] StringStartsWith 'dev'",
conditionVersion: "2.0",
});
activity-log-encryption-enabled
Severity: high · Enforcement: advisory
Ensure Activity Log encryption is enabled for data protection.
- 4.6 — Securely manage enterprise assets and software. Example implementations include managing configuration through version-controlled Infrastructure-as-Code (IaC) and accessing administrative interfaces over secure network protocols, such as Secure Shell (SSH) and Hypertext Transfer Protocol Secure (HTTPS). Do not use insecure management protocols, such as Telnet (Teletype Network) and HTTP, unless operationally essential.
Remediation
Fix: Enable Activity Log Encryption
Your Activity Log data needs encryption both at rest and in transit to protect audit trail integrity.
Configure Storage Account with encryption for Activity Log destination:
const logStorage = new azure.storage.StorageAccount("activityLogStorage", {
resourceGroupName: resourceGroup.name,
location: "eastus",
sku: { name: "Standard_GRS" },
kind: "StorageV2",
enableHttpsTrafficOnly: true, // Required: HTTPS-only for encryption in transit
minimumTlsVersion: "TLS1_2", // Required: Minimum TLS 1.2
encryption: {
services: {
blob: {
enabled: true, // Required: Blob encryption at rest
keyType: "Account",
},
file: {
enabled: true,
},
},
keySource: "Microsoft.Storage", // Or "Microsoft.Keyvault" for customer-managed keys
},
});
For customer-managed keys, integrate with Key Vault:
const logStorage = new azure.storage.StorageAccount("activityLogStorage", {
resourceGroupName: resourceGroup.name,
location: "eastus",
sku: { name: "Standard_GRS" },
kind: "StorageV2",
enableHttpsTrafficOnly: true,
minimumTlsVersion: "TLS1_2",
encryption: {
services: {
blob: { enabled: true, keyType: "Account" },
},
keySource: "Microsoft.Keyvault", // Use customer-managed keys
keyVaultProperties: {
keyName: encryptionKey.name,
keyVaultUri: keyVault.properties.vaultUri,
},
},
});
Configure Activity Log diagnostic settings with encrypted destination:
const activityLogDiagnostic = new azure.monitor.DiagnosticSetting("activityLogDiag", {
name: "activity-log-encrypted",
resourceUri: pulumi.interpolate`/subscriptions/${subscriptionId}`,
workspaceId: workspace.id, // Log Analytics (encrypted by default)
storageAccountId: logStorage.id, // Encrypted Storage Account
logs: [
{ category: "Administrative", enabled: true },
{ category: "Security", enabled: true },
],
});
Note: Encryption requirements per CIS Controls v8 IG1 4.6:
- At rest: Storage accounts must have blob encryption enabled
- In transit: Storage accounts must enforce HTTPS-only traffic with TLS 1.2+
- Customer-managed keys: Optional but recommended for additional control
Log Analytics workspaces use Azure-managed encryption by default.
activity-log-file-integrity-enabled
Severity: high · Enforcement: advisory
Ensure Activity Log file integrity monitoring is enabled for security monitoring.
- 4.6 — Securely manage enterprise assets and software. Example implementations include managing configuration through version-controlled Infrastructure-as-Code (IaC) and accessing administrative interfaces over secure network protocols, such as Secure Shell (SSH) and Hypertext Transfer Protocol Secure (HTTPS). Do not use insecure management protocols, such as Telnet (Teletype Network) and HTTP, unless operationally essential.
Remediation
Fix: Enable Activity Log File Integrity Monitoring
Your Activity Log diagnostic settings need file integrity monitoring configured to detect tampering with audit logs.
Configure Activity Log diagnostic settings with file integrity monitoring:
const activityLogDiagnostic = new azure.monitor.DiagnosticSetting("activityLogDiag", {
name: "activity-log-to-workspace",
resourceUri: pulumi.interpolate`/subscriptions/${subscriptionId}`,
workspaceId: workspace.id,
logs: [
{ category: "Administrative", enabled: true },
{ category: "Security", enabled: true },
{ category: "Alert", enabled: true },
{ category: "Policy", enabled: true },
],
});
Apply tags to the Log Analytics Workspace to indicate file integrity monitoring:
const workspace = new azure.operationalinsights.Workspace("securityWorkspace", {
// ... other properties
tags: {
FileIntegrity: "enabled", // File integrity monitoring enabled
HashValidation: "sha256", // Hash-based integrity validation
IntegrityAlerting: "enabled", // Alerts on integrity violations
},
});
For Storage Account destinations, apply tags to indicate immutable storage:
const logStorage = new azure.storage.StorageAccount("logStorage", {
// ... other properties
tags: {
ImmutableStorage: "enabled",
WORMCompliance: "enabled",
TamperProof: "enabled",
},
});
Note: DiagnosticSetting resources do NOT support tags. Apply tags to the destination resources (Log Analytics Workspace or Storage Account) to indicate file integrity monitoring is configured per CIS Controls v8 IG1 4.6. Immutable storage prevents log tampering and ensures audit trail integrity.
activity-log-monitor-integration
Severity: medium · Enforcement: advisory
Ensure Activity Log is integrated with advanced monitoring solutions for comprehensive log analysis.
- 3.14 — Log sensitive data access, including modification and disposal.
Remediation
Fix: Configure Activity Log Advanced Monitor Integration
Your Activity Log needs integration with advanced monitoring solutions for comprehensive log analysis and threat detection.
Set up Activity Log diagnostic settings with Log Analytics workspace:
const activityLogDiagnostic = new azure.monitor.DiagnosticSetting("activityLogDiag", {
name: "activity-log-advanced-monitoring",
resourceUri: pulumi.interpolate`/subscriptions/${subscriptionId}`,
workspaceId: workspace.id, // Link to Log Analytics workspace
eventHubAuthorizationRuleId: eventHubRule.id, // Optional: for real-time processing
logs: [
{ category: "Administrative", enabled: true },
{ category: "Security", enabled: true },
{ category: "ServiceHealth", enabled: true },
{ category: "Alert", enabled: true },
{ category: "Recommendation", enabled: true },
{ category: "Policy", enabled: true },
{ category: "Autoscale", enabled: true },
{ category: "ResourceHealth", enabled: true },
],
metrics: [
{ category: "AllMetrics", enabled: true },
],
});
Configure Log Analytics workspace with advanced analytics tags:
const workspace = new azure.operationalinsights.Workspace("securityWorkspace", {
// ... other properties
retentionInDays: 730, // Advanced analytics requires longer retention
tags: {
AdvancedAnalytics: "enabled",
KQLQueries: "enabled",
Workbooks: "enabled",
MachineLearning: "enabled",
AnomalyDetection: "enabled",
},
});
Create alert rules for proactive monitoring:
const securityAlert = new azure.monitor.ActivityLogAlert("securityAlert", {
// ... configure alerts for critical security events
tags: {
ActivityLogBased: "true",
SecurityMonitoring: "enabled",
ThreatDetection: "enabled",
},
});
Note: DiagnosticSetting resources do NOT support tags. Apply tags to the destination resources (Log Analytics Workspace, Activity Log Alerts, etc.) to indicate monitoring capabilities. This enables sophisticated log analysis, correlation, and real-time threat detection per CIS Controls v8 IG3 3.14.
activity-log-monitoring
Severity: medium · Enforcement: advisory
Ensure Activity Log monitoring is properly configured.
- 3.8 — Document data flows. Data flow documentation includes service provider data flows and should be based on the enterprise’s data management process. Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
Remediation
Fix: Enable Activity Log Monitoring Integration
Your Activity Log needs to send data to a monitoring solution for centralized audit trail visibility.
Configure Activity Log diagnostic settings:
const activityLogDiagnostic = new azure.monitor.DiagnosticSetting("activityLogDiag", {
name: "activity-log-monitoring",
resourceUri: pulumi.interpolate`/subscriptions/${subscriptionId}`,
workspaceId: workspace.id, // Required: Log Analytics workspace
storageAccountId: storageAccount.id, // Optional: for long-term retention
logs: [
{ category: "Administrative", enabled: true },
{ category: "Security", enabled: true },
{ category: "Alert", enabled: true },
{ category: "Policy", enabled: true },
],
retentionPolicy: {
enabled: true,
days: 365, // Minimum 365 days recommended
},
});
Ensure a Log Analytics workspace exists:
const workspace = new azure.operationalinsights.Workspace("monitoringWorkspace", {
resourceGroupName: resourceGroup.name,
location: "eastus",
sku: { name: "PerGB2018" },
retentionInDays: 365,
});
Note: Activity Log monitoring provides audit trail visibility and enables security analysis per CIS Controls v8 IG2 3.8. Include all critical log categories (Administrative, Security, Alert, Policy) for comprehensive monitoring.
activity-log-multi-region-trail-enabled
Severity: high · Enforcement: advisory
Ensure Activity Log multi-region trail is enabled for resilience and compliance.
- 4.6 — Securely manage enterprise assets and software. Example implementations include managing configuration through version-controlled Infrastructure-as-Code (IaC) and accessing administrative interfaces over secure network protocols, such as Secure Shell (SSH) and Hypertext Transfer Protocol Secure (HTTPS). Do not use insecure management protocols, such as Telnet (Teletype Network) and HTTP, unless operationally essential.
Remediation
Fix: Configure Activity Log Multi-Region Trail
Your Activity Log needs to send data to destinations in multiple regions for disaster recovery and business continuity.
Deploy Log Analytics workspaces in multiple regions:
const workspaceEast = new azure.operationalinsights.Workspace("workspaceEast", {
resourceGroupName: resourceGroup.name,
location: "eastus",
sku: { name: "PerGB2018" },
retentionInDays: 365,
});
const workspaceWest = new azure.operationalinsights.Workspace("workspaceWest", {
resourceGroupName: resourceGroup.name,
location: "westus2", // Geographically redundant region
sku: { name: "PerGB2018" },
retentionInDays: 365,
});
Configure Activity Log to send to primary workspace:
const activityLogDiagnostic = new azure.monitor.DiagnosticSetting("activityLogDiag", {
name: "activity-log-multi-region",
resourceUri: pulumi.interpolate`/subscriptions/${subscriptionId}`,
workspaceId: workspaceEast.id, // Primary region
storageAccountId: storageWest.id, // Secondary region for redundancy
logs: [
{ category: "Administrative", enabled: true },
{ category: "Security", enabled: true },
{ category: "Alert", enabled: true },
{ category: "Policy", enabled: true },
],
});
Recommended region pairs:
- East US + West US 2
- East US 2 + Central US
- North Europe + West Europe
- Southeast Asia + East Asia
Note: Multi-region configuration ensures audit logs remain available during regional outages per CIS Controls v8 IG1 4.6. Deploy Activity Log destinations in at least 2 geographically redundant regions.
activity-log-security-trail-enabled
Severity: high · Enforcement: advisory
Ensure Activity Log security trail is enabled for security monitoring and compliance.
- 4.1 — Establish and maintain a documented secure configuration process for enterprise assets (end-user devices, including portable and mobile, non-computing/IoT devices, and servers) and software (operating systems and applications). Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
Remediation
Fix: Enable Activity Log Security Trail
Your Activity Log needs to capture security events and administrative actions for compliance and security monitoring.
Configure Activity Log diagnostic settings with security categories:
const activityLogDiagnostic = new azure.monitor.DiagnosticSetting("activityLogSecurityTrail", {
name: "activity-log-security-trail",
resourceUri: pulumi.interpolate`/subscriptions/${subscriptionId}`,
workspaceId: workspace.id, // Send to Log Analytics workspace
storageAccountId: storageAccount.id, // Optional: for archival
logs: [
{ category: "Security", enabled: true }, // Required: Security events
{ category: "Administrative", enabled: true }, // Required: Admin actions
{ category: "Policy", enabled: true }, // Policy evaluation events
{ category: "Alert", enabled: true }, // Alert events
],
retentionPolicy: {
enabled: true,
days: 365, // Minimum 365 days for security trail
},
});
Ensure at least one destination is configured:
// Option 1: Log Analytics workspace (recommended)
const workspace = new azure.operationalinsights.Workspace("securityWorkspace", {
resourceGroupName: resourceGroup.name,
location: "eastus",
sku: { name: "PerGB2018" },
retentionInDays: 365,
});
// Option 2: Storage Account for archival
const storageAccount = new azure.storage.StorageAccount("securityLogs", {
resourceGroupName: resourceGroup.name,
location: "eastus",
sku: { name: "Standard_GRS" },
kind: "StorageV2",
});
// Option 3: Event Hub for real-time processing
const eventHub = new azure.eventhub.EventHub("securityEvents", {
// ... configure Event Hub
});
Note: Security trail captures security-relevant events including authentication, authorization, resource changes, and policy violations per CIS Controls v8 IG1 4.1. Both Security and Administrative categories are required for comprehensive audit trails.
activity-log-storage-dataevents
Severity: medium · Enforcement: advisory
Ensure Activity Log captures Storage Account data events for comprehensive log analysis.
- 3.14 — Log sensitive data access, including modification and disposal.
Remediation
Fix: Enable Storage Account Data Events Logging
Your Storage Accounts need data plane diagnostic settings to capture data access events for comprehensive log analysis.
Configure diagnostic settings for Storage Account blob service:
const blobDiagnostic = new azure.monitor.DiagnosticSetting("blobDataEvents", {
name: "blob-data-events",
resourceUri: pulumi.interpolate`${storageAccount.id}/blobServices/default`,
workspaceId: workspace.id, // Send to Log Analytics
logs: [
{ category: "StorageRead", enabled: true },
{ category: "StorageWrite", enabled: true },
{ category: "StorageDelete", enabled: true },
],
metrics: [
{ category: "Transaction", enabled: true },
],
});
Tag your Storage Account to indicate data event logging:
const storageAccount = new azure.storage.StorageAccount("myStorage", {
resourceGroupName: resourceGroup.name,
location: "eastus",
sku: { name: "Standard_LRS" },
kind: "StorageV2",
tags: {
DataEventLogging: "enabled",
StorageDataEvents: "enabled",
AuditDataAccess: "enabled",
},
});
Configure Activity Log to capture subscription-level storage events:
const activityLogDiagnostic = new azure.monitor.DiagnosticSetting("activityLogDiag", {
name: "activity-log-storage-events",
resourceUri: pulumi.interpolate`/subscriptions/${subscriptionId}`,
workspaceId: workspace.id,
logs: [
{ category: "Administrative", enabled: true }, // Captures storage account management operations
{ category: "Security", enabled: true },
],
retentionPolicy: {
enabled: true,
days: 365,
},
});
Optional: Configure Event Grid for real-time storage events:
const eventSubscription = new azure.eventgrid.EventSubscription("storageEvents", {
scope: storageAccount.id,
destination: {
endpointType: "eventhub",
// ... configure destination
},
filter: {
includedEventTypes: [
"Microsoft.Storage.BlobCreated",
"Microsoft.Storage.BlobDeleted",
],
},
});
Note: Data plane logging captures individual blob read/write/delete operations. This provides comprehensive audit trails for data access monitoring per CIS Controls v8 IG3 3.14. Configure for blob, file, table, and queue services as needed.
activity-log-trail-enabled
Severity: high · Enforcement: advisory
Collect audit logs from Activity Log trails.
- 8.2 — Collect audit logs. Ensure that logging, per the enterprise’s audit log management process, has been enabled across enterprise assets.
Remediation
Fix: Enable Activity Log Trail Collection
Your subscription needs Activity Log diagnostic settings configured to collect comprehensive audit trails.
Configure Activity Log diagnostic settings at subscription level:
const activityLogDiagnostic = new azure.monitor.DiagnosticSetting("activityLogTrail", {
name: "activity-log-audit-trail",
resourceUri: pulumi.interpolate`/subscriptions/${subscriptionId}`,
workspaceId: workspace.id, // Required: Log Analytics workspace
storageAccountId: storageAccount.id, // Optional: for long-term archival
eventHubAuthorizationRuleId: eventHubRule.id, // Optional: for real-time streaming
logs: [
{ category: "Administrative", enabled: true },
{ category: "Security", enabled: true },
{ category: "Alert", enabled: true },
{ category: "Policy", enabled: true },
{ category: "Recommendation", enabled: true },
{ category: "ResourceHealth", enabled: true },
{ category: "ServiceHealth", enabled: true },
{ category: "Autoscale", enabled: true },
],
metrics: [
{ category: "AllMetrics", enabled: true },
],
retentionPolicy: {
enabled: true,
days: 365, // Minimum 365 days recommended
},
});
Create required destination resources:
// Log Analytics workspace for analysis
const workspace = new azure.operationalinsights.Workspace("auditWorkspace", {
resourceGroupName: resourceGroup.name,
location: "eastus",
sku: { name: "PerGB2018" },
retentionInDays: 365,
});
// Storage Account for archival (optional)
const storageAccount = new azure.storage.StorageAccount("auditLogs", {
resourceGroupName: resourceGroup.name,
location: "eastus",
sku: { name: "Standard_GRS" },
kind: "StorageV2",
});
Optional: Configure Activity Log alerts for critical events:
const criticalAlert = new azure.monitor.ActivityLogAlert("criticalEvents", {
resourceGroupName: resourceGroup.name,
scopes: [pulumi.interpolate`/subscriptions/${subscriptionId}`],
condition: {
allOf: [
{ field: "category", equals: "Security" },
{ field: "level", equals: "Critical" },
],
},
actions: {
actionGroups: [actionGroup.id],
},
});
Note: Activity Log trails provide comprehensive audit logs for all subscription-level activities per CIS Controls v8 IG1 8.2. Enable all relevant categories and configure at least one destination (Log Analytics, Storage Account, or Event Hub).
aks-cluster-public-access
Severity: high · Enforcement: advisory
Require AKS clusters to be private clusters
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Configure AKS as a Private Cluster
const aksCluster = new azurenative.containerservice.ManagedCluster("my-aks-cluster", {
apiServerAccessProfile: {
enablePrivateCluster: true, // Enable private cluster mode
privateDNSZone: "system", // Use system-managed private DNS zone
enablePrivateClusterPublicFQDN: false, // Disable public FQDN
},
// ... other config
});
api-management-diagnostic-settings-exist
Severity: medium · Enforcement: advisory
Ensure API Management has diagnostic settings configured (existence check).
- 3.14 — Log sensitive data access, including modification and disposal.
Remediation
Fix: Enable Execution Logging for API Management
Your API Management service needs advanced execution logging. Configure detailed logging with Log Analytics:
const diagnostics = new azure.insights.DiagnosticSetting("apimExecutionLogs", {
name: "apim-execution-diagnostics",
resourceUri: apimService.id,
workspaceId: logAnalyticsWorkspace.id,
logs: [
{
category: "GatewayLogs", // Request/response execution logs
enabled: true,
retentionPolicy: { enabled: true, days: 365 },
},
{
category: "WebSocketConnectionLogs", // WebSocket execution
enabled: true,
retentionPolicy: { enabled: true, days: 365 },
},
],
metrics: [{
category: "AllMetrics", // Performance metrics
enabled: true,
}],
});
Note: This policy checks that diagnostic settings exist. Use the ‘diagnostic-setting-configuration’ resource policy to validate the configuration details of diagnostic settings.
app-service-managed-updates
Severity: medium · Enforcement: advisory
Ensure App Service applications have managed updates enabled to maintain current software versions and security patches.
- 2.2 — Ensure that only currently supported software is designated as authorized in the software inventory for enterprise assets. If software is unsupported, yet necessary for the fulfillment of the enterprise’s mission, document an exception detailing mitigating controls and residual risk acceptance. For any unsupported software without an exception documentation, designate as unauthorized. Review the software list to verify software support at least monthly, or more frequently.
Remediation
Fix: Enable Managed Updates for App Service
Your App Service needs to be configured for managed updates to ensure security patches are applied automatically. Configure the required settings and tags:
Step 1: Enable Always On and Update Settings
const webApp = new azure.web.WebApp("myWebApp", {
resourceGroupName: resourceGroup.name,
serverFarmId: plan.id,
siteConfig: {
alwaysOn: true, // Keep app loaded for consistent updates
// Other site config settings
},
tags: {
ManagedUpdates: "enabled", // Document that managed updates are enabled
},
});
Step 2: Configure with Auto-Swap for Staging (Optional but Recommended)
const webApp = new azure.web.WebApp("myWebApp", {
resourceGroupName: resourceGroup.name,
serverFarmId: plan.id,
siteConfig: {
alwaysOn: true,
autoSwapSlotName: "production", // Auto-swap from staging after updates
},
tags: {
ManagedUpdates: "enabled",
UpdateStrategy: "staging-swap",
},
});
// Create a staging slot for testing updates
const stagingSlot = new azure.web.WebAppSlot("staging", {
name: webApp.name,
slot: "staging",
resourceGroupName: resourceGroup.name,
siteConfig: {
alwaysOn: true,
},
});
Accepted Tag Values
The ManagedUpdates tag must be set to one of: enabled, true, yes, or 1.
This ensures your App Service automatically receives security patches and platform updates while maintaining availability.
application-gateway-https-redirection
Severity: high · Enforcement: advisory
Ensure Application Gateway enforces HTTPS redirection to protect data in transit.
- 3.1 — Establish and maintain a documented data management process. In the process, address data sensitivity, data owner, handling of data, data retention limits, and disposal requirements, based on sensitivity and retention standards for the enterprise. Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
Remediation
Fix: Configure HTTP to HTTPS Redirection
Your Application Gateway has HTTP listeners but doesn’t redirect traffic to HTTPS. All HTTP traffic should be automatically redirected to HTTPS for security.
Configure HTTP to HTTPS Redirection
const appGateway = new azure.network.ApplicationGateway("myGateway", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
// ... other configuration ...
// Define HTTP listener (port 80)
httpListeners: [
{
name: "http-listener",
protocol: "Http",
frontendIPConfiguration: { id: frontendIP.id },
frontendPort: { id: frontendPort80.id },
},
{
name: "https-listener",
protocol: "Https",
frontendIPConfiguration: { id: frontendIP.id },
frontendPort: { id: frontendPort443.id },
sslCertificate: { id: sslCert.id },
},
],
// Create redirect configuration
redirectConfigurations: [{
name: "http-to-https-redirect",
redirectType: "Permanent", // HTTP 301
targetListener: {
id: pulumi.interpolate`${appGateway.id}/httpListeners/https-listener`,
},
includePath: true,
includeQueryString: true,
}],
// Apply redirect to HTTP listener
requestRoutingRules: [
{
name: "http-redirect-rule",
ruleType: "Basic",
priority: 100,
httpListener: {
id: pulumi.interpolate`${appGateway.id}/httpListeners/http-listener`,
},
redirectConfiguration: {
id: pulumi.interpolate`${appGateway.id}/redirectConfigurations/http-to-https-redirect`,
},
},
{
name: "https-rule",
ruleType: "Basic",
priority: 110,
httpListener: {
id: pulumi.interpolate`${appGateway.id}/httpListeners/https-listener`,
},
backendAddressPool: { id: backendPool.id },
backendHttpSettings: { id: backendSettings.id },
},
],
});
Note: Using “Permanent” (HTTP 301) redirect type tells browsers to always use HTTPS for future requests, improving security and performance.
application-gateway-tls
Severity: high · Enforcement: advisory
Require Application Gateway to have secure TLS configuration
- 3.1 — Establish and maintain a documented data management process. In the process, address data sensitivity, data owner, handling of data, data retention limits, and disposal requirements, based on sensitivity and retention standards for the enterprise. Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
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
});
application-gateway-waf
Severity: high · Enforcement: advisory
Require Application Gateway to have Web Application Firewall enabled
- 13.1 — Centralize security event alerting across enterprise assets for log correlation and analysis. Best practice implementation requires the use of a SIEM, which includes vendor-defined event correlation alerts. A log analytics platform configured with security-relevant correlation alerts also satisfies this Safeguard.
Remediation
Fix: Enable Web Application Firewall
const appGateway = new azurenative.network.ApplicationGateway("my-app-gateway", {
sku: {
name: "WAF_v2",
tier: "WAF_v2", // Use WAF-enabled SKU
},
webApplicationFirewallConfiguration: {
enabled: true, // Enable WAF protection
firewallMode: "Prevention", // Block threats
ruleSetType: "OWASP",
ruleSetVersion: "3.2",
},
// ... other config
});
azure-database-backup-enabled
Severity: medium · Enforcement: advisory
Perform automated backups for Azure Database instances.
- 11.2 — Perform automated backups of in-scope enterprise assets. Run backups weekly, or more frequently, based on the sensitivity of the data.
Remediation
Fix: Configure SQL Database Backup Retention
Your SQL database doesn’t have adequate backup retention configured. Set the backup retention period:
const database = new azure.sql.Database("myDatabase", {
// ... other properties
backupRetentionDays: 7, // Minimum 7 days, adjust based on requirements
});
For additional protection, enable geo-redundant backups:
const database = new azure.sql.Database("myDatabase", {
// ... other properties
backupRetentionDays: 7,
geoRedundantBackup: "Enabled", // Replicate backups to paired region
});
Azure SQL automatically performs backups; this setting controls how long they’re retained. Consider longer retention periods (30+ days) for production databases.
azure-database-deletion-protection-enabled
Severity: high · Enforcement: advisory
Ensure Azure Database deletion protection is enabled.
- 12.2 — Establish and maintain a secure network architecture. A secure network architecture must address segmentation, least privilege, and availability, at a minimum. Example implementations may include documentation, policy, and design components.
Remediation
Fix: Protect SQL Server from Accidental Deletion
Azure SQL Server doesn’t have a built-in deletion protection flag. Instead, protect your server using resource locks, which prevent accidental deletion:
Using Pulumi to apply a CanNotDelete lock:
import * as azure from "@pulumi/azure-native";
const sqlServer = new azure.sql.Server("mySqlServer", {
resourceGroupName: resourceGroup.name,
// ... your existing server configuration
});
// Apply a CanNotDelete lock to prevent accidental deletion
const lock = new azure.authorization.ManagementLockAtResourceLevel("sqlServerLock", {
level: "CanNotDelete",
lockName: "sql-server-deletion-lock",
resourceGroupName: resourceGroup.name,
resourceName: sqlServer.name,
resourceProviderNamespace: "Microsoft.Sql",
resourceType: "servers",
parentResourcePath: "",
notes: "Prevents accidental deletion of production SQL Server",
});
Additionally, configure RBAC permissions to limit who can delete the server, and ensure databases have adequate backup retention (35+ days recommended) for recovery options.
azure-database-multi-region-support
Severity: high · Enforcement: advisory
Ensure Azure Database multi-region support is enabled.
- 12.2 — Establish and maintain a secure network architecture. A secure network architecture must address segmentation, least privilege, and availability, at a minimum. Example implementations may include documentation, policy, and design components.
Remediation
Fix: Configure Multi-Region Support for SQL Server
Your SQL Server lacks multi-region support for disaster recovery. While SQL Servers themselves don’t have multi-region settings, configure your databases with geo-replication:
Option 1: Configure geo-replication for a database:
import * as azure from "@pulumi/azure-native";
// Create a secondary server in a different region
const secondaryServer = new azure.sql.Server("secondarySqlServer", {
resourceGroupName: resourceGroup.name,
location: "westus2", // Different from primary region
// ... other server configuration
});
// Create a geo-replicated secondary database
const replicaDatabase = new azure.sql.Database("replicaDatabase", {
serverName: secondaryServer.name,
resourceGroupName: resourceGroup.name,
location: secondaryServer.location,
createMode: "Secondary",
sourceDatabaseId: primaryDatabase.id,
});
Option 2: Use failover groups for automatic failover:
const failoverGroup = new azure.sql.FailoverGroup("sqlFailoverGroup", {
serverName: primaryServer.name,
resourceGroupName: resourceGroup.name,
readWriteEndpoint: {
failoverPolicy: "Automatic",
failoverWithDataLossGracePeriodMinutes: 60,
},
partnerServers: [{ id: secondaryServer.id }],
databases: [primaryDatabase.id],
});
Failover groups provide a listener endpoint that automatically redirects connections during failover, simplifying application configuration.
backup-instance-configuration
Severity: medium · Enforcement: advisory
Ensure backup instances are properly configured with backup policies and cross-region replication.
- 11.2 — Perform automated backups of in-scope enterprise assets. Run backups weekly, or more frequently, based on the sensitivity of the data.
Remediation
Fix: Configure Backup Instance Properly
Ensure your backup instance has proper configuration:
const backupInstance = new azure.dataprotection.BackupInstance("disk-backup", {
resourceGroupName: resourceGroup.name,
vaultName: backupVault.name,
properties: {
dataSourceInfo: {
resourceId: disk.id,
resourceType: "Microsoft.Compute/disks",
},
policyInfo: {
policyId: backupPolicy.id,
},
},
});
Note: Ensure the backup vault has appropriate storage settings and the backup policy defines proper retention.
cdn-distribution-logging-enabled
Severity: medium · Enforcement: advisory
Collect audit logs from CDN distributions for security monitoring.
- 8.2 — Collect audit logs. Ensure that logging, per the enterprise’s audit log management process, has been enabled across enterprise assets.
Remediation
Fix: Enable CDN Distribution Logging
Your CDN needs audit logging configured. Enable diagnostic settings to collect access logs:
const cdnProfile = new azure.cdn.Profile("myCdnProfile", {
// ... other properties
tags: {
"LoggingEnabled": "enabled",
"CDNLogging": "true",
},
});
// Configure diagnostic settings for the CDN profile
const cdnDiagnostics = new azure.insights.DiagnosticSetting("cdnDiagnostics", {
name: "cdn-logging",
resourceUri: cdnProfile.id,
workspaceId: logAnalyticsWorkspace.id, // Send logs to Log Analytics
logs: [
{
category: "AzureCdnAccessLog", // Access logs for standard CDN
enabled: true,
retentionPolicy: { enabled: true, days: 90 },
},
],
});
For Azure Front Door (modern CDN):
logs: [
{
category: "FrontDoorAccessLog", // Access logs
enabled: true,
},
{
category: "FrontDoorHealthProbeLog", // Health probe logs
enabled: true,
},
],
container-instance-privileged-mode
Severity: high · Enforcement: advisory
Ensure Container Instances user for privileged mode check.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Disable Privileged Mode for Container Instances
Your container is configured to run in privileged mode. Remove privileged access and use proper security context:
const containerGroup = new azure.containerinstance.ContainerGroup("myContainerGroup", {
containers: [{
name: "myContainer",
image: "nginx:latest",
resources: {
requests: { cpu: 1, memoryInGB: 1.5 },
limits: { cpu: 2, memoryInGB: 3 }, // Add resource limits
},
securityContext: {
privileged: false, // Never set to true
runAsUser: 1000, // Non-root user
runAsGroup: 1000, // Non-root group
readOnlyRootFilesystem: true, // Make root filesystem read-only
capabilities: {
drop: ["ALL"], // Drop all capabilities
// Only add specific needed capabilities
},
},
}],
osType: "Linux",
ipAddress: { type: "Private" }, // Use private networking
});
Critical: Never set privileged: true or add dangerous capabilities like SYS_ADMIN, NET_ADMIN, or SYS_PTRACE.
cosmos-db-autoscaling-enabled
Severity: medium · Enforcement: advisory
Ensure Cosmos DB autoscaling is enabled.
- 12.2 — Establish and maintain a secure network architecture. A secure network architecture must address segmentation, least privilege, and availability, at a minimum. Example implementations may include documentation, policy, and design components.
Remediation
Fix: Enable Autoscaling for Cosmos DB
Your Cosmos DB account needs autoscaling configured for optimal performance and cost management:
const cosmosAccount = new azure.documentdb.DatabaseAccount("myCosmosDb", {
// ... other properties
});
// For individual containers, enable autoscale throughput
const container = new azure.documentdb.SqlResourceSqlContainer("myContainer", {
// ... other properties
options: {
autoscaleSettings: {
maxThroughput: 4000, // Maximum RU/s - scales from 10% (400) to this value
},
},
});
Autoscaling helps manage costs by automatically adjusting throughput based on actual usage.
cosmos-db-customer-key-encryption
Severity: high · Enforcement: advisory
Require Cosmos DB to use customer-managed keys
- 3.11 — Encrypt sensitive data at rest on servers, applications, and databases. Storage-layer encryption, also known as server-side encryption, meets the minimum requirement of this Safeguard. Additional encryption methods may include application-layer encryption, also known as client-side encryption, where access to the data storage device(s) does not permit access to the plain-text data.
Remediation
Fix: Enable Customer-Managed Keys for Cosmos DB
const cosmosAccount = new azurenative.cosmosdb.DatabaseAccount("my-cosmos-account", {
databaseAccountOfferType: "Standard",
keyVaultKeyUri: key.keyUriWithVersion, // Reference Key Vault key for encryption
identity: {
type: "UserAssigned", // Use managed identity
userAssignedIdentities: { [identity.id]: {} },
},
defaultIdentity: pulumi.interpolate`UserAssignedIdentity=${identity.id}`,
// ... other config
});
cosmos-db-in-backup-plan
Severity: medium · Enforcement: advisory
Require Cosmos DB account to have backup policies configured
- 11.2 — Perform automated backups of in-scope enterprise assets. Run backups weekly, or more frequently, based on the sensitivity of the data.
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
});
cosmos-db-pitr-enabled
Severity: medium · Enforcement: advisory
Ensure Cosmos DB point-in-time recovery is enabled for data protection.
- 11.2 — Perform automated backups of in-scope enterprise assets. Run backups weekly, or more frequently, based on the sensitivity of the data.
Remediation
Fix: Enable Point-in-Time Recovery for Cosmos DB
Your Cosmos DB account needs continuous backup for point-in-time recovery:
const cosmosAccount = new azure.documentdb.DatabaseAccount("myCosmosDb", {
// ... other properties
backupPolicy: {
type: "Continuous", // Enable continuous backup for PITR
continuousModeProperties: {
tier: "Continuous7Days", // or "Continuous30Days"
},
},
});
Note: Continuous backup allows restore to any point within the retention period (7 or 30 days).
defender-for-cloud-enabled
Severity: high · Enforcement: advisory
Ensure Microsoft Defender for Cloud is enabled for asset discovery and threat detection.
- 1.2 — Ensure that a process exists to address unauthorized assets on a weekly basis. The enterprise may choose to remove the asset from the network, deny the asset from connecting remotely to the network, or quarantine the asset.
Remediation
Fix: Enable Microsoft Defender for Cloud
Your resources need Microsoft Defender for Cloud enabled for threat detection:
// Enable Advanced Threat Protection for a resource
const atp = new azure.security.AdvancedThreatProtection("myResourceATP", {
resourceId: storageAccount.id, // or other resource
isEnabled: true, // Enable threat detection
});
// Tag your resource to indicate Defender is enabled
tags: {
"DefenderForCloud": "enabled",
"AdvancedThreatProtection": "true",
"SecurityMonitoring": "configured",
}
Enable Defender for specific resource types: Storage, SQL, Key Vault, Kubernetes, App Service, etc.
diagnostic-setting-configuration
Severity: medium · Enforcement: advisory
Ensure diagnostic settings are properly configured with required destinations, log categories, and retention policies.
- 3.14 — Log sensitive data access, including modification and disposal.
Remediation
Fix: Configure Diagnostic Settings Properly
Ensure your diagnostic settings include required destinations, log categories, and retention:
const diagnostics = new azure.monitor.DiagnosticSetting("myDiagnostics", {
resourceUri: resource.id,
workspaceId: logAnalyticsWorkspace.id, // Log Analytics
storageAccountId: storageAccount.id, // Storage for retention
logs: [
{
category: "AuditLogs",
enabled: true,
retentionPolicy: { enabled: true, days: 365 },
},
],
metrics: [
{
category: "AllMetrics",
enabled: true,
},
],
});
Note: Configure appropriate log categories based on the resource type being monitored.
dms-instance-public-access
Severity: high · Enforcement: advisory
Prevent public accessibility of Database Migration Service instances.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Disable Public Access for Database Migration Service
Your Database Migration Service allows public network access. Disable it and use VNet integration:
const dmsService = new azure.datamigration.Service("myDMS", {
// ... other properties
publicNetworkAccess: "Disabled", // Block public access
virtualSubnetId: privateSubnet.id, // Deploy in private subnet
tags: {
"PrivateEndpoint": "configured",
"NetworkSecurityGroup": "associated",
},
});
This ensures the DMS instance is only accessible from within your virtual network.
function-public-access
Severity: high · Enforcement: advisory
Ensure Azure Functions restrict public access.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Restrict Public Access to Function App
Your Function App allows unrestricted public access. Configure IP restrictions to limit access to specific IP ranges:
Option 1: Configure IP Security Restrictions
const functionApp = new azure.web.WebApp("myFunction", {
kind: "functionapp",
resourceGroupName: resourceGroup.name,
serverFarmId: plan.id,
siteConfig: {
ipSecurityRestrictions: [
{
ipAddress: "203.0.113.0/24", // Your allowed IP range
action: "Allow",
priority: 100,
name: "AllowOfficeNetwork",
},
{
ipAddress: "198.51.100.10/32", // Specific VPN IP
action: "Allow",
priority: 110,
name: "AllowVPN",
},
],
scmIpSecurityRestrictions: [ // Also restrict deployment access
{
ipAddress: "203.0.113.0/24",
action: "Allow",
priority: 100,
name: "AllowOfficeDeployments",
},
],
},
httpsOnly: true, // Enforce HTTPS
});
Option 2: Disable Public Network Access (use with VNet integration)
const functionApp = new azure.web.WebApp("myFunction", {
kind: "functionapp",
resourceGroupName: resourceGroup.name,
serverFarmId: plan.id,
publicNetworkAccess: "Disabled", // Block all public access
virtualNetworkSubnetId: subnet.id, // Require VNet integration
httpsOnly: true,
});
Do not use: ipAddress: "0.0.0.0/0" or leaving ipSecurityRestrictions empty, as these allow unrestricted public access.
function-scaling-check
Severity: low · Enforcement: advisory
Ensure Functions have proper scaling configuration for monitoring and performance.
- 3.8 — Document data flows. Data flow documentation includes service provider data flows and should be based on the enterprise’s data management process. Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
Remediation
Fix: Configure Function App Scaling Monitoring
Your Function App lacks proper scaling configuration and monitoring. Configure Application Insights and scaling settings for performance monitoring:
Step 1: Create Application Insights
const appInsights = new azure.applicationinsights.Component("myAppInsights", {
resourceGroupName: resourceGroup.name,
applicationType: "web",
kind: "web",
});
Step 2: Configure Function App with Monitoring
const functionApp = new azure.web.WebApp("myFunction", {
kind: "functionapp",
resourceGroupName: resourceGroup.name,
serverFarmId: plan.id,
siteConfig: {
appSettings: [
{
name: "APPINSIGHTS_INSTRUMENTATIONKEY",
value: appInsights.instrumentationKey,
},
{
name: "APPLICATIONINSIGHTS_CONNECTION_STRING",
value: appInsights.connectionString,
},
],
},
tags: {
ScalingMonitoring: "enabled",
PerformanceMonitoring: "enabled",
ApplicationInsights: "enabled",
},
});
Step 3: Configure Auto-Scale Rules (for Premium/Dedicated Plans)
const autoScaleSetting = new azure.monitor.AutoscaleSetting("functionAutoScale", {
resourceGroupName: resourceGroup.name,
targetResourceUri: plan.id,
profiles: [{
name: "Auto scale by CPU",
capacity: {
minimum: "1",
maximum: "10",
default: "1",
},
rules: [{
scaleAction: {
direction: "Increase",
type: "ChangeCount",
value: "1",
cooldown: "PT5M",
},
metricTrigger: {
metricName: "CpuPercentage",
metricResourceUri: plan.id,
operator: "GreaterThan",
statistic: "Average",
threshold: 70,
timeAggregation: "Average",
timeGrain: "PT1M",
timeWindow: "PT5M",
},
}],
}],
});
This enables comprehensive performance monitoring and intelligent scaling based on actual usage metrics.
function-vnet-integration
Severity: medium · Enforcement: advisory
Ensure Azure Functions are integrated with Virtual Networks.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Enable VNet Integration for Function App
Your Function App is not integrated with a Virtual Network. Configure VNet integration to isolate your function from the public internet:
Step 1: Create a Dedicated Subnet for Function App
const functionSubnet = new azure.network.Subnet("functionSubnet", {
virtualNetworkName: vnet.name,
resourceGroupName: resourceGroup.name,
addressPrefix: "10.0.2.0/24",
delegations: [{
name: "delegation",
serviceName: "Microsoft.Web/serverFarms", // Required for Function Apps
}],
});
Step 2: Configure Function App with VNet Integration
const functionApp = new azure.web.WebApp("myFunction", {
kind: "functionapp",
resourceGroupName: resourceGroup.name,
serverFarmId: plan.id, // Must be Premium or Dedicated plan for VNet
virtualNetworkSubnetId: functionSubnet.id, // Connect to VNet subnet
siteConfig: {
vnetRouteAllEnabled: true, // Route ALL outbound traffic through VNet
},
});
Complete Example with Private Subnet
const functionApp = new azure.web.WebApp("myFunction", {
kind: "functionapp",
resourceGroupName: resourceGroup.name,
serverFarmId: premiumPlan.id,
virtualNetworkSubnetId: functionSubnet.id,
siteConfig: {
vnetRouteAllEnabled: true,
ipSecurityRestrictions: [], // No public inbound access
},
publicNetworkAccess: "Disabled", // Completely disable public access
});
Note: VNet integration requires an App Service Plan (Premium V2 or higher) or Elastic Premium plan. Consumption plans do not support VNet integration.
hdinsight-cluster-public-access
Severity: high · Enforcement: advisory
Ensure HDInsight clusters restrict public access to master nodes.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Disable Public Access for HDInsight Cluster
Your HDInsight cluster allows public access. Configure private link for secure connectivity:
const hdinsightCluster = new azure.hdinsight.Cluster("myHDInsight", {
// ... other properties
computeProfile: {
roles: [{
name: "headnode",
targetInstanceCount: 2,
hardwareProfile: { vmSize: "Standard_D3_v2" },
virtualNetworkProfile: {
id: vnet.id,
subnet: privateSubnet.id,
},
}],
},
networkProperties: {
resourceProviderConnection: "Inbound", // Disable outbound public access
privateLink: "Enabled", // Enable private link
},
tags: {
"PublicAccess": "disabled",
"PrivateLink": "enabled",
},
});
hdinsight-kerberos-enabled
Severity: high · Enforcement: advisory
Ensure HDInsight Kerberos is enabled.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Enable Kerberos Authentication for HDInsight
Your HDInsight cluster needs Kerberos authentication for secure access:
const hdinsightCluster = new azure.hdinsight.Cluster("myHDInsight", {
// ... other properties
securityProfile: {
directoryType: "ActiveDirectory", // Use Azure AD Domain Services
domain: "yourdomain.com",
domainUsername: "admin@yourdomain.com",
domainUserPassword: domainPassword,
ldapsUrls: ["ldaps://yourdomain.com:636"],
clusterUsersGroupDNs: ["CN=HadoopUsers,OU=Groups,DC=yourdomain,DC=com"],
},
tags: {
"KerberosEnabled": "true",
"EnterpriseSecurityPackage": "enabled",
},
});
Requirements: Azure AD Domain Services must be configured before enabling Kerberos.
key-vault-certificate-expiration-check
Severity: high · Enforcement: advisory
Ensure Key Vault certificate expiration check.
- 12.2 — Design and maintain a secure network architecture. A secure network architecture must address segmentation, least privilege, and availability, at a minimum. Example implementations may include documentation, policy, and design components.
Remediation
Fix: Monitor Certificate Expiration in Key Vault
Your Key Vault needs certificate expiration monitoring configured:
const keyVault = new azure.keyvault.Vault("myVault", {
// ... other properties
properties: {
enableSoftDelete: true,
softDeleteRetentionInDays: 90,
enablePurgeProtection: true,
},
tags: {
"CertificateMonitoring": "enabled",
"AutoRenewal": "configured",
},
});
// Set up alerts for certificate expiration
const actionGroup = new azure.insights.ActionGroup("certExpiryAlert", {
// ... notification configuration
});
const metricAlert = new azure.insights.MetricAlert("certExpiryMetric", {
// ... alert rules for certificates expiring within 30 days
criteria: {
allOf: [{
metricName: "ServiceApiLatency", // Use appropriate certificate metric
timeAggregation: "Average",
operator: "GreaterThan",
threshold: 30, // Days until expiration
}],
},
});
Enable auto-renewal for certificates where supported by the Certificate Authority.
key-vault-key-not-scheduled-for-deletion
Severity: high · Enforcement: advisory
Ensure Key Vault keys are not scheduled for deletion and have proper lifecycle management.
- 3.11 — Encrypt sensitive data at rest on servers, applications, and databases. Storage-layer encryption, also known as server-side encryption, meets the minimum requirement of this Safeguard. Additional encryption methods may include application-layer encryption, also known as client-side encryption, where access to the data storage device(s) does not permit access to the plain-text data.
Remediation
Fix: Protect Keys from Deletion in Key Vault
Your Key Vault keys lack deletion protection. Enable soft delete and purge protection:
const keyVault = new azure.keyvault.Vault("myVault", {
// ... other properties
properties: {
enableSoftDelete: true, // Enable soft delete (mandatory)
softDeleteRetentionInDays: 90, // Retain deleted items for 90 days
enablePurgeProtection: true, // Prevent permanent deletion during retention
sku: { family: "A", name: "standard" },
},
});
Important: Once enabled, purge protection cannot be disabled. Ensure this is intended for production vaults.
key-vault-key-rotation-enabled
Severity: medium · Enforcement: advisory
Require Key Vault keys to have rotation policies configured
- 4.6 — Securely manage enterprise assets and software. Example implementations include managing configuration through version-controlled Infrastructure-as-Code (IaC) and accessing administrative interfaces over secure network protocols, such as Secure Shell (SSH) and Hypertext Transfer Protocol Secure (HTTPS). Do not use insecure management protocols, such as Telnet (Teletype Network) and HTTP, unless operationally essential.
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-using-cmk
Severity: high · Enforcement: advisory
Ensure Key Vault uses customer-managed keys for enhanced encryption security.
- 3.11 — Encrypt sensitive data at rest on servers, applications, and databases. Storage-layer encryption, also known as server-side encryption, meets the minimum requirement of this Safeguard. Additional encryption methods may include application-layer encryption, also known as client-side encryption, where access to the data storage device(s) does not permit access to the plain-text data.
Remediation
Fix: Use Customer-Managed Keys with Hardware Security Modules
Your resources should use customer-managed keys from Key Vault with HSM protection:
const keyVault = new azure.keyvault.Vault("myVault", {
// ... other properties
properties: {
sku: {
family: "A",
name: "premium", // Premium SKU required for HSM
},
enableSoftDelete: true,
enablePurgeProtection: true,
},
tags: {
"HSMProtection": "enabled",
"CustomerManagedKeys": "configured",
},
});
// Create an HSM-protected key
const hsmKey = new azure.keyvault.Key("hsmKey", {
keyName: "hsm-encryption-key",
vaultName: keyVault.name,
resourceGroupName: resourceGroup.name,
properties: {
kty: "RSA-HSM", // HSM-protected key type
keySize: 2048,
keyOps: ["encrypt", "decrypt", "sign", "verify", "wrapKey", "unwrapKey"],
},
});
Note: Premium SKU required for HSM support. HSM keys cannot be exported and provide FIPS 140-2 Level 2 validation.
load-balancer-cross-zone-enabled
Severity: medium · Enforcement: advisory
Ensure Load Balancer cross-zone load balancing is enabled.
- 12.2 — Design and maintain a secure network architecture. A secure network architecture must address segmentation, least privilege, and availability, at a minimum. Example implementations may include documentation, policy, and design components.
Remediation
Fix: Enable Cross-Zone Load Balancing
Your Load Balancer needs to be configured for cross-zone load balancing to ensure high availability across availability zones.
Configure Zone-Redundant Load Balancer
// Create zone-redundant public IP
const publicIP = new azure.network.PublicIPAddress("myPublicIP", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
publicIPAllocationMethod: "Static",
sku: {
name: "Standard", // Standard SKU required for zone redundancy
tier: "Regional",
},
zones: ["1", "2", "3"], // Deploy across all availability zones
});
// Create zone-redundant load balancer
const loadBalancer = new azure.network.LoadBalancer("myLB", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: {
name: "Standard", // Standard SKU supports cross-zone balancing
tier: "Regional",
},
frontendIPConfigurations: [{
name: "frontend",
publicIPAddress: { id: publicIP.id },
zones: ["1", "2", "3"], // Zone-redundant configuration
}],
// ... backend pools, rules, probes ...
});
Deploy Backend Resources Across Zones
Ensure your backend resources (VMs, VMSS) are distributed across zones:
const vmss = new azure.compute.VirtualMachineScaleSet("backend-vmss", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: {
name: "Standard_D2s_v3",
capacity: 3,
},
zones: ["1", "2", "3"], // Distribute instances across zones
// ... other VMSS configuration ...
});
Optimize Health Probes for Fast Failover
const loadBalancer = new azure.network.LoadBalancer("myLB", {
// ... other configuration ...
probes: [{
name: "health-probe",
protocol: "Http", // Note: Azure Load Balancer probes don't support HTTPS protocol
port: 443, // The probe connects to HTTPS port, but protocol detection is TCP/HTTP only
requestPath: "/health",
intervalInSeconds: 5, // Fast probe interval
numberOfProbes: 2, // Quick failover (5s * 2 = 10s total)
}],
});
Note: Standard SKU Load Balancers are zone-redundant by default when the frontend IP is zone-redundant. Basic SKU does not support availability zones - upgrade to Standard.
load-balancer-deletion-protection
Severity: high · Enforcement: advisory
Ensure critical Load Balancers have resource locks to prevent accidental deletion.
- 12.2 — Design and maintain a secure network architecture. A secure network architecture must address segmentation, least privilege, and availability, at a minimum.
Remediation
Fix: Apply Resource Lock to Load Balancer
Your Load Balancer needs deletion protection. Apply a resource lock to prevent accidental deletion:
const lock = new azure.authorization.ManagementLockAtResourceLevel("lb-delete-lock", {
level: "CanNotDelete",
lockName: "lb-delete-lock",
notes: "Prevents accidental deletion of production load balancer",
resourceGroupName: resourceGroup.name,
resourceName: loadBalancer.name,
resourceProviderNamespace: "Microsoft.Network",
resourceType: "loadBalancers",
});
Lock Levels
- CanNotDelete: Users can read and modify the resource, but cannot delete it
- ReadOnly: Users can read the resource, but cannot modify or delete it
load-balancer-https-listeners
Severity: high · Enforcement: advisory
Ensure Load Balancer uses TLS/HTTPS listeners only for secure communication.
- 3.1 — Establish and maintain a documented data management process. In the process, address data sensitivity, data owner, handling of data, data retention limits, and disposal requirements, based on sensitivity and retention standards for the enterprise. Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
Remediation
Fix: Use HTTPS Listeners Instead of HTTP
Your Load Balancer has HTTP listeners without HTTPS alternatives. All traffic should be encrypted in transit using HTTPS.
Replace HTTP Listeners with HTTPS
const loadBalancer = new azure.network.LoadBalancer("myLB", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: { name: "Standard" },
frontendIPConfigurations: [{
name: "frontend",
publicIPAddress: { id: publicIP.id },
}],
loadBalancingRules: [
// Remove HTTP rule (port 80)
// {
// name: "http-rule",
// frontendPort: 80,
// backendPort: 80,
// protocol: "Tcp",
// },
// Add HTTPS rule (port 443)
{
name: "https-rule",
frontendPort: 443,
backendPort: 443,
protocol: "Tcp",
frontendIPConfiguration: {
id: pulumi.interpolate`${loadBalancer.id}/frontendIPConfigurations/frontend`,
},
backendAddressPool: {
id: pulumi.interpolate`${loadBalancer.id}/backendAddressPools/backend`,
},
probe: {
id: pulumi.interpolate`${loadBalancer.id}/probes/https-probe`,
},
},
],
probes: [{
name: "https-probe",
protocol: "Http", // Note: Azure Load Balancer probes don't support HTTPS protocol
port: 443, // The probe connects to HTTPS port, but protocol detection is TCP/HTTP only
requestPath: "/health",
intervalInSeconds: 15,
numberOfProbes: 2,
}],
});
Note: For Azure Application Gateway (Layer 7), you can also implement HTTP to HTTPS redirection. For Azure Load Balancer (Layer 4), you should only expose HTTPS ports.
load-balancer-logging
Severity: medium · Enforcement: advisory
Ensure Load Balancer has logging enabled for comprehensive security and performance analysis.
- 3.14 — Log sensitive data access, including modification and disposal.
Remediation
Fix: Enable Diagnostic Logging for Load Balancer
Your Load Balancer doesn’t have diagnostic settings configured. Enable logging to monitor load balancer health, performance, and security events.
Configure Diagnostic Settings
const logAnalyticsWorkspace = new azure.operationalinsights.Workspace("logs", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: { name: "PerGB2018" },
});
const loadBalancer = new azure.network.LoadBalancer("myLB", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
// ... other load balancer configuration
});
const diagnosticSetting = new azure.monitor.DiagnosticSetting("lb-diagnostics", {
resourceUri: loadBalancer.id,
workspaceId: logAnalyticsWorkspace.id,
logs: [
{
category: "LoadBalancerAlertEvent",
enabled: true,
retentionPolicy: {
enabled: true,
days: 365,
},
},
{
category: "LoadBalancerProbeHealthStatus",
enabled: true,
retentionPolicy: {
enabled: true,
days: 365,
},
},
],
metrics: [{
category: "AllMetrics",
enabled: true,
retentionPolicy: {
enabled: true,
days: 365,
},
}],
});
Send Logs to Multiple Destinations
For comprehensive monitoring, send logs to multiple destinations:
const diagnosticSetting = new azure.monitor.DiagnosticSetting("lb-diagnostics", {
resourceUri: loadBalancer.id,
workspaceId: logAnalyticsWorkspace.id,
storageAccountId: storageAccount.id, // Long-term storage
eventHubAuthorizationRuleId: eventHubRule.id, // Real-time processing
logs: [
{ category: "LoadBalancerAlertEvent", enabled: true },
{ category: "LoadBalancerProbeHealthStatus", enabled: true },
],
metrics: [{ category: "AllMetrics", enabled: true }],
});
Note: Standard Load Balancers support diagnostic logging. Basic SKU load balancers have limited logging capabilities - consider upgrading to Standard.
log-analytics-encrypted
Severity: high · Enforcement: advisory
Ensure Log Analytics workspaces use encryption for data protection at rest.
- 3.11 — Encrypt sensitive data at rest on servers, applications, and databases. Storage-layer encryption, also known as server-side encryption, meets the minimum requirement of this Safeguard. Additional encryption methods may include application-layer encryption, also known as client-side encryption, where access to the data storage device(s) does not permit access to the plain-text data.
Remediation
Fix: Enable Customer-Managed Key Encryption for Log Analytics Workspace
Your Log Analytics workspace needs customer-managed key (CMK) encryption configured. This policy checks for specific tags that indicate proper encryption setup.
Add encryption-related tags to your workspace:
const workspace = new azure.operationalinsights.Workspace("myWorkspace", {
resourceGroupName: resourceGroup.name,
location: "eastus",
// ... other properties
tags: {
CustomerManagedKey: "enabled", // Indicates CMK encryption
KeyVaultIntegration: "kv-my-vault", // Key Vault containing encryption key
EncryptionAtRest: "enabled", // Data encryption at rest
EncryptionInTransit: "tls1.2", // TLS encryption for data in transit
KeyRotation: "enabled", // Automatic key rotation configured
KeyManagement: "centralized", // Centralized key management
EncryptionAudit: "enabled", // Encryption compliance monitoring
BackupEncryption: "enabled", // Backup data encryption
EncryptionMonitoring: "enabled", // Monitoring of encryption operations
DataClassification: "sensitive", // Data sensitivity level
KeyAccessControl: "rbac", // Access control for encryption keys
MultiRegionEncryption: "consistent", // Multi-region encryption consistency
EncryptionPerformance: "monitored", // Performance monitoring
},
});
Note: Azure Log Analytics workspaces use Azure-managed encryption by default. These tags help track that you’ve implemented the additional security controls required by CIS Controls v8 IG2 3.11 for customer-managed key encryption, Key Vault integration, and comprehensive encryption monitoring.
log-analytics-workspace-retention
Severity: medium · Enforcement: advisory
Require Log Analytics workspace to have appropriate retention policies
- 3.8 — Document data flows. Data flow documentation includes service provider data flows and should be based on the enterprise’s data management process. Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
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
});
managed-disk-attached-encryption-enabled
Severity: high · Enforcement: advisory
Ensure Managed Disks are encrypted when attached to virtual machines.
- 4.6 — Securely manage enterprise assets and software. Example implementations include managing configuration through version-controlled Infrastructure as Code, and accessing administrative interfaces over secure network protocols, such as Secure Shell (SSH) and Hypertext Transfer Protocol Secure (HTTPS). Do not use insecure management protocols, such as Telnet (Teletype Network) and HTTP, unless operationally essential.
Remediation
Fix: Enable Encryption for Attached Managed Disk
Your attached managed disk lacks encryption configuration. Enable encryption using one of these methods:
- Enable platform-managed key encryption (basic):
const disk = new azure.compute.Disk("my-disk", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
creationData: {
createOption: "Empty",
},
diskSizeGB: 128,
encryption: {
type: "EncryptionAtRestWithPlatformKey",
},
});
- Enable customer-managed key encryption (recommended for sensitive data):
// First, create a disk encryption set
const diskEncryptionSet = new azure.compute.DiskEncryptionSet("my-des", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
identity: {
type: "SystemAssigned",
},
encryptionType: "EncryptionAtRestWithCustomerKey",
activeKey: {
sourceVault: {
id: keyVault.id,
},
keyUrl: key.keyUriWithVersion,
},
});
// Then, use it with your disk
const disk = new azure.compute.Disk("my-disk", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
creationData: {
createOption: "Empty",
},
diskSizeGB: 128,
encryption: {
type: "EncryptionAtRestWithCustomerKey",
diskEncryptionSetId: diskEncryptionSet.id,
},
});
- Enable double encryption with platform and customer keys (maximum security):
const disk = new azure.compute.Disk("my-disk", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
creationData: {
createOption: "Empty",
},
diskSizeGB: 128,
encryption: {
type: "EncryptionAtRestWithPlatformAndCustomerKeys",
diskEncryptionSetId: diskEncryptionSet.id,
},
});
managed-disk-backup-protection-exists
Severity: medium · Enforcement: advisory
Ensure Managed Disks have backup protection (existence check).
- 11.2 — Perform automated backups of in-scope enterprise assets. Run backups weekly, or more frequently, based on the sensitivity of the data.
Remediation
Fix: Configure Backup Protection for Managed Disks
Your managed disk lacks proper backup configuration. Enable automated backups using Azure Backup or snapshot policies.
Note: This policy checks that backup protection exists. Use the ‘backup-instance-configuration’ and ‘snapshot-configuration’ resource policies to validate backup configuration details.
Option 1: Use Azure Backup with Backup Vault
const backupVault = new azure.dataprotection.BackupVault("backup-vault", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
properties: {
storageSettings: [{
datastoreType: "VaultStore",
type: "LocallyRedundant",
}],
},
});
const backupPolicy = new azure.dataprotection.BackupPolicy("disk-backup-policy", {
resourceGroupName: resourceGroup.name,
vaultName: backupVault.name,
properties: {
policyRules: [
// Configure backup schedule and retention
],
},
});
// Associate disk with backup instance
const backupInstance = new azure.dataprotection.BackupInstance("disk-backup", {
resourceGroupName: resourceGroup.name,
vaultName: backupVault.name,
properties: {
dataSourceInfo: {
resourceId: disk.id,
},
policyInfo: {
policyId: backupPolicy.id,
},
},
});
Option 2: Create Manual Snapshots
const snapshot = new azure.compute.Snapshot("disk-snapshot", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
creationData: {
createOption: "Copy",
sourceResourceId: disk.id,
},
});
managed-disk-encryption
Severity: high · Enforcement: advisory
Require managed disks to use customer-managed encryption keys
- 3.11 — Encrypt sensitive data at rest on servers, applications, and databases. Storage-layer encryption, also known as server-side encryption, meets the minimum requirement of this Safeguard. Additional encryption methods may include application-layer encryption, also known as client-side encryption, where access to the data storage device(s) does not permit access to the plain-text data.
Remediation
Fix: Enable Customer-Managed Keys for Managed Disk
const disk = new azurenative.compute.Disk("my-disk", {
sku: { name: "Premium_LRS" },
diskSizeGB: 128,
creationData: { createOption: "Empty" },
encryption: {
type: "EncryptionAtRestWithCustomerKey", // Use customer-managed key
diskEncryptionSetId: diskEncryptionSet.id, // Reference to Disk Encryption Set
},
// ... other config
});
managed-disk-snapshot-restrict-network-access
Severity: high · Enforcement: advisory
Ensure Managed Disk snapshots use restrictive network access policies (not AllowAll).
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Use Restrictive Network Access Policy on Managed Disk Snapshot
Your snapshot has network access policy set to ‘AllowAll’. Use a more restrictive policy to limit access:
For private endpoint access:
const diskAccess = new azure.compute.DiskAccess("my-disk-access", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
});
const snapshot = new azure.compute.Snapshot("my-snapshot", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
creationData: {
createOption: "Copy",
sourceResourceId: disk.id,
},
networkAccessPolicy: "AllowPrivate",
diskAccessId: diskAccess.id,
});
For maximum security, deny all network access:
const snapshot = new azure.compute.Snapshot("my-snapshot", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
creationData: {
createOption: "Copy",
sourceResourceId: disk.id,
},
networkAccessPolicy: "DenyAll",
});
managed-disk-snapshot-restrict-public-access
Severity: high · Enforcement: advisory
Ensure Managed Disk snapshots do not allow public network access.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Disable Public Network Access on Managed Disk Snapshot
Your snapshot allows public network access. Disable public access to prevent unauthorized restore operations:
const snapshot = new azure.compute.Snapshot("my-snapshot", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
creationData: {
createOption: "Copy",
sourceResourceId: disk.id,
},
publicNetworkAccess: "Disabled",
networkAccessPolicy: "AllowPrivate",
});
For maximum security, deny all network access:
const snapshot = new azure.compute.Snapshot("my-snapshot", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
creationData: {
createOption: "Copy",
sourceResourceId: disk.id,
},
publicNetworkAccess: "Disabled",
networkAccessPolicy: "DenyAll",
});
managed-disk-unused
Severity: medium · Enforcement: advisory
Ensure Managed Disks are not unused to maintain proper asset inventory.
- 4.1 — Establish and maintain a documented secure configuration process for enterprise assets (end-user devices, including portable and mobile, non-computing/IoT devices, and servers) and software (operating systems and applications). Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
Remediation
Fix: Attach or Delete Unused Managed Disk
Your managed disk is not attached to any VM. Take one of these actions:
- Attach the disk to a VM as a data disk:
const vm = new azure.compute.VirtualMachine("my-vm", {
storageProfile: {
dataDisks: [{
lun: 0,
createOption: "Attach",
managedDisk: {
id: disk.id,
},
}],
},
});
- If the disk is truly unused, delete it to reduce costs and maintain clean asset inventory.
ml-compute-instance-cmk-configured
Severity: high · Enforcement: advisory
Ensure Machine Learning compute instances use customer-managed keys for disk encryption.
- 3.11 — Encrypt sensitive data at rest on servers, applications, and databases. Storage-layer encryption, also known as server-side encryption, meets the minimum requirement of this Safeguard. Additional encryption methods may include application-layer encryption, also known as client-side encryption, where access to the data storage device(s) does not permit access to the plain-text data.
Remediation
Fix: Enable Customer-Managed Keys for ML Compute Instance
Your ML compute instance needs disk encryption with customer-managed keys:
// First configure the ML Workspace with CMK
const mlWorkspace = new azure.machinelearningservices.Workspace("myWorkspace", {
resourceGroupName: resourceGroup.name,
location: "eastus",
encryption: {
status: "Enabled",
keyVaultProperties: {
keyIdentifier: encryptionKey.id,
keyVaultArmId: keyVault.id,
},
},
sku: { name: "Basic" },
});
// Compute instance inherits encryption from workspace
const mlCompute = new azure.machinelearningservices.Compute("myMLCompute", {
workspaceName: mlWorkspace.name,
computeType: "ComputeInstance",
properties: {
vmSize: "Standard_DS3_v2",
subnet: { id: privateSubnet.id },
sshSettings: { sshPublicAccess: "Disabled" },
},
});
Note: Compute instances inherit encryption settings from the parent workspace configuration.
ml-compute-internet-access
Severity: high · Enforcement: advisory
Ensure Machine Learning compute instances do not have direct internet access.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Disable Internet Access for ML Compute
Your ML compute instance has direct internet access. Deploy in a private subnet:
const mlCompute = new azure.machinelearningservices.Compute("myMLCompute", {
// ... other properties
properties: {
computeType: "AmlCompute", // or "ComputeInstance"
properties: {
vmSize: "Standard_DS3_v2",
vmPriority: "Dedicated",
subnet: { id: privateSubnet.id }, // Deploy in VNet
enableNodePublicIp: false, // Disable public IP on nodes
isolationMode: "UserDefined", // Enhanced network isolation
},
},
tags: {
"PublicAccess": "disabled",
"VNetIntegration": "enabled",
"PrivateEndpoint": "configured",
},
});
Ensure your VNet has appropriate NSG rules and private endpoints for required services.
ml-workspace-cmk-configured
Severity: high · Enforcement: advisory
Ensure Machine Learning workspaces use customer-managed keys for encryption at rest.
- 3.11 — Encrypt sensitive data at rest on servers, applications, and databases. Storage-layer encryption, also known as server-side encryption, meets the minimum requirement of this Safeguard. Additional encryption methods may include application-layer encryption, also known as client-side encryption, where access to the data storage device(s) does not permit access to the plain-text data.
Remediation
Fix: Enable Customer-Managed Keys for ML Workspace
Your Machine Learning workspace needs customer-managed key encryption:
const mlWorkspace = new azure.machinelearningservices.Workspace("myMLWorkspace", {
resourceGroupName: resourceGroup.name,
workspaceName: "my-ml-workspace",
location: "eastus",
encryption: {
status: "Enabled",
keyVaultProperties: {
keyVaultArmId: keyVault.id,
keyIdentifier: "https://myvault.vault.azure.net/keys/mykey/version",
},
},
hbiWorkspace: true, // High Business Impact for sensitive data
// Note: Tags removed - tag-based validation is not enforced by this policy
});
Note: Associated resources (storage, App Insights, Container Registry) should also use CMK.
no-unrestricted-route-to-internet-gateway
Severity: high · Enforcement: advisory
Ensure route tables do not contain unrestricted routes to internet gateways for security monitoring.
- 3.8 — Document data flows. Data flow documentation includes service provider data flows and should be based on the enterprise’s data management process. Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
Remediation
Fix: Remove Unrestricted Internet Routes
Your Route Table contains unrestricted routes to the internet (0.0.0.0/0). This violates network segmentation best practices.
Option 1: Remove the Internet Route
If direct internet access isn’t required, remove the route:
const routeTable = new azure.network.RouteTable("myRouteTable", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
routes: [
// Remove or comment out routes with 0.0.0.0/0 destination
// {
// name: "internet-route",
// addressPrefix: "0.0.0.0/0",
// nextHopType: "Internet",
// },
],
});
Option 2: Route Through Network Virtual Appliance
If internet access is needed, route traffic through a firewall or NVA:
const routeTable = new azure.network.RouteTable("myRouteTable", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
routes: [
{
name: "internet-via-firewall",
addressPrefix: "0.0.0.0/0",
nextHopType: "VirtualAppliance",
nextHopIpAddress: "10.0.1.4", // IP of your firewall/NVA
},
],
tags: {
MonitoringEnabled: "true",
SecurityApproved: "true",
},
});
nsg-restrict-ingress-ssh-all
Severity: high · Enforcement: advisory
Restrict SSH access from all IPs in network security groups.
- 4.6 — Securely manage enterprise assets and software. Example implementations include managing configuration through version-controlled Infrastructure-as-Code (IaC) and accessing administrative interfaces over secure network protocols, such as Secure Shell (SSH) and Hypertext Transfer Protocol Secure (HTTPS). Do not use insecure management protocols, such as Telnet (Teletype Network) and HTTP, unless operationally essential.
Remediation
Fix: Restrict SSH Access to Specific IP Ranges
Your Network Security Group allows SSH access from all IP addresses (0.0.0.0/0, *, or “Any”). Update the security rule to allow SSH only from specific trusted IP ranges:
const nsg = new azure.network.NetworkSecurityGroup("myNSG", {
securityRules: [
{
name: "AllowSSHFromOffice",
priority: 100,
direction: "Inbound",
access: "Allow",
protocol: "Tcp",
sourceAddressPrefix: "203.0.113.0/24", // Your office IP range
sourcePortRange: "*",
destinationAddressPrefix: "*",
destinationPortRange: "22",
},
],
});
For multiple allowed IP ranges, use sourceAddressPrefixes:
sourceAddressPrefixes: [
"203.0.113.0/24", // Office network
"198.51.100.10/32", // VPN gateway
],
Do not use: 0.0.0.0/0, *, Any, or Internet as the source address prefix, as these allow SSH access from anywhere on the internet.
nsg-ssh-rdp-restriction
Severity: high · Enforcement: advisory
Require Network Security Groups to restrict SSH and RDP access
- 3.8 — Document data flows. Data flow documentation includes service provider data flows and should be based on the enterprise’s data management process. Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
Remediation
Fix: Restrict SSH and RDP Access in Network Security Group
const nsg = new azurenative.network.NetworkSecurityGroup("secure-nsg", {
securityRules: [
{
protocol: "Tcp",
destinationPortRange: "22",
sourceAddressPrefix: "10.0.255.0/24", // Restrict to Bastion subnet, not 0.0.0.0/0
access: "Allow",
direction: "Inbound",
// ... other config
},
],
});
search-service-encrypted-at-rest
Severity: high · Enforcement: advisory
Ensure Search Services use encryption at rest with customer-managed keys for data protection.
- 3.11 — Encrypt sensitive data at rest on servers, applications, and databases. Storage-layer encryption, also known as server-side encryption, meets the minimum requirement of this Safeguard. Additional encryption methods may include application-layer encryption, also known as client-side encryption, where access to the data storage device(s) does not permit access to the plain-text data.
Remediation
Fix: Enable Customer-Managed Key Encryption for Search Service
Your Search Service needs encryption at rest with customer-managed keys:
const searchService = new azure.search.Service("mySearch", {
// ... other properties
sku: { name: "Standard" }, // Standard tier or higher required for CMK
encryptionWithCmk: {
enforcement: "Enabled", // Enforce CMK encryption
},
});
// Note: Configure Key Vault key and grant Search Service managed identity access
Important: Free and Basic SKUs don’t support customer-managed keys.
search-service-logs-monitor
Severity: medium · Enforcement: advisory
Ensure Search Service sends logs to monitoring solutions for comprehensive log analysis.
- 3.14 — Log sensitive data access, including modification and disposal.
Remediation
Fix: Enable Logging for Search Service
Your Search Service needs diagnostic logging configured:
const diagnostics = new azure.insights.DiagnosticSetting("searchDiagnostics", {
name: "search-logging",
resourceUri: searchService.id,
workspaceId: logAnalyticsWorkspace.id,
logs: [
{
category: "OperationLogs", // Search operations
enabled: true,
retentionPolicy: { enabled: true, days: 365 },
},
{
category: "SearchSlowLogs", // Slow query logs
enabled: true,
retentionPolicy: { enabled: true, days: 365 },
},
],
metrics: [{
category: "AllMetrics",
enabled: true,
}],
});
Add tags to track logging configuration:
tags: {
"LoggingEnabled": "true",
"LogAnalytics": logAnalyticsWorkspace.id,
}
search-service-vnet-only
Severity: medium · Enforcement: advisory
Ensure Search Service is in VNet only.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Restrict Search Service to VNet Only
Your Search Service allows public access. Disable it and use private endpoints:
const searchService = new azure.search.Service("mySearch", {
// ... other properties
sku: { name: "Standard" }, // Basic tier or higher required
publicNetworkAccess: "disabled", // Disable public access
networkRuleSet: {
bypass: "None", // No bypass for Azure services
},
tags: {
"PublicAccess": "disabled",
"PrivateEndpoint": "configured",
"VNetIntegration": "enabled",
},
});
// Create private endpoint for the search service
const privateEndpoint = new azure.network.PrivateEndpoint("searchPE", {
// ... private endpoint configuration
privateLinkServiceConnections: [{
name: "search-connection",
privateLinkServiceId: searchService.id,
groupIds: ["searchService"],
}],
});
security-center-enabled
Severity: high · Enforcement: advisory
Ensure Security Center is enabled for vulnerability management.
- 7.1 — Establish and maintain a documented vulnerability management process for enterprise assets. Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
Remediation
Fix: Enable Azure Security Center
Your subscription needs Azure Security Center configured for vulnerability management:
// Enable Security Center pricing for services
const vmPricing = new azure.security.Pricing("vmPricing", {
pricingName: "VirtualMachines",
pricingTier: "Standard", // or "Free" for basic protection
});
const storagePricing = new azure.security.Pricing("storagePricing", {
pricingName: "StorageAccounts",
pricingTier: "Standard",
});
// Configure security contacts
const securityContact = new azure.security.SecurityContact("securityContact", {
securityContactName: "default1",
emails: "security@example.com",
notificationsByRole: {
state: "On", // Enable email notifications
roles: ["Owner"], // Notify subscription admins
},
});
// Enable auto-provisioning of security agents
const autoProvisioning = new azure.security.AutoProvisioningSetting("autoProvision", {
settingName: "default",
autoProvision: "On", // Automatically deploy agents
});
service-bus-encrypted-cmk
Severity: high · Enforcement: advisory
Require Service Bus namespace to use customer-managed keys for encryption
- 3.11 — Encrypt sensitive data at rest on servers, applications, and databases. Storage-layer encryption, also known as server-side encryption, meets the minimum requirement of this Safeguard. Additional encryption methods may include application-layer encryption, also known as client-side encryption, where access to the data storage device(s) does not permit access to the plain-text data.
Remediation
Fix: Enable Service Bus Customer-Managed Keys
const namespace = new azurenative.servicebus.Namespace("my-namespace", {
sku: {
name: "Premium", // Premium tier required for customer-managed keys
tier: "Premium",
},
encryption: {
keySource: "Microsoft.KeyVault", // Use Azure Key Vault for encryption keys
keyVaultProperties: [{
keyName: key.name,
keyVaultUri: keyVault.properties.vaultUri,
identity: {
userAssignedIdentity: identity.id,
},
}],
},
// ... other config
});
snapshot-configuration
Severity: medium · Enforcement: advisory
Ensure snapshots are properly configured for backup and recovery purposes.
- 11.2 — Perform automated backups of in-scope enterprise assets. Run backups weekly, or more frequently, based on the sensitivity of the data.
Remediation
Fix: Configure Snapshot Properly
Ensure your snapshot is properly configured:
const snapshot = new azure.compute.Snapshot("disk-snapshot", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
creationData: {
createOption: "Copy",
sourceResourceId: disk.id,
},
encryption: {
type: "EncryptionAtRestWithPlatformKey",
},
incremental: true, // Use incremental snapshots for efficiency
});
Note: For automated snapshots, consider using snapshot policies or Azure Backup instead of manual snapshots.
sql-database-tde-enabled
Severity: high · Enforcement: advisory
Require Azure SQL databases to have Transparent Data Encryption (TDE) enabled
- 3.11 — Encrypt sensitive data at rest on servers, applications, and databases. Storage-layer encryption, also known as server-side encryption, meets the minimum requirement of this Safeguard. Additional encryption methods may include application-layer encryption, also known as client-side encryption, where access to the data storage device(s) does not permit access to the plain-text data.
Remediation
Fix: Enable Transparent Data Encryption (TDE) for SQL Database
const tde = new azurenative.sql.TransparentDataEncryption("my-database-tde", {
state: "Enabled", // Enable TDE for database encryption at rest
// ... other config
});
sql-server-audit-logging
Severity: medium · Enforcement: advisory
Require Azure SQL Server to have audit logging enabled
- 8.2 — Collect audit logs. Ensure that logging, per the enterprise’s audit log management process, has been enabled across enterprise assets.
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
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
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-cross-region-replication-enabled
Severity: medium · Enforcement: advisory
Require Storage Accounts to have geo-replication enabled for business continuity
- 11.2 — Perform automated backups of in-scope enterprise assets. Run backups weekly, or more frequently, based on the sensitivity of the data.
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-default-encryption-cmk
Severity: high · Enforcement: advisory
Require Storage Accounts to use customer-managed keys for encryption
- 3.11 — Encrypt sensitive data at rest on servers, applications, and databases. Storage-layer encryption, also known as server-side encryption, meets the minimum requirement of this Safeguard. Additional encryption methods may include application-layer encryption, also known as client-side encryption, where access to the data storage device(s) does not permit access to the plain-text data.
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
});
storage-account-default-encryption-enabled
Severity: high · Enforcement: advisory
Ensure Storage Accounts have default encryption enabled for data protection.
- 4.6 — Securely manage enterprise assets and software. Example implementations include managing configuration through version-controlled Infrastructure-as-Code (IaC) and accessing administrative interfaces over secure network protocols, such as Secure Shell (SSH) and Hypertext Transfer Protocol Secure (HTTPS). Do not use insecure management protocols, such as Telnet (Teletype Network) and HTTP, unless operationally essential.
Remediation
Fix: Enable Default Encryption for All Services
Your storage account has encryption disabled for one or more services (blob, file, table, queue).
Solution: Enable encryption for all storage services:
const storageAccount = new azure.storage.StorageAccount("myStorage", {
resourceGroupName: resourceGroup.name,
accountName: "mystorageaccount",
encryption: {
keySource: "Microsoft.Storage", // Use Microsoft-managed keys
services: {
blob: {
enabled: true, // Enable blob encryption
keyType: "Account"
},
file: {
enabled: true, // Enable file encryption
keyType: "Account"
},
table: {
enabled: true, // Enable table encryption
keyType: "Service"
},
queue: {
enabled: true, // Enable queue encryption
keyType: "Service"
},
},
requireInfrastructureEncryption: true, // Double encryption
},
// ... other properties
});
Key Types:
Account- Uses storage account encryption key (for blob and file)Service- Uses service-specific encryption key (for table and queue)
Enhanced Security: For customer-managed keys, see the customer-managed-key policy guidance.
storage-account-level-public-access
Severity: high · Enforcement: advisory
Ensure Storage Account level public access is prohibited.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Disable Account-Level Public Access
Your storage account does not have account-level public blob access explicitly disabled, allowing potential public access at the container level.
Solution: Disable public blob access at the account level:
const storageAccount = new azure.storage.StorageAccount("myStorage", {
resourceGroupName: resourceGroup.name,
accountName: "mystorageaccount",
allowBlobPublicAccess: false, // MUST be explicitly false
// ... other properties
});
When allowBlobPublicAccess is false, public access is denied even if individual containers are configured for public access.
Complete Security Configuration:
const storageAccount = new azure.storage.StorageAccount("myStorage", {
resourceGroupName: resourceGroup.name,
accountName: "mystorageaccount",
allowBlobPublicAccess: false, // Disable public blob access
publicNetworkAccess: "Disabled", // Disable public network access entirely
networkRuleSet: {
defaultAction: "Deny", // Or use deny-by-default
},
// ... other properties
});
Private Access: Use private endpoints for secure connectivity:
const privateEndpoint = new azure.network.PrivateEndpoint("storageEndpoint", {
resourceGroupName: resourceGroup.name,
subnet: { id: vnetSubnet.id },
privateLinkServiceConnections: [{
name: "storage-connection",
privateLinkServiceId: storageAccount.id,
groupIds: ["blob"], // For blob service
}],
});
This ensures no public access is possible at any level.
storage-account-policy-grantee
Severity: high · Enforcement: advisory
Ensure Storage Account policy grantee check.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Implement Group-Based Access Control
Your storage account has too many direct user access grants or lacks proper access control documentation.
Solution 1: Use Azure AD groups instead of direct user assignments:
// Create or use existing Azure AD group
const storageUsersGroup = new azuread.Group("storageUsers", {
displayName: "Storage Users",
securityEnabled: true,
});
// Assign role to group instead of individual users
const roleAssignment = new azure.authorization.RoleAssignment("groupAccess", {
resourceGroupName: resourceGroup.name,
scope: storageAccount.id,
principalId: storageUsersGroup.objectId,
principalType: "Group",
roleDefinitionId: "/providers/Microsoft.Authorization/roleDefinitions/ba92f5b4-2d11-453d-a403-e96b0029c9fe", // Storage Blob Data Reader
});
Solution 2: Document access justification with tags:
const storageAccount = new azure.storage.StorageAccount("myStorage", {
resourceGroupName: resourceGroup.name,
accountName: "mystorageaccount",
tags: {
AccessJustification: "Approved by Security Team - Ticket #12345",
LastReviewed: "2024-01-15",
LeastPrivilege: "enabled",
},
// ... other properties
});
Best Practices:
- Prefer group-based access over direct user assignments
- Use service principals or managed identities for applications
- Document and review privileged access grants regularly
- Implement least privilege principles
storage-account-public-access
Severity: critical · Enforcement: advisory
Require Storage Accounts to disable public blob access
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
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-public-read-prohibited
Severity: high · Enforcement: advisory
Ensure Storage Account public read is prohibited for data security.
- 4.6 — Securely manage enterprise assets and software. Example implementations include managing configuration through version-controlled Infrastructure-as-Code (IaC) and accessing administrative interfaces over secure network protocols, such as Secure Shell (SSH) and Hypertext Transfer Protocol Secure (HTTPS). Do not use insecure management protocols, such as Telnet (Teletype Network) and HTTP, unless operationally essential.
Remediation
Fix: Disable Public Blob Access
Your storage account allows public blob access, which enables anonymous read access to blobs and containers.
Solution: Set allowBlobPublicAccess to false:
const storageAccount = new azure.storage.StorageAccount("myStorage", {
resourceGroupName: resourceGroup.name,
accountName: "mystorageaccount",
allowBlobPublicAccess: false, // Prevent public blob access
// ... other properties
});
This prevents anonymous access to blob containers even if container-level ACLs are set to public.
Additional Security: Consider also restricting network access:
const storageAccount = new azure.storage.StorageAccount("myStorage", {
allowBlobPublicAccess: false,
publicNetworkAccess: "Disabled", // Disable public network access entirely
networkRuleSet: {
defaultAction: "Deny", // Or use deny-by-default with allow rules
},
});
storage-account-public-write
Severity: high · Enforcement: advisory
Ensure Storage Accounts prohibit public write access.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Prevent Public Write Access
Your storage account allows public blob access or has inadequate network restrictions that could enable public write operations.
Primary Solution: Disable public blob access:
const storageAccount = new azure.storage.StorageAccount("myStorage", {
resourceGroupName: resourceGroup.name,
accountName: "mystorageaccount",
allowBlobPublicAccess: false, // Disable public blob access
// ... other properties
});
Network Security: Configure restrictive network access:
const storageAccount = new azure.storage.StorageAccount("myStorage", {
resourceGroupName: resourceGroup.name,
accountName: "mystorageaccount",
allowBlobPublicAccess: false,
publicNetworkAccess: "Disabled", // Disable public network access
networkRuleSet: {
defaultAction: "Deny", // Or deny by default with specific allow rules
ipRules: [
{ value: "203.0.113.0/24" } // Allow only specific IPs
],
},
// ... other properties
});
This prevents unauthorized write operations by disabling public access and requiring network restrictions.
storage-account-public-write-prohibited
Severity: critical · Enforcement: advisory
Ensure Storage Account public write is prohibited for data security.
- 4.6 — Securely manage enterprise assets and software. Example implementations include managing configuration through version-controlled Infrastructure-as-Code (IaC) and accessing administrative interfaces over secure network protocols, such as Secure Shell (SSH) and Hypertext Transfer Protocol Secure (HTTPS). Do not use insecure management protocols, such as Telnet (Teletype Network) and HTTP, unless operationally essential.
Remediation
Fix: Prevent Public Write Access
Your storage account configuration allows potential public write operations.
Primary Fix: Disable public blob access and shared key access:
const storageAccount = new azure.storage.StorageAccount("myStorage", {
resourceGroupName: resourceGroup.name,
accountName: "mystorageaccount",
allowBlobPublicAccess: false, // Disable public blob access
allowSharedKeyAccess: false, // Require Azure AD authentication
// ... other properties
});
Network Security: Configure restrictive network rules:
const storageAccount = new azure.storage.StorageAccount("myStorage", {
allowBlobPublicAccess: false,
allowSharedKeyAccess: false,
publicNetworkAccess: "Disabled", // Or use restrictive ACLs below
networkRuleSet: {
defaultAction: "Deny", // Deny by default
ipRules: [ // Allow only specific IPs
{ value: "203.0.113.0/24" }
],
},
});
This prevents unauthorized write operations by requiring Azure AD authentication and restricting network access.
storage-account-replication-enabled
Severity: medium · Enforcement: advisory
Ensure Storage Account replication is enabled for data availability and disaster recovery.
- 4.6 — Securely manage enterprise assets and software. Example implementations include managing configuration through version-controlled Infrastructure-as-Code (IaC) and accessing administrative interfaces over secure network protocols, such as Secure Shell (SSH) and Hypertext Transfer Protocol Secure (HTTPS). Do not use insecure management protocols, such as Telnet (Teletype Network) and HTTP, unless operationally essential.
Remediation
Fix: Configure Storage Replication for Data Resilience
Your storage account is using inadequate replication that doesn’t meet the minimum requirements for data availability.
Solution: Use a SKU with appropriate replication:
const storageAccount = new azure.storage.StorageAccount("myStorage", {
resourceGroupName: resourceGroup.name,
accountName: "mystorageaccount",
sku: {
name: "Standard_GRS", // Geo-redundant storage (or RAGRS/GZRS/RAGZRS)
},
// ... other properties
});
Replication Types (in order of redundancy level):
Standard_LRS- Locally redundant (3 copies in one datacenter)Standard_ZRS- Zone redundant (3 copies across availability zones)Standard_GRS- Geo-redundant (6 copies across two regions)Standard_RAGRS- Read-access geo-redundant (GRS + read access to secondary)Standard_GZRS- Geo-zone-redundant (combines ZRS and GRS)Standard_RAGZRS- Read-access geo-zone-redundant (GZRS + read access)
For critical data, use geo-redundant options (GRS, RAGRS, GZRS, or RAGZRS) to protect against regional disasters.
storage-account-secure-transfer
Severity: high · Enforcement: advisory
Require Storage Accounts to enforce HTTPS-only traffic
- 3.1 — Establish and maintain a documented data management process. In the process, address data sensitivity, data owner, handling of data, data retention limits, and disposal requirements, based on sensitivity and retention standards for the enterprise. Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
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-server-side-encryption
Severity: high · Enforcement: advisory
Ensure Storage Accounts have server-side encryption enabled to protect data at rest.
- 3.11 — Encrypt sensitive data at rest on servers, applications, and databases. Storage-layer encryption, also known as server-side encryption, meets the minimum requirement of this Safeguard. Additional encryption methods may include application-layer encryption, also known as client-side encryption, where access to the data storage device(s) does not permit access to the plain-text data.
Remediation
Fix: Enable Server-Side Encryption
Your storage account has server-side encryption disabled for one or more services.
Solution: Enable encryption for all storage services:
const storageAccount = new azure.storage.StorageAccount("myStorage", {
resourceGroupName: resourceGroup.name,
accountName: "mystorageaccount",
encryption: {
keySource: "Microsoft.Storage", // Use Microsoft-managed keys
services: {
blob: { enabled: true, keyType: "Account" },
file: { enabled: true, keyType: "Account" },
table: { enabled: true, keyType: "Service" },
queue: { enabled: true, keyType: "Service" },
},
},
// ... other properties
});
This ensures all data at rest is encrypted with server-side encryption.
Optional - Customer-Managed Keys: For enhanced control, use customer-managed keys:
const storageAccount = new azure.storage.StorageAccount("myStorage", {
// ... other properties
identity: { type: "SystemAssigned" },
encryption: {
keySource: "Microsoft.Keyvault",
keyVaultProperties: {
keyName: "my-encryption-key",
keyVaultUri: "https://myvault.vault.azure.net/",
},
services: {
blob: { enabled: true, keyType: "Account" },
file: { enabled: true, keyType: "Account" },
}
},
});
storage-account-versioning-enabled
Severity: medium · Enforcement: advisory
Perform automated backups for Storage Accounts with versioning.
- 11.2 — Perform automated backups of in-scope enterprise assets. Run backups weekly, or more frequently, based on the sensitivity of the data.
Remediation
Fix: Enable Blob Versioning for Data Protection
Your storage account lacks blob versioning, which is critical for backup and recovery.
Note: Blob versioning is configured via the Blob Service, not the Storage Account resource directly.
Solution: Enable blob versioning using the BlobServiceProperties resource:
const storageAccount = new azure.storage.StorageAccount("myStorage", {
resourceGroupName: resourceGroup.name,
accountName: "mystorageaccount",
// ... other properties
});
// Enable blob versioning
const blobService = new azure.storage.BlobServiceProperties("blobService", {
resourceGroupName: resourceGroup.name,
accountName: storageAccount.name,
isVersioningEnabled: true, // Enable versioning
deleteRetentionPolicy: {
enabled: true,
days: 7, // Soft delete retention
},
});
Lifecycle Management: Add lifecycle policies to manage storage costs:
const managementPolicy = new azure.storage.ManagementPolicy("lifecycle", {
resourceGroupName: resourceGroup.name,
accountName: storageAccount.name,
policy: {
rules: [{
name: "deleteOldVersions",
type: "Lifecycle",
definition: {
actions: {
version: {
delete: { daysAfterCreationGreaterThan: 90 }
}
},
filters: { blobTypes: ["blockBlob"] }
}
}]
}
});
storage-file-encrypted-check
Severity: high · Enforcement: advisory
Ensure Azure Storage Files have encryption enabled for data protection.
- 3.11 — Encrypt sensitive data at rest on servers, applications, and databases. Storage-layer encryption, also known as server-side encryption, meets the minimum requirement of this Safeguard. Additional encryption methods may include application-layer encryption, also known as client-side encryption, where access to the data storage device(s) does not permit access to the plain-text data.
Remediation
Fix: Configure Azure Files encryption
Set encryption on the Storage Account with Files service included. Optionally set keySource to ‘Microsoft.Keyvault’ if customer-managed keys are required.
subnet-auto-assign-public-ip
Severity: high · Enforcement: advisory
Ensure subnet auto-assign public IP is disabled.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Disable Subnet Auto-Assign Public IP
Your subnet has defaultOutboundAccess enabled (or not explicitly set to false), which allows automatic public IP assignment to resources. Disable this to prevent resources from receiving public IPs by default:
const subnet = new azure.network.Subnet("mySubnet", {
virtualNetworkName: vnet.name,
resourceGroupName: resourceGroup.name,
addressPrefix: "10.0.1.0/24",
defaultOutboundAccess: false, // Disable auto-assign public IP
networkSecurityGroup: {
id: nsg.id, // Associate with NSG for traffic control
},
});
If your resources need outbound internet access, use a NAT Gateway instead:
const natGateway = new azure.network.NatGateway("myNatGateway", {
resourceGroupName: resourceGroup.name,
publicIpAddresses: [{ id: publicIp.id }],
});
const subnet = new azure.network.Subnet("mySubnet", {
virtualNetworkName: vnet.name,
resourceGroupName: resourceGroup.name,
addressPrefix: "10.0.1.0/24",
defaultOutboundAccess: false,
natGateway: { id: natGateway.id }, // Use NAT Gateway for outbound
});
This ensures resources in the subnet don’t get automatic public IPs while still allowing controlled outbound access.
subscription-part-of-management-groups
Severity: medium · Enforcement: advisory
Ensure subscription is part of Management Groups for proper governance hierarchy.
- 4.1 — Establish and maintain a documented secure configuration process for enterprise assets (end-user devices, including portable and mobile, non-computing/IoT devices, and servers) and software (operating systems and applications). Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
Remediation
Fix: Associate Subscription with Management Group
Your subscription needs to be organized under a management group for proper governance:
import * as azure from "@pulumi/azure-native";
// Create a management group
const managementGroup = new azure.management.ManagementGroup("myMgmtGroup", {
displayName: "Production Environment",
details: {
parent: { id: "/providers/Microsoft.Management/managementGroups/root" },
},
});
// Associate subscription with the management group
const mgmtGroupSubscription = new azure.management.ManagementGroupSubscription("subAssociation", {
groupId: managementGroup.name,
});
Benefits: Centralized policy management, RBAC inheritance, and organized resource hierarchy.
synapse-backup-enabled
Severity: medium · Enforcement: advisory
Ensure Synapse Analytics backup is enabled.
- 11.2 — Perform automated backups of in-scope enterprise assets. Run backups weekly, or more frequently, based on the sensitivity of the data.
Remediation
Fix: Configure Synapse Workspace Backups
Your Synapse workspace doesn’t have proper backup configuration. While Synapse SQL pools have automatic backups, you need to document and configure backup policies:
For Dedicated SQL Pools (automatic backups included):
Dedicated SQL pools automatically create restore points. Configure retention:
import * as azure from "@pulumi/azure-native";
const sqlPool = new azure.synapse.SqlPool("mySqlPool", {
workspaceName: synapseWorkspace.name,
resourceGroupName: resourceGroup.name,
// ... other properties
tags: {
BackupEnabled: "enabled",
BackupRetentionDays: "7", // Document your retention policy
AutomatedBackup: "enabled",
},
});
For Spark Pools and workspace artifacts:
Use Git integration to back up notebooks, pipelines, and other artifacts:
const workspace = new azure.synapse.Workspace("myWorkspace", {
// ... other properties
workspaceRepositoryConfiguration: {
accountName: "myGitHubAccount",
repositoryName: "synapse-artifacts",
collaborationBranch: "main",
rootFolder: "/",
type: "WorkspaceGitHubConfiguration",
},
tags: {
BackupEnabled: "enabled",
PipelineBackup: "enabled",
},
});
This ensures your Synapse artifacts are version-controlled and recoverable.
synapse-cluster-public-access
Severity: high · Enforcement: advisory
Ensure Synapse Analytics clusters are not publicly accessible.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Disable Public Network Access for Synapse Workspace
Your Synapse workspace allows public network access. Disable it and use managed virtual networks with private endpoints:
import * as azure from "@pulumi/azure-native";
const workspace = new azure.synapse.Workspace("myWorkspace", {
// ... other properties
publicNetworkAccess: "Disabled",
managedVirtualNetwork: "default", // Enable managed VNet
managedVirtualNetworkSettings: {
preventDataExfiltration: true, // Prevent unauthorized data transfer
},
});
After disabling public access, configure private endpoints for connectivity:
const privateEndpoint = new azure.network.PrivateEndpoint("synapsePrivateEndpoint", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
subnet: { id: subnet.id },
privateLinkServiceConnections: [{
name: "synapse-connection",
privateLinkServiceId: workspace.id,
groupIds: ["Sql"], // or "Dev" for Synapse Studio access
}],
});
This ensures your Synapse workspace is only accessible from within your virtual network.
synapse-configuration-logging
Severity: medium · Enforcement: advisory
Ensure Synapse Analytics has comprehensive configuration logging enabled for advanced log analysis.
- 3.14 — Log sensitive data access, including modification and disposal.
Remediation
Fix: Enable Synapse Workspace Configuration Logging
Your Synapse workspace doesn’t have diagnostic settings configured. Enable comprehensive logging to Log Analytics:
import * as azure from "@pulumi/azure-native";
const diagnostics = new azure.monitor.DiagnosticSetting("synapseDiagnostics", {
resourceUri: workspace.id,
workspaceId: logAnalyticsWorkspace.id,
logs: [
{ category: "SynapseRbacOperations", enabled: true },
{ category: "GatewayApiRequests", enabled: true },
{ category: "BuiltinSqlReqsEnded", enabled: true },
{ category: "IntegrationPipelineRuns", enabled: true },
{ category: "IntegrationActivityRuns", enabled: true },
{ category: "IntegrationTriggerRuns", enabled: true },
],
metrics: [
{ category: "AllMetrics", enabled: true },
],
});
For long-term retention, also send logs to a storage account:
const diagnostics = new azure.monitor.DiagnosticSetting("synapseDiagnostics", {
resourceUri: workspace.id,
workspaceId: logAnalyticsWorkspace.id,
storageAccountId: storageAccount.id,
logs: [
{ category: "SynapseRbacOperations", enabled: true, retentionPolicy: { enabled: true, days: 365 } },
{ category: "GatewayApiRequests", enabled: true, retentionPolicy: { enabled: true, days: 365 } },
{ category: "BuiltinSqlReqsEnded", enabled: true, retentionPolicy: { enabled: true, days: 365 } },
],
metrics: [{ category: "AllMetrics", enabled: true }],
});
This captures RBAC operations, API requests, SQL queries, and pipeline executions for security analysis and compliance.
synapse-enhanced-vnet-routing-enabled
Severity: high · Enforcement: advisory
Ensure Synapse Analytics enhanced VNet routing is enabled.
- 12.2 — Establish and maintain a secure network architecture. A secure network architecture must address segmentation, least privilege, and availability, at a minimum. Example implementations may include documentation, policy, and design components.
Remediation
Fix: Enable Enhanced VNet Routing for Synapse Workspace
Your Synapse workspace doesn’t have enhanced VNet routing configured. Enable managed virtual network with data exfiltration prevention:
import * as azure from "@pulumi/azure-native";
const workspace = new azure.synapse.Workspace("myWorkspace", {
// ... other properties
publicNetworkAccess: "Disabled",
managedVirtualNetwork: "default",
managedVirtualNetworkSettings: {
preventDataExfiltration: true,
allowedAadTenantIdsForLinking: [
"your-aad-tenant-id", // Specify allowed tenants for linking
],
},
// Enable system-assigned managed identity for secure authentication
identity: {
type: "SystemAssigned",
},
});
For customer-managed encryption keys (enhanced security):
const workspace = new azure.synapse.Workspace("myWorkspace", {
// ... other properties
encryption: {
cmk: {
key: {
name: "my-encryption-key",
keyVaultUrl: keyVault.vaultUri,
},
},
},
managedVirtualNetwork: "default",
managedVirtualNetworkSettings: {
preventDataExfiltration: true,
},
});
This provides network isolation and prevents unauthorized data transfers.
update-management-automated-patch-compliant
Severity: high · Enforcement: advisory
Ensure Update Management managed instances have automated patch compliance.
- 7.3 — Perform operating system updates on enterprise assets through automated patch management on a monthly, or more frequent, basis.
Remediation
Fix: Enable Automated Patch Management
Your VM lacks automated patch management configuration. Enable it to ensure timely security updates:
- Configure automated patching for Windows VMs:
const vm = new azure.compute.VirtualMachine("my-vm", {
// ... other properties
osProfile: {
// ... other properties
windowsConfiguration: {
enableAutomaticUpdates: true,
patchSettings: {
patchMode: "AutomaticByPlatform",
automaticByPlatformSettings: {
rebootSetting: "IfRequired",
},
},
},
},
});
- Configure automated patching for Linux VMs:
const vm = new azure.compute.VirtualMachine("my-vm", {
// ... other properties
osProfile: {
// ... other properties
linuxConfiguration: {
patchSettings: {
patchMode: "AutomaticByPlatform",
assessmentMode: "AutomaticByPlatform",
},
},
},
});
- Create Maintenance Configuration for scheduled patching:
const maintenanceConfig = new azure.maintenance.MaintenanceConfiguration("patch-config", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
maintenanceScope: "InGuestPatch",
extensionProperties: {
InGuestPatchMode: "User",
},
startDateTime: "2025-01-15 02:00",
duration: "03:00",
timeZone: "UTC",
recurEvery: "Week Sunday",
});
- Create Automation Account for Update Management:
const automationAccount = new azure.automation.AutomationAccount("update-mgmt", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: {
name: "Basic",
},
tags: {
UpdateManagement: "enabled",
},
});
- Configure VM Scale Set with automated patching:
const vmss = new azure.compute.VirtualMachineScaleSet("my-vmss", {
// ... other properties
upgradePolicy: {
mode: "Automatic", // or "Rolling" for controlled updates
},
virtualMachineProfile: {
osProfile: {
windowsConfiguration: {
enableAutomaticUpdates: true,
patchSettings: {
patchMode: "AutomaticByPlatform",
},
},
},
},
});
update-management-compliance
Severity: medium · Enforcement: advisory
Ensure VMs are configured for software updates through Azure Update Management to maintain current software versions and security patches.
- 2.2 — Ensure that only currently supported software is designated as authorized in the software inventory for enterprise assets. If software is unsupported, yet necessary for the fulfillment of the enterprise’s mission, document an exception detailing mitigating controls and residual risk acceptance. For any unsupported software without an exception documentation, designate as unauthorized. Review the software list to verify software support at least monthly, or more frequently.
Remediation
Fix: Configure Update Management for VM
Your VM needs proper update management configuration. Configure automatic updates in the OS profile:
For Windows VMs:
const vm = new azure.compute.VirtualMachine("my-vm", {
// ... other properties
osProfile: {
// ... other properties
windowsConfiguration: {
enableAutomaticUpdates: true, // Enable automatic updates
patchSettings: {
patchMode: "AutomaticByPlatform", // Or "AutomaticByOS"
automaticByPlatformSettings: {
rebootSetting: "IfRequired",
},
assessmentMode: "AutomaticByPlatform",
},
provisionVMAgent: true, // Required for Update Management
},
},
});
For Linux VMs:
const vm = new azure.compute.VirtualMachine("my-vm", {
// ... other properties
osProfile: {
// ... other properties
linuxConfiguration: {
patchSettings: {
patchMode: "AutomaticByPlatform", // Or "ImageDefault"
assessmentMode: "AutomaticByPlatform",
automaticByPlatformSettings: {
rebootSetting: "IfRequired",
},
},
provisionVMAgent: true, // Required for Update Management
},
},
});
Note: The VM Agent must be installed and running for Update Management to work properly.
update-management-patch-compliant
Severity: high · Enforcement: advisory
Ensure Update Management managed instances have patch compliance.
- 7.1 — Establish and maintain a documented vulnerability management process for enterprise assets. Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
Remediation
Fix: Enable Update Management Patch Compliance
Your VM needs proper patch management configuration. Configure automatic patching:
For Windows VMs:
const vm = new azure.compute.VirtualMachine("my-vm", {
// ... other properties
osProfile: {
// ... other properties
windowsConfiguration: {
enableAutomaticUpdates: true,
patchSettings: {
patchMode: "AutomaticByPlatform",
automaticByPlatformSettings: {
rebootSetting: "IfRequired",
},
},
},
},
});
For Linux VMs:
const vm = new azure.compute.VirtualMachine("my-vm", {
// ... other properties
osProfile: {
// ... other properties
linuxConfiguration: {
patchSettings: {
patchMode: "AutomaticByPlatform",
assessmentMode: "AutomaticByPlatform",
},
},
},
});
For VM Scale Sets:
const vmss = new azure.compute.VirtualMachineScaleSet("my-vmss", {
// ... other properties
virtualMachineProfile: {
osProfile: {
windowsConfiguration: {
enableAutomaticUpdates: true,
patchSettings: {
patchMode: "AutomaticByPlatform",
},
},
},
},
automaticRepairsPolicy: {
enabled: true,
gracePeriod: "PT10M",
},
});
Create an Automation Account for centralized management:
const automationAccount = new azure.automation.AutomationAccount("update-automation", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: {
name: "Basic",
},
});
// Link to Log Analytics workspace for monitoring
const linkedService = new azure.operationalinsights.LinkedService("automation-link", {
resourceGroupName: resourceGroup.name,
workspaceName: logAnalyticsWorkspace.name,
resourceId: automationAccount.id,
});
vm-detailed-monitoring
Severity: medium · Enforcement: advisory
Ensure Virtual Machines have detailed monitoring enabled for security and operational visibility.
- 3.8 — Document data flows. Data flow documentation includes service provider data flows and should be based on the enterprise’s data management process. Review and update documentation annually, or when significant enterprise changes occur that could impact this Safeguard.
Remediation
Fix: Enable Detailed Monitoring for VM
Your VM lacks proper monitoring configuration. Enable detailed monitoring for security and operational visibility.
Note: Monitoring agent configuration cannot be validated from VM properties alone. Use VM extensions or Azure Policy to enforce monitoring agent deployment.
Enable Boot Diagnostics
const vm = new azure.compute.VirtualMachine("my-vm", {
// ... other properties
diagnosticsProfile: {
bootDiagnostics: {
enabled: true,
storageUri: storageAccount.primaryBlobEndpoint, // Optional: specify storage account
},
},
});
Install Monitoring Agents via Extensions
For comprehensive monitoring, install the Azure Monitor Agent or Log Analytics Agent using VM extensions:
const workspace = new azure.operationalinsights.Workspace("monitoring-workspace", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: {
name: "PerGB2018",
},
});
// For Linux VMs
const monitoringExtension = new azure.compute.VirtualMachineExtension("monitoring-ext", {
resourceGroupName: resourceGroup.name,
vmName: vm.name,
publisher: "Microsoft.Azure.Monitor",
type: "AzureMonitorLinuxAgent",
typeHandlerVersion: "1.0",
autoUpgradeMinorVersion: true,
settings: {
workspaceId: workspace.customerId,
},
protectedSettings: {
workspaceKey: workspace.primarySharedKey,
},
});
vm-in-vnet
Severity: medium · Enforcement: advisory
Ensure Virtual Machines are deployed within a Virtual Network for secure network isolation.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Deploy VM in Virtual Network
Your VM must be deployed within a Virtual Network (VNet) for network isolation. Configure network interfaces properly:
- Create a VNet and subnet:
const vnet = new azure.network.VirtualNetwork("my-vnet", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
addressSpace: {
addressPrefixes: ["10.0.0.0/16"],
},
});
const subnet = new azure.network.Subnet("my-subnet", {
resourceGroupName: resourceGroup.name,
virtualNetworkName: vnet.name,
addressPrefix: "10.0.1.0/24",
});
- Create a network interface attached to the subnet:
const nic = new azure.network.NetworkInterface("my-nic", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
ipConfigurations: [{
name: "ipconfig1",
subnet: {
id: subnet.id,
},
privateIPAllocationMethod: "Dynamic",
}],
});
- Attach the network interface to your VM:
const vm = new azure.compute.VirtualMachine("my-vm", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
networkProfile: {
networkInterfaces: [{
id: nic.id,
primary: true,
}],
},
// ... other properties
});
Note: Ensure you’re using Azure Resource Manager (ARM) deployment model, not the classic deployment model.
vm-managed-identity
Severity: medium · Enforcement: advisory
Ensure Virtual Machines have managed identity attached.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Enable Managed Identity for Virtual Machine
Your VM does not have managed identity configured. Enable managed identity to eliminate the need for storing credentials:
- Configure system-assigned managed identity (recommended):
const vm = new azure.compute.VirtualMachine("my-vm", {
// ... other properties
identity: {
type: "SystemAssigned",
},
});
- Alternatively, use user-assigned managed identity:
const userIdentity = new azure.managedidentity.UserAssignedIdentity("my-identity", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
});
const vm = new azure.compute.VirtualMachine("my-vm", {
// ... other properties
identity: {
type: "UserAssigned",
userAssignedIdentities: {
[userIdentity.id]: {},
},
},
});
- For maximum flexibility, use both system-assigned and user-assigned:
const vm = new azure.compute.VirtualMachine("my-vm", {
// ... other properties
identity: {
type: "SystemAssigned,UserAssigned",
userAssignedIdentities: {
[userIdentity.id]: {},
},
},
});
After enabling managed identity, you can grant the VM access to Azure resources without storing credentials in your code.
vm-no-public-ip
Severity: high · Enforcement: advisory
Ensure Virtual Machines have no public IP.
- 12.2 — Design and maintain a secure network architecture. A secure network architecture must address segmentation, least privilege, and availability, at a minimum. Example implementations may include documentation, policy, and design components.
Remediation
Fix: Remove Public IP from Virtual Machine
Your VM has a public IP address which increases attack surface. Remove it and use secure access methods:
- Create VM without public IP address:
const nic = new azure.network.NetworkInterface("my-nic", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
ipConfigurations: [{
name: "ipconfig1",
subnet: {
id: subnet.id,
},
privateIPAllocationMethod: "Dynamic",
// Do NOT add publicIPAddress here
}],
});
const vm = new azure.compute.VirtualMachine("my-vm", {
// ... other properties
networkProfile: {
networkInterfaces: [{
id: nic.id,
}],
},
});
- Use Azure Bastion for secure RDP/SSH access (recommended):
const bastionSubnet = new azure.network.Subnet("AzureBastionSubnet", {
resourceGroupName: resourceGroup.name,
virtualNetworkName: vnet.name,
addressPrefix: "10.0.255.0/27", // Must be /27 or larger
});
const bastionPublicIp = new azure.network.PublicIPAddress("bastion-ip", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: {
name: "Standard",
},
publicIPAllocationMethod: "Static",
});
const bastion = new azure.network.BastionHost("my-bastion", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
ipConfigurations: [{
name: "ipconfig",
subnet: {
id: bastionSubnet.id,
},
publicIPAddress: {
id: bastionPublicIp.id,
},
}],
});
- Use NAT Gateway for outbound internet access only:
const natGatewayPublicIp = new azure.network.PublicIPAddress("nat-ip", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: {
name: "Standard",
},
publicIPAllocationMethod: "Static",
});
const natGateway = new azure.network.NatGateway("my-nat", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: {
name: "Standard",
},
publicIpAddresses: [{
id: natGatewayPublicIp.id,
}],
});
const subnet = new azure.network.Subnet("my-subnet", {
resourceGroupName: resourceGroup.name,
virtualNetworkName: vnet.name,
addressPrefix: "10.0.1.0/24",
natGateway: {
id: natGateway.id,
},
});
vm-not-publicly-accessible
Severity: high · Enforcement: advisory
Ensure Virtual Machines are not directly accessible from the internet for data protection and access control.
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Remove Public IP from VM Network Interface
Your VM is accessible from the internet via public IP. Remove public IP and use secure access methods:
- Create network interface without public IP:
const nic = new azure.network.NetworkInterface("my-nic", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
ipConfigurations: [{
name: "ipconfig1",
subnet: {
id: subnet.id,
},
privateIPAllocationMethod: "Dynamic",
// Do NOT include publicIPAddress configuration
}],
});
- Use Azure Bastion for secure remote access:
const bastionSubnet = new azure.network.Subnet("AzureBastionSubnet", {
resourceGroupName: resourceGroup.name,
virtualNetworkName: vnet.name,
addressPrefix: "10.0.255.0/27", // Minimum /27 required
});
const bastionPublicIp = new azure.network.PublicIPAddress("bastion-ip", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: {
name: "Standard",
},
publicIPAllocationMethod: "Static",
});
const bastion = new azure.network.BastionHost("my-bastion", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
ipConfigurations: [{
name: "ipconfig",
subnet: {
id: bastionSubnet.id,
},
publicIPAddress: {
id: bastionPublicIp.id,
},
}],
});
- Use VPN Gateway for site-to-site connectivity:
const gatewaySubnet = new azure.network.Subnet("GatewaySubnet", {
resourceGroupName: resourceGroup.name,
virtualNetworkName: vnet.name,
addressPrefix: "10.0.254.0/27",
});
const vpnGatewayPublicIp = new azure.network.PublicIPAddress("vpn-ip", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: {
name: "Basic",
},
publicIPAllocationMethod: "Dynamic",
});
const vpnGateway = new azure.network.VirtualNetworkGateway("my-vpn", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
ipConfigurations: [{
name: "ipconfig",
subnet: {
id: gatewaySubnet.id,
},
publicIPAddress: {
id: vpnGatewayPublicIp.id,
},
}],
gatewayType: "Vpn",
vpnType: "RouteBased",
sku: {
name: "VpnGw1",
tier: "VpnGw1",
},
});
- Use NAT Gateway for outbound-only internet access:
const natGatewayPublicIp = new azure.network.PublicIPAddress("nat-ip", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: {
name: "Standard",
},
publicIPAllocationMethod: "Static",
});
const natGateway = new azure.network.NatGateway("my-nat", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: {
name: "Standard",
},
publicIpAddresses: [{
id: natGatewayPublicIp.id,
}],
});
const subnet = new azure.network.Subnet("private-subnet", {
resourceGroupName: resourceGroup.name,
virtualNetworkName: vnet.name,
addressPrefix: "10.0.1.0/24",
natGateway: {
id: natGateway.id,
},
});
vm-scale-set-public-ip
Severity: critical · Enforcement: advisory
Require VM Scale Sets to have no public IP addresses
- 3.3 — Configure data access control lists based on a user’s need to know. Apply data access control lists, also known as access permissions, to local and remote file systems, databases, and applications.
Remediation
Fix: Remove Public IP Configuration from VM Scale Set
const vmss = new azurenative.compute.VirtualMachineScaleSet("my-vmss", {
sku: { name: "Standard_D2s_v3" },
virtualMachineProfile: {
networkProfile: {
networkInterfaceConfigurations: [{
name: "vmss-nic",
primary: true,
ipConfigurations: [{
name: "ipconfig1",
subnet: { id: subnet.id },
// Do NOT include publicIPAddressConfiguration
}],
}],
},
// ... other config
},
});
vmss-load-balancer-healthcheck-required
Severity: medium · Enforcement: advisory
Ensure VM Scale Sets have Load Balancer health check required for availability monitoring.
- 4.6 — Securely manage enterprise assets and software. Example implementations include managing configuration through version-controlled Infrastructure as Code, and accessing administrative interfaces over secure network protocols, such as Secure Shell (SSH) and Hypertext Transfer Protocol Secure (HTTPS). Do not use insecure management protocols, such as Telnet (Teletype Network) and HTTP, unless operationally essential.
Remediation
Fix: Configure Load Balancer with Health Checks for VM Scale Set
Your VM Scale Set lacks load balancer association with health checks. Configure it properly:
- Create a Load Balancer with health probes:
const lb = new azure.network.LoadBalancer("my-lb", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: {
name: "Standard",
},
frontendIPConfigurations: [{
name: "frontend",
subnet: {
id: subnet.id,
},
}],
probes: [{
name: "health-probe",
protocol: "Http", // or "Https", "Tcp"
port: 80,
requestPath: "/health", // For HTTP/HTTPS
intervalInSeconds: 15,
numberOfProbes: 2,
}],
backendAddressPools: [{
name: "backend-pool",
}],
loadBalancingRules: [{
name: "lb-rule",
protocol: "Tcp",
frontendPort: 80,
backendPort: 80,
frontendIPConfiguration: {
id: pulumi.interpolate`${lb.id}/frontendIPConfigurations/frontend`,
},
backendAddressPool: {
id: pulumi.interpolate`${lb.id}/backendAddressPools/backend-pool`,
},
probe: {
id: pulumi.interpolate`${lb.id}/probes/health-probe`,
},
}],
});
- Associate VM Scale Set with the Load Balancer:
const vmss = new azure.compute.VirtualMachineScaleSet("my-vmss", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: {
name: "Standard_B2s",
tier: "Standard",
capacity: 2,
},
virtualMachineProfile: {
networkProfile: {
networkInterfaceConfigurations: [{
name: "nic-config",
primary: true,
ipConfigurations: [{
name: "ip-config",
subnet: {
id: subnet.id,
},
loadBalancerBackendAddressPools: [{
id: pulumi.interpolate`${lb.id}/backendAddressPools/backend-pool`,
}],
}],
}],
},
// ... other properties
},
});
- Alternatively, use Application Gateway with health probes:
const appGw = new azure.network.ApplicationGateway("my-appgw", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: {
name: "Standard_v2",
tier: "Standard_v2",
capacity: 2,
},
probes: [{
name: "health-probe",
protocol: "Http",
path: "/health",
interval: 30,
timeout: 30,
unhealthyThreshold: 3,
host: "localhost",
}],
// ... other configuration
});
- Add Application Health Extension to VM Scale Set for enhanced monitoring:
const vmss = new azure.compute.VirtualMachineScaleSet("my-vmss", {
// ... other properties
virtualMachineProfile: {
extensionProfile: {
extensions: [{
name: "HealthExtension",
publisher: "Microsoft.ManagedServices",
type: "ApplicationHealthLinux", // or ApplicationHealthWindows
typeHandlerVersion: "1.0",
autoUpgradeMinorVersion: true,
settings: {
protocol: "http",
port: 80,
requestPath: "/health",
},
}],
},
// ... other properties
},
});
vnet-flow-logs-enabled
Severity: medium · Enforcement: advisory
Collect audit logs from VNet flow logs for network monitoring.
- 8.2 — Collect audit logs. Ensure that logging, per the enterprise’s audit log management process, has been enabled across enterprise assets.
Remediation
Fix: Enable VNet Flow Logs for Network Monitoring
Your Virtual Network does not have flow logs configured for security monitoring and audit collection. Enable flow logs to track network traffic:
Step 1: Create a Network Watcher (if not exists)
const networkWatcher = new azure.network.NetworkWatcher("myNetworkWatcher", {
resourceGroupName: resourceGroup.name,
location: "eastus",
});
Step 2: Create a Storage Account for Flow Logs
const storageAccount = new azure.storage.StorageAccount("flowlogsstorage", {
resourceGroupName: resourceGroup.name,
kind: "StorageV2",
sku: { name: "Standard_LRS" },
});
Step 3: Create a Log Analytics Workspace (recommended)
const workspace = new azure.operationalinsights.Workspace("myWorkspace", {
resourceGroupName: resourceGroup.name,
sku: { name: "PerGB2018" },
retentionInDays: 30,
});
Step 4: Enable Flow Logs on Your VNet/NSG
const flowLog = new azure.network.FlowLog("myFlowLog", {
networkWatcherName: networkWatcher.name,
resourceGroupName: resourceGroup.name,
targetResourceId: nsg.id, // Or vnet.id for VNet flow logs
storageId: storageAccount.id,
enabled: true,
retentionPolicy: {
enabled: true,
days: 30,
},
flowAnalyticsConfiguration: {
networkWatcherFlowAnalyticsConfiguration: {
enabled: true,
workspaceResourceId: workspace.id,
trafficAnalyticsInterval: 10,
},
},
});
This captures all network traffic for security analysis and compliance audit trails.
vnet-nsg-associated-to-nic
Severity: medium · Enforcement: advisory
Ensure VNet network security groups are associated with network interfaces to maintain proper security controls.
- 1.1 — Establish and maintain an accurate, detailed, and up-to-date inventory of all enterprise assets with the potential to store or process data, to include: end-user devices (including portable and mobile), network devices, non-computing/IoT devices, and servers. Ensure the inventory records the network address (if static), hardware address, machine name, enterprise asset owner, department for each asset, and whether the asset has been approved to connect to the network. For mobile end-user devices, MDM type tools can support this process, where appropriate. This inventory includes assets connected to the infrastructure physically, virtually, remotely, and those within cloud environments. Additionally, it includes assets that are regularly connected to the enterprise’s network infrastructure, even if they are not under control of the enterprise. Review and update the inventory of all enterprise assets bi-annually, or more frequently.
Remediation
Fix: Associate Network Security Group to Network Interface
Your Network Interface doesn’t have a Network Security Group (NSG) associated with it. NSGs provide network-level access control for your resources.
Associate NSG to Network Interface
const nsg = new azure.network.NetworkSecurityGroup("myNSG", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
securityRules: [
{
name: "AllowHTTPS",
priority: 100,
direction: "Inbound",
access: "Allow",
protocol: "Tcp",
sourcePortRange: "*",
destinationPortRange: "443",
sourceAddressPrefix: "*",
destinationAddressPrefix: "*",
},
],
});
const nic = new azure.network.NetworkInterface("myNIC", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
networkSecurityGroup: {
id: nsg.id,
},
ipConfigurations: [{
name: "ipconfig1",
subnet: { id: subnet.id },
privateIPAllocationMethod: "Dynamic",
}],
});
Note: You can associate NSGs at either the NIC level or the subnet level. NIC-level NSGs provide more granular control, while subnet-level NSGs apply to all resources in the subnet.
vnet-nsg-unused
Severity: low · Enforcement: advisory
Ensure VNet network security groups are not unused to maintain asset inventory hygiene.
- 1.1 — Establish and maintain an accurate, detailed, and up-to-date inventory of all enterprise assets with the potential to store or process data, to include: end-user devices (including portable and mobile), network devices, non-computing/IoT devices, and servers. Ensure the inventory records the network address (if static), hardware address, machine name, enterprise asset owner, department for each asset, and whether the asset has been approved to connect to the network. For mobile end-user devices, MDM type tools can support this process, where appropriate. This inventory includes assets connected to the infrastructure physically, virtually, remotely, and those within cloud environments. Additionally, it includes assets that are regularly connected to the enterprise’s network infrastructure, even if they are not under control of the enterprise. Review and update the inventory of all enterprise assets bi-annually, or more frequently.
Remediation
Fix: Remove Unused Network Security Group
Your Network Security Group is not associated with any subnet or network interface. Unused NSGs create management overhead and potential security risks.
Option 1: Associate NSG with a Subnet
const subnet = new azure.network.Subnet("mySubnet", {
resourceGroupName: resourceGroup.name,
virtualNetworkName: vnet.name,
addressPrefix: "10.0.1.0/24",
networkSecurityGroup: {
id: nsg.id,
},
});
Option 2: Associate NSG with a Network Interface
const nic = new azure.network.NetworkInterface("myNIC", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
networkSecurityGroup: {
id: nsg.id,
},
ipConfigurations: [{
name: "ipconfig1",
subnet: { id: subnet.id },
}],
});
Option 3: Delete Unused NSG
If the NSG is not needed, remove it from your Pulumi program:
// Remove or comment out the unused NSG resource
// const nsg = new azure.network.NetworkSecurityGroup("unusedNSG", {
// ...
// });
Note: Before deleting an NSG, verify it’s not referenced by any resources outside your Pulumi program.
vnet-public-ip-associated
Severity: low · Enforcement: advisory
Ensure VNet public IPs are associated with resources to maintain asset inventory hygiene.
- 1.1 — Establish and maintain an accurate, detailed, and up-to-date inventory of all enterprise assets with the potential to store or process data, to include: end-user devices (including portable and mobile), network devices, non-computing/IoT devices, and servers. Ensure the inventory records the network address (if static), hardware address, machine name, enterprise asset owner, department for each asset, and whether the asset has been approved to connect to the network. For mobile end-user devices, MDM type tools can support this process, where appropriate. This inventory includes assets connected to the infrastructure physically, virtually, remotely, and those within cloud environments. Additionally, it includes assets that are regularly connected to the enterprise’s network infrastructure, even if they are not under control of the enterprise. Review and update the inventory of all enterprise assets bi-annually, or more frequently.
Remediation
Fix: Associate Public IP with a Resource
Your Public IP Address is not associated with any resource. Unassociated public IPs incur costs and create potential security risks.
Option 1: Associate with Network Interface
const publicIP = new azure.network.PublicIPAddress("myPublicIP", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
publicIPAllocationMethod: "Static",
sku: { name: "Standard" },
});
const nic = new azure.network.NetworkInterface("myNIC", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
ipConfigurations: [{
name: "ipconfig1",
subnet: { id: subnet.id },
publicIPAddress: {
id: publicIP.id,
},
privateIPAllocationMethod: "Dynamic",
}],
});
Option 2: Associate with Load Balancer
const loadBalancer = new azure.network.LoadBalancer("myLB", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
frontendIPConfigurations: [{
name: "frontend",
publicIPAddress: {
id: publicIP.id,
},
}],
});
Option 3: Delete Unused Public IP
If not needed, remove it from your Pulumi program:
// Remove or comment out the unused Public IP resource
// const publicIP = new azure.network.PublicIPAddress("unusedIP", {
// ...
// });
Note: Unused public IPs incur charges. Delete them if they’re not needed to reduce costs and attack surface.
waf-logging-enabled
Severity: medium · Enforcement: advisory
Ensure Web Application Firewall logging is enabled for security analysis.
- 3.14 — Log sensitive data access, including modification and disposal.
Remediation
Fix: Enable WAF Logging on Application Gateway
Your Application Gateway with WAF enabled doesn’t have diagnostic settings configured. WAF logs are critical for detecting and responding to security threats.
Configure WAF Diagnostic Settings
const logAnalyticsWorkspace = new azure.operationalinsights.Workspace("logs", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: { name: "PerGB2018" },
});
const appGateway = new azure.network.ApplicationGateway("myGateway", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
// ... Application Gateway configuration with WAF enabled
webApplicationFirewallConfiguration: {
enabled: true,
firewallMode: "Prevention",
ruleSetType: "OWASP",
ruleSetVersion: "3.2",
},
});
const diagnosticSetting = new azure.monitor.DiagnosticSetting("waf-diagnostics", {
resourceUri: appGateway.id,
workspaceId: logAnalyticsWorkspace.id,
logs: [
{
category: "ApplicationGatewayAccessLog",
enabled: true,
retentionPolicy: {
enabled: true,
days: 365,
},
},
{
category: "ApplicationGatewayPerformanceLog",
enabled: true,
retentionPolicy: {
enabled: true,
days: 365,
},
},
{
category: "ApplicationGatewayFirewallLog", // Critical for WAF
enabled: true,
retentionPolicy: {
enabled: true,
days: 365,
},
},
],
metrics: [{
category: "AllMetrics",
enabled: true,
retentionPolicy: {
enabled: true,
days: 365,
},
}],
});
Note: The ApplicationGatewayFirewallLog category contains WAF detection and prevention events. This is essential for security monitoring and incident response.
wafv2-logging-enabled
Severity: high · Enforcement: advisory
Collect audit logs from Web Application Firewall v2 for security monitoring.
- 8.2 — Collect audit logs. Ensure that logging, per the enterprise’s audit log management process, has been enabled across enterprise assets.
Remediation
Fix: Enable WAF v2 Audit Logging
Your Application Gateway WAF v2 doesn’t have comprehensive audit logging configured. WAF logs are essential for threat detection and compliance.
Configure WAF v2 Diagnostic Settings
const logAnalyticsWorkspace = new azure.operationalinsights.Workspace("logs", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: { name: "PerGB2018" },
});
const appGateway = new azure.network.ApplicationGateway("myGateway", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
// Use WAF_v2 SKU
sku: {
name: "WAF_v2",
tier: "WAF_v2",
capacity: 2,
},
webApplicationFirewallConfiguration: {
enabled: true,
firewallMode: "Prevention",
ruleSetType: "OWASP",
ruleSetVersion: "3.2",
},
});
const diagnosticSetting = new azure.monitor.DiagnosticSetting("waf-diagnostics", {
resourceUri: appGateway.id,
workspaceId: logAnalyticsWorkspace.id,
logs: [
{
category: "ApplicationGatewayAccessLog",
enabled: true,
retentionPolicy: { enabled: true, days: 30 },
},
{
category: "ApplicationGatewayFirewallLog",
enabled: true,
retentionPolicy: { enabled: true, days: 30 },
},
{
category: "ApplicationGatewayPerformanceLog",
enabled: true,
retentionPolicy: { enabled: true, days: 30 },
},
],
metrics: [{
category: "AllMetrics",
enabled: true,
}],
});
Add Storage Account for Long-Term Retention
const storageAccount = new azure.storage.StorageAccount("waflogstorage", {
resourceGroupName: resourceGroup.name,
location: resourceGroup.location,
sku: { name: "Standard_LRS" },
kind: "StorageV2",
});
const diagnosticSetting = new azure.monitor.DiagnosticSetting("waf-diagnostics", {
resourceUri: appGateway.id,
workspaceId: logAnalyticsWorkspace.id,
storageAccountId: storageAccount.id, // For long-term retention
logs: [
{ category: "ApplicationGatewayAccessLog", enabled: true },
{ category: "ApplicationGatewayFirewallLog", enabled: true },
{ category: "ApplicationGatewayPerformanceLog", enabled: true },
],
});
Note: WAF v2 provides enhanced threat detection. Ensure your Application Gateway uses the WAF_v2 SKU tier for full functionality.