diff --git a/infrastructure/afd-apim-pe/main.bicep b/infrastructure/afd-apim-pe/main.bicep index d9ca9bce..a2af8052 100644 --- a/infrastructure/afd-apim-pe/main.bicep +++ b/infrastructure/afd-apim-pe/main.bicep @@ -72,7 +72,15 @@ module appInsightsModule '../../shared/bicep/modules/monitor/v1/appinsights.bice var appInsightsId = appInsightsModule.outputs.id var appInsightsInstrumentationKey = appInsightsModule.outputs.instrumentationKey -// 3. Virtual Network and Subnets +// 3. Network Watcher (required for NSG flow logs) +module networkWatcherModule '../../shared/bicep/modules/network-watcher/v1/network-watcher.bicep' = { + name: 'networkWatcherModule' + params: { + location: location + } +} + +// 4. Virtual Network and Subnets // We are using a standard NSG for our subnets here. Production workloads should use a relevant, custom NSG for each subnet. // We also do not presently use a custom route table for the subnets, which is a best practice for production workloads. @@ -81,6 +89,9 @@ var appInsightsInstrumentationKey = appInsightsModule.outputs.instrumentationKey resource nsg 'Microsoft.Network/networkSecurityGroups@2025-01-01' = { name: 'nsg-default' location: location + dependsOn: [ + networkWatcherModule + ] } module vnetModule '../../shared/bicep/modules/vnet/v1/vnet.bicep' = { @@ -150,7 +161,7 @@ resource acaSubnetResource 'Microsoft.Network/virtualNetworks/subnets@2024-05-01 var apimSubnetResourceId = apimSubnetResource.id var acaSubnetResourceId = acaSubnetResource.id -// 4. Azure Container App Environment (ACAE) +// 5. Azure Container App Environment (ACAE) module acaEnvModule '../../shared/bicep/modules/aca/v1/environment.bicep' = if (useACA) { name: 'acaEnvModule' params: { @@ -161,7 +172,7 @@ module acaEnvModule '../../shared/bicep/modules/aca/v1/environment.bicep' = if ( } } -// 5. Azure Container Apps (ACA) for Mock Web API +// 6. Azure Container Apps (ACA) for Mock Web API module acaModule1 '../../shared/bicep/modules/aca/v1/containerapp.bicep' = if (useACA) { name: 'acaModule-1' params: { @@ -180,7 +191,7 @@ module acaModule2 '../../shared/bicep/modules/aca/v1/containerapp.bicep' = if (u } } -// 6. API Management +// 7. API Management module apimModule '../../shared/bicep/modules/apim/v1/apim.bicep' = { name: 'apimModule' params: { @@ -193,7 +204,7 @@ module apimModule '../../shared/bicep/modules/apim/v1/apim.bicep' = { } } -// 7. APIM Policy Fragments +// 8. APIM Policy Fragments module policyFragmentModule '../../shared/bicep/modules/apim/v1/policy-fragment.bicep' = [for pf in policyFragments: { name: 'pf-${pf.name}' params:{ @@ -207,7 +218,7 @@ module policyFragmentModule '../../shared/bicep/modules/apim/v1/policy-fragment. ] }] -// 8. APIM Backends for ACA +// 9. APIM Backends for ACA module backendModule1 '../../shared/bicep/modules/apim/v1/backend.bicep' = if (useACA) { name: 'aca-backend-1' params: { @@ -256,7 +267,7 @@ module backendPoolModule '../../shared/bicep/modules/apim/v1/backend-pool.bicep' ] } -// 9. APIM APIs +// 10. APIM APIs module apisModule '../../shared/bicep/modules/apim/v1/api.bicep' = [for api in apis: if(length(apis) > 0) { name: 'api-${api.name}' params: { @@ -275,7 +286,7 @@ module apisModule '../../shared/bicep/modules/apim/v1/api.bicep' = [for api in a ] }] -// 10. APIM Private DNS Zone, VNet Link, and (optional) DNS Zone Group +// 11. APIM Private DNS Zone, VNet Link, and (optional) DNS Zone Group module apimDnsPrivateLinkModule '../../shared/bicep/modules/dns/v1/dns-private-link.bicep' = { name: 'apimDnsPrivateLinkModule' params: { @@ -288,7 +299,7 @@ module apimDnsPrivateLinkModule '../../shared/bicep/modules/dns/v1/dns-private-l } } -// 11. ACA Private DNS Zone (regional, e.g., eastus2.azurecontainerapps.io), VNet Link, and wildcard A record via shared module +// 12. ACA Private DNS Zone (regional, e.g., eastus2.azurecontainerapps.io), VNet Link, and wildcard A record via shared module module acaDnsPrivateZoneModule '../../shared/bicep/modules/dns/v1/aca-dns-private-zone.bicep' = if (useACA && !empty(acaSubnetResourceId)) { name: 'acaDnsPrivateZoneModule' params: { @@ -298,7 +309,7 @@ module acaDnsPrivateZoneModule '../../shared/bicep/modules/dns/v1/aca-dns-privat } } -// 12. Front Door +// 13. Front Door module afdModule '../../shared/bicep/modules/afd/v1/afd.bicep' = { name: 'afdModule' params: { diff --git a/infrastructure/appgw-apim-pe/main.bicep b/infrastructure/appgw-apim-pe/main.bicep index ab705a8d..680d23cf 100644 --- a/infrastructure/appgw-apim-pe/main.bicep +++ b/infrastructure/appgw-apim-pe/main.bicep @@ -96,16 +96,30 @@ module appInsightsModule '../../shared/bicep/modules/monitor/v1/appinsights.bice var appInsightsId = appInsightsModule.outputs.id var appInsightsInstrumentationKey = appInsightsModule.outputs.instrumentationKey -// 3. Virtual Network and Subnets +// 3. Network Watcher (required for NSG flow logs) +module networkWatcherModule '../../shared/bicep/modules/network-watcher/v1/network-watcher.bicep' = { + name: 'networkWatcherModule' + params: { + location: location + } +} + +// 4. Virtual Network and Subnets resource nsgDefault 'Microsoft.Network/networkSecurityGroups@2025-01-01' = { name: 'nsg-default' location: location + dependsOn: [ + networkWatcherModule + ] } // App Gateway needs a specific NSG resource nsgAppGw 'Microsoft.Network/networkSecurityGroups@2025-01-01' = { name: 'nsg-appgw' location: location + dependsOn: [ + networkWatcherModule + ] properties: { securityRules: [ { @@ -226,7 +240,7 @@ var acaSubnetResourceId = '${vnetModule.outputs.vnetId}/subnets/${acaSubnetNam var appgwSubnetResourceId = '${vnetModule.outputs.vnetId}/subnets/${appgwSubnetName}' var peSubnetResourceId = '${vnetModule.outputs.vnetId}/subnets/${privateEndpointSubnetName}' -// 4. User Assigned Managed Identity +// 5. User Assigned Managed Identity // https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/managed-identity/user-assigned-identity module uamiModule 'br/public:avm/res/managed-identity/user-assigned-identity:0.4.2' = { name: 'uamiModule' @@ -236,7 +250,7 @@ module uamiModule 'br/public:avm/res/managed-identity/user-assigned-identity:0.4 } } -// 5. Key Vault +// 6. Key Vault // https://learn.microsoft.com/azure/templates/microsoft.keyvault/vaults // This assignment is helpful for testing to allow you to examine and administer the Key Vault. Adjust accordingly for real workloads! var keyVaultAdminRoleAssignment = setCurrentUserAsKeyVaultAdmin && !empty(currentUserId) ? [ @@ -269,7 +283,7 @@ module keyVaultModule 'br/public:avm/res/key-vault/vault:0.13.3' = { } } -// 6. Public IP for Application Gateway +// 7. Public IP for Application Gateway // https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/network/public-ip-address module appgwPipModule 'br/public:avm/res/network/public-ip-address:0.9.1' = { name: 'appgwPipModule' @@ -282,7 +296,7 @@ module appgwPipModule 'br/public:avm/res/network/public-ip-address:0.9.1' = { } } -// 7. WAF Policy for Application Gateway +// 8. WAF Policy for Application Gateway // https://learn.microsoft.com/azure/templates/microsoft.network/applicationgatewaywebapplicationfirewallpolicies resource wafPolicy 'Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies@2025-01-01' = { name: 'waf-${resourceSuffix}' @@ -308,7 +322,7 @@ resource wafPolicy 'Microsoft.Network/ApplicationGatewayWebApplicationFirewallPo } } -// 8. Azure Container App Environment (ACAE) +// 9. Azure Container App Environment (ACAE) module acaEnvModule '../../shared/bicep/modules/aca/v1/environment.bicep' = if (useACA) { name: 'acaEnvModule' params: { @@ -319,7 +333,7 @@ module acaEnvModule '../../shared/bicep/modules/aca/v1/environment.bicep' = if ( } } -// 9. Azure Container Apps (ACA) for Mock Web API +// 10. Azure Container Apps (ACA) for Mock Web API module acaModule1 '../../shared/bicep/modules/aca/v1/containerapp.bicep' = if (useACA) { name: 'acaModule-1' params: { @@ -338,7 +352,7 @@ module acaModule2 '../../shared/bicep/modules/aca/v1/containerapp.bicep' = if (u } } -// 10. API Management +// 11. API Management module apimModule '../../shared/bicep/modules/apim/v1/apim.bicep' = { name: 'apimModule' params: { @@ -352,7 +366,7 @@ module apimModule '../../shared/bicep/modules/apim/v1/apim.bicep' = { } } -// 11. APIM Policy Fragments +// 12. APIM Policy Fragments module policyFragmentModule '../../shared/bicep/modules/apim/v1/policy-fragment.bicep' = [for pf in policyFragments: { name: 'pf-${pf.name}' params:{ @@ -366,7 +380,7 @@ module policyFragmentModule '../../shared/bicep/modules/apim/v1/policy-fragment. ] }] -// 12. APIM Backends for ACA +// 13. APIM Backends for ACA module backendModule1 '../../shared/bicep/modules/apim/v1/backend.bicep' = if (useACA) { name: 'aca-backend-1' params: { @@ -415,7 +429,7 @@ module backendPoolModule '../../shared/bicep/modules/apim/v1/backend-pool.bicep' ] } -// 13. APIM APIs +// 14. APIM APIs module apisModule '../../shared/bicep/modules/apim/v1/api.bicep' = [for api in apis: if(length(apis) > 0) { name: 'api-${api.name}' params: { @@ -434,7 +448,7 @@ module apisModule '../../shared/bicep/modules/apim/v1/api.bicep' = [for api in a ] }] -// 14. Private Endpoint for APIM +// 15. Private Endpoint for APIM // https://learn.microsoft.com/azure/templates/microsoft.network/privateendpoints resource apimPrivateEndpoint 'Microsoft.Network/privateEndpoints@2024-05-01' = { name: 'pe-apim-${resourceSuffix}' @@ -457,7 +471,7 @@ resource apimPrivateEndpoint 'Microsoft.Network/privateEndpoints@2024-05-01' = { } } -// 15. Private DNS Zone Group for APIM Private Endpoint +// 16. Private DNS Zone Group for APIM Private Endpoint // https://learn.microsoft.com/azure/templates/microsoft.network/privateendpoints/privatednszoneegroups resource apimPrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2024-05-01' = { name: 'apim-dns-zone-group' @@ -474,7 +488,7 @@ resource apimPrivateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZ } } -// 16. APIM Private DNS Zone, VNet Link +// 17. APIM Private DNS Zone, VNet Link module apimDnsPrivateLinkModule '../../shared/bicep/modules/dns/v1/dns-private-link.bicep' = { name: 'apimDnsPrivateLinkModule' params: { @@ -487,7 +501,7 @@ module apimDnsPrivateLinkModule '../../shared/bicep/modules/dns/v1/dns-private-l } } -// 17. ACA Private DNS Zone +// 18. ACA Private DNS Zone module acaDnsPrivateZoneModule '../../shared/bicep/modules/dns/v1/aca-dns-private-zone.bicep' = if (useACA) { name: 'acaDnsPrivateZoneModule' params: { @@ -497,7 +511,7 @@ module acaDnsPrivateZoneModule '../../shared/bicep/modules/dns/v1/aca-dns-privat } } -// 18. Application Gateway +// 19. Application Gateway // https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/network/application-gateway module appgwModule 'br/public:avm/res/network/application-gateway:0.7.2' = { name: 'appgwModule' diff --git a/infrastructure/appgw-apim/main.bicep b/infrastructure/appgw-apim/main.bicep index 55c1184c..5e56b26b 100644 --- a/infrastructure/appgw-apim/main.bicep +++ b/infrastructure/appgw-apim/main.bicep @@ -106,11 +106,22 @@ module appInsightsModule '../../shared/bicep/modules/monitor/v1/appinsights.bice var appInsightsId = appInsightsModule.outputs.id var appInsightsInstrumentationKey = appInsightsModule.outputs.instrumentationKey -// 3. Virtual Network and Subnets +// 3. Network Watcher (required for NSG flow logs) +module networkWatcherModule '../../shared/bicep/modules/network-watcher/v1/network-watcher.bicep' = { + name: 'networkWatcherModule' + params: { + location: location + } +} + +// 4. Virtual Network and Subnets // https://learn.microsoft.com/azure/templates/microsoft.network/networksecuritygroups resource nsgDefault 'Microsoft.Network/networkSecurityGroups@2025-01-01' = { name: 'nsg-default' location: location + dependsOn: [ + networkWatcherModule + ] } // App Gateway needs a specific NSG @@ -118,6 +129,9 @@ resource nsgDefault 'Microsoft.Network/networkSecurityGroups@2025-01-01' = { resource nsgAppGw 'Microsoft.Network/networkSecurityGroups@2025-01-01' = { name: 'nsg-appgw' location: location + dependsOn: [ + networkWatcherModule + ] properties: { securityRules: [ { @@ -172,6 +186,9 @@ resource nsgApimV1 'Microsoft.Network/networkSecurityGroups@2025-01-01' = if (is // resource nsgApimV1 'Microsoft.Network/networkSecurityGroups@2025-01-01' = { name: 'nsg-apim' location: location + dependsOn: [ + networkWatcherModule + ] properties: { securityRules: [ // INBOUND Security Rules @@ -344,7 +361,7 @@ var apimSubnetResourceId = '${vnetModule.outputs.vnetId}/subnets/${apimSubnetNa var acaSubnetResourceId = '${vnetModule.outputs.vnetId}/subnets/${acaSubnetName}' var appgwSubnetResourceId = '${vnetModule.outputs.vnetId}/subnets/${appgwSubnetName}' -// 4. User Assigned Managed Identity +// 5. User Assigned Managed Identity // https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/managed-identity/user-assigned-identity module uamiModule 'br/public:avm/res/managed-identity/user-assigned-identity:0.4.2' = { name: 'uamiModule' @@ -354,7 +371,7 @@ module uamiModule 'br/public:avm/res/managed-identity/user-assigned-identity:0.4 } } -// 5. Key Vault +// 6. Key Vault // https://learn.microsoft.com/azure/templates/microsoft.keyvault/vaults // This assignment is helpful for testing to allow you to examine and administer the Key Vault. Adjust accordingly for real workloads! var keyVaultAdminRoleAssignment = setCurrentUserAsKeyVaultAdmin && !empty(currentUserId) ? [ @@ -387,7 +404,7 @@ module keyVaultModule 'br/public:avm/res/key-vault/vault:0.13.3' = { } } -// 6. Public IP for Application Gateway +// 7. Public IP for Application Gateway // https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/network/public-ip-address module appgwPipModule 'br/public:avm/res/network/public-ip-address:0.9.1' = { name: 'appgwPipModule' @@ -400,7 +417,7 @@ module appgwPipModule 'br/public:avm/res/network/public-ip-address:0.9.1' = { } } -// 7. WAF Policy for Application Gateway +// 8. WAF Policy for Application Gateway // https://learn.microsoft.com/azure/templates/microsoft.network/applicationgatewaywebapplicationfirewallpolicies resource wafPolicy 'Microsoft.Network/ApplicationGatewayWebApplicationFirewallPolicies@2025-01-01' = { name: 'waf-${resourceSuffix}' @@ -426,7 +443,7 @@ resource wafPolicy 'Microsoft.Network/ApplicationGatewayWebApplicationFirewallPo } } -// 8. Azure Container App Environment (ACAE) +// 9. Azure Container App Environment (ACAE) module acaEnvModule '../../shared/bicep/modules/aca/v1/environment.bicep' = if (useACA) { name: 'acaEnvModule' params: { @@ -437,7 +454,7 @@ module acaEnvModule '../../shared/bicep/modules/aca/v1/environment.bicep' = if ( } } -// 9. Azure Container Apps (ACA) for Mock Web API +// 10. Azure Container Apps (ACA) for Mock Web API module acaModule1 '../../shared/bicep/modules/aca/v1/containerapp.bicep' = if (useACA) { name: 'acaModule-1' params: { @@ -455,7 +472,7 @@ module acaModule2 '../../shared/bicep/modules/aca/v1/containerapp.bicep' = if (u } } -// 10. API Management (VNet Internal) +// 11. API Management (VNet Internal) module apimModule '../../shared/bicep/modules/apim/v1/apim.bicep' = { name: 'apimModule' params: { @@ -470,7 +487,7 @@ module apimModule '../../shared/bicep/modules/apim/v1/apim.bicep' = { } } -// 11. APIM Policy Fragments +// 12. APIM Policy Fragments module policyFragmentModule '../../shared/bicep/modules/apim/v1/policy-fragment.bicep' = [for pf in policyFragments: { name: 'pf-${pf.name}' params:{ @@ -484,7 +501,7 @@ module policyFragmentModule '../../shared/bicep/modules/apim/v1/policy-fragment. ] }] -// 12. APIM Backends for ACA +// 13. APIM Backends for ACA module backendModule1 '../../shared/bicep/modules/apim/v1/backend.bicep' = if (useACA) { name: 'aca-backend-1' params: { @@ -533,7 +550,7 @@ module backendPoolModule '../../shared/bicep/modules/apim/v1/backend-pool.bicep' ] } -// 13. APIM APIs +// 14. APIM APIs module apisModule '../../shared/bicep/modules/apim/v1/api.bicep' = [for api in apis: if(length(apis) > 0) { name: 'api-${api.name}' params: { @@ -552,7 +569,7 @@ module apisModule '../../shared/bicep/modules/apim/v1/api.bicep' = [for api in a ] }] -// 14. Application Gateway +// 15. Application Gateway // https://github.com/Azure/bicep-registry-modules/tree/main/avm/res/network/application-gateway module appgwModule 'br/public:avm/res/network/application-gateway:0.7.2' = { name: 'appgwModule' diff --git a/shared/bicep/modules/network-watcher/v1/network-watcher.bicep b/shared/bicep/modules/network-watcher/v1/network-watcher.bicep new file mode 100644 index 00000000..2b9a1a32 --- /dev/null +++ b/shared/bicep/modules/network-watcher/v1/network-watcher.bicep @@ -0,0 +1,37 @@ +/** + * @module network-watcher-v1 + * @description This module defines the Azure Network Watcher resource using Bicep. + * Network Watcher is required for NSG flow logs and other network monitoring features. + */ + + +// ------------------------------ +// PARAMETERS +// ------------------------------ + +@description('Location to be used for Network Watcher. Defaults to the resource group location') +param location string = resourceGroup().location + +@description('Name of the Network Watcher resource. Defaults to "NetworkWatcher_".') +param networkWatcherName string = 'NetworkWatcher_${location}' + + +// ------------------------------ +// RESOURCES +// ------------------------------ + +// https://learn.microsoft.com/azure/templates/microsoft.network/networkwatchers +resource networkWatcher 'Microsoft.Network/networkWatchers@2023-11-01' = { + name: networkWatcherName + location: location + properties: {} +} + + +// ------------------------------ +// OUTPUTS +// ------------------------------ + +output id string = networkWatcher.id +output name string = networkWatcher.name +output location string = networkWatcher.location