From 9d8d1e29bb05e688a8f412a5865b0df81b5d8fd6 Mon Sep 17 00:00:00 2001 From: Shivam Gupta Date: Tue, 29 Jul 2025 21:50:42 -0700 Subject: [PATCH 01/12] added ARM, bicep and terraform templates for NGroups ACI --- recipe-packs/ACI-NGroups-Gateway.yaml | 535 ++++++++++++++++++ recipe-packs/ACI-NGroups-LoadBalancer.yaml | 450 +++++++++++++++ recipe-packs/ACI-Ngroups-basic.yaml | 85 +++ recipe-packs/bicep/ACI-NGroups-Gateway.bicep | 519 +++++++++++++++++ .../bicep/ACI-NGroups-LoadBalancer.bicep | 433 ++++++++++++++ recipe-packs/bicep/Ngroups-basic.bicep | 76 +++ recipe-packs/terraform/README.md | 139 +++++ .../terraform/aci-ngroups-basic/README.md | 79 +++ .../terraform/aci-ngroups-basic/main.tf | 147 +++++ .../terraform/aci-ngroups-basic/outputs.tf | 36 ++ .../terraform.tfvars.example | 21 + .../terraform/aci-ngroups-basic/variables.tf | 69 +++ .../terraform/aci-ngroups-gateway/README.md | 183 ++++++ .../terraform/aci-ngroups-gateway/main.tf | 400 +++++++++++++ .../terraform/aci-ngroups-gateway/outputs.tf | 96 ++++ .../terraform.tfvars.example | 39 ++ .../aci-ngroups-gateway/variables.tf | 142 +++++ .../aci-ngroups-loadbalancer/README.md | 224 ++++++++ .../aci-ngroups-loadbalancer/main.tf | 387 +++++++++++++ .../aci-ngroups-loadbalancer/outputs.tf | 121 ++++ .../terraform.tfvars.example | 48 ++ .../aci-ngroups-loadbalancer/variables.tf | 172 ++++++ 22 files changed, 4401 insertions(+) create mode 100644 recipe-packs/ACI-NGroups-Gateway.yaml create mode 100644 recipe-packs/ACI-NGroups-LoadBalancer.yaml create mode 100644 recipe-packs/ACI-Ngroups-basic.yaml create mode 100644 recipe-packs/bicep/ACI-NGroups-Gateway.bicep create mode 100644 recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep create mode 100644 recipe-packs/bicep/Ngroups-basic.bicep create mode 100644 recipe-packs/terraform/README.md create mode 100644 recipe-packs/terraform/aci-ngroups-basic/README.md create mode 100644 recipe-packs/terraform/aci-ngroups-basic/main.tf create mode 100644 recipe-packs/terraform/aci-ngroups-basic/outputs.tf create mode 100644 recipe-packs/terraform/aci-ngroups-basic/terraform.tfvars.example create mode 100644 recipe-packs/terraform/aci-ngroups-basic/variables.tf create mode 100644 recipe-packs/terraform/aci-ngroups-gateway/README.md create mode 100644 recipe-packs/terraform/aci-ngroups-gateway/main.tf create mode 100644 recipe-packs/terraform/aci-ngroups-gateway/outputs.tf create mode 100644 recipe-packs/terraform/aci-ngroups-gateway/terraform.tfvars.example create mode 100644 recipe-packs/terraform/aci-ngroups-gateway/variables.tf create mode 100644 recipe-packs/terraform/aci-ngroups-loadbalancer/README.md create mode 100644 recipe-packs/terraform/aci-ngroups-loadbalancer/main.tf create mode 100644 recipe-packs/terraform/aci-ngroups-loadbalancer/outputs.tf create mode 100644 recipe-packs/terraform/aci-ngroups-loadbalancer/terraform.tfvars.example create mode 100644 recipe-packs/terraform/aci-ngroups-loadbalancer/variables.tf diff --git a/recipe-packs/ACI-NGroups-Gateway.yaml b/recipe-packs/ACI-NGroups-Gateway.yaml new file mode 100644 index 00000000..7269689b --- /dev/null +++ b/recipe-packs/ACI-NGroups-Gateway.yaml @@ -0,0 +1,535 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "apiVersion": { + "type": "string", + "defaultValue": "2024-09-01-preview", + "maxLength": 32 + }, + "nGroupsNameParam": { + "type": "string", + "defaultValue": "nGroups_lin100_regional_ag", + "maxLength": 64 + }, + "containerGroupProfileName": { + "type": "string", + "defaultValue": "cgp", + "maxLength": 64 + }, + "applicationGatewayName": { + "type": "string", + "defaultValue": "agw1", + "maxLength": 64 + }, + "publicIPName": { + "type": "string", + "defaultValue": "publicIP", + "maxLength": 64 + }, + "backendAddressPoolName": { + "type": "string", + "defaultValue": "bepool", + "maxLength": 64 + }, + "vnetName": { + "type": "string", + "defaultValue": "vnet1", + "maxLength": 64 + }, + "networkSecurityGroupName": { + "type": "string", + "defaultValue": "nsg1", + "maxLength": 64 + }, + "desiredCount": { + "type": "int", + "defaultValue": 100 + }, + "maintainDesiredCount": { + "type": "bool", + "defaultValue": true + }, + "zones": { + "type": "array", + "defaultValue": [] + }, + "vnetAddressPrefix": { + "type": "string", + "defaultValue": "172.16.0.0/23", + "maxLength": 64 + }, + "aciSubnetAddressPrefix": { + "type": "string", + "defaultValue": "172.16.0.0/25", + "maxLength": 64 + }, + "appGatewaySubnetAddressPrefix": { + "type": "string", + "defaultValue": "172.16.1.0/25", + "maxLength": 64 + }, + "aciSubnetName": { + "type": "string", + "defaultValue": "aciSubnet", + "maxLength": 64 + }, + "appGatewaySubnetName": { + "type": "string", + "defaultValue": "appgatewaySubnet", + "maxLength": 64 + }, + "ddosProtectionPlanName": { + "type": "string", + "defaultValue": "ddosProtectionPlan", + "maxLength": 64 + } + }, + "variables": { + "description": "This ARM template is an example template of using an App Gateway with a NGroup.", + "resourcePrefix": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/')]", + "applicationGatewayApiVersion": "2022-09-01", + "prefixCG": "cg-lin100-regional-ag-" + }, + "resources": [ + { + "apiVersion": "[parameters('apiVersion')]", + "type": "Microsoft.ContainerInstance/containerGroupProfiles", + "name": "[parameters('containerGroupProfileName')]", + "location": "[resourceGroup().location]", + "properties": { + "sku": "Standard", + "containers": [ + { + "name": "web", + "properties": { + "image": "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e", + "ports": [ + { + "protocol": "TCP", + "port": 80 + } + ], + "resources": { + "requests": { + "memoryInGB": 1.0, + "cpu": 1.0 + } + } + } + } + ], + "restartPolicy": "Always", + "ipAddress": { + "ports": [ + { + "protocol": "TCP", + "port": 80 + } + ], + "type": "Private" + }, + "osType": "Linux" + } + }, + { + "apiVersion": "[parameters('apiVersion')]", + "type": "Microsoft.ContainerInstance/NGroups", + "name": "[parameters('nGroupsNameParam)]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[concat('Microsoft.ContainerInstance/containerGroupProfiles/', parameters('containerGroupProfileName'))]", + "[resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName'))]", + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ], + "properties": { + "elasticProfile": { + "desiredCount": "[parameters('desiredCount')]", + "maintainDesiredCount": "[parameters('maintainDesiredCount')]", + "containerGroupNamingPolicy": { + "guidNamingPolicy":{ + "prefix":"[variables('prefixCG')]" + } + } + }, + "containerGroupProfiles": [ + { + "resource": { + "id": "[concat(variables('resourcePrefix'), 'Microsoft.ContainerInstance/containerGroupProfiles/', parameters('containerGroupProfileName'))]" + }, + "containerGroupProperties": { + "subnetIds": [ + { + "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('aciSubnetName'))]", + "name": "[parameters('aciSubnetName')]" + } + ] + }, + "networkProfile": { + "applicationGateway": { + "resource": { + "id": "[resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName'))]" + }, + "backendAddressPools": [ + { + "resource": { + "id": "[resourceId('Microsoft.Network/applicationGateways/backendAddressPools', parameters('applicationGatewayName'), parameters('backendAddressPoolName'))]" + } + } + ] + } + } + } + ] + }, + "zones": "[parameters('zones')]", + "tags": { + "cirrusTestScenario": "lin-100.regional.appgateway", + "reprovision.enabled": true + } + }, + { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "[variables('applicationGatewayApiVersion')]", + "name": "[parameters('publicIPName')]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard", + "tier": "Regional" + }, + "properties": { + "publicIPAddressVersion": "IPv4", + "publicIPAllocationMethod": "Static", + "idleTimeoutInMinutes": 5, + "ipTags": [], + "ddosSettings": { + "protectionMode": "VirtualNetworkInherited" + } + } + }, + { + "type": "Microsoft.Network/applicationGateways", + "apiVersion": "[variables('applicationGatewayApiVersion')]", + "name": "[parameters('applicationGatewayName')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('appGatewaySubnetName'))]", + "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPName'))]" + ], + "properties": { + "sku": { + "name": "Standard_v2", + "tier": "Standard_v2" + }, + "gatewayIPConfigurations": [ + { + "name": "appGatewayIpConfig", + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/gatewayIPConfigurations/appGatewayIpConfig')]", + "properties": { + "subnet": { + "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('appGatewaySubnetName'))]" + } + } + } + ], + "sslCertificates": [], + "trustedRootCertificates": [], + "trustedClientCertificates": [], + "sslProfiles": [], + "frontendIPConfigurations": [ + { + "name": "appGwPublicFrontendIpIPv4", + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/frontendIPConfigurations/appGwPublicFrontendIpIPv4')]", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPName'))]" + } + } + } + ], + "frontendPorts": [ + { + "name": "port_80", + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/frontendPorts/port_80')]", + "properties": { + "port": 80 + } + } + ], + "backendAddressPools": [ + { + "name": "[parameters('backendAddressPoolName')]", + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), concat('/backendAddressPools/', parameters('backendAddressPoolName')))]", + "properties": { + "backendAddresses": [] + } + } + ], + "loadDistributionPolicies": [], + "backendHttpSettingsCollection": [ + { + "name": "[concat(parameters('applicationGatewayName'), '-be-settings')]", + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), concat('/backendHttpSettingsCollection/', parameters('applicationGatewayName'), '-be-settings'))]", + "properties": { + "port": 80, + "protocol": "Http", + "cookieBasedAffinity": "Disabled", + "pickHostNameFromBackendAddress": false, + "requestTimeout": 60, + "probe": { + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/probes/healthprobe')]" + } + } + } + ], + "backendSettingsCollection": [], + "httpListeners": [ + { + "name": "[concat(parameters('applicationGatewayName'), '-listener')]", + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), concat('/httpListeners/', parameters('applicationGatewayName'), '-listener'))]", + "properties": { + "frontendIPConfiguration": { + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/frontendIPConfigurations/appGwPublicFrontendIpIPv4')]" + }, + "frontendPort": { + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/frontendPorts/port_80')]" + }, + "protocol": "Http", + "hostNames": [], + "requireServerNameIndication": false, + "customErrorConfigurations": [] + } + } + ], + "listeners": [], + "urlPathMaps": [], + "requestRoutingRules": [ + { + "name": "[concat(parameters('applicationGatewayName'), '-routerule')]", + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), concat('/requestRoutingRules/', parameters('applicationGatewayName'), '-routerule'))]", + "properties": { + "ruleType": "Basic", + "priority": 1, + "httpListener": { + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), concat('/httpListeners/', parameters('applicationGatewayName'), '-listener'))]" + }, + "backendAddressPool": { + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), concat('/backendAddressPools/', parameters('backendAddressPoolName')))]" + }, + "backendHttpSettings": { + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), concat('/backendHttpSettingsCollection/', parameters('applicationGatewayName'), '-be-settings'))]" + } + } + } + ], + "routingRules": [], + "probes": [ + { + "name": "healthprobe", + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/probes/healthprobe')]", + "properties": { + "protocol": "Http", + "host": "127.0.0.1", + "path": "/", + "interval": 3600, + "timeout": 3600, + "unhealthyThreshold": 3, + "pickHostNameFromBackendHttpSettings": false, + "minServers": 0, + "match": {} + } + } + ], + "rewriteRuleSets": [], + "redirectConfigurations": [], + "privateLinkConfigurations": [], + "enableHttp2": false, + "autoscaleConfiguration": { + "minCapacity": 0, + "maxCapacity": 3 + } + } + }, + { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "[variables('applicationGatewayApiVersion')]", + "name": "[concat(parameters('vnetName'), '/', parameters('aciSubnetName'))]", + "dependsOn": [ + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ], + "properties": { + "addressPrefix": "[parameters('aciSubnetAddressPrefix')]", + "serviceEndpoints": [], + "delegations": [ + { + "name": "ACIDelegationService", + "id": "[concat(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('aciSubnetName')), '/delegations/ACIDelegationService')]", + "properties": { + "serviceName": "Microsoft.ContainerInstance/containerGroups" + }, + "type": "Microsoft.Network/virtualNetworks/subnets/delegations" + } + ], + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]" + }, + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Enabled" + } + }, + { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "[variables('applicationGatewayApiVersion')]", + "name": "[concat(parameters('vnetName'), '/', parameters('appGatewaySubnetName'))]", + "dependsOn": [ + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ], + "properties": { + "addressPrefix":"[parameters('appGatewaySubnetAddressPrefix')]", + "applicationGatewayIPConfigurations": [ + { + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/gatewayIPConfigurations/appGatewayIpConfig')]" + } + ], + "serviceEndpoints": [], + "delegations": [], + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]" + }, + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Enabled" + } + }, + { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "[variables('applicationGatewayApiVersion')]", + "name": "[parameters('vnetName')]", + "location": "[resourceGroup().location]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[parameters('vnetAddressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[parameters('appGatewaySubnetName')]", + "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('appGatewaySubnetName'))]", + "properties": { + "addressPrefix":"[parameters('appGatewaySubnetAddressPrefix')]", + "applicationGatewayIPConfigurations": [ + { + "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/gatewayIPConfigurations/appGatewayIpConfig')]" + } + ], + "serviceEndpoints": [], + "delegations": [], + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]" + }, + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Enabled" + }, + "type": "Microsoft.Network/virtualNetworks/subnets" + }, + { + "name": "[parameters('aciSubnetName')]", + "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('aciSubnetName'))]", + "properties": { + "addressPrefix": "[parameters('aciSubnetAddressPrefix')]", + "serviceEndpoints": [], + "delegations": [ + { + "name": "ACIDelegationService", + "id": "[concat(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('aciSubnetName')), '/delegations/ACIDelegationService')]", + "properties": { + "serviceName": "Microsoft.ContainerInstance/containerGroups" + }, + "type": "Microsoft.Network/virtualNetworks/subnets/delegations" + } + ], + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]" + }, + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Enabled" + }, + "type": "Microsoft.Network/virtualNetworks/subnets" + } + ], + "virtualNetworkPeerings": [], + "enableDdosProtection": false + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]" + ] + }, + { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "[variables('applicationGatewayApiVersion')]", + "name": "[parameters('networkSecurityGroupName')]", + "location": "[resourceGroup().location]", + "properties": { + "securityRules": [ + { + "name": "AppGatewayV2ProbeInbound", + "properties": { + "access": "Allow", + "description": "Allow traffic from GatewayManager. This rule is needed for application gateway probes to work.", + "destinationAddressPrefix": "*", + "destinationPortRange": "65200-65535", + "direction": "Inbound", + "protocol": "Tcp", + "priority": 100, + "sourceAddressPrefix": "GatewayManager", + "sourcePortRange": "*" + } + }, + { + "name": "AllowHTTPInbound", + "properties": { + "access": "Allow", + "description": "Allow Internet traffic on port 80", + "destinationAddressPrefix": "*", + "destinationPortRange": "80", + "direction": "Inbound", + "protocol": "Tcp", + "priority": 110, + "sourceAddressPrefix": "Internet", + "sourcePortRange": "*" + } + }, + { + "name": "AllowPublicIPAddress", + "properties": { + "access": "Allow", + "description": "Allow traffic from public ip address", + "destinationAddressPrefix": "[reference(parameters('publicIPName')).IpAddress]", + "destinationPortRange": "80", + "direction": "Inbound", + "protocol": "Tcp", + "priority": 111, + "sourceAddressPrefix": "Internet", + "sourcePortRange": "*" + } + }, + { + "name": "AllowVirtualNetworkInbound", + "properties": { + "access": "Allow", + "description": "Allow Internet traffic to Virtual network", + "destinationAddressPrefix": "VirtualNetwork", + "destinationPortRange": "80", + "direction": "Inbound", + "protocol": "*", + "priority": 112, + "sourceAddressPrefix": "*", + "sourcePortRange": "*" + } + } + ] + } + } + ] +} \ No newline at end of file diff --git a/recipe-packs/ACI-NGroups-LoadBalancer.yaml b/recipe-packs/ACI-NGroups-LoadBalancer.yaml new file mode 100644 index 00000000..dd5c70d9 --- /dev/null +++ b/recipe-packs/ACI-NGroups-LoadBalancer.yaml @@ -0,0 +1,450 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "apiVersion": { + "type": "string", + "defaultValue": "2024-09-01-preview", + "maxLength": 32 + }, + "nGroupsParamName": { + "type": "string", + "defaultValue": "nGroups_lin100_reg_lb", + "maxLength": 64 + }, + "containerGroupProfileName": { + "type": "string", + "defaultValue": "cgp_1", + "maxLength": 64 + }, + "loadBalancerName": { + "type": "string", + "defaultValue": "slb_1", + "maxLength": 64 + }, + "backendAddressPoolName": { + "type": "string", + "defaultValue": "bepool_1", + "maxLength": 64 + }, + "vnetName": { + "type": "string", + "defaultValue": "vnet_1", + "maxLength": 64 + }, + "subnetName": { + "type": "string", + "defaultValue": "subnet_1", + "maxLength": 64 + }, + "networkSecurityGroupName": { + "type": "string", + "defaultValue": "nsg_1", + "maxLength": 64 + }, + "inboundPublicIPName": { + "type": "string", + "defaultValue": "inboundPublicIP", + "maxLength": 64 + }, + "outboundPublicIPName": { + "type": "string", + "defaultValue": "outboundPublicIP", + "maxLength": 64 + }, + "outboundPublicIPPrefixName": { + "type": "string", + "defaultValue": "outBoundPublicIPPrefix", + "metadata": { + "description": "Name of the NAT gateway public IP" + } + }, + "natGatewayName": { + "type": "string", + "defaultValue": "natGateway1" + }, + "frontendIPName": { + "type": "string", + "defaultValue": "loadBalancerFrontend", + "maxLength": 64 + }, + "httpRuleName": { + "type": "string", + "defaultValue": "httpRule", + "maxLength": 64 + }, + "healthProbeName": { + "type": "string", + "defaultValue": "healthProbe", + "maxLength": 64 + }, + "vnetAddressPrefix": { + "type": "string", + "defaultValue": "172.19.0.0/16", + "maxLength": 64 + }, + "subnetAddressPrefix": { + "type": "string", + "defaultValue": "172.19.1.0/24", + "maxLength": 64 + }, + "desiredCount": { + "type": "int", + "defaultValue": 100 + }, + "zones": { + "type": "array", + "defaultValue": [] + }, + "maintainDesiredCount": { + "type": "bool", + "defaultValue": true + }, + "domainNameLabel": { + "type": "string", + "defaultValue": "ngroupsdemo", + "maxLength": 64 + }, + "inboundNatRuleName": { + "type": "string", + "defaultValue": "inboundNatRule", + "maxLength": 64 + } + }, + "variables": { + "description": "This ARM template is an example template to test the load balancer integration with NGroups.", + "cgProfileName": "[parameters('containerGroupProfileName')]", + "prefixCG": "cg-lin100-regional-lb-", + "nGroupsName": "[parameters('nGroupsParamName')]", + "resourcePrefix": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/')]", + "loadBalancerApiVersion": "2022-07-01", + "vnetApiVersion": "2022-07-01", + "publicIPVersion": "2022-07-01", + "ddosProtectionPlanName": "ddosProtectionPlan" + }, + "resources": [ + { + "apiVersion": "[parameters('apiVersion')]", + "type": "Microsoft.ContainerInstance/containerGroupProfiles", + "name": "[variables('cgProfileName')]", + "location": "[resourceGroup().location]", + "properties": { + "sku": "Standard", + "containers": [ + { + "name": "web", + "properties": { + "image": "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e", + "ports": [ + { + "protocol": "TCP", + "port": 80 + } + ], + "resources": { + "requests": { + "memoryInGB": 1.0, + "cpu": 1.0 + } + } + } + } + ], + "restartPolicy": "Always", + "ipAddress": { + "ports": [ + { + "protocol": "TCP", + "port": 80 + } + ], + "type": "Private" + }, + "osType": "Linux" + } + }, + { + "apiVersion": "[parameters('apiVersion')]", + "type": "Microsoft.ContainerInstance/NGroups", + "name": "[variables('nGroupsName')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[concat('Microsoft.ContainerInstance/containerGroupProfiles/', variables('cgProfileName'))]", + "[resourceId('Microsoft.Network/loadBalancers', parameters('loadBalancerName'))]", + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ], + "properties": { + "elasticProfile": { + "desiredCount": "[parameters('desiredCount')]", + "maintainDesiredCount": "[parameters('maintainDesiredCount')]", + "containerGroupNamingPolicy": { + "guidNamingPolicy":{ + "prefix":"[variables('prefixCG')]" + } + } + }, + "containerGroupProfiles": [ + { + "resource": { + "id": "[concat(variables('resourcePrefix'), 'Microsoft.ContainerInstance/containerGroupProfiles/', variables('cgProfileName'))]" + }, + "containerGroupProperties": { + "subnetIds": [ + { + "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('subnetName'))]", + "name": "[parameters('subnetName')]" + } + ] + }, + "networkProfile": { + "loadBalancer": { + "backendAddressPools": [ + { + "resource": { + "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('loadBalancerName'), parameters('backendAddressPoolName'))]" + } + } + ] + } + } + } + ] + }, + "zones": "[parameters('zones')]", + "tags": { + "cirrusTestScenario": "lin-100.regional.loadbalancer", + "reprovision.enabled": true + } + }, + { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "[variables('publicIPVersion')]", + "name": "[parameters('inboundPublicIPName')]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard", + "tier": "Regional" + }, + "properties": { + "publicIPAddressVersion": "IPv4", + "publicIPAllocationMethod": "Static", + "idleTimeoutInMinutes": 4, + "ipTags": [], + "dnsSettings": { + "domainNameLabel": "[parameters('domainNameLabel')]" + } + } + }, + { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "[variables('publicIPVersion')]", + "name": "[parameters('outboundPublicIPName')]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard", + "tier": "Regional" + }, + "properties": { + "publicIPAddressVersion": "IPv4", + "publicIPAllocationMethod": "Static", + "idleTimeoutInMinutes": 4, + "ipTags": [] + } + }, + { + "type": "Microsoft.Network/loadBalancers", + "apiVersion": "[variables('loadBalancerApiVersion')]", + "name": "[parameters('loadBalancerName')]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard" + }, + "properties": { + "frontendIPConfigurations": [ + { + "properties": { + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('inboundPublicIPName'))]" + }, + "privateIPAllocationMethod": "Dynamic" + }, + "name": "[parameters('frontendIPName')]" + } + ], + "backendAddressPools": [ + { + "name": "[parameters('backendAddressPoolName')]", + "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('loadBalancerName'), parameters('backendAddressPoolName'))]", + "properties": { + "loadBalancerBackendAddresses": [] + } + } + ], + "probes": [ + { + "name": "[parameters('healthProbeName')]", + "properties": { + "protocol": "Tcp", + "port": 80, + "intervalInSeconds": 5, + "numberOfProbes": 2, + "probeThreshold": 1 + } + } + ], + "loadBalancingRules": [ + { + "name": "[parameters('httpRuleName')]", + "properties": { + "frontendIPConfiguration": { + "id": "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('loadBalancerName'), parameters('frontendIPName'))]" + }, + "frontendPort": 80, + "backendPort": 80, + "enableFloatingIP": false, + "idleTimeoutInMinutes": 15, + "protocol": "Tcp", + "enableTcpReset": true, + "loadDistribution": "Default", + "disableOutboundSnat": false, + "backendAddressPools": [ + { + "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('loadBalancerName'), parameters('backendAddressPoolName'))]" + } + ], + "probe": { + "id": "[resourceId('Microsoft.Network/loadBalancers/probes', parameters('loadBalancerName'), parameters('healthProbeName'))]" + } + } + } + ], + "inboundNatRules": [ + { + "name": "[parameters('inboundNatRuleName')]", + "properties": { + "backendAddressPool": { + "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('loadBalancerName'), parameters('backendAddressPoolName'))]" + }, + "backendPort": "80", + "enableFloatingIP": "false", + "enableTcpReset": "false", + "frontendIPConfiguration": { + "id": "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('loadBalancerName'), parameters('frontendIPName'))]" + }, + "frontendPortRangeEnd": "331", + "frontendPortRangeStart": "81", + "idleTimeoutInMinutes": "4", + "protocol": "Tcp" + } + } + ], + "outboundRules": [], + "inboundNatPools": [] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/publicIPAddresses', parameters('inboundPublicIPName'))]", + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ] + }, + { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "[variables('vnetApiVersion')]", + "name": "[parameters('vnetName')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[concat('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]", + "[resourceId('Microsoft.Network/natGateways', parameters('natGatewayName'))]" + ], + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[parameters('vnetAddressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[parameters('subnetName')]", + "properties": { + "addressPrefix": "[parameters('subnetAddressPrefix')]", + "serviceEndpoints": [], + "delegations": [ + { + "name": "Microsoft.ContainerInstance.containerGroups", + "id": "[concat(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('subnetName')), '/delegations/Microsoft.ContainerInstance.containerGroups')]", + "properties": { + "serviceName": "Microsoft.ContainerInstance/containerGroups" + }, + "type": "Microsoft.Network/virtualNetworks/subnets/delegations" + } + ], + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Enabled", + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]" + }, + "natGateway": { + "id": "[resourceId('Microsoft.Network/natGateways', parameters('natGatewayName'))]" + } + }, + "type": "Microsoft.Network/virtualNetworks/subnets" + } + ], + "virtualNetworkPeerings": [], + "enableDdosProtection": true, + "ddosProtectionPlan": { + "id": "[resourceId('Microsoft.Network/ddosProtectionPlans', variables('ddosProtectionPlanName'))]" + } + } + }, + { + "type": "Microsoft.Network/ddosProtectionPlans", + "apiVersion": "[variables('loadBalancerApiVersion')]", + "name": "[variables('ddosProtectionPlanName')]", + "location": "[resourceGroup().location]" + }, + { + "type": "Microsoft.Network/natGateways", + "apiVersion": "[variables('loadBalancerApiVersion')]", + "name": "[parameters('natGatewayName')]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard" + }, + "properties": { + "idleTimeoutInMinutes": 4, + "publicIpAddresses": [ + { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('outboundPublicIPName'))]" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/publicIPAddresses', parameters('outboundPublicIPName'))]" + ] + }, + { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "[variables('loadBalancerApiVersion')]", + "name": "[parameters('networkSecurityGroupName')]", + "location": "[resourceGroup().location]", + "properties": { + "securityRules": [ + { + "name": "AllowHTTPInbound", + "properties": { + "access": "Allow", + "description": "Allow Internet traffic on port range", + "destinationAddressPrefix": "*", + "destinationPortRanges": ["80-331"], + "direction": "Inbound", + "protocol": "*", + "priority": 100, + "sourceAddressPrefix": "Internet", + "sourcePortRange": "*" + } + } + ] + } + } + ] +} diff --git a/recipe-packs/ACI-Ngroups-basic.yaml b/recipe-packs/ACI-Ngroups-basic.yaml new file mode 100644 index 00000000..1e4a54c1 --- /dev/null +++ b/recipe-packs/ACI-Ngroups-basic.yaml @@ -0,0 +1,85 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + }, + "variables": { + "cgProfileName": "cgp_1", + "nGroupsName": "ngroup_lin1_basic", + "apiVersion": "2024-09-01-preview", + "desiredCount": 1, + "prefixCG": "cg-lin1-basic-", + "resourcePrefix": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/')]" + }, + "resources": [ + { + "apiVersion": "[variables('apiVersion')]", + "type": "Microsoft.ContainerInstance/containerGroupProfiles", + "name": "[variables('cgProfileName')]", + "location": "[resourceGroup().location]", + "properties": { + "sku": "Standard", + "containers": [ + { + "name": "aci-helloworld", + "properties": { + "image": "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e", + "ports": [ + { + "protocol": "TCP", + "port": 80 + } + ], + "resources": { + "requests": { + "memoryInGB": 1.0, + "cpu": 1.0 + } + } + } + } + ], + "restartPolicy": "Always", + "ipAddress": { + "ports": [ + { + "protocol": "TCP", + "port": 80 + } + ], + "type": "Public" + }, + "osType": "Linux" + } + }, + { + "apiVersion": "[variables('apiVersion')]", + "type": "Microsoft.ContainerInstance/NGroups", + "name": "[variables('nGroupsName')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[concat('Microsoft.ContainerInstance/containerGroupProfiles/', variables('cgProfileName'))]" + ], + "properties": { + "elasticProfile": { + "desiredCount": "[variables('desiredCount')]", + "containerGroupNamingPolicy": { + "guidNamingPolicy":{ + "prefix":"[variables('prefixCG')]" + } + } + }, + "containerGroupProfiles": [ + { + "resource": { + "id": "[concat(variables('resourcePrefix'), 'Microsoft.ContainerInstance/containerGroupProfiles/', variables('cgProfileName'))]" + } + } + ] + }, + "tags": { + "cirrusTestScenario": "lin-1.basic" + } + } + ] +} diff --git a/recipe-packs/bicep/ACI-NGroups-Gateway.bicep b/recipe-packs/bicep/ACI-NGroups-Gateway.bicep new file mode 100644 index 00000000..222d52d7 --- /dev/null +++ b/recipe-packs/bicep/ACI-NGroups-Gateway.bicep @@ -0,0 +1,519 @@ +@description('Container Instance API version') +@maxLength(32) +param apiVersion string = '2024-09-01-preview' + +@description('NGroups parameter name') +@maxLength(64) +param nGroupsNameParam string = 'nGroups_lin100_regional_ag' + +@description('Container Group Profile name') +@maxLength(64) +param containerGroupProfileName string = 'cgp' + +@description('Application Gateway name') +@maxLength(64) +param applicationGatewayName string = 'agw1' + +@description('Public IP name') +@maxLength(64) +param publicIPName string = 'publicIP' + +@description('Backend Address Pool name') +@maxLength(64) +param backendAddressPoolName string = 'bepool' + +@description('Virtual Network name') +@maxLength(64) +param vnetName string = 'vnet1' + +@description('Network Security Group name') +@maxLength(64) +param networkSecurityGroupName string = 'nsg1' + +@description('Desired container count') +param desiredCount int = 100 + +@description('Maintain desired count') +param maintainDesiredCount bool = true + +@description('Availability zones') +param zones array = [] + +@description('Virtual Network address prefix') +@maxLength(64) +param vnetAddressPrefix string = '172.16.0.0/23' + +@description('ACI Subnet address prefix') +@maxLength(64) +param aciSubnetAddressPrefix string = '172.16.0.0/25' + +@description('Application Gateway Subnet address prefix') +@maxLength(64) +param appGatewaySubnetAddressPrefix string = '172.16.1.0/25' + +@description('ACI Subnet name') +@maxLength(64) +param aciSubnetName string = 'aciSubnet' + +@description('Application Gateway Subnet name') +@maxLength(64) +param appGatewaySubnetName string = 'appgatewaySubnet' + +@description('DDoS Protection Plan name') +@maxLength(64) +param ddosProtectionPlanName string = 'ddosProtectionPlan' + +// Variables +var resourcePrefix = '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/' +var applicationGatewayApiVersion = '2022-09-01' +var prefixCG = 'cg-lin100-regional-ag-' + +// Network Security Group +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2022-09-01' = { + name: networkSecurityGroupName + location: resourceGroup().location + properties: { + securityRules: [ + { + name: 'AppGatewayV2ProbeInbound' + properties: { + access: 'Allow' + description: 'Allow traffic from GatewayManager. This rule is needed for application gateway probes to work.' + destinationAddressPrefix: '*' + destinationPortRange: '65200-65535' + direction: 'Inbound' + protocol: 'Tcp' + priority: 100 + sourceAddressPrefix: 'GatewayManager' + sourcePortRange: '*' + } + } + { + name: 'AllowHTTPInbound' + properties: { + access: 'Allow' + description: 'Allow Internet traffic on port 80' + destinationAddressPrefix: '*' + destinationPortRange: '80' + direction: 'Inbound' + protocol: 'Tcp' + priority: 110 + sourceAddressPrefix: 'Internet' + sourcePortRange: '*' + } + } + { + name: 'AllowPublicIPAddress' + properties: { + access: 'Allow' + description: 'Allow traffic from public ip address' + destinationAddressPrefix: publicIPAddress.properties.ipAddress + destinationPortRange: '80' + direction: 'Inbound' + protocol: 'Tcp' + priority: 111 + sourceAddressPrefix: 'Internet' + sourcePortRange: '*' + } + } + { + name: 'AllowVirtualNetworkInbound' + properties: { + access: 'Allow' + description: 'Allow Internet traffic to Virtual network' + destinationAddressPrefix: 'VirtualNetwork' + destinationPortRange: '80' + direction: 'Inbound' + protocol: '*' + priority: 112 + sourceAddressPrefix: '*' + sourcePortRange: '*' + } + } + ] + } +} + +// Public IP Address +resource publicIPAddress 'Microsoft.Network/publicIPAddresses@2022-09-01' = { + name: publicIPName + location: resourceGroup().location + sku: { + name: 'Standard' + tier: 'Regional' + } + properties: { + publicIPAddressVersion: 'IPv4' + publicIPAllocationMethod: 'Static' + idleTimeoutInMinutes: 5 + ipTags: [] + ddosSettings: { + protectionMode: 'VirtualNetworkInherited' + } + } +} + +// Virtual Network +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-09-01' = { + name: vnetName + location: resourceGroup().location + properties: { + addressSpace: { + addressPrefixes: [ + vnetAddressPrefix + ] + } + subnets: [ + { + name: appGatewaySubnetName + id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, appGatewaySubnetName) + properties: { + addressPrefix: appGatewaySubnetAddressPrefix + applicationGatewayIPConfigurations: [ + { + id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/gatewayIPConfigurations/appGatewayIpConfig' + } + ] + serviceEndpoints: [] + delegations: [] + networkSecurityGroup: { + id: networkSecurityGroup.id + } + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } + type: 'Microsoft.Network/virtualNetworks/subnets' + } + { + name: aciSubnetName + id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, aciSubnetName) + properties: { + addressPrefix: aciSubnetAddressPrefix + serviceEndpoints: [] + delegations: [ + { + name: 'ACIDelegationService' + id: '${resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, aciSubnetName)}/delegations/ACIDelegationService' + properties: { + serviceName: 'Microsoft.ContainerInstance/containerGroups' + } + type: 'Microsoft.Network/virtualNetworks/subnets/delegations' + } + ] + networkSecurityGroup: { + id: networkSecurityGroup.id + } + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } + type: 'Microsoft.Network/virtualNetworks/subnets' + } + ] + virtualNetworkPeerings: [] + enableDdosProtection: false + } + dependsOn: [ + networkSecurityGroup + ] +} + +// ACI Subnet (separate resource for proper dependency management) +resource aciSubnet 'Microsoft.Network/virtualNetworks/subnets@2022-09-01' = { + parent: virtualNetwork + name: aciSubnetName + properties: { + addressPrefix: aciSubnetAddressPrefix + serviceEndpoints: [] + delegations: [ + { + name: 'ACIDelegationService' + id: '${resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, aciSubnetName)}/delegations/ACIDelegationService' + properties: { + serviceName: 'Microsoft.ContainerInstance/containerGroups' + } + type: 'Microsoft.Network/virtualNetworks/subnets/delegations' + } + ] + networkSecurityGroup: { + id: networkSecurityGroup.id + } + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } +} + +// Application Gateway Subnet (separate resource for proper dependency management) +resource appGatewaySubnet 'Microsoft.Network/virtualNetworks/subnets@2022-09-01' = { + parent: virtualNetwork + name: appGatewaySubnetName + properties: { + addressPrefix: appGatewaySubnetAddressPrefix + applicationGatewayIPConfigurations: [ + { + id: '${applicationGateway.id}/gatewayIPConfigurations/appGatewayIpConfig' + } + ] + serviceEndpoints: [] + delegations: [] + networkSecurityGroup: { + id: networkSecurityGroup.id + } + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + } +} + +// Application Gateway +resource applicationGateway 'Microsoft.Network/applicationGateways@2022-09-01' = { + name: applicationGatewayName + location: resourceGroup().location + properties: { + sku: { + name: 'Standard_v2' + tier: 'Standard_v2' + } + gatewayIPConfigurations: [ + { + name: 'appGatewayIpConfig' + id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/gatewayIPConfigurations/appGatewayIpConfig' + properties: { + subnet: { + id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, appGatewaySubnetName) + } + } + } + ] + sslCertificates: [] + trustedRootCertificates: [] + trustedClientCertificates: [] + sslProfiles: [] + frontendIPConfigurations: [ + { + name: 'appGwPublicFrontendIpIPv4' + id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/frontendIPConfigurations/appGwPublicFrontendIpIPv4' + properties: { + privateIPAllocationMethod: 'Dynamic' + publicIPAddress: { + id: publicIPAddress.id + } + } + } + ] + frontendPorts: [ + { + name: 'port_80' + id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/frontendPorts/port_80' + properties: { + port: 80 + } + } + ] + backendAddressPools: [ + { + name: backendAddressPoolName + id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/backendAddressPools/${backendAddressPoolName}' + properties: { + backendAddresses: [] + } + } + ] + loadDistributionPolicies: [] + backendHttpSettingsCollection: [ + { + name: '${applicationGatewayName}-be-settings' + id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/backendHttpSettingsCollection/${applicationGatewayName}-be-settings' + properties: { + port: 80 + protocol: 'Http' + cookieBasedAffinity: 'Disabled' + pickHostNameFromBackendAddress: false + requestTimeout: 60 + probe: { + id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/probes/healthprobe' + } + } + } + ] + backendSettingsCollection: [] + httpListeners: [ + { + name: '${applicationGatewayName}-listener' + id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/httpListeners/${applicationGatewayName}-listener' + properties: { + frontendIPConfiguration: { + id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/frontendIPConfigurations/appGwPublicFrontendIpIPv4' + } + frontendPort: { + id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/frontendPorts/port_80' + } + protocol: 'Http' + hostNames: [] + requireServerNameIndication: false + customErrorConfigurations: [] + } + } + ] + listeners: [] + urlPathMaps: [] + requestRoutingRules: [ + { + name: '${applicationGatewayName}-routerule' + id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/requestRoutingRules/${applicationGatewayName}-routerule' + properties: { + ruleType: 'Basic' + priority: 1 + httpListener: { + id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/httpListeners/${applicationGatewayName}-listener' + } + backendAddressPool: { + id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/backendAddressPools/${backendAddressPoolName}' + } + backendHttpSettings: { + id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/backendHttpSettingsCollection/${applicationGatewayName}-be-settings' + } + } + } + ] + routingRules: [] + probes: [ + { + name: 'healthprobe' + id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/probes/healthprobe' + properties: { + protocol: 'Http' + host: '127.0.0.1' + path: '/' + interval: 3600 + timeout: 3600 + unhealthyThreshold: 3 + pickHostNameFromBackendHttpSettings: false + minServers: 0 + match: {} + } + } + ] + rewriteRuleSets: [] + redirectConfigurations: [] + privateLinkConfigurations: [] + enableHttp2: false + autoscaleConfiguration: { + minCapacity: 0 + maxCapacity: 3 + } + } + dependsOn: [ + appGatewaySubnet + publicIPAddress + ] +} + +// Container Group Profile +resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfiles@2024-09-01-preview' = { + name: containerGroupProfileName + location: resourceGroup().location + properties: { + sku: 'Standard' + containers: [ + { + name: 'web' + properties: { + image: 'mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e' + ports: [ + { + protocol: 'TCP' + port: 80 + } + ] + resources: { + requests: { + memoryInGB: json('1.0') + cpu: json('1.0') + } + } + } + } + ] + restartPolicy: 'Always' + ipAddress: { + ports: [ + { + protocol: 'TCP' + port: 80 + } + ] + type: 'Private' + } + osType: 'Linux' + } +} + +// NGroups +resource nGroups 'Microsoft.ContainerInstance/NGroups@2024-09-01-preview' = { + name: nGroupsNameParam + location: resourceGroup().location + zones: zones + properties: { + elasticProfile: { + desiredCount: desiredCount + maintainDesiredCount: maintainDesiredCount + containerGroupNamingPolicy: { + guidNamingPolicy: { + prefix: prefixCG + } + } + } + containerGroupProfiles: [ + { + resource: { + id: '${resourcePrefix}Microsoft.ContainerInstance/containerGroupProfiles/${containerGroupProfileName}' + } + containerGroupProperties: { + subnetIds: [ + { + id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, aciSubnetName) + name: aciSubnetName + } + ] + } + networkProfile: { + applicationGateway: { + resource: { + id: applicationGateway.id + } + backendAddressPools: [ + { + resource: { + id: resourceId('Microsoft.Network/applicationGateways/backendAddressPools', applicationGatewayName, backendAddressPoolName) + } + } + ] + } + } + } + ] + } + tags: { + cirrusTestScenario: 'lin-100.regional.appgateway' + 'reprovision.enabled': true + } + dependsOn: [ + containerGroupProfile + applicationGateway + virtualNetwork + ] +} + +// Outputs +output virtualNetworkId string = virtualNetwork.id +output aciSubnetId string = aciSubnet.id +output appGatewaySubnetId string = appGatewaySubnet.id +output applicationGatewayId string = applicationGateway.id +output backendAddressPoolId string = applicationGateway.properties.backendAddressPools[0].id +output publicIPId string = publicIPAddress.id +output publicIPAddress string = publicIPAddress.properties.ipAddress +output networkSecurityGroupId string = networkSecurityGroup.id +output containerGroupProfileId string = containerGroupProfile.id +output nGroupsId string = nGroups.id +output frontendIPConfigurationId string = applicationGateway.properties.frontendIPConfigurations[0].id +output httpListenerId string = applicationGateway.properties.httpListeners[0].id +output healthProbeId string = applicationGateway.properties.probes[0].id diff --git a/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep new file mode 100644 index 00000000..5fe1b9dc --- /dev/null +++ b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep @@ -0,0 +1,433 @@ +@description('Container Instance API version') +@maxLength(32) +param apiVersion string = '2024-09-01-preview' + +@description('NGroups parameter name') +@maxLength(64) +param nGroupsParamName string = 'nGroups_lin100_reg_lb' + +@description('Container Group Profile name') +@maxLength(64) +param containerGroupProfileName string = 'cgp_1' + +@description('Load Balancer name') +@maxLength(64) +param loadBalancerName string = 'slb_1' + +@description('Backend Address Pool name') +@maxLength(64) +param backendAddressPoolName string = 'bepool_1' + +@description('Virtual Network name') +@maxLength(64) +param vnetName string = 'vnet_1' + +@description('Subnet name') +@maxLength(64) +param subnetName string = 'subnet_1' + +@description('Network Security Group name') +@maxLength(64) +param networkSecurityGroupName string = 'nsg_1' + +@description('Inbound Public IP name') +@maxLength(64) +param inboundPublicIPName string = 'inboundPublicIP' + +@description('Outbound Public IP name') +@maxLength(64) +param outboundPublicIPName string = 'outboundPublicIP' + +@description('Name of the NAT gateway public IP') +param outboundPublicIPPrefixName string = 'outBoundPublicIPPrefix' + +@description('NAT Gateway name') +param natGatewayName string = 'natGateway1' + +@description('Frontend IP name') +@maxLength(64) +param frontendIPName string = 'loadBalancerFrontend' + +@description('HTTP Rule name') +@maxLength(64) +param httpRuleName string = 'httpRule' + +@description('Health Probe name') +@maxLength(64) +param healthProbeName string = 'healthProbe' + +@description('Virtual Network address prefix') +@maxLength(64) +param vnetAddressPrefix string = '172.19.0.0/16' + +@description('Subnet address prefix') +@maxLength(64) +param subnetAddressPrefix string = '172.19.1.0/24' + +@description('Desired container count') +param desiredCount int = 100 + +@description('Availability zones') +param zones array = [] + +@description('Maintain desired count') +param maintainDesiredCount bool = true + +@description('Domain name label for public IP') +@maxLength(64) +param domainNameLabel string = 'ngroupsdemo' + +@description('Inbound NAT Rule name') +@maxLength(64) +param inboundNatRuleName string = 'inboundNatRule' + +// Variables +var cgProfileName = containerGroupProfileName +var prefixCG = 'cg-lin100-regional-lb-' +var nGroupsName = nGroupsParamName +var resourcePrefix = '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/' +var loadBalancerApiVersion = '2022-07-01' +var vnetApiVersion = '2022-07-01' +var publicIPVersion = '2022-07-01' +var ddosProtectionPlanName = 'ddosProtectionPlan' + +// DDoS Protection Plan +resource ddosProtectionPlan 'Microsoft.Network/ddosProtectionPlans@2022-07-01' = { + name: ddosProtectionPlanName + location: resourceGroup().location +} + +// Network Security Group +resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2022-07-01' = { + name: networkSecurityGroupName + location: resourceGroup().location + properties: { + securityRules: [ + { + name: 'AllowHTTPInbound' + properties: { + access: 'Allow' + description: 'Allow Internet traffic on port range' + destinationAddressPrefix: '*' + destinationPortRanges: [ + '80-331' + ] + direction: 'Inbound' + protocol: '*' + priority: 100 + sourceAddressPrefix: 'Internet' + sourcePortRange: '*' + } + } + ] + } +} + +// Inbound Public IP +resource inboundPublicIP 'Microsoft.Network/publicIPAddresses@2022-07-01' = { + name: inboundPublicIPName + location: resourceGroup().location + sku: { + name: 'Standard' + tier: 'Regional' + } + properties: { + publicIPAddressVersion: 'IPv4' + publicIPAllocationMethod: 'Static' + idleTimeoutInMinutes: 4 + ipTags: [] + dnsSettings: { + domainNameLabel: domainNameLabel + } + } +} + +// Outbound Public IP +resource outboundPublicIP 'Microsoft.Network/publicIPAddresses@2022-07-01' = { + name: outboundPublicIPName + location: resourceGroup().location + sku: { + name: 'Standard' + tier: 'Regional' + } + properties: { + publicIPAddressVersion: 'IPv4' + publicIPAllocationMethod: 'Static' + idleTimeoutInMinutes: 4 + ipTags: [] + } +} + +// NAT Gateway +resource natGateway 'Microsoft.Network/natGateways@2022-07-01' = { + name: natGatewayName + location: resourceGroup().location + sku: { + name: 'Standard' + } + properties: { + idleTimeoutInMinutes: 4 + publicIpAddresses: [ + { + id: outboundPublicIP.id + } + ] + } + dependsOn: [ + outboundPublicIP + ] +} + +// Virtual Network +resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-07-01' = { + name: vnetName + location: resourceGroup().location + properties: { + addressSpace: { + addressPrefixes: [ + vnetAddressPrefix + ] + } + subnets: [ + { + name: subnetName + properties: { + addressPrefix: subnetAddressPrefix + serviceEndpoints: [] + delegations: [ + { + name: 'Microsoft.ContainerInstance.containerGroups' + id: '${resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, subnetName)}/delegations/Microsoft.ContainerInstance.containerGroups' + properties: { + serviceName: 'Microsoft.ContainerInstance/containerGroups' + } + type: 'Microsoft.Network/virtualNetworks/subnets/delegations' + } + ] + privateEndpointNetworkPolicies: 'Disabled' + privateLinkServiceNetworkPolicies: 'Enabled' + networkSecurityGroup: { + id: networkSecurityGroup.id + } + natGateway: { + id: natGateway.id + } + } + type: 'Microsoft.Network/virtualNetworks/subnets' + } + ] + virtualNetworkPeerings: [] + enableDdosProtection: true + ddosProtectionPlan: { + id: ddosProtectionPlan.id + } + } + dependsOn: [ + networkSecurityGroup + natGateway + ] +} + +// Load Balancer +resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' = { + name: loadBalancerName + location: resourceGroup().location + sku: { + name: 'Standard' + } + properties: { + frontendIPConfigurations: [ + { + properties: { + publicIPAddress: { + id: inboundPublicIP.id + } + privateIPAllocationMethod: 'Dynamic' + } + name: frontendIPName + } + ] + backendAddressPools: [ + { + name: backendAddressPoolName + id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', loadBalancerName, backendAddressPoolName) + properties: { + loadBalancerBackendAddresses: [] + } + } + ] + probes: [ + { + name: healthProbeName + properties: { + protocol: 'Tcp' + port: 80 + intervalInSeconds: 5 + numberOfProbes: 2 + probeThreshold: 1 + } + } + ] + loadBalancingRules: [ + { + name: httpRuleName + properties: { + frontendIPConfiguration: { + id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', loadBalancerName, frontendIPName) + } + frontendPort: 80 + backendPort: 80 + enableFloatingIP: false + idleTimeoutInMinutes: 15 + protocol: 'Tcp' + enableTcpReset: true + loadDistribution: 'Default' + disableOutboundSnat: false + backendAddressPools: [ + { + id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', loadBalancerName, backendAddressPoolName) + } + ] + probe: { + id: resourceId('Microsoft.Network/loadBalancers/probes', loadBalancerName, healthProbeName) + } + } + } + ] + inboundNatRules: [ + { + name: inboundNatRuleName + properties: { + backendAddressPool: { + id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', loadBalancerName, backendAddressPoolName) + } + backendPort: '80' + enableFloatingIP: 'false' + enableTcpReset: 'false' + frontendIPConfiguration: { + id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', loadBalancerName, frontendIPName) + } + frontendPortRangeEnd: '331' + frontendPortRangeStart: '81' + idleTimeoutInMinutes: '4' + protocol: 'Tcp' + } + } + ] + outboundRules: [] + inboundNatPools: [] + } + dependsOn: [ + inboundPublicIP + virtualNetwork + ] +} + +// Container Group Profile +resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfiles@2024-09-01-preview' = { + name: cgProfileName + location: resourceGroup().location + properties: { + sku: 'Standard' + containers: [ + { + name: 'web' + properties: { + image: 'mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e' + ports: [ + { + protocol: 'TCP' + port: 80 + } + ] + resources: { + requests: { + memoryInGB: json('1.0') + cpu: json('1.0') + } + } + } + } + ] + restartPolicy: 'Always' + ipAddress: { + ports: [ + { + protocol: 'TCP' + port: 80 + } + ] + type: 'Private' + } + osType: 'Linux' + } +} + +// NGroups +resource nGroups 'Microsoft.ContainerInstance/NGroups@2024-09-01-preview' = { + name: nGroupsName + location: resourceGroup().location + zones: zones + properties: { + elasticProfile: { + desiredCount: desiredCount + maintainDesiredCount: maintainDesiredCount + containerGroupNamingPolicy: { + guidNamingPolicy: { + prefix: prefixCG + } + } + } + containerGroupProfiles: [ + { + resource: { + id: '${resourcePrefix}Microsoft.ContainerInstance/containerGroupProfiles/${cgProfileName}' + } + containerGroupProperties: { + subnetIds: [ + { + id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, subnetName) + name: subnetName + } + ] + } + networkProfile: { + loadBalancer: { + backendAddressPools: [ + { + resource: { + id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', loadBalancerName, backendAddressPoolName) + } + } + ] + } + } + } + ] + } + tags: { + cirrusTestScenario: 'lin-100.regional.loadbalancer' + 'reprovision.enabled': true + } + dependsOn: [ + containerGroupProfile + loadBalancer + virtualNetwork + ] +} + +// Outputs +output virtualNetworkId string = virtualNetwork.id +output subnetId string = virtualNetwork.properties.subnets[0].id +output loadBalancerId string = loadBalancer.id +output frontendIPConfigurationId string = loadBalancer.properties.frontendIPConfigurations[0].id +output backendAddressPoolId string = loadBalancer.properties.backendAddressPools[0].id +output healthProbeId string = loadBalancer.properties.probes[0].id +output inboundPublicIPId string = inboundPublicIP.id +output outboundPublicIPId string = outboundPublicIP.id +output inboundPublicIPFQDN string = inboundPublicIP.properties.dnsSettings.fqdn +output natGatewayId string = natGateway.id +output networkSecurityGroupId string = networkSecurityGroup.id +output ddosProtectionPlanId string = ddosProtectionPlan.id +output containerGroupProfileId string = containerGroupProfile.id +output nGroupsId string = nGroups.id diff --git a/recipe-packs/bicep/Ngroups-basic.bicep b/recipe-packs/bicep/Ngroups-basic.bicep new file mode 100644 index 00000000..84511864 --- /dev/null +++ b/recipe-packs/bicep/Ngroups-basic.bicep @@ -0,0 +1,76 @@ +// Variables +var cgProfileName = 'cgp_1' +var nGroupsName = 'ngroup_lin1_basic' +var apiVersion = '2024-09-01-preview' +var desiredCount = 1 +var prefixCG = 'cg-lin1-basic-' +var resourcePrefix = '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/' + +// Container Group Profile +resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfiles@2024-09-01-preview' = { + name: cgProfileName + location: resourceGroup().location + properties: { + sku: 'Standard' + containers: [ + { + name: 'aci-helloworld' + properties: { + image: 'mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e' + ports: [ + { + protocol: 'TCP' + port: 80 + } + ] + resources: { + requests: { + memoryInGB: 1.0 + cpu: 1.0 + } + } + } + } + ] + restartPolicy: 'Always' + ipAddress: { + ports: [ + { + protocol: 'TCP' + port: 80 + } + ] + type: 'Public' + } + osType: 'Linux' + } +} + +// NGroups resource +resource nGroups 'Microsoft.ContainerInstance/NGroups@2024-09-01-preview' = { + name: nGroupsName + location: resourceGroup().location + dependsOn: [ + containerGroupProfile + ] + properties: { + elasticProfile: { + desiredCount: desiredCount + containerGroupNamingPolicy: { + guidNamingPolicy: { + prefix: prefixCG + } + } + } + containerGroupProfiles: [ + { + resource: { + id: '${resourcePrefix}Microsoft.ContainerInstance/containerGroupProfiles/${cgProfileName}' + } + } + ] + } + tags: { + cirrusTestScenario: 'lin-1.basic' + } +} diff --git a/recipe-packs/terraform/README.md b/recipe-packs/terraform/README.md new file mode 100644 index 00000000..5ffffebe --- /dev/null +++ b/recipe-packs/terraform/README.md @@ -0,0 +1,139 @@ +# Terraform Configurations for Azure Container Instance NGroups + +This directory contains Terraform configurations for deploying Azure Container Instance NGroups with different networking setups. + +## Directory Structure + +``` +terraform/ +├── README.md # This file +├── ngroups-basic/ # Basic NGroups configuration +│ ├── main.tf # Basic setup with public networking +│ ├── variables.tf # Variable definitions +│ ├── outputs.tf # Output values +│ ├── terraform.tfvars.example # Example variable values +│ └── README.md # Detailed documentation +└── ngroups-gateway/ # Advanced NGroups with Application Gateway + ├── main.tf # Full networking stack with App Gateway + ├── variables.tf # Variable definitions + ├── outputs.tf # Output values + ├── terraform.tfvars.example # Example variable values + └── README.md # Detailed documentation +``` + +## Configurations + +### 1. NGroups Basic (`ngroups-basic/`) + +**Purpose**: Simple deployment of Container Group Profile and NGroups with public networking. + +**Use Cases**: +- Development and testing +- Simple container workloads +- Quick proof-of-concept deployments + +**Resources Created**: +- Container Group Profile with public IP +- NGroups with basic elastic scaling + +**Based on ARM Template**: `ACI-Ngroups-basic.yaml` + +### 2. NGroups Gateway (`ngroups-gateway/`) + +**Purpose**: Production-ready deployment with Application Gateway, Virtual Network, and comprehensive networking. + +**Use Cases**: +- Production workloads +- Load-balanced container applications +- Secure private networking +- High-availability setups + +**Resources Created**: +- Virtual Network with dedicated subnets +- Network Security Group with security rules +- Public IP and Application Gateway +- Container Group Profile with private networking +- NGroups with Application Gateway integration + +**Based on ARM Template**: `ACI-NGroups-Gateway.yaml` + +## Quick Start + +### Basic Configuration + +```bash +cd ngroups-basic +cp terraform.tfvars.example terraform.tfvars +# Edit terraform.tfvars with your resource group name +terraform init +terraform plan +terraform apply +``` + +### Gateway Configuration + +```bash +cd ngroups-gateway +cp terraform.tfvars.example terraform.tfvars +# Edit terraform.tfvars with your resource group name and desired settings +terraform init +terraform plan +terraform apply +``` + +## Prerequisites + +- **Terraform** >= 0.14 +- **Azure CLI** installed and authenticated +- **Azure Subscription** with appropriate permissions +- **Existing Resource Group** for deployment + +## Authentication + +Ensure you're authenticated with Azure: + +```bash +az login +az account set --subscription "your-subscription-id" +``` + +## Important Notes + +### Preview Features +Both configurations use Azure Container Instance preview features: +- **Container Group Profiles** +- **NGroups** + +These are deployed using ARM templates within Terraform until native support is available. + +### Resource Dependencies +- Basic: Minimal dependencies, quick deployment +- Gateway: Complex networking dependencies, longer deployment time + +### Cost Considerations +- Basic: Lower cost, minimal networking resources +- Gateway: Higher cost due to Application Gateway and networking components + +## Migration Between Configurations + +You can migrate from basic to gateway configuration: + +1. Export container configuration from basic setup +2. Deploy gateway configuration +3. Update DNS/routing to point to new Application Gateway +4. Destroy basic setup + +## Support and Troubleshooting + +- Check individual README files in each directory for detailed troubleshooting +- Review Azure Portal for resource deployment status +- Use `terraform plan` to preview changes before applying + +## Contributing + +When adding new configurations: +1. Create a new directory under `terraform/` +2. Include all standard Terraform files (main.tf, variables.tf, outputs.tf) +3. Provide comprehensive README documentation +4. Include example tfvars file +5. Update this main README with the new configuration details diff --git a/recipe-packs/terraform/aci-ngroups-basic/README.md b/recipe-packs/terraform/aci-ngroups-basic/README.md new file mode 100644 index 00000000..89086175 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-basic/README.md @@ -0,0 +1,79 @@ +# Azure Container Instance NGroups Terraform Configuration + +This Terraform configuration converts the ARM template for Azure Container Instance NGroups to Terraform format. + +## Prerequisites + +- Terraform >= 0.14 +- Azure CLI installed and authenticated +- An existing Azure Resource Group + +## Resources Created + +1. **Container Group Profile** - Defines the template for container groups +2. **NGroups** - Manages elastic scaling of container groups + +## Usage + +1. Copy the example variables file: + ```bash + cp terraform.tfvars.example terraform.tfvars + ``` + +2. Edit `terraform.tfvars` with your specific values: + ```hcl + resource_group_name = "your-resource-group-name" + ``` + +3. Initialize Terraform: + ```bash + terraform init + ``` + +4. Plan the deployment: + ```bash + terraform plan + ``` + +5. Apply the configuration: + ```bash + terraform apply + ``` + +## Important Notes + +- **Preview Features**: Both Container Group Profiles and NGroups are preview features in Azure +- **ARM Template Deployment**: Since these resources are not yet fully supported in the AzureRM provider, this configuration uses `azurerm_resource_group_template_deployment` to deploy ARM templates within Terraform +- **Dependencies**: The NGroups resource depends on the Container Group Profile being created first + +## Variables + +| Variable | Description | Default | +|----------|-------------|---------| +| `resource_group_name` | Name of the Azure Resource Group | (required) | +| `location` | Azure region | Resource group location | +| `cg_profile_name` | Container Group Profile name | `cgp_1` | +| `ngroups_name` | NGroups resource name | `ngroup_lin1_basic` | +| `desired_count` | Desired container count | `1` | +| `prefix_cg` | Container group prefix | `cg-lin1-basic-` | +| `container_image` | Container image to deploy | ACI Hello World | +| `container_port` | Container port | `80` | +| `memory_gb` | Memory allocation in GB | `1.0` | +| `cpu_cores` | CPU allocation | `1.0` | +| `tags` | Resource tags | Default tags | + +## Outputs + +- `resource_group_name` - Name of the resource group +- `resource_group_location` - Location of the resource group +- `subscription_id` - Azure subscription ID +- `container_group_profile_name` - Name of the container group profile +- `ngroups_name` - Name of the NGroups resource +- Deployment IDs for both resources + +## Clean Up + +To destroy the resources: +```bash +terraform destroy +``` diff --git a/recipe-packs/terraform/aci-ngroups-basic/main.tf b/recipe-packs/terraform/aci-ngroups-basic/main.tf new file mode 100644 index 00000000..aef46571 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-basic/main.tf @@ -0,0 +1,147 @@ +# Configure the Azure Provider +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~>3.0" + } + } +} + +# Configure the Microsoft Azure Provider +provider "azurerm" { + features {} +} + +# Data source to get current resource group +data "azurerm_resource_group" "current" { + name = var.resource_group_name +} + +# Data source to get current subscription +data "azurerm_client_config" "current" {} + +# Local variables +locals { + cg_profile_name = "cgp_1" + ngroups_name = "ngroup_lin1_basic" + api_version = "2024-09-01-preview" + desired_count = 1 + prefix_cg = "cg-lin1-basic-" + resource_prefix = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${data.azurerm_resource_group.current.name}/providers/" +} + +# Template deployment for Container Group Profile (using ARM template within Terraform) +resource "azurerm_resource_group_template_deployment" "container_group_profile" { + name = "cgp-deployment" + resource_group_name = data.azurerm_resource_group.current.name + deployment_mode = "Incremental" + + template_content = jsonencode({ + "$schema" = "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" + contentVersion = "1.0.0.0" + parameters = {} + variables = { + cgProfileName = local.cg_profile_name + apiVersion = local.api_version + } + resources = [ + { + apiVersion = local.api_version + type = "Microsoft.ContainerInstance/containerGroupProfiles" + name = local.cg_profile_name + location = data.azurerm_resource_group.current.location + properties = { + sku = "Standard" + containers = [ + { + name = "aci-helloworld" + properties = { + image = "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e" + ports = [ + { + protocol = "TCP" + port = 80 + } + ] + resources = { + requests = { + memoryInGB = 1.0 + cpu = 1.0 + } + } + } + } + ] + restartPolicy = "Always" + ipAddress = { + ports = [ + { + protocol = "TCP" + port = 80 + } + ] + type = "Public" + } + osType = "Linux" + } + } + ] + }) +} + +# Template deployment for NGroups +resource "azurerm_resource_group_template_deployment" "ngroups" { + name = "ngroups-deployment" + resource_group_name = data.azurerm_resource_group.current.name + deployment_mode = "Incremental" + + depends_on = [ + azurerm_resource_group_template_deployment.container_group_profile + ] + + template_content = jsonencode({ + "$schema" = "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" + contentVersion = "1.0.0.0" + parameters = {} + variables = { + cgProfileName = local.cg_profile_name + nGroupsName = local.ngroups_name + apiVersion = local.api_version + desiredCount = local.desired_count + prefixCG = local.prefix_cg + resourcePrefix = local.resource_prefix + } + resources = [ + { + apiVersion = local.api_version + type = "Microsoft.ContainerInstance/NGroups" + name = local.ngroups_name + location = data.azurerm_resource_group.current.location + dependsOn = [ + "Microsoft.ContainerInstance/containerGroupProfiles/${local.cg_profile_name}" + ] + properties = { + elasticProfile = { + desiredCount = local.desired_count + containerGroupNamingPolicy = { + guidNamingPolicy = { + prefix = local.prefix_cg + } + } + } + containerGroupProfiles = [ + { + resource = { + id = "${local.resource_prefix}Microsoft.ContainerInstance/containerGroupProfiles/${local.cg_profile_name}" + } + } + ] + } + tags = { + cirrusTestScenario = "lin-1.basic" + } + } + ] + }) +} diff --git a/recipe-packs/terraform/aci-ngroups-basic/outputs.tf b/recipe-packs/terraform/aci-ngroups-basic/outputs.tf new file mode 100644 index 00000000..dd7735f8 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-basic/outputs.tf @@ -0,0 +1,36 @@ +# Output values for the Terraform configuration + +output "resource_group_name" { + description = "Name of the resource group" + value = data.azurerm_resource_group.current.name +} + +output "resource_group_location" { + description = "Location of the resource group" + value = data.azurerm_resource_group.current.location +} + +output "subscription_id" { + description = "Azure subscription ID" + value = data.azurerm_client_config.current.subscription_id +} + +output "container_group_profile_name" { + description = "Name of the container group profile" + value = local.cg_profile_name +} + +output "ngroups_name" { + description = "Name of the NGroups resource" + value = local.ngroups_name +} + +output "container_group_profile_deployment_id" { + description = "Deployment ID for container group profile" + value = azurerm_resource_group_template_deployment.container_group_profile.id +} + +output "ngroups_deployment_id" { + description = "Deployment ID for NGroups resource" + value = azurerm_resource_group_template_deployment.ngroups.id +} diff --git a/recipe-packs/terraform/aci-ngroups-basic/terraform.tfvars.example b/recipe-packs/terraform/aci-ngroups-basic/terraform.tfvars.example new file mode 100644 index 00000000..8b3d9663 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-basic/terraform.tfvars.example @@ -0,0 +1,21 @@ +# Example Terraform variables file +# Copy this file to terraform.tfvars and modify the values as needed + +# Required variables +resource_group_name = "my-resource-group" + +# Optional variables (defaults will be used if not specified) +# location = "East US" +# cg_profile_name = "cgp_1" +# ngroups_name = "ngroup_lin1_basic" +# desired_count = 1 +# prefix_cg = "cg-lin1-basic-" +# container_port = 80 +# memory_gb = 1.0 +# cpu_cores = 1.0 + +# tags = { +# cirrusTestScenario = "lin-1.basic" +# environment = "development" +# project = "my-project" +# } diff --git a/recipe-packs/terraform/aci-ngroups-basic/variables.tf b/recipe-packs/terraform/aci-ngroups-basic/variables.tf new file mode 100644 index 00000000..6e4ab868 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-basic/variables.tf @@ -0,0 +1,69 @@ +# Variable definitions for the Terraform configuration + +variable "resource_group_name" { + description = "Name of the Azure Resource Group" + type = string +} + +variable "location" { + description = "Azure region for resource deployment" + type = string + default = null # Will use resource group location if not specified +} + +variable "cg_profile_name" { + description = "Name of the Container Group Profile" + type = string + default = "cgp_1" +} + +variable "ngroups_name" { + description = "Name of the NGroups resource" + type = string + default = "ngroup_lin1_basic" +} + +variable "desired_count" { + description = "Desired count for elastic profile" + type = number + default = 1 +} + +variable "prefix_cg" { + description = "Prefix for container group naming" + type = string + default = "cg-lin1-basic-" +} + +variable "container_image" { + description = "Container image to deploy" + type = string + default = "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e" +} + +variable "container_port" { + description = "Port to expose on the container" + type = number + default = 80 +} + +variable "memory_gb" { + description = "Memory allocation in GB" + type = number + default = 1.0 +} + +variable "cpu_cores" { + description = "CPU allocation" + type = number + default = 1.0 +} + +variable "tags" { + description = "Tags to apply to resources" + type = map(string) + default = { + cirrusTestScenario = "lin-1.basic" + environment = "development" + } +} diff --git a/recipe-packs/terraform/aci-ngroups-gateway/README.md b/recipe-packs/terraform/aci-ngroups-gateway/README.md new file mode 100644 index 00000000..1f58dbd1 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-gateway/README.md @@ -0,0 +1,183 @@ +# Azure Container Instance NGroups with Application Gateway - Terraform Configuration + +This Terraform configuration converts the ARM template for Azure Container Instance NGroups with Application Gateway to Terraform format. This is a comprehensive setup that includes networking infrastructure, security groups, and application gateway integration. + +## Architecture Overview + +This configuration deploys: + +1. **Virtual Network** with two subnets: + - ACI Subnet (for container instances) + - Application Gateway Subnet +2. **Network Security Group** with rules for Application Gateway and HTTP traffic +3. **Public IP** for the Application Gateway +4. **Application Gateway** with health probes and routing rules +5. **Container Group Profile** with private networking +6. **NGroups** with Application Gateway integration for elastic scaling + +## Prerequisites + +- Terraform >= 0.14 +- Azure CLI installed and authenticated +- An existing Azure Resource Group +- Sufficient Azure permissions to create networking and container resources + +## Resources Created + +### Networking +- **Virtual Network** (`azurerm_virtual_network`) +- **ACI Subnet** with Container Instance delegation (`azurerm_subnet`) +- **Application Gateway Subnet** (`azurerm_subnet`) +- **Network Security Group** with security rules (`azurerm_network_security_group`) +- **Public IP** for Application Gateway (`azurerm_public_ip`) + +### Application Gateway +- **Application Gateway** with Standard_v2 SKU (`azurerm_application_gateway`) +- Backend address pool for container instances +- HTTP listener on port 80 +- Health probe configuration +- Request routing rules + +### Container Instance Resources +- **Container Group Profile** (via ARM template deployment) +- **NGroups** with Application Gateway integration (via ARM template deployment) + +## Usage + +### 1. Setup Variables + +Copy the example variables file: +```bash +cp ngroups-gateway.tfvars.example ngroups-gateway.tfvars +``` + +Edit `ngroups-gateway.tfvars` with your specific values: +```hcl +resource_group_name = "your-resource-group-name" +desired_count = 50 # Adjust based on your needs +``` + +### 2. Initialize Terraform + +```bash +terraform init +``` + +### 3. Plan the Deployment + +```bash +terraform plan -var-file="ngroups-gateway.tfvars" +``` + +### 4. Apply the Configuration + +```bash +terraform apply -var-file="ngroups-gateway.tfvars" +``` + +## Configuration Details + +### Network Configuration + +- **VNet Address Space**: `172.16.0.0/23` (default) +- **ACI Subnet**: `172.16.0.0/25` (default) +- **App Gateway Subnet**: `172.16.1.0/25` (default) + +### Security Rules + +The Network Security Group includes: +- Application Gateway V2 probe traffic (ports 65200-65535) +- HTTP traffic on port 80 +- Public IP specific access +- Virtual Network internal traffic + +### Application Gateway + +- **SKU**: Standard_v2 with autoscaling (0-3 instances) +- **Frontend**: Public IP configuration +- **Backend**: Integrated with NGroups container instances +- **Health Probe**: HTTP probe on path "/" + +### Container Configuration + +- **Desired Count**: 100 instances (default, configurable) +- **Maintain Desired Count**: Enabled +- **Container Image**: ACI Hello World (configurable) +- **Networking**: Private IP addresses within VNet +- **Auto-scaling**: Managed by NGroups + +## Variables + +| Variable | Description | Default | Required | +|----------|-------------|---------|----------| +| `resource_group_name` | Resource Group name | - | ✅ | +| `api_version` | Container Instance API version | `2024-09-01-preview` | ❌ | +| `desired_count` | Desired container count | `100` | ❌ | +| `vnet_address_prefix` | VNet address space | `172.16.0.0/23` | ❌ | +| `aci_subnet_address_prefix` | ACI subnet CIDR | `172.16.0.0/25` | ❌ | +| `app_gateway_subnet_address_prefix` | App Gateway subnet CIDR | `172.16.1.0/25` | ❌ | +| `container_image` | Container image | ACI Hello World | ❌ | +| `maintain_desired_count` | Maintain desired count | `true` | ❌ | + +See `ngroups-gateway-variables.tf` for complete variable list. + +## Outputs + +Key outputs include: +- Virtual Network and subnet IDs +- Application Gateway ID and public IP +- Backend address pool ID +- Container Group Profile and NGroups deployment IDs +- Network Security Group ID + +## Important Notes + +### Preview Features +- **Container Group Profiles** and **NGroups** are preview features +- These resources use ARM template deployments within Terraform +- Future versions may support native Terraform resources + +### Dependencies +- The configuration properly handles resource dependencies +- Application Gateway is created before NGroups deployment +- Subnets are associated with NSG after creation + +### Scaling +- NGroups automatically manages container instance scaling +- Application Gateway distributes traffic across container instances +- Health probes ensure only healthy instances receive traffic + +## Troubleshooting + +### Common Issues + +1. **Subnet Address Conflicts**: Ensure subnet CIDRs don't overlap +2. **Application Gateway Startup**: May take 10-15 minutes to fully provision +3. **Container Health**: Check that containers respond to health probes + +### Monitoring + +Monitor the deployment through: +- Azure Portal for resource status +- Application Gateway metrics for traffic distribution +- Container Instance logs for application health + +## Clean Up + +To destroy all resources: +```bash +terraform destroy -var-file="ngroups-gateway.tfvars" +``` + +**Note**: This will delete all networking infrastructure and container resources. + +## Migration Path + +When native Terraform support becomes available for Container Group Profiles and NGroups: +1. Replace ARM template deployments with native resources +2. Update variable references +3. Test the migration in a development environment + +## Support + +This configuration is based on the ARM template `ACI-NGroups-Gateway.yaml` and maintains feature parity while providing Terraform workflow benefits. diff --git a/recipe-packs/terraform/aci-ngroups-gateway/main.tf b/recipe-packs/terraform/aci-ngroups-gateway/main.tf new file mode 100644 index 00000000..356db183 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-gateway/main.tf @@ -0,0 +1,400 @@ +# Configure the Azure Provider +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~>3.0" + } + } +} + +# Configure the Microsoft Azure Provider +provider "azurerm" { + features {} +} + +# Data source to get current resource group +data "azurerm_resource_group" "current" { + name = var.resource_group_name +} + +# Data source to get current subscription +data "azurerm_client_config" "current" {} + +# Local variables +locals { + api_version = var.api_version + ngroups_name = var.ngroups_name_param + container_group_profile_name = var.container_group_profile_name + application_gateway_name = var.application_gateway_name + public_ip_name = var.public_ip_name + backend_address_pool_name = var.backend_address_pool_name + vnet_name = var.vnet_name + network_security_group_name = var.network_security_group_name + desired_count = var.desired_count + maintain_desired_count = var.maintain_desired_count + zones = var.zones + vnet_address_prefix = var.vnet_address_prefix + aci_subnet_address_prefix = var.aci_subnet_address_prefix + app_gateway_subnet_address_prefix = var.app_gateway_subnet_address_prefix + aci_subnet_name = var.aci_subnet_name + app_gateway_subnet_name = var.app_gateway_subnet_name + ddos_protection_plan_name = var.ddos_protection_plan_name + + description = "This ARM template is an example template of using an App Gateway with a NGroup." + resource_prefix = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${data.azurerm_resource_group.current.name}/providers/" + application_gateway_api_version = "2022-09-01" + prefix_cg = "cg-lin100-regional-ag-" +} + +# Network Security Group +resource "azurerm_network_security_group" "nsg" { + name = local.network_security_group_name + location = data.azurerm_resource_group.current.location + resource_group_name = data.azurerm_resource_group.current.name + + security_rule { + name = "AppGatewayV2ProbeInbound" + priority = 100 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "65200-65535" + source_address_prefix = "GatewayManager" + destination_address_prefix = "*" + description = "Allow traffic from GatewayManager. This rule is needed for application gateway probes to work." + } + + security_rule { + name = "AllowHTTPInbound" + priority = 110 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "80" + source_address_prefix = "Internet" + destination_address_prefix = "*" + description = "Allow Internet traffic on port 80" + } + + security_rule { + name = "AllowPublicIPAddress" + priority = 111 + direction = "Inbound" + access = "Allow" + protocol = "Tcp" + source_port_range = "*" + destination_port_range = "80" + source_address_prefix = "Internet" + destination_address_prefix = azurerm_public_ip.public_ip.ip_address + description = "Allow traffic from public ip address" + } + + security_rule { + name = "AllowVirtualNetworkInbound" + priority = 112 + direction = "Inbound" + access = "Allow" + protocol = "*" + source_port_range = "*" + destination_port_range = "80" + source_address_prefix = "*" + destination_address_prefix = "VirtualNetwork" + description = "Allow Internet traffic to Virtual network" + } +} + +# Virtual Network +resource "azurerm_virtual_network" "vnet" { + name = local.vnet_name + location = data.azurerm_resource_group.current.location + resource_group_name = data.azurerm_resource_group.current.name + address_space = [local.vnet_address_prefix] + + depends_on = [azurerm_network_security_group.nsg] +} + +# ACI Subnet +resource "azurerm_subnet" "aci_subnet" { + name = local.aci_subnet_name + resource_group_name = data.azurerm_resource_group.current.name + virtual_network_name = azurerm_virtual_network.vnet.name + address_prefixes = [local.aci_subnet_address_prefix] + + delegation { + name = "ACIDelegationService" + + service_delegation { + name = "Microsoft.ContainerInstance/containerGroups" + } + } + + private_endpoint_network_policies_enabled = false + private_link_service_network_policies_enabled = true + + depends_on = [azurerm_virtual_network.vnet] +} + +# Application Gateway Subnet +resource "azurerm_subnet" "app_gateway_subnet" { + name = local.app_gateway_subnet_name + resource_group_name = data.azurerm_resource_group.current.name + virtual_network_name = azurerm_virtual_network.vnet.name + address_prefixes = [local.app_gateway_subnet_address_prefix] + + private_endpoint_network_policies_enabled = false + private_link_service_network_policies_enabled = true + + depends_on = [azurerm_virtual_network.vnet] +} + +# Associate NSG with ACI Subnet +resource "azurerm_subnet_network_security_group_association" "aci_subnet_nsg" { + subnet_id = azurerm_subnet.aci_subnet.id + network_security_group_id = azurerm_network_security_group.nsg.id +} + +# Associate NSG with Application Gateway Subnet +resource "azurerm_subnet_network_security_group_association" "app_gateway_subnet_nsg" { + subnet_id = azurerm_subnet.app_gateway_subnet.id + network_security_group_id = azurerm_network_security_group.nsg.id +} + +# Public IP for Application Gateway +resource "azurerm_public_ip" "public_ip" { + name = local.public_ip_name + location = data.azurerm_resource_group.current.location + resource_group_name = data.azurerm_resource_group.current.name + allocation_method = "Static" + sku = "Standard" + sku_tier = "Regional" + ip_version = "IPv4" + idle_timeout_in_minutes = 5 + + ddos_protection_mode = "VirtualNetworkInherited" +} + +# Application Gateway +resource "azurerm_application_gateway" "app_gateway" { + name = local.application_gateway_name + location = data.azurerm_resource_group.current.location + resource_group_name = data.azurerm_resource_group.current.name + + sku { + name = "Standard_v2" + tier = "Standard_v2" + } + + autoscale_configuration { + min_capacity = 0 + max_capacity = 3 + } + + gateway_ip_configuration { + name = "appGatewayIpConfig" + subnet_id = azurerm_subnet.app_gateway_subnet.id + } + + frontend_port { + name = "port_80" + port = 80 + } + + frontend_ip_configuration { + name = "appGwPublicFrontendIpIPv4" + public_ip_address_id = azurerm_public_ip.public_ip.id + } + + backend_address_pool { + name = local.backend_address_pool_name + } + + backend_http_settings { + name = "${local.application_gateway_name}-be-settings" + cookie_based_affinity = "Disabled" + port = 80 + protocol = "Http" + request_timeout = 60 + probe_name = "healthprobe" + } + + http_listener { + name = "${local.application_gateway_name}-listener" + frontend_ip_configuration_name = "appGwPublicFrontendIpIPv4" + frontend_port_name = "port_80" + protocol = "Http" + } + + request_routing_rule { + name = "${local.application_gateway_name}-routerule" + rule_type = "Basic" + priority = 1 + http_listener_name = "${local.application_gateway_name}-listener" + backend_address_pool_name = local.backend_address_pool_name + backend_http_settings_name = "${local.application_gateway_name}-be-settings" + } + + probe { + name = "healthprobe" + protocol = "Http" + path = "/" + host = "127.0.0.1" + interval = 3600 + timeout = 3600 + unhealthy_threshold = 3 + } + + depends_on = [ + azurerm_subnet.app_gateway_subnet, + azurerm_public_ip.public_ip + ] +} + +# Template deployment for Container Group Profile (using ARM template within Terraform) +resource "azurerm_resource_group_template_deployment" "container_group_profile" { + name = "cgp-deployment" + resource_group_name = data.azurerm_resource_group.current.name + deployment_mode = "Incremental" + + template_content = jsonencode({ + "$schema" = "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" + contentVersion = "1.0.0.0" + parameters = {} + variables = { + containerGroupProfileName = local.container_group_profile_name + apiVersion = local.api_version + } + resources = [ + { + apiVersion = local.api_version + type = "Microsoft.ContainerInstance/containerGroupProfiles" + name = local.container_group_profile_name + location = data.azurerm_resource_group.current.location + properties = { + sku = "Standard" + containers = [ + { + name = "web" + properties = { + image = var.container_image + ports = [ + { + protocol = "TCP" + port = var.container_port + } + ] + resources = { + requests = { + memoryInGB = var.memory_gb + cpu = var.cpu_cores + } + } + } + } + ] + restartPolicy = "Always" + ipAddress = { + ports = [ + { + protocol = "TCP" + port = var.container_port + } + ] + type = "Private" + } + osType = "Linux" + } + } + ] + }) +} + +# Template deployment for NGroups +resource "azurerm_resource_group_template_deployment" "ngroups" { + name = "ngroups-deployment" + resource_group_name = data.azurerm_resource_group.current.name + deployment_mode = "Incremental" + + depends_on = [ + azurerm_resource_group_template_deployment.container_group_profile, + azurerm_application_gateway.app_gateway, + azurerm_virtual_network.vnet + ] + + template_content = jsonencode({ + "$schema" = "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" + contentVersion = "1.0.0.0" + parameters = {} + variables = { + containerGroupProfileName = local.container_group_profile_name + nGroupsNameParam = local.ngroups_name + applicationGatewayName = local.application_gateway_name + vnetName = local.vnet_name + aciSubnetName = local.aci_subnet_name + backendAddressPoolName = local.backend_address_pool_name + apiVersion = local.api_version + desiredCount = local.desired_count + maintainDesiredCount = local.maintain_desired_count + prefixCG = local.prefix_cg + resourcePrefix = local.resource_prefix + } + resources = [ + { + apiVersion = local.api_version + type = "Microsoft.ContainerInstance/NGroups" + name = local.ngroups_name + location = data.azurerm_resource_group.current.location + dependsOn = [ + "Microsoft.ContainerInstance/containerGroupProfiles/${local.container_group_profile_name}" + ] + properties = { + elasticProfile = { + desiredCount = local.desired_count + maintainDesiredCount = local.maintain_desired_count + containerGroupNamingPolicy = { + guidNamingPolicy = { + prefix = local.prefix_cg + } + } + } + containerGroupProfiles = [ + { + resource = { + id = "${local.resource_prefix}Microsoft.ContainerInstance/containerGroupProfiles/${local.container_group_profile_name}" + } + containerGroupProperties = { + subnetIds = [ + { + id = azurerm_subnet.aci_subnet.id + name = local.aci_subnet_name + } + ] + } + networkProfile = { + applicationGateway = { + resource = { + id = azurerm_application_gateway.app_gateway.id + } + backendAddressPools = [ + { + resource = { + id = "${azurerm_application_gateway.app_gateway.id}/backendAddressPools/${local.backend_address_pool_name}" + } + } + ] + } + } + } + ] + } + zones = local.zones + tags = { + "cirrusTestScenario" = "lin-100.regional.appgateway" + "reprovision.enabled" = "true" + } + } + ] + }) +} diff --git a/recipe-packs/terraform/aci-ngroups-gateway/outputs.tf b/recipe-packs/terraform/aci-ngroups-gateway/outputs.tf new file mode 100644 index 00000000..ad2994c5 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-gateway/outputs.tf @@ -0,0 +1,96 @@ +# Output values for the NGroups Gateway Terraform configuration + +output "resource_group_name" { + description = "Name of the resource group" + value = data.azurerm_resource_group.current.name +} + +output "resource_group_location" { + description = "Location of the resource group" + value = data.azurerm_resource_group.current.location +} + +output "subscription_id" { + description = "Azure subscription ID" + value = data.azurerm_client_config.current.subscription_id +} + +output "virtual_network_id" { + description = "ID of the Virtual Network" + value = azurerm_virtual_network.vnet.id +} + +output "virtual_network_name" { + description = "Name of the Virtual Network" + value = azurerm_virtual_network.vnet.name +} + +output "aci_subnet_id" { + description = "ID of the ACI subnet" + value = azurerm_subnet.aci_subnet.id +} + +output "app_gateway_subnet_id" { + description = "ID of the Application Gateway subnet" + value = azurerm_subnet.app_gateway_subnet.id +} + +output "network_security_group_id" { + description = "ID of the Network Security Group" + value = azurerm_network_security_group.nsg.id +} + +output "public_ip_address" { + description = "Public IP address" + value = azurerm_public_ip.public_ip.ip_address +} + +output "public_ip_id" { + description = "ID of the Public IP" + value = azurerm_public_ip.public_ip.id +} + +output "application_gateway_id" { + description = "ID of the Application Gateway" + value = azurerm_application_gateway.app_gateway.id +} + +output "application_gateway_name" { + description = "Name of the Application Gateway" + value = azurerm_application_gateway.app_gateway.name +} + +output "backend_address_pool_id" { + description = "ID of the backend address pool" + value = "${azurerm_application_gateway.app_gateway.id}/backendAddressPools/${local.backend_address_pool_name}" +} + +output "container_group_profile_name" { + description = "Name of the container group profile" + value = local.container_group_profile_name +} + +output "ngroups_name" { + description = "Name of the NGroups resource" + value = local.ngroups_name +} + +output "container_group_profile_deployment_id" { + description = "Deployment ID for container group profile" + value = azurerm_resource_group_template_deployment.container_group_profile.id +} + +output "ngroups_deployment_id" { + description = "Deployment ID for NGroups resource" + value = azurerm_resource_group_template_deployment.ngroups.id +} + +output "application_gateway_frontend_ip" { + description = "Frontend IP configuration of the Application Gateway" + value = azurerm_application_gateway.app_gateway.frontend_ip_configuration[0].name +} + +output "application_gateway_backend_pool" { + description = "Backend address pool name" + value = local.backend_address_pool_name +} diff --git a/recipe-packs/terraform/aci-ngroups-gateway/terraform.tfvars.example b/recipe-packs/terraform/aci-ngroups-gateway/terraform.tfvars.example new file mode 100644 index 00000000..737df049 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-gateway/terraform.tfvars.example @@ -0,0 +1,39 @@ +# Example Terraform variables file for NGroups Gateway configuration +# Copy this file to ngroups-gateway.tfvars and modify the values as needed + +# Required variables +resource_group_name = "my-ngroups-gateway-rg" + +# Optional variables (defaults will be used if not specified) +# api_version = "2024-09-01-preview" +# ngroups_name_param = "nGroups_lin100_regional_ag" +# container_group_profile_name = "cgp" +# application_gateway_name = "agw1" +# public_ip_name = "publicIP" +# backend_address_pool_name = "bepool" +# vnet_name = "vnet1" +# network_security_group_name = "nsg1" +# desired_count = 100 +# maintain_desired_count = true +# zones = [] + +# Network configuration +# vnet_address_prefix = "172.16.0.0/23" +# aci_subnet_address_prefix = "172.16.0.0/25" +# app_gateway_subnet_address_prefix = "172.16.1.0/25" +# aci_subnet_name = "aciSubnet" +# app_gateway_subnet_name = "appgatewaySubnet" + +# Container configuration +# container_image = "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e" +# container_port = 80 +# memory_gb = 1.0 +# cpu_cores = 1.0 + +# Tags +# tags = { +# cirrusTestScenario = "lin-100.regional.appgateway" +# "reprovision.enabled" = "true" +# environment = "development" +# project = "my-project" +# } diff --git a/recipe-packs/terraform/aci-ngroups-gateway/variables.tf b/recipe-packs/terraform/aci-ngroups-gateway/variables.tf new file mode 100644 index 00000000..ba65b918 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-gateway/variables.tf @@ -0,0 +1,142 @@ +# Variable definitions for the NGroups Gateway Terraform configuration + +variable "resource_group_name" { + description = "Name of the Azure Resource Group" + type = string +} + +variable "api_version" { + description = "API version for Container Instance resources" + type = string + default = "2024-09-01-preview" +} + +variable "ngroups_name_param" { + description = "Name of the NGroups resource" + type = string + default = "nGroups_lin100_regional_ag" +} + +variable "container_group_profile_name" { + description = "Name of the Container Group Profile" + type = string + default = "cgp" +} + +variable "application_gateway_name" { + description = "Name of the Application Gateway" + type = string + default = "agw1" +} + +variable "public_ip_name" { + description = "Name of the Public IP address" + type = string + default = "publicIP" +} + +variable "backend_address_pool_name" { + description = "Name of the backend address pool" + type = string + default = "bepool" +} + +variable "vnet_name" { + description = "Name of the Virtual Network" + type = string + default = "vnet1" +} + +variable "network_security_group_name" { + description = "Name of the Network Security Group" + type = string + default = "nsg1" +} + +variable "desired_count" { + description = "Desired number of container instances" + type = number + default = 100 +} + +variable "maintain_desired_count" { + description = "Whether to maintain the desired count" + type = bool + default = true +} + +variable "zones" { + description = "Availability zones for the resources" + type = list(string) + default = [] +} + +variable "vnet_address_prefix" { + description = "Address prefix for the Virtual Network" + type = string + default = "172.16.0.0/23" +} + +variable "aci_subnet_address_prefix" { + description = "Address prefix for the ACI subnet" + type = string + default = "172.16.0.0/25" +} + +variable "app_gateway_subnet_address_prefix" { + description = "Address prefix for the Application Gateway subnet" + type = string + default = "172.16.1.0/25" +} + +variable "aci_subnet_name" { + description = "Name of the ACI subnet" + type = string + default = "aciSubnet" +} + +variable "app_gateway_subnet_name" { + description = "Name of the Application Gateway subnet" + type = string + default = "appgatewaySubnet" +} + +variable "ddos_protection_plan_name" { + description = "Name of the DDoS protection plan" + type = string + default = "ddosProtectionPlan" +} + +variable "container_image" { + description = "Container image to deploy" + type = string + default = "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e" +} + +variable "container_port" { + description = "Port to expose on the container" + type = number + default = 80 +} + +variable "memory_gb" { + description = "Memory allocation in GB" + type = number + default = 1.0 +} + +variable "cpu_cores" { + description = "CPU allocation" + type = number + default = 1.0 +} + +variable "tags" { + description = "Tags to apply to resources" + type = map(string) + default = { + cirrusTestScenario = "lin-100.regional.appgateway" + "reprovision.enabled" = "true" + environment = "development" + } +} diff --git a/recipe-packs/terraform/aci-ngroups-loadbalancer/README.md b/recipe-packs/terraform/aci-ngroups-loadbalancer/README.md new file mode 100644 index 00000000..8dbb3071 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-loadbalancer/README.md @@ -0,0 +1,224 @@ +# Azure Container Instance NGroups with Load Balancer - Terraform Configuration + +This Terraform configuration converts the ARM template for Azure Container Instance NGroups with Load Balancer integration to Terraform format. This is an advanced setup that includes comprehensive networking infrastructure, load balancing, NAT gateway, and DDoS protection. + +## Architecture Overview + +This configuration deploys a complete infrastructure stack: + +1. **Virtual Network** with dedicated subnet and Container Instance delegation +2. **Network Security Group** with security rules for HTTP traffic (ports 80-331) +3. **DDoS Protection Plan** for enhanced security +4. **NAT Gateway** for outbound internet connectivity +5. **Public IP addresses** for inbound and outbound traffic +6. **Standard Load Balancer** with health probes and load balancing rules +7. **Container Group Profile** with private networking +8. **NGroups** with Load Balancer integration for elastic scaling + +## Prerequisites + +- Terraform >= 0.14 +- Azure CLI installed and authenticated +- An existing Azure Resource Group +- Sufficient Azure permissions to create networking, load balancing, and container resources + +## Resources Created + +### Networking Infrastructure +- **Virtual Network** (`azurerm_virtual_network`) with DDoS protection +- **Subnet** with Container Instance delegation (`azurerm_subnet`) +- **Network Security Group** with HTTP security rules (`azurerm_network_security_group`) +- **DDoS Protection Plan** (`azurerm_network_ddos_protection_plan`) + +### Public IP and NAT +- **Inbound Public IP** for Load Balancer (`azurerm_public_ip`) +- **Outbound Public IP** for NAT Gateway (`azurerm_public_ip`) +- **NAT Gateway** for outbound connectivity (`azurerm_nat_gateway`) + +### Load Balancing +- **Standard Load Balancer** (`azurerm_lb`) +- **Backend Address Pool** (`azurerm_lb_backend_address_pool`) +- **Health Probe** (`azurerm_lb_probe`) +- **Load Balancing Rule** (`azurerm_lb_rule`) +- **Inbound NAT Rule** for port range 81-331 (`azurerm_lb_nat_rule`) + +### Container Instance Resources +- **Container Group Profile** (via ARM template deployment) +- **NGroups** with Load Balancer integration (via ARM template deployment) + +## Usage + +### 1. Setup Variables + +Copy the example variables file: +```bash +cp terraform.tfvars.example terraform.tfvars +``` + +Edit `terraform.tfvars` with your specific values: +```hcl +resource_group_name = "your-resource-group-name" +desired_count = 50 # Adjust based on your needs +domain_name_label = "your-unique-domain-label" +``` + +### 2. Initialize Terraform + +```bash +terraform init +``` + +### 3. Plan the Deployment + +```bash +terraform plan +``` + +### 4. Apply the Configuration + +```bash +terraform apply +``` + +## Configuration Details + +### Network Configuration + +- **VNet Address Space**: `172.19.0.0/16` (default) +- **Subnet Address Space**: `172.19.1.0/24` (default) +- **Container Instance Delegation**: Enabled on subnet +- **NAT Gateway**: Provides outbound internet access +- **DDoS Protection**: Standard protection enabled + +### Security Configuration + +The Network Security Group includes: +- **AllowHTTPInbound**: Allows internet traffic on ports 80-331 +- **Priority**: 100 +- **Protocol**: All protocols (*) + +### Load Balancer Configuration + +- **SKU**: Standard Load Balancer +- **Frontend**: Public IP with configurable domain name +- **Backend Pool**: Integrated with NGroups container instances +- **Health Probe**: TCP probe on port 80 +- **Load Balancing Rule**: HTTP traffic (port 80) +- **NAT Rule**: Port range 81-331 for direct access + +### Container Configuration + +- **Desired Count**: 100 instances (default, configurable) +- **Maintain Desired Count**: Enabled +- **Container Image**: ACI Hello World (configurable) +- **Networking**: Private IP addresses within VNet +- **Auto-scaling**: Managed by NGroups +- **Load Balancer Integration**: Automatic backend pool membership + +## Variables + +| Variable | Description | Default | Required | +|----------|-------------|---------|----------| +| `resource_group_name` | Resource Group name | - | ✅ | +| `api_version` | Container Instance API version | `2024-09-01-preview` | ❌ | +| `desired_count` | Desired container count | `100` | ❌ | +| `vnet_address_prefix` | VNet address space | `172.19.0.0/16` | ❌ | +| `subnet_address_prefix` | Subnet CIDR | `172.19.1.0/24` | ❌ | +| `domain_name_label` | Public IP domain label | `ngroupsdemo` | ❌ | +| `container_image` | Container image | ACI Hello World | ❌ | +| `maintain_desired_count` | Maintain desired count | `true` | ❌ | + +See `variables.tf` for complete variable list. + +## Outputs + +Key outputs include: +- Virtual Network and subnet IDs +- Load Balancer ID and frontend IP +- Public IP addresses and FQDN +- Backend address pool and health probe IDs +- NAT Gateway and DDoS protection plan IDs +- Container Group Profile and NGroups deployment IDs + +## Important Notes + +### Preview Features +- **Container Group Profiles** and **NGroups** are preview features +- These resources use ARM template deployments within Terraform +- Future versions may support native Terraform resources + +### Dependencies +- The configuration properly handles complex resource dependencies +- Load Balancer is created before NGroups deployment +- NAT Gateway and DDoS protection are configured before VNet + +### Scaling and Performance +- NGroups automatically manages container instance scaling +- Load Balancer distributes traffic across container instances +- Health probes ensure only healthy instances receive traffic +- NAT Gateway provides dedicated outbound connectivity + +### Security Considerations +- DDoS protection provides enhanced security +- Network Security Group controls inbound traffic +- Private networking within VNet for container instances +- Outbound traffic routed through NAT Gateway + +## Troubleshooting + +### Common Issues + +1. **Domain Name Label Conflicts**: Ensure unique domain name labels +2. **Address Space Conflicts**: Verify subnet CIDRs don't overlap +3. **Load Balancer Startup**: May take 10-15 minutes to fully provision +4. **Container Health**: Check that containers respond to TCP health probes on port 80 + +### Monitoring + +Monitor the deployment through: +- Azure Portal for resource status +- Load Balancer metrics for traffic distribution +- Container Instance logs for application health +- NAT Gateway metrics for outbound connectivity + +## Cost Considerations + +This configuration includes several billable resources: +- **Standard Load Balancer**: Hourly charges + data processing +- **NAT Gateway**: Hourly charges + data processing +- **DDoS Protection Plan**: Monthly subscription fee +- **Public IP addresses**: Hourly charges for static IPs +- **Container Instances**: Per-second billing based on resources + +## Clean Up + +To destroy all resources: +```bash +terraform destroy +``` + +**Note**: This will delete all networking infrastructure and container resources. + +## Migration Path + +When native Terraform support becomes available for Container Group Profiles and NGroups: +1. Replace ARM template deployments with native resources +2. Update variable references +3. Test the migration in a development environment + +## Performance and Scaling + +### Load Balancer Features +- **Session Persistence**: Configurable via load balancing rules +- **Health Monitoring**: TCP health probes with configurable thresholds +- **Traffic Distribution**: Default round-robin distribution +- **Connection Draining**: Graceful handling of unhealthy instances + +### NGroups Auto-scaling +- **Desired Count Management**: Automatically maintains specified instance count +- **Zone Distribution**: Optional availability zone distribution +- **Container Lifecycle**: Automatic container restart and replacement + +## Support + +This configuration is based on the ARM template `ACI-NGroups-LoadBalancer.yaml` and maintains feature parity while providing Terraform workflow benefits. diff --git a/recipe-packs/terraform/aci-ngroups-loadbalancer/main.tf b/recipe-packs/terraform/aci-ngroups-loadbalancer/main.tf new file mode 100644 index 00000000..5b0b9956 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-loadbalancer/main.tf @@ -0,0 +1,387 @@ +# Configure the Azure Provider +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~>3.0" + } + } +} + +# Configure the Microsoft Azure Provider +provider "azurerm" { + features {} +} + +# Data source to get current resource group +data "azurerm_resource_group" "current" { + name = var.resource_group_name +} + +# Data source to get current subscription +data "azurerm_client_config" "current" {} + +# Local variables +locals { + api_version = var.api_version + ngroups_name = var.ngroups_param_name + container_group_profile_name = var.container_group_profile_name + load_balancer_name = var.load_balancer_name + backend_address_pool_name = var.backend_address_pool_name + vnet_name = var.vnet_name + subnet_name = var.subnet_name + network_security_group_name = var.network_security_group_name + inbound_public_ip_name = var.inbound_public_ip_name + outbound_public_ip_name = var.outbound_public_ip_name + outbound_public_ip_prefix_name = var.outbound_public_ip_prefix_name + nat_gateway_name = var.nat_gateway_name + frontend_ip_name = var.frontend_ip_name + http_rule_name = var.http_rule_name + health_probe_name = var.health_probe_name + vnet_address_prefix = var.vnet_address_prefix + subnet_address_prefix = var.subnet_address_prefix + desired_count = var.desired_count + zones = var.zones + maintain_desired_count = var.maintain_desired_count + domain_name_label = var.domain_name_label + inbound_nat_rule_name = var.inbound_nat_rule_name + + description = "This ARM template is an example template to test the load balancer integration with NGroups." + cg_profile_name = var.container_group_profile_name + prefix_cg = "cg-lin100-regional-lb-" + resource_prefix = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${data.azurerm_resource_group.current.name}/providers/" + load_balancer_api_version = "2022-07-01" + vnet_api_version = "2022-07-01" + public_ip_version = "2022-07-01" + ddos_protection_plan_name = "ddosProtectionPlan" +} + +# DDoS Protection Plan +resource "azurerm_network_ddos_protection_plan" "ddos_plan" { + name = local.ddos_protection_plan_name + location = data.azurerm_resource_group.current.location + resource_group_name = data.azurerm_resource_group.current.name +} + +# Network Security Group +resource "azurerm_network_security_group" "nsg" { + name = local.network_security_group_name + location = data.azurerm_resource_group.current.location + resource_group_name = data.azurerm_resource_group.current.name + + security_rule { + name = "AllowHTTPInbound" + priority = 100 + direction = "Inbound" + access = "Allow" + protocol = "*" + source_port_range = "*" + destination_port_ranges = ["80-331"] + source_address_prefix = "Internet" + destination_address_prefix = "*" + description = "Allow Internet traffic on port range" + } +} + +# Outbound Public IP for NAT Gateway +resource "azurerm_public_ip" "outbound_public_ip" { + name = local.outbound_public_ip_name + location = data.azurerm_resource_group.current.location + resource_group_name = data.azurerm_resource_group.current.name + allocation_method = "Static" + sku = "Standard" + sku_tier = "Regional" + ip_version = "IPv4" + idle_timeout_in_minutes = 4 +} + +# Inbound Public IP for Load Balancer +resource "azurerm_public_ip" "inbound_public_ip" { + name = local.inbound_public_ip_name + location = data.azurerm_resource_group.current.location + resource_group_name = data.azurerm_resource_group.current.name + allocation_method = "Static" + sku = "Standard" + sku_tier = "Regional" + ip_version = "IPv4" + idle_timeout_in_minutes = 4 + domain_name_label = local.domain_name_label +} + +# NAT Gateway +resource "azurerm_nat_gateway" "nat_gateway" { + name = local.nat_gateway_name + location = data.azurerm_resource_group.current.location + resource_group_name = data.azurerm_resource_group.current.name + sku_name = "Standard" + idle_timeout_in_minutes = 4 +} + +# Associate NAT Gateway with Outbound Public IP +resource "azurerm_nat_gateway_public_ip_association" "nat_gateway_ip" { + nat_gateway_id = azurerm_nat_gateway.nat_gateway.id + public_ip_address_id = azurerm_public_ip.outbound_public_ip.id +} + +# Virtual Network +resource "azurerm_virtual_network" "vnet" { + name = local.vnet_name + location = data.azurerm_resource_group.current.location + resource_group_name = data.azurerm_resource_group.current.name + address_space = [local.vnet_address_prefix] + + ddos_protection_plan { + id = azurerm_network_ddos_protection_plan.ddos_plan.id + enable = true + } + + depends_on = [ + azurerm_network_security_group.nsg, + azurerm_nat_gateway.nat_gateway + ] +} + +# Subnet +resource "azurerm_subnet" "subnet" { + name = local.subnet_name + resource_group_name = data.azurerm_resource_group.current.name + virtual_network_name = azurerm_virtual_network.vnet.name + address_prefixes = [local.subnet_address_prefix] + + delegation { + name = "Microsoft.ContainerInstance.containerGroups" + + service_delegation { + name = "Microsoft.ContainerInstance/containerGroups" + } + } + + private_endpoint_network_policies_enabled = false + private_link_service_network_policies_enabled = true + + depends_on = [azurerm_virtual_network.vnet] +} + +# Associate NSG with Subnet +resource "azurerm_subnet_network_security_group_association" "subnet_nsg" { + subnet_id = azurerm_subnet.subnet.id + network_security_group_id = azurerm_network_security_group.nsg.id +} + +# Associate NAT Gateway with Subnet +resource "azurerm_subnet_nat_gateway_association" "subnet_nat" { + subnet_id = azurerm_subnet.subnet.id + nat_gateway_id = azurerm_nat_gateway.nat_gateway.id +} + +# Load Balancer +resource "azurerm_lb" "load_balancer" { + name = local.load_balancer_name + location = data.azurerm_resource_group.current.location + resource_group_name = data.azurerm_resource_group.current.name + sku = "Standard" + + frontend_ip_configuration { + name = local.frontend_ip_name + public_ip_address_id = azurerm_public_ip.inbound_public_ip.id + } + + depends_on = [ + azurerm_public_ip.inbound_public_ip, + azurerm_virtual_network.vnet + ] +} + +# Load Balancer Backend Address Pool +resource "azurerm_lb_backend_address_pool" "backend_pool" { + loadbalancer_id = azurerm_lb.load_balancer.id + name = local.backend_address_pool_name +} + +# Load Balancer Health Probe +resource "azurerm_lb_probe" "health_probe" { + loadbalancer_id = azurerm_lb.load_balancer.id + name = local.health_probe_name + protocol = "Tcp" + port = 80 + interval_in_seconds = 5 + number_of_probes = 2 + probe_threshold = 1 +} + +# Load Balancer Rule +resource "azurerm_lb_rule" "http_rule" { + loadbalancer_id = azurerm_lb.load_balancer.id + name = local.http_rule_name + protocol = "Tcp" + frontend_port = 80 + backend_port = 80 + frontend_ip_configuration_name = local.frontend_ip_name + backend_address_pool_ids = [azurerm_lb_backend_address_pool.backend_pool.id] + probe_id = azurerm_lb_probe.health_probe.id + enable_floating_ip = false + idle_timeout_in_minutes = 15 + enable_tcp_reset = true + load_distribution = "Default" + disable_outbound_snat = false +} + +# Load Balancer Inbound NAT Rule +resource "azurerm_lb_nat_rule" "inbound_nat_rule" { + resource_group_name = data.azurerm_resource_group.current.name + loadbalancer_id = azurerm_lb.load_balancer.id + name = local.inbound_nat_rule_name + protocol = "Tcp" + frontend_port_start = 81 + frontend_port_end = 331 + backend_port = 80 + frontend_ip_configuration_name = local.frontend_ip_name + enable_floating_ip = false + enable_tcp_reset = false + idle_timeout_in_minutes = 4 + backend_address_pool_id = azurerm_lb_backend_address_pool.backend_pool.id +} + +# Template deployment for Container Group Profile (using ARM template within Terraform) +resource "azurerm_resource_group_template_deployment" "container_group_profile" { + name = "cgp-deployment" + resource_group_name = data.azurerm_resource_group.current.name + deployment_mode = "Incremental" + + template_content = jsonencode({ + "$schema" = "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" + contentVersion = "1.0.0.0" + parameters = {} + variables = { + cgProfileName = local.cg_profile_name + apiVersion = local.api_version + } + resources = [ + { + apiVersion = local.api_version + type = "Microsoft.ContainerInstance/containerGroupProfiles" + name = local.cg_profile_name + location = data.azurerm_resource_group.current.location + properties = { + sku = "Standard" + containers = [ + { + name = "web" + properties = { + image = var.container_image + ports = [ + { + protocol = "TCP" + port = var.container_port + } + ] + resources = { + requests = { + memoryInGB = var.memory_gb + cpu = var.cpu_cores + } + } + } + } + ] + restartPolicy = "Always" + ipAddress = { + ports = [ + { + protocol = "TCP" + port = var.container_port + } + ] + type = "Private" + } + osType = "Linux" + } + } + ] + }) +} + +# Template deployment for NGroups +resource "azurerm_resource_group_template_deployment" "ngroups" { + name = "ngroups-deployment" + resource_group_name = data.azurerm_resource_group.current.name + deployment_mode = "Incremental" + + depends_on = [ + azurerm_resource_group_template_deployment.container_group_profile, + azurerm_lb.load_balancer, + azurerm_virtual_network.vnet + ] + + template_content = jsonencode({ + "$schema" = "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" + contentVersion = "1.0.0.0" + parameters = {} + variables = { + cgProfileName = local.cg_profile_name + nGroupsName = local.ngroups_name + loadBalancerName = local.load_balancer_name + vnetName = local.vnet_name + subnetName = local.subnet_name + backendAddressPoolName = local.backend_address_pool_name + apiVersion = local.api_version + desiredCount = local.desired_count + maintainDesiredCount = local.maintain_desired_count + prefixCG = local.prefix_cg + resourcePrefix = local.resource_prefix + } + resources = [ + { + apiVersion = local.api_version + type = "Microsoft.ContainerInstance/NGroups" + name = local.ngroups_name + location = data.azurerm_resource_group.current.location + dependsOn = [ + "Microsoft.ContainerInstance/containerGroupProfiles/${local.cg_profile_name}" + ] + properties = { + elasticProfile = { + desiredCount = local.desired_count + maintainDesiredCount = local.maintain_desired_count + containerGroupNamingPolicy = { + guidNamingPolicy = { + prefix = local.prefix_cg + } + } + } + containerGroupProfiles = [ + { + resource = { + id = "${local.resource_prefix}Microsoft.ContainerInstance/containerGroupProfiles/${local.cg_profile_name}" + } + containerGroupProperties = { + subnetIds = [ + { + id = azurerm_subnet.subnet.id + name = local.subnet_name + } + ] + } + networkProfile = { + loadBalancer = { + backendAddressPools = [ + { + resource = { + id = azurerm_lb_backend_address_pool.backend_pool.id + } + } + ] + } + } + } + ] + } + zones = local.zones + tags = { + "cirrusTestScenario" = "lin-100.regional.loadbalancer" + "reprovision.enabled" = "true" + } + } + ] + }) +} diff --git a/recipe-packs/terraform/aci-ngroups-loadbalancer/outputs.tf b/recipe-packs/terraform/aci-ngroups-loadbalancer/outputs.tf new file mode 100644 index 00000000..4faf4b34 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-loadbalancer/outputs.tf @@ -0,0 +1,121 @@ +# Output values for the ACI NGroups LoadBalancer Terraform configuration + +output "resource_group_name" { + description = "Name of the resource group" + value = data.azurerm_resource_group.current.name +} + +output "resource_group_location" { + description = "Location of the resource group" + value = data.azurerm_resource_group.current.location +} + +output "subscription_id" { + description = "Azure subscription ID" + value = data.azurerm_client_config.current.subscription_id +} + +output "virtual_network_id" { + description = "ID of the Virtual Network" + value = azurerm_virtual_network.vnet.id +} + +output "virtual_network_name" { + description = "Name of the Virtual Network" + value = azurerm_virtual_network.vnet.name +} + +output "subnet_id" { + description = "ID of the subnet" + value = azurerm_subnet.subnet.id +} + +output "network_security_group_id" { + description = "ID of the Network Security Group" + value = azurerm_network_security_group.nsg.id +} + +output "ddos_protection_plan_id" { + description = "ID of the DDoS Protection Plan" + value = azurerm_network_ddos_protection_plan.ddos_plan.id +} + +output "nat_gateway_id" { + description = "ID of the NAT Gateway" + value = azurerm_nat_gateway.nat_gateway.id +} + +output "inbound_public_ip_address" { + description = "Inbound public IP address" + value = azurerm_public_ip.inbound_public_ip.ip_address +} + +output "inbound_public_ip_fqdn" { + description = "Fully qualified domain name of the inbound public IP" + value = azurerm_public_ip.inbound_public_ip.fqdn +} + +output "outbound_public_ip_address" { + description = "Outbound public IP address" + value = azurerm_public_ip.outbound_public_ip.ip_address +} + +output "load_balancer_id" { + description = "ID of the Load Balancer" + value = azurerm_lb.load_balancer.id +} + +output "load_balancer_frontend_ip_id" { + description = "ID of the Load Balancer frontend IP configuration" + value = azurerm_lb.load_balancer.frontend_ip_configuration[0].id +} + +output "backend_address_pool_id" { + description = "ID of the backend address pool" + value = azurerm_lb_backend_address_pool.backend_pool.id +} + +output "health_probe_id" { + description = "ID of the health probe" + value = azurerm_lb_probe.health_probe.id +} + +output "http_rule_id" { + description = "ID of the HTTP load balancing rule" + value = azurerm_lb_rule.http_rule.id +} + +output "inbound_nat_rule_id" { + description = "ID of the inbound NAT rule" + value = azurerm_lb_nat_rule.inbound_nat_rule.id +} + +output "container_group_profile_name" { + description = "Name of the container group profile" + value = local.cg_profile_name +} + +output "ngroups_name" { + description = "Name of the NGroups resource" + value = local.ngroups_name +} + +output "container_group_profile_deployment_id" { + description = "Deployment ID for container group profile" + value = azurerm_resource_group_template_deployment.container_group_profile.id +} + +output "ngroups_deployment_id" { + description = "Deployment ID for NGroups resource" + value = azurerm_resource_group_template_deployment.ngroups.id +} + +output "load_balancer_frontend_ip" { + description = "Frontend IP configuration of the Load Balancer" + value = local.frontend_ip_name +} + +output "backend_pool_name" { + description = "Backend address pool name" + value = local.backend_address_pool_name +} diff --git a/recipe-packs/terraform/aci-ngroups-loadbalancer/terraform.tfvars.example b/recipe-packs/terraform/aci-ngroups-loadbalancer/terraform.tfvars.example new file mode 100644 index 00000000..85ce9dea --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-loadbalancer/terraform.tfvars.example @@ -0,0 +1,48 @@ +# Example Terraform variables file for ACI NGroups LoadBalancer configuration +# Copy this file to terraform.tfvars and modify the values as needed + +# Required variables +resource_group_name = "my-ngroups-loadbalancer-rg" + +# Optional variables (defaults will be used if not specified) +# api_version = "2024-09-01-preview" +# ngroups_param_name = "nGroups_lin100_reg_lb" +# container_group_profile_name = "cgp_1" +# load_balancer_name = "slb_1" +# backend_address_pool_name = "bepool_1" +# vnet_name = "vnet_1" +# subnet_name = "subnet_1" +# network_security_group_name = "nsg_1" +# inbound_public_ip_name = "inboundPublicIP" +# outbound_public_ip_name = "outboundPublicIP" +# nat_gateway_name = "natGateway1" +# frontend_ip_name = "loadBalancerFrontend" +# http_rule_name = "httpRule" +# health_probe_name = "healthProbe" +# inbound_nat_rule_name = "inboundNatRule" + +# Network configuration +# vnet_address_prefix = "172.19.0.0/16" +# subnet_address_prefix = "172.19.1.0/24" + +# Scaling configuration +# desired_count = 100 +# maintain_desired_count = true +# zones = [] + +# Public IP configuration +# domain_name_label = "ngroupsdemo" + +# Container configuration +# container_image = "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e" +# container_port = 80 +# memory_gb = 1.0 +# cpu_cores = 1.0 + +# Tags +# tags = { +# cirrusTestScenario = "lin-100.regional.loadbalancer" +# "reprovision.enabled" = "true" +# environment = "development" +# project = "my-project" +# } diff --git a/recipe-packs/terraform/aci-ngroups-loadbalancer/variables.tf b/recipe-packs/terraform/aci-ngroups-loadbalancer/variables.tf new file mode 100644 index 00000000..5a1625d0 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-loadbalancer/variables.tf @@ -0,0 +1,172 @@ +# Variable definitions for the ACI NGroups LoadBalancer Terraform configuration + +variable "resource_group_name" { + description = "Name of the Azure Resource Group" + type = string +} + +variable "api_version" { + description = "API version for Container Instance resources" + type = string + default = "2024-09-01-preview" +} + +variable "ngroups_param_name" { + description = "Name of the NGroups resource" + type = string + default = "nGroups_lin100_reg_lb" +} + +variable "container_group_profile_name" { + description = "Name of the Container Group Profile" + type = string + default = "cgp_1" +} + +variable "load_balancer_name" { + description = "Name of the Load Balancer" + type = string + default = "slb_1" +} + +variable "backend_address_pool_name" { + description = "Name of the backend address pool" + type = string + default = "bepool_1" +} + +variable "vnet_name" { + description = "Name of the Virtual Network" + type = string + default = "vnet_1" +} + +variable "subnet_name" { + description = "Name of the Subnet" + type = string + default = "subnet_1" +} + +variable "network_security_group_name" { + description = "Name of the Network Security Group" + type = string + default = "nsg_1" +} + +variable "inbound_public_ip_name" { + description = "Name of the inbound public IP address" + type = string + default = "inboundPublicIP" +} + +variable "outbound_public_ip_name" { + description = "Name of the outbound public IP address" + type = string + default = "outboundPublicIP" +} + +variable "outbound_public_ip_prefix_name" { + description = "Name of the NAT gateway public IP prefix" + type = string + default = "outBoundPublicIPPrefix" +} + +variable "nat_gateway_name" { + description = "Name of the NAT Gateway" + type = string + default = "natGateway1" +} + +variable "frontend_ip_name" { + description = "Name of the load balancer frontend IP configuration" + type = string + default = "loadBalancerFrontend" +} + +variable "http_rule_name" { + description = "Name of the HTTP load balancing rule" + type = string + default = "httpRule" +} + +variable "health_probe_name" { + description = "Name of the health probe" + type = string + default = "healthProbe" +} + +variable "vnet_address_prefix" { + description = "Address prefix for the Virtual Network" + type = string + default = "172.19.0.0/16" +} + +variable "subnet_address_prefix" { + description = "Address prefix for the subnet" + type = string + default = "172.19.1.0/24" +} + +variable "desired_count" { + description = "Desired number of container instances" + type = number + default = 100 +} + +variable "zones" { + description = "Availability zones for the resources" + type = list(string) + default = [] +} + +variable "maintain_desired_count" { + description = "Whether to maintain the desired count" + type = bool + default = true +} + +variable "domain_name_label" { + description = "Domain name label for the public IP" + type = string + default = "ngroupsdemo" +} + +variable "inbound_nat_rule_name" { + description = "Name of the inbound NAT rule" + type = string + default = "inboundNatRule" +} + +variable "container_image" { + description = "Container image to deploy" + type = string + default = "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e" +} + +variable "container_port" { + description = "Port to expose on the container" + type = number + default = 80 +} + +variable "memory_gb" { + description = "Memory allocation in GB" + type = number + default = 1.0 +} + +variable "cpu_cores" { + description = "CPU allocation" + type = number + default = 1.0 +} + +variable "tags" { + description = "Tags to apply to resources" + type = map(string) + default = { + cirrusTestScenario = "lin-100.regional.loadbalancer" + "reprovision.enabled" = "true" + environment = "development" + } +} From 5e11fd8eed1462933b196a4f65f9d92ba386388b Mon Sep 17 00:00:00 2001 From: Shivam Gupta Date: Tue, 29 Jul 2025 22:06:11 -0700 Subject: [PATCH 02/12] added confidential container recipe --- recipe-packs/ACI-NGroups-Confidential.json | 98 ++++++++ ...-Gateway.yaml => ACI-NGroups-Gateway.json} | 0 ...cer.yaml => ACI-NGroups-LoadBalancer.json} | 0 ...oups-basic.yaml => ACI-Ngroups-basic.json} | 0 .../bicep/ACI-NGroups-Confidential.bicep | 83 +++++++ .../aci-ngroups-confidential/README.md | 222 ++++++++++++++++++ .../aci-ngroups-confidential/main.tf | 198 ++++++++++++++++ .../aci-ngroups-confidential/outputs.tf | 22 ++ .../terraform.tfvars.example | 23 ++ .../aci-ngroups-confidential/variables.tf | 49 ++++ 10 files changed, 695 insertions(+) create mode 100644 recipe-packs/ACI-NGroups-Confidential.json rename recipe-packs/{ACI-NGroups-Gateway.yaml => ACI-NGroups-Gateway.json} (100%) rename recipe-packs/{ACI-NGroups-LoadBalancer.yaml => ACI-NGroups-LoadBalancer.json} (100%) rename recipe-packs/{ACI-Ngroups-basic.yaml => ACI-Ngroups-basic.json} (100%) create mode 100644 recipe-packs/bicep/ACI-NGroups-Confidential.bicep create mode 100644 recipe-packs/terraform/aci-ngroups-confidential/README.md create mode 100644 recipe-packs/terraform/aci-ngroups-confidential/main.tf create mode 100644 recipe-packs/terraform/aci-ngroups-confidential/outputs.tf create mode 100644 recipe-packs/terraform/aci-ngroups-confidential/terraform.tfvars.example create mode 100644 recipe-packs/terraform/aci-ngroups-confidential/variables.tf diff --git a/recipe-packs/ACI-NGroups-Confidential.json b/recipe-packs/ACI-NGroups-Confidential.json new file mode 100644 index 00000000..eace2f81 --- /dev/null +++ b/recipe-packs/ACI-NGroups-Confidential.json @@ -0,0 +1,98 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + }, + "variables": { + "cgProfileName": "cgp_1", + "nGroupsName": "ngroup_confidential_basic", + "apiVersion": "2024-09-01-preview", + "desiredCount": 1, + "prefixCG": "cg-confidential-", + "resourcePrefix": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/')]" + }, + "resources": [ + { + "apiVersion": "[variables('apiVersion')]", + "type": "Microsoft.ContainerInstance/containerGroupProfiles", + "name": "[variables('cgProfileName')]", + "location": "[resourceGroup().location]", + "properties": { + "sku": "Confidential", + "confidentialComputeProperties": { + "ccePolicy": "" + }, + "containers": [ + { + "name": "aci-helloworld", + "properties": { + "image": "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e", + "ports": [ + { + "protocol": "TCP", + "port": 80 + } + ], + "resources": { + "requests": { + "memoryInGB": 1.0, + "cpu": 1.0 + } + } + } + } + ], + "restartPolicy": "Always", + "ipAddress": { + "ports": [ + { + "protocol": "TCP", + "port": 80 + } + ], + "type": "Public" + }, + "osType": "Linux" + } + }, + { + "apiVersion": "[variables('apiVersion')]", + "type": "Microsoft.ContainerInstance/NGroups", + "name": "[variables('nGroupsName')]", + "location": "[resourceGroup().location]", + "dependsOn": [ + "[concat('Microsoft.ContainerInstance/containerGroupProfiles/', variables('cgProfileName'))]" + ], + "properties": { + "elasticProfile": { + "desiredCount": "[variables('desiredCount')]", + "containerGroupNamingPolicy": { + "guidNamingPolicy":{ + "prefix":"[variables('prefixCG')]" + } + } + }, + "containerGroupProfiles": [ + { + "resource": { + "id": "[concat(variables('resourcePrefix'), 'Microsoft.ContainerInstance/containerGroupProfiles/', variables('cgProfileName'))]" + } + } + ] + }, + "tags": { + "cirrusTestScenario": "confidential-1.basic" + } + } + ], + "outputs": { + "containerGroupProfileId": { + "type": "string", + "value": "[resourceId('Microsoft.ContainerInstance/containerGroupProfiles', variables('cgProfileName'))]" + }, + "nGroupId": { + "type": "string", + "value": "[resourceId('Microsoft.ContainerInstance/NGroups', variables('nGroupsName'))]" + } + } +} diff --git a/recipe-packs/ACI-NGroups-Gateway.yaml b/recipe-packs/ACI-NGroups-Gateway.json similarity index 100% rename from recipe-packs/ACI-NGroups-Gateway.yaml rename to recipe-packs/ACI-NGroups-Gateway.json diff --git a/recipe-packs/ACI-NGroups-LoadBalancer.yaml b/recipe-packs/ACI-NGroups-LoadBalancer.json similarity index 100% rename from recipe-packs/ACI-NGroups-LoadBalancer.yaml rename to recipe-packs/ACI-NGroups-LoadBalancer.json diff --git a/recipe-packs/ACI-Ngroups-basic.yaml b/recipe-packs/ACI-Ngroups-basic.json similarity index 100% rename from recipe-packs/ACI-Ngroups-basic.yaml rename to recipe-packs/ACI-Ngroups-basic.json diff --git a/recipe-packs/bicep/ACI-NGroups-Confidential.bicep b/recipe-packs/bicep/ACI-NGroups-Confidential.bicep new file mode 100644 index 00000000..8e85c1b9 --- /dev/null +++ b/recipe-packs/bicep/ACI-NGroups-Confidential.bicep @@ -0,0 +1,83 @@ +// Variables (using var instead of parameters since the ARM template uses variables) +var cgProfileName = 'cgp_1' +var nGroupsName = 'ngroup_confidential_basic' +var apiVersion = '2024-09-01-preview' +var desiredCount = 1 +var prefixCG = 'cg-confidential-' +var resourcePrefix = '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/' + +// Container Group Profile with Confidential Computing +resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfiles@2024-09-01-preview' = { + name: cgProfileName + location: resourceGroup().location + properties: { + sku: 'Confidential' + confidentialComputeProperties: { + ccePolicy: '' + } + containers: [ + { + name: 'aci-helloworld' + properties: { + image: 'mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e' + ports: [ + { + protocol: 'TCP' + port: 80 + } + ] + resources: { + requests: { + memoryInGB: json('1.0') + cpu: json('1.0') + } + } + } + } + ] + restartPolicy: 'Always' + ipAddress: { + ports: [ + { + protocol: 'TCP' + port: 80 + } + ] + type: 'Public' + } + osType: 'Linux' + } +} + +// NGroups for Confidential Computing +resource nGroups 'Microsoft.ContainerInstance/NGroups@2024-09-01-preview' = { + name: nGroupsName + location: resourceGroup().location + properties: { + elasticProfile: { + desiredCount: desiredCount + containerGroupNamingPolicy: { + guidNamingPolicy: { + prefix: prefixCG + } + } + } + containerGroupProfiles: [ + { + resource: { + id: '${resourcePrefix}Microsoft.ContainerInstance/containerGroupProfiles/${cgProfileName}' + } + } + ] + } + tags: { + cirrusTestScenario: 'confidential-1.basic' + } + dependsOn: [ + containerGroupProfile + ] +} + +// Outputs +output containerGroupProfileId string = containerGroupProfile.id +output nGroupId string = nGroups.id diff --git a/recipe-packs/terraform/aci-ngroups-confidential/README.md b/recipe-packs/terraform/aci-ngroups-confidential/README.md new file mode 100644 index 00000000..62fcdf6b --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-confidential/README.md @@ -0,0 +1,222 @@ +# Azure Container Instance NGroups with Confidential Computing - Terraform Configuration + +This Terraform configuration converts the ARM template for Azure Container Instance NGroups with Confidential Computing capabilities to Terraform format. This demonstrates the use of confidential computing features with elastic container scaling. + +## Architecture Overview + +This configuration deploys: + +1. **Container Group Profile** with Confidential Computing SKU +2. **NGroups** for elastic scaling of confidential container instances +3. **Confidential Compute Environment** with CCE (Confidential Computing Enclave) policy support + +## Prerequisites + +- Terraform >= 0.14 +- Azure CLI installed and authenticated +- An existing Azure Resource Group +- Azure subscription with access to Confidential Computing features +- Sufficient Azure permissions to create Container Instance resources + +## Resources Created + +### Confidential Computing Resources +- **Container Group Profile** (`azurerm_resource_group_template_deployment`) with Confidential SKU +- **NGroups** (`azurerm_resource_group_template_deployment`) for elastic scaling +- **CCE Policy** configuration for confidential computing + +## Usage + +### 1. Setup Variables + +Copy the example variables file: +```bash +cp terraform.tfvars.example terraform.tfvars +``` + +Edit `terraform.tfvars` with your specific values: +```hcl +resource_group_name = "your-resource-group-name" +desired_count = 1 # Start with 1 for confidential computing +``` + +### 2. Initialize Terraform + +```bash +terraform init +``` + +### 3. Plan the Deployment + +```bash +terraform plan +``` + +### 4. Apply the Configuration + +```bash +terraform apply +``` + +## Configuration Details + +### Confidential Computing Features + +- **SKU**: Confidential (specialized for secure enclaves) +- **CCE Policy**: Configurable Confidential Computing Enclave policy +- **Secure Execution**: Containers run in hardware-protected environments +- **Memory Encryption**: Runtime memory encryption +- **Attestation**: Support for remote attestation of the secure environment + +### Container Configuration + +- **Desired Count**: 1 instance (default, suitable for confidential workloads) +- **Container Image**: ACI Hello World (configurable) +- **Networking**: Public IP addresses (configurable to Private) +- **Auto-scaling**: Managed by NGroups +- **Resource Allocation**: 1 CPU, 1GB RAM (optimized for confidential computing) + +### Security Features + +- **Hardware-level Security**: Intel SGX or AMD SEV-based protection +- **Encrypted Memory**: Runtime memory encryption +- **Secure Boot**: Verified boot process +- **Isolation**: Strong isolation between containers and host + +## Variables + +| Variable | Description | Default | Required | +|----------|-------------|---------|----------| +| `resource_group_name` | Resource Group name | - | ✅ | +| `api_version` | Container Instance API version | `2024-09-01-preview` | ❌ | +| `container_group_profile_name` | Profile name | `cgp_1` | ❌ | +| `ngroups_name` | NGroups name | `ngroup_confidential_basic` | ❌ | +| `desired_count` | Desired container count | `1` | ❌ | +| `prefix_cg` | Container naming prefix | `cg-confidential-` | ❌ | +| `container_image` | Container image | ACI Hello World | ❌ | +| `tags` | Resource tags | `{}` | ❌ | + +See `variables.tf` for complete variable list. + +## Outputs + +Key outputs include: +- Container Group Profile ID +- NGroups ID +- Resource Group name +- ARM deployment names + +## Important Notes + +### Preview Features +- **Confidential Computing** is a preview feature +- **Container Group Profiles** and **NGroups** are preview features +- These resources use ARM template deployments within Terraform +- Future versions may support native Terraform resources + +### Confidential Computing Considerations +- Limited to specific Azure regions with confidential computing support +- Requires specialized VM SKUs (DCsv2, DCsv3, etc.) +- May have different pricing compared to standard container instances +- Performance characteristics may differ from standard containers + +### Dependencies +- Confidential computing hardware availability in target region +- Proper Azure subscription permissions for confidential computing +- Compatible container images (some images may need modification) + +### Security and Compliance +- Ideal for processing sensitive data +- Supports regulatory compliance requirements +- Provides hardware-level data protection +- Enables secure multi-party computation scenarios + +## Troubleshooting + +### Common Issues + +1. **Region Availability**: Ensure confidential computing is available in your region +2. **Subscription Limits**: Check quota limits for confidential computing resources +3. **Container Compatibility**: Verify container images work with confidential computing +4. **CCE Policy**: Ensure CCE policy is properly configured if using custom policies + +### Monitoring + +Monitor the deployment through: +- Azure Portal for resource status +- Container Instance logs for application health +- Azure Monitor for confidential computing metrics +- Attestation logs for security verification + +## Cost Considerations + +Confidential computing resources typically have: +- **Premium Pricing**: Higher cost than standard container instances +- **Specialized Hardware**: Limited availability may affect pricing +- **Per-second Billing**: Based on confidential computing resources +- **Regional Variations**: Pricing may vary by region + +## Clean Up + +To destroy all resources: +```bash +terraform destroy +``` + +**Note**: This will delete all confidential computing resources. + +## Security Best Practices + +### CCE Policy Management +- Use specific CCE policies for production workloads +- Regularly update and validate policies +- Implement proper key management for encrypted data + +### Container Security +- Use verified and signed container images +- Implement proper access controls +- Monitor for security events and attestation failures + +### Network Security +- Consider using Private IP addresses for production +- Implement proper network segmentation +- Use Azure Private Link where applicable + +## Migration Path + +When native Terraform support becomes available: +1. Replace ARM template deployments with native resources +2. Update variable references +3. Test the migration in a development environment +4. Validate confidential computing functionality + +## Performance Considerations + +### Confidential Computing Performance +- **Startup Time**: May be longer than standard containers +- **Memory Overhead**: Additional memory used for security features +- **CPU Performance**: Slight overhead for encryption/decryption +- **Network Latency**: Potential impact from security processing + +### Scaling Behavior +- **Conservative Scaling**: Start with lower desired counts +- **Monitoring**: Close monitoring of performance metrics +- **Resource Allocation**: Adequate CPU/memory for security overhead + +## Use Cases + +### Ideal Scenarios +- **Sensitive Data Processing**: Financial, healthcare, personal data +- **Regulatory Compliance**: GDPR, HIPAA, SOX requirements +- **Multi-party Computation**: Secure collaboration between organizations +- **Edge Computing**: Secure processing at edge locations + +### Example Applications +- **Data Analytics**: Secure analysis of sensitive datasets +- **AI/ML Workloads**: Confidential machine learning training +- **Database Processing**: Secure database operations +- **API Services**: Secure API endpoints for sensitive operations + +## Support + +This configuration is based on the ARM template `ACI-NGroups-Confidential.yaml` and provides confidential computing capabilities while maintaining Terraform workflow benefits. diff --git a/recipe-packs/terraform/aci-ngroups-confidential/main.tf b/recipe-packs/terraform/aci-ngroups-confidential/main.tf new file mode 100644 index 00000000..7e40dfe5 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-confidential/main.tf @@ -0,0 +1,198 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~> 3.0" + } + } +} + +provider "azurerm" { + features {} +} + +# Data source for current resource group +data "azurerm_resource_group" "main" { + name = var.resource_group_name +} + +# Data source for current client configuration +data "azurerm_client_config" "current" {} + +# Container Group Profile ARM Template Deployment +resource "azurerm_resource_group_template_deployment" "container_group_profile" { + name = "cgp-confidential-deployment" + resource_group_name = data.azurerm_resource_group.main.name + deployment_mode = "Incremental" + + template_content = jsonencode({ + "$schema" = "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" + contentVersion = "1.0.0.0" + parameters = { + cgProfileName = { + type = "string" + } + apiVersion = { + type = "string" + } + } + resources = [ + { + apiVersion = "[parameters('apiVersion')]" + type = "Microsoft.ContainerInstance/containerGroupProfiles" + name = "[parameters('cgProfileName')]" + location = "[resourceGroup().location]" + properties = { + sku = "Confidential" + confidentialComputeProperties = { + ccePolicy = "" + } + containers = [ + { + name = "aci-helloworld" + properties = { + image = var.container_image + ports = [ + { + protocol = "TCP" + port = 80 + } + ] + resources = { + requests = { + memoryInGB = 1.0 + cpu = 1.0 + } + } + } + } + ] + restartPolicy = "Always" + ipAddress = { + ports = [ + { + protocol = "TCP" + port = 80 + } + ] + type = "Public" + } + osType = "Linux" + } + } + ] + outputs = { + containerGroupProfileId = { + type = "string" + value = "[resourceId('Microsoft.ContainerInstance/containerGroupProfiles', parameters('cgProfileName'))]" + } + } + }) + + parameters_content = jsonencode({ + cgProfileName = { + value = var.container_group_profile_name + } + apiVersion = { + value = var.api_version + } + }) + + tags = var.tags +} + +# NGroups ARM Template Deployment +resource "azurerm_resource_group_template_deployment" "ngroups" { + name = "ngroups-confidential-deployment" + resource_group_name = data.azurerm_resource_group.main.name + deployment_mode = "Incremental" + + template_content = jsonencode({ + "$schema" = "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" + contentVersion = "1.0.0.0" + parameters = { + nGroupsName = { + type = "string" + } + cgProfileName = { + type = "string" + } + apiVersion = { + type = "string" + } + desiredCount = { + type = "int" + } + prefixCG = { + type = "string" + } + resourcePrefix = { + type = "string" + } + } + resources = [ + { + apiVersion = "[parameters('apiVersion')]" + type = "Microsoft.ContainerInstance/NGroups" + name = "[parameters('nGroupsName')]" + location = "[resourceGroup().location]" + dependsOn = [ + "[concat('Microsoft.ContainerInstance/containerGroupProfiles/', parameters('cgProfileName'))]" + ] + properties = { + elasticProfile = { + desiredCount = "[parameters('desiredCount')]" + containerGroupNamingPolicy = { + guidNamingPolicy = { + prefix = "[parameters('prefixCG')]" + } + } + } + containerGroupProfiles = [ + { + resource = { + id = "[concat(parameters('resourcePrefix'), 'Microsoft.ContainerInstance/containerGroupProfiles/', parameters('cgProfileName'))]" + } + } + ] + } + tags = { + "cirrusTestScenario" = "confidential-1.basic" + } + } + ] + outputs = { + nGroupId = { + type = "string" + value = "[resourceId('Microsoft.ContainerInstance/NGroups', parameters('nGroupsName'))]" + } + } + }) + + parameters_content = jsonencode({ + nGroupsName = { + value = var.ngroups_name + } + cgProfileName = { + value = var.container_group_profile_name + } + apiVersion = { + value = var.api_version + } + desiredCount = { + value = var.desired_count + } + prefixCG = { + value = var.prefix_cg + } + resourcePrefix = { + value = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${data.azurerm_resource_group.main.name}/providers/" + } + }) + + tags = var.tags + + depends_on = [ + azurerm_resource_group_template_deployment.container_group_profile + ] +} diff --git a/recipe-packs/terraform/aci-ngroups-confidential/outputs.tf b/recipe-packs/terraform/aci-ngroups-confidential/outputs.tf new file mode 100644 index 00000000..b3091b8b --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-confidential/outputs.tf @@ -0,0 +1,22 @@ +output "container_group_profile_id" { + description = "The ID of the Container Group Profile" + value = jsondecode(azurerm_resource_group_template_deployment.container_group_profile.output_content).containerGroupProfileId.value +} + +output "ngroups_id" { + description = "The ID of the NGroups resource" + value = jsondecode(azurerm_resource_group_template_deployment.ngroups.output_content).nGroupId.value +} + +output "resource_group_name" { + description = "The name of the resource group" + value = data.azurerm_resource_group.main.name +} + +output "deployment_names" { + description = "Names of the ARM template deployments" + value = { + container_group_profile = azurerm_resource_group_template_deployment.container_group_profile.name + ngroups = azurerm_resource_group_template_deployment.ngroups.name + } +} diff --git a/recipe-packs/terraform/aci-ngroups-confidential/terraform.tfvars.example b/recipe-packs/terraform/aci-ngroups-confidential/terraform.tfvars.example new file mode 100644 index 00000000..09b48366 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-confidential/terraform.tfvars.example @@ -0,0 +1,23 @@ +# Example terraform.tfvars file +# Copy this file to terraform.tfvars and update the values + +# Required: Resource group name where resources will be deployed +resource_group_name = "your-resource-group-name" + +# Optional: Override default values if needed +# api_version = "2024-09-01-preview" +# container_group_profile_name = "cgp_1" +# ngroups_name = "ngroup_confidential_basic" +# desired_count = 1 +# prefix_cg = "cg-confidential-" + +# Optional: Custom container image +# container_image = "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e" + +# Optional: Custom tags +# tags = { +# scenario = "confidential-computing" +# purpose = "demo" +# environment = "test" +# owner = "your-name" +# } diff --git a/recipe-packs/terraform/aci-ngroups-confidential/variables.tf b/recipe-packs/terraform/aci-ngroups-confidential/variables.tf new file mode 100644 index 00000000..a539a6c7 --- /dev/null +++ b/recipe-packs/terraform/aci-ngroups-confidential/variables.tf @@ -0,0 +1,49 @@ +variable "resource_group_name" { + description = "The name of the resource group where resources will be deployed" + type = string +} + +variable "api_version" { + description = "Container Instance API version" + type = string + default = "2024-09-01-preview" +} + +variable "container_group_profile_name" { + description = "Name of the Container Group Profile" + type = string + default = "cgp_1" +} + +variable "ngroups_name" { + description = "Name of the NGroups resource" + type = string + default = "ngroup_confidential_basic" +} + +variable "desired_count" { + description = "Desired number of container instances" + type = number + default = 1 +} + +variable "prefix_cg" { + description = "Prefix for container group naming" + type = string + default = "cg-confidential-" +} + +variable "container_image" { + description = "Container image to deploy" + type = string + default = "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e" +} + +variable "tags" { + description = "Tags to apply to resources" + type = map(string) + default = { + scenario = "confidential-computing" + purpose = "demo" + } +} From b020c2d98225f35be4440061a1b5f58bb493ecf0 Mon Sep 17 00:00:00 2001 From: Shivam Gupta Date: Mon, 18 Aug 2025 18:16:56 -0700 Subject: [PATCH 03/12] addressed comments --- recipe-packs/bicep/ACI-NGroups-Confidential.bicep | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/recipe-packs/bicep/ACI-NGroups-Confidential.bicep b/recipe-packs/bicep/ACI-NGroups-Confidential.bicep index 8e85c1b9..84b7a567 100644 --- a/recipe-packs/bicep/ACI-NGroups-Confidential.bicep +++ b/recipe-packs/bicep/ACI-NGroups-Confidential.bicep @@ -81,3 +81,12 @@ resource nGroups 'Microsoft.ContainerInstance/NGroups@2024-09-01-preview' = { // Outputs output containerGroupProfileId string = containerGroupProfile.id output nGroupId string = nGroups.id + +output result object = { + values: { + containerGroupProfileId: containerGroupProfile.id + nGroupsId: nGroups.id + location: resourceGroup().location + desiredCount: desiredCount + } +} From 57a973ae27377aac3f7ea49236e7c5f40695b60c Mon Sep 17 00:00:00 2001 From: Shivam Gupta Date: Mon, 18 Aug 2025 18:19:24 -0700 Subject: [PATCH 04/12] Removed ARM templates --- recipe-packs/ACI-NGroups-Confidential.json | 98 ---- recipe-packs/ACI-NGroups-Gateway.json | 535 --------------------- recipe-packs/ACI-NGroups-LoadBalancer.json | 450 ----------------- recipe-packs/ACI-Ngroups-basic.json | 85 ---- 4 files changed, 1168 deletions(-) delete mode 100644 recipe-packs/ACI-NGroups-Confidential.json delete mode 100644 recipe-packs/ACI-NGroups-Gateway.json delete mode 100644 recipe-packs/ACI-NGroups-LoadBalancer.json delete mode 100644 recipe-packs/ACI-Ngroups-basic.json diff --git a/recipe-packs/ACI-NGroups-Confidential.json b/recipe-packs/ACI-NGroups-Confidential.json deleted file mode 100644 index eace2f81..00000000 --- a/recipe-packs/ACI-NGroups-Confidential.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - }, - "variables": { - "cgProfileName": "cgp_1", - "nGroupsName": "ngroup_confidential_basic", - "apiVersion": "2024-09-01-preview", - "desiredCount": 1, - "prefixCG": "cg-confidential-", - "resourcePrefix": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/')]" - }, - "resources": [ - { - "apiVersion": "[variables('apiVersion')]", - "type": "Microsoft.ContainerInstance/containerGroupProfiles", - "name": "[variables('cgProfileName')]", - "location": "[resourceGroup().location]", - "properties": { - "sku": "Confidential", - "confidentialComputeProperties": { - "ccePolicy": "" - }, - "containers": [ - { - "name": "aci-helloworld", - "properties": { - "image": "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e", - "ports": [ - { - "protocol": "TCP", - "port": 80 - } - ], - "resources": { - "requests": { - "memoryInGB": 1.0, - "cpu": 1.0 - } - } - } - } - ], - "restartPolicy": "Always", - "ipAddress": { - "ports": [ - { - "protocol": "TCP", - "port": 80 - } - ], - "type": "Public" - }, - "osType": "Linux" - } - }, - { - "apiVersion": "[variables('apiVersion')]", - "type": "Microsoft.ContainerInstance/NGroups", - "name": "[variables('nGroupsName')]", - "location": "[resourceGroup().location]", - "dependsOn": [ - "[concat('Microsoft.ContainerInstance/containerGroupProfiles/', variables('cgProfileName'))]" - ], - "properties": { - "elasticProfile": { - "desiredCount": "[variables('desiredCount')]", - "containerGroupNamingPolicy": { - "guidNamingPolicy":{ - "prefix":"[variables('prefixCG')]" - } - } - }, - "containerGroupProfiles": [ - { - "resource": { - "id": "[concat(variables('resourcePrefix'), 'Microsoft.ContainerInstance/containerGroupProfiles/', variables('cgProfileName'))]" - } - } - ] - }, - "tags": { - "cirrusTestScenario": "confidential-1.basic" - } - } - ], - "outputs": { - "containerGroupProfileId": { - "type": "string", - "value": "[resourceId('Microsoft.ContainerInstance/containerGroupProfiles', variables('cgProfileName'))]" - }, - "nGroupId": { - "type": "string", - "value": "[resourceId('Microsoft.ContainerInstance/NGroups', variables('nGroupsName'))]" - } - } -} diff --git a/recipe-packs/ACI-NGroups-Gateway.json b/recipe-packs/ACI-NGroups-Gateway.json deleted file mode 100644 index 7269689b..00000000 --- a/recipe-packs/ACI-NGroups-Gateway.json +++ /dev/null @@ -1,535 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "apiVersion": { - "type": "string", - "defaultValue": "2024-09-01-preview", - "maxLength": 32 - }, - "nGroupsNameParam": { - "type": "string", - "defaultValue": "nGroups_lin100_regional_ag", - "maxLength": 64 - }, - "containerGroupProfileName": { - "type": "string", - "defaultValue": "cgp", - "maxLength": 64 - }, - "applicationGatewayName": { - "type": "string", - "defaultValue": "agw1", - "maxLength": 64 - }, - "publicIPName": { - "type": "string", - "defaultValue": "publicIP", - "maxLength": 64 - }, - "backendAddressPoolName": { - "type": "string", - "defaultValue": "bepool", - "maxLength": 64 - }, - "vnetName": { - "type": "string", - "defaultValue": "vnet1", - "maxLength": 64 - }, - "networkSecurityGroupName": { - "type": "string", - "defaultValue": "nsg1", - "maxLength": 64 - }, - "desiredCount": { - "type": "int", - "defaultValue": 100 - }, - "maintainDesiredCount": { - "type": "bool", - "defaultValue": true - }, - "zones": { - "type": "array", - "defaultValue": [] - }, - "vnetAddressPrefix": { - "type": "string", - "defaultValue": "172.16.0.0/23", - "maxLength": 64 - }, - "aciSubnetAddressPrefix": { - "type": "string", - "defaultValue": "172.16.0.0/25", - "maxLength": 64 - }, - "appGatewaySubnetAddressPrefix": { - "type": "string", - "defaultValue": "172.16.1.0/25", - "maxLength": 64 - }, - "aciSubnetName": { - "type": "string", - "defaultValue": "aciSubnet", - "maxLength": 64 - }, - "appGatewaySubnetName": { - "type": "string", - "defaultValue": "appgatewaySubnet", - "maxLength": 64 - }, - "ddosProtectionPlanName": { - "type": "string", - "defaultValue": "ddosProtectionPlan", - "maxLength": 64 - } - }, - "variables": { - "description": "This ARM template is an example template of using an App Gateway with a NGroup.", - "resourcePrefix": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/')]", - "applicationGatewayApiVersion": "2022-09-01", - "prefixCG": "cg-lin100-regional-ag-" - }, - "resources": [ - { - "apiVersion": "[parameters('apiVersion')]", - "type": "Microsoft.ContainerInstance/containerGroupProfiles", - "name": "[parameters('containerGroupProfileName')]", - "location": "[resourceGroup().location]", - "properties": { - "sku": "Standard", - "containers": [ - { - "name": "web", - "properties": { - "image": "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e", - "ports": [ - { - "protocol": "TCP", - "port": 80 - } - ], - "resources": { - "requests": { - "memoryInGB": 1.0, - "cpu": 1.0 - } - } - } - } - ], - "restartPolicy": "Always", - "ipAddress": { - "ports": [ - { - "protocol": "TCP", - "port": 80 - } - ], - "type": "Private" - }, - "osType": "Linux" - } - }, - { - "apiVersion": "[parameters('apiVersion')]", - "type": "Microsoft.ContainerInstance/NGroups", - "name": "[parameters('nGroupsNameParam)]", - "location": "[resourceGroup().location]", - "dependsOn": [ - "[concat('Microsoft.ContainerInstance/containerGroupProfiles/', parameters('containerGroupProfileName'))]", - "[resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName'))]", - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ], - "properties": { - "elasticProfile": { - "desiredCount": "[parameters('desiredCount')]", - "maintainDesiredCount": "[parameters('maintainDesiredCount')]", - "containerGroupNamingPolicy": { - "guidNamingPolicy":{ - "prefix":"[variables('prefixCG')]" - } - } - }, - "containerGroupProfiles": [ - { - "resource": { - "id": "[concat(variables('resourcePrefix'), 'Microsoft.ContainerInstance/containerGroupProfiles/', parameters('containerGroupProfileName'))]" - }, - "containerGroupProperties": { - "subnetIds": [ - { - "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('aciSubnetName'))]", - "name": "[parameters('aciSubnetName')]" - } - ] - }, - "networkProfile": { - "applicationGateway": { - "resource": { - "id": "[resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName'))]" - }, - "backendAddressPools": [ - { - "resource": { - "id": "[resourceId('Microsoft.Network/applicationGateways/backendAddressPools', parameters('applicationGatewayName'), parameters('backendAddressPoolName'))]" - } - } - ] - } - } - } - ] - }, - "zones": "[parameters('zones')]", - "tags": { - "cirrusTestScenario": "lin-100.regional.appgateway", - "reprovision.enabled": true - } - }, - { - "type": "Microsoft.Network/publicIPAddresses", - "apiVersion": "[variables('applicationGatewayApiVersion')]", - "name": "[parameters('publicIPName')]", - "location": "[resourceGroup().location]", - "sku": { - "name": "Standard", - "tier": "Regional" - }, - "properties": { - "publicIPAddressVersion": "IPv4", - "publicIPAllocationMethod": "Static", - "idleTimeoutInMinutes": 5, - "ipTags": [], - "ddosSettings": { - "protectionMode": "VirtualNetworkInherited" - } - } - }, - { - "type": "Microsoft.Network/applicationGateways", - "apiVersion": "[variables('applicationGatewayApiVersion')]", - "name": "[parameters('applicationGatewayName')]", - "location": "[resourceGroup().location]", - "dependsOn": [ - "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('appGatewaySubnetName'))]", - "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPName'))]" - ], - "properties": { - "sku": { - "name": "Standard_v2", - "tier": "Standard_v2" - }, - "gatewayIPConfigurations": [ - { - "name": "appGatewayIpConfig", - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/gatewayIPConfigurations/appGatewayIpConfig')]", - "properties": { - "subnet": { - "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('appGatewaySubnetName'))]" - } - } - } - ], - "sslCertificates": [], - "trustedRootCertificates": [], - "trustedClientCertificates": [], - "sslProfiles": [], - "frontendIPConfigurations": [ - { - "name": "appGwPublicFrontendIpIPv4", - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/frontendIPConfigurations/appGwPublicFrontendIpIPv4')]", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIPName'))]" - } - } - } - ], - "frontendPorts": [ - { - "name": "port_80", - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/frontendPorts/port_80')]", - "properties": { - "port": 80 - } - } - ], - "backendAddressPools": [ - { - "name": "[parameters('backendAddressPoolName')]", - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), concat('/backendAddressPools/', parameters('backendAddressPoolName')))]", - "properties": { - "backendAddresses": [] - } - } - ], - "loadDistributionPolicies": [], - "backendHttpSettingsCollection": [ - { - "name": "[concat(parameters('applicationGatewayName'), '-be-settings')]", - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), concat('/backendHttpSettingsCollection/', parameters('applicationGatewayName'), '-be-settings'))]", - "properties": { - "port": 80, - "protocol": "Http", - "cookieBasedAffinity": "Disabled", - "pickHostNameFromBackendAddress": false, - "requestTimeout": 60, - "probe": { - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/probes/healthprobe')]" - } - } - } - ], - "backendSettingsCollection": [], - "httpListeners": [ - { - "name": "[concat(parameters('applicationGatewayName'), '-listener')]", - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), concat('/httpListeners/', parameters('applicationGatewayName'), '-listener'))]", - "properties": { - "frontendIPConfiguration": { - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/frontendIPConfigurations/appGwPublicFrontendIpIPv4')]" - }, - "frontendPort": { - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/frontendPorts/port_80')]" - }, - "protocol": "Http", - "hostNames": [], - "requireServerNameIndication": false, - "customErrorConfigurations": [] - } - } - ], - "listeners": [], - "urlPathMaps": [], - "requestRoutingRules": [ - { - "name": "[concat(parameters('applicationGatewayName'), '-routerule')]", - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), concat('/requestRoutingRules/', parameters('applicationGatewayName'), '-routerule'))]", - "properties": { - "ruleType": "Basic", - "priority": 1, - "httpListener": { - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), concat('/httpListeners/', parameters('applicationGatewayName'), '-listener'))]" - }, - "backendAddressPool": { - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), concat('/backendAddressPools/', parameters('backendAddressPoolName')))]" - }, - "backendHttpSettings": { - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), concat('/backendHttpSettingsCollection/', parameters('applicationGatewayName'), '-be-settings'))]" - } - } - } - ], - "routingRules": [], - "probes": [ - { - "name": "healthprobe", - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/probes/healthprobe')]", - "properties": { - "protocol": "Http", - "host": "127.0.0.1", - "path": "/", - "interval": 3600, - "timeout": 3600, - "unhealthyThreshold": 3, - "pickHostNameFromBackendHttpSettings": false, - "minServers": 0, - "match": {} - } - } - ], - "rewriteRuleSets": [], - "redirectConfigurations": [], - "privateLinkConfigurations": [], - "enableHttp2": false, - "autoscaleConfiguration": { - "minCapacity": 0, - "maxCapacity": 3 - } - } - }, - { - "type": "Microsoft.Network/virtualNetworks/subnets", - "apiVersion": "[variables('applicationGatewayApiVersion')]", - "name": "[concat(parameters('vnetName'), '/', parameters('aciSubnetName'))]", - "dependsOn": [ - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ], - "properties": { - "addressPrefix": "[parameters('aciSubnetAddressPrefix')]", - "serviceEndpoints": [], - "delegations": [ - { - "name": "ACIDelegationService", - "id": "[concat(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('aciSubnetName')), '/delegations/ACIDelegationService')]", - "properties": { - "serviceName": "Microsoft.ContainerInstance/containerGroups" - }, - "type": "Microsoft.Network/virtualNetworks/subnets/delegations" - } - ], - "networkSecurityGroup": { - "id": "[resourceId('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]" - }, - "privateEndpointNetworkPolicies": "Disabled", - "privateLinkServiceNetworkPolicies": "Enabled" - } - }, - { - "type": "Microsoft.Network/virtualNetworks/subnets", - "apiVersion": "[variables('applicationGatewayApiVersion')]", - "name": "[concat(parameters('vnetName'), '/', parameters('appGatewaySubnetName'))]", - "dependsOn": [ - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ], - "properties": { - "addressPrefix":"[parameters('appGatewaySubnetAddressPrefix')]", - "applicationGatewayIPConfigurations": [ - { - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/gatewayIPConfigurations/appGatewayIpConfig')]" - } - ], - "serviceEndpoints": [], - "delegations": [], - "networkSecurityGroup": { - "id": "[resourceId('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]" - }, - "privateEndpointNetworkPolicies": "Disabled", - "privateLinkServiceNetworkPolicies": "Enabled" - } - }, - { - "type": "Microsoft.Network/virtualNetworks", - "apiVersion": "[variables('applicationGatewayApiVersion')]", - "name": "[parameters('vnetName')]", - "location": "[resourceGroup().location]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[parameters('vnetAddressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[parameters('appGatewaySubnetName')]", - "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('appGatewaySubnetName'))]", - "properties": { - "addressPrefix":"[parameters('appGatewaySubnetAddressPrefix')]", - "applicationGatewayIPConfigurations": [ - { - "id": "[concat(resourceId('Microsoft.Network/applicationGateways', parameters('applicationGatewayName')), '/gatewayIPConfigurations/appGatewayIpConfig')]" - } - ], - "serviceEndpoints": [], - "delegations": [], - "networkSecurityGroup": { - "id": "[resourceId('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]" - }, - "privateEndpointNetworkPolicies": "Disabled", - "privateLinkServiceNetworkPolicies": "Enabled" - }, - "type": "Microsoft.Network/virtualNetworks/subnets" - }, - { - "name": "[parameters('aciSubnetName')]", - "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('aciSubnetName'))]", - "properties": { - "addressPrefix": "[parameters('aciSubnetAddressPrefix')]", - "serviceEndpoints": [], - "delegations": [ - { - "name": "ACIDelegationService", - "id": "[concat(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('aciSubnetName')), '/delegations/ACIDelegationService')]", - "properties": { - "serviceName": "Microsoft.ContainerInstance/containerGroups" - }, - "type": "Microsoft.Network/virtualNetworks/subnets/delegations" - } - ], - "networkSecurityGroup": { - "id": "[resourceId('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]" - }, - "privateEndpointNetworkPolicies": "Disabled", - "privateLinkServiceNetworkPolicies": "Enabled" - }, - "type": "Microsoft.Network/virtualNetworks/subnets" - } - ], - "virtualNetworkPeerings": [], - "enableDdosProtection": false - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]" - ] - }, - { - "type": "Microsoft.Network/networkSecurityGroups", - "apiVersion": "[variables('applicationGatewayApiVersion')]", - "name": "[parameters('networkSecurityGroupName')]", - "location": "[resourceGroup().location]", - "properties": { - "securityRules": [ - { - "name": "AppGatewayV2ProbeInbound", - "properties": { - "access": "Allow", - "description": "Allow traffic from GatewayManager. This rule is needed for application gateway probes to work.", - "destinationAddressPrefix": "*", - "destinationPortRange": "65200-65535", - "direction": "Inbound", - "protocol": "Tcp", - "priority": 100, - "sourceAddressPrefix": "GatewayManager", - "sourcePortRange": "*" - } - }, - { - "name": "AllowHTTPInbound", - "properties": { - "access": "Allow", - "description": "Allow Internet traffic on port 80", - "destinationAddressPrefix": "*", - "destinationPortRange": "80", - "direction": "Inbound", - "protocol": "Tcp", - "priority": 110, - "sourceAddressPrefix": "Internet", - "sourcePortRange": "*" - } - }, - { - "name": "AllowPublicIPAddress", - "properties": { - "access": "Allow", - "description": "Allow traffic from public ip address", - "destinationAddressPrefix": "[reference(parameters('publicIPName')).IpAddress]", - "destinationPortRange": "80", - "direction": "Inbound", - "protocol": "Tcp", - "priority": 111, - "sourceAddressPrefix": "Internet", - "sourcePortRange": "*" - } - }, - { - "name": "AllowVirtualNetworkInbound", - "properties": { - "access": "Allow", - "description": "Allow Internet traffic to Virtual network", - "destinationAddressPrefix": "VirtualNetwork", - "destinationPortRange": "80", - "direction": "Inbound", - "protocol": "*", - "priority": 112, - "sourceAddressPrefix": "*", - "sourcePortRange": "*" - } - } - ] - } - } - ] -} \ No newline at end of file diff --git a/recipe-packs/ACI-NGroups-LoadBalancer.json b/recipe-packs/ACI-NGroups-LoadBalancer.json deleted file mode 100644 index dd5c70d9..00000000 --- a/recipe-packs/ACI-NGroups-LoadBalancer.json +++ /dev/null @@ -1,450 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "apiVersion": { - "type": "string", - "defaultValue": "2024-09-01-preview", - "maxLength": 32 - }, - "nGroupsParamName": { - "type": "string", - "defaultValue": "nGroups_lin100_reg_lb", - "maxLength": 64 - }, - "containerGroupProfileName": { - "type": "string", - "defaultValue": "cgp_1", - "maxLength": 64 - }, - "loadBalancerName": { - "type": "string", - "defaultValue": "slb_1", - "maxLength": 64 - }, - "backendAddressPoolName": { - "type": "string", - "defaultValue": "bepool_1", - "maxLength": 64 - }, - "vnetName": { - "type": "string", - "defaultValue": "vnet_1", - "maxLength": 64 - }, - "subnetName": { - "type": "string", - "defaultValue": "subnet_1", - "maxLength": 64 - }, - "networkSecurityGroupName": { - "type": "string", - "defaultValue": "nsg_1", - "maxLength": 64 - }, - "inboundPublicIPName": { - "type": "string", - "defaultValue": "inboundPublicIP", - "maxLength": 64 - }, - "outboundPublicIPName": { - "type": "string", - "defaultValue": "outboundPublicIP", - "maxLength": 64 - }, - "outboundPublicIPPrefixName": { - "type": "string", - "defaultValue": "outBoundPublicIPPrefix", - "metadata": { - "description": "Name of the NAT gateway public IP" - } - }, - "natGatewayName": { - "type": "string", - "defaultValue": "natGateway1" - }, - "frontendIPName": { - "type": "string", - "defaultValue": "loadBalancerFrontend", - "maxLength": 64 - }, - "httpRuleName": { - "type": "string", - "defaultValue": "httpRule", - "maxLength": 64 - }, - "healthProbeName": { - "type": "string", - "defaultValue": "healthProbe", - "maxLength": 64 - }, - "vnetAddressPrefix": { - "type": "string", - "defaultValue": "172.19.0.0/16", - "maxLength": 64 - }, - "subnetAddressPrefix": { - "type": "string", - "defaultValue": "172.19.1.0/24", - "maxLength": 64 - }, - "desiredCount": { - "type": "int", - "defaultValue": 100 - }, - "zones": { - "type": "array", - "defaultValue": [] - }, - "maintainDesiredCount": { - "type": "bool", - "defaultValue": true - }, - "domainNameLabel": { - "type": "string", - "defaultValue": "ngroupsdemo", - "maxLength": 64 - }, - "inboundNatRuleName": { - "type": "string", - "defaultValue": "inboundNatRule", - "maxLength": 64 - } - }, - "variables": { - "description": "This ARM template is an example template to test the load balancer integration with NGroups.", - "cgProfileName": "[parameters('containerGroupProfileName')]", - "prefixCG": "cg-lin100-regional-lb-", - "nGroupsName": "[parameters('nGroupsParamName')]", - "resourcePrefix": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/')]", - "loadBalancerApiVersion": "2022-07-01", - "vnetApiVersion": "2022-07-01", - "publicIPVersion": "2022-07-01", - "ddosProtectionPlanName": "ddosProtectionPlan" - }, - "resources": [ - { - "apiVersion": "[parameters('apiVersion')]", - "type": "Microsoft.ContainerInstance/containerGroupProfiles", - "name": "[variables('cgProfileName')]", - "location": "[resourceGroup().location]", - "properties": { - "sku": "Standard", - "containers": [ - { - "name": "web", - "properties": { - "image": "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e", - "ports": [ - { - "protocol": "TCP", - "port": 80 - } - ], - "resources": { - "requests": { - "memoryInGB": 1.0, - "cpu": 1.0 - } - } - } - } - ], - "restartPolicy": "Always", - "ipAddress": { - "ports": [ - { - "protocol": "TCP", - "port": 80 - } - ], - "type": "Private" - }, - "osType": "Linux" - } - }, - { - "apiVersion": "[parameters('apiVersion')]", - "type": "Microsoft.ContainerInstance/NGroups", - "name": "[variables('nGroupsName')]", - "location": "[resourceGroup().location]", - "dependsOn": [ - "[concat('Microsoft.ContainerInstance/containerGroupProfiles/', variables('cgProfileName'))]", - "[resourceId('Microsoft.Network/loadBalancers', parameters('loadBalancerName'))]", - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ], - "properties": { - "elasticProfile": { - "desiredCount": "[parameters('desiredCount')]", - "maintainDesiredCount": "[parameters('maintainDesiredCount')]", - "containerGroupNamingPolicy": { - "guidNamingPolicy":{ - "prefix":"[variables('prefixCG')]" - } - } - }, - "containerGroupProfiles": [ - { - "resource": { - "id": "[concat(variables('resourcePrefix'), 'Microsoft.ContainerInstance/containerGroupProfiles/', variables('cgProfileName'))]" - }, - "containerGroupProperties": { - "subnetIds": [ - { - "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('subnetName'))]", - "name": "[parameters('subnetName')]" - } - ] - }, - "networkProfile": { - "loadBalancer": { - "backendAddressPools": [ - { - "resource": { - "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('loadBalancerName'), parameters('backendAddressPoolName'))]" - } - } - ] - } - } - } - ] - }, - "zones": "[parameters('zones')]", - "tags": { - "cirrusTestScenario": "lin-100.regional.loadbalancer", - "reprovision.enabled": true - } - }, - { - "type": "Microsoft.Network/publicIPAddresses", - "apiVersion": "[variables('publicIPVersion')]", - "name": "[parameters('inboundPublicIPName')]", - "location": "[resourceGroup().location]", - "sku": { - "name": "Standard", - "tier": "Regional" - }, - "properties": { - "publicIPAddressVersion": "IPv4", - "publicIPAllocationMethod": "Static", - "idleTimeoutInMinutes": 4, - "ipTags": [], - "dnsSettings": { - "domainNameLabel": "[parameters('domainNameLabel')]" - } - } - }, - { - "type": "Microsoft.Network/publicIPAddresses", - "apiVersion": "[variables('publicIPVersion')]", - "name": "[parameters('outboundPublicIPName')]", - "location": "[resourceGroup().location]", - "sku": { - "name": "Standard", - "tier": "Regional" - }, - "properties": { - "publicIPAddressVersion": "IPv4", - "publicIPAllocationMethod": "Static", - "idleTimeoutInMinutes": 4, - "ipTags": [] - } - }, - { - "type": "Microsoft.Network/loadBalancers", - "apiVersion": "[variables('loadBalancerApiVersion')]", - "name": "[parameters('loadBalancerName')]", - "location": "[resourceGroup().location]", - "sku": { - "name": "Standard" - }, - "properties": { - "frontendIPConfigurations": [ - { - "properties": { - "publicIPAddress": { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('inboundPublicIPName'))]" - }, - "privateIPAllocationMethod": "Dynamic" - }, - "name": "[parameters('frontendIPName')]" - } - ], - "backendAddressPools": [ - { - "name": "[parameters('backendAddressPoolName')]", - "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('loadBalancerName'), parameters('backendAddressPoolName'))]", - "properties": { - "loadBalancerBackendAddresses": [] - } - } - ], - "probes": [ - { - "name": "[parameters('healthProbeName')]", - "properties": { - "protocol": "Tcp", - "port": 80, - "intervalInSeconds": 5, - "numberOfProbes": 2, - "probeThreshold": 1 - } - } - ], - "loadBalancingRules": [ - { - "name": "[parameters('httpRuleName')]", - "properties": { - "frontendIPConfiguration": { - "id": "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('loadBalancerName'), parameters('frontendIPName'))]" - }, - "frontendPort": 80, - "backendPort": 80, - "enableFloatingIP": false, - "idleTimeoutInMinutes": 15, - "protocol": "Tcp", - "enableTcpReset": true, - "loadDistribution": "Default", - "disableOutboundSnat": false, - "backendAddressPools": [ - { - "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('loadBalancerName'), parameters('backendAddressPoolName'))]" - } - ], - "probe": { - "id": "[resourceId('Microsoft.Network/loadBalancers/probes', parameters('loadBalancerName'), parameters('healthProbeName'))]" - } - } - } - ], - "inboundNatRules": [ - { - "name": "[parameters('inboundNatRuleName')]", - "properties": { - "backendAddressPool": { - "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('loadBalancerName'), parameters('backendAddressPoolName'))]" - }, - "backendPort": "80", - "enableFloatingIP": "false", - "enableTcpReset": "false", - "frontendIPConfiguration": { - "id": "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('loadBalancerName'), parameters('frontendIPName'))]" - }, - "frontendPortRangeEnd": "331", - "frontendPortRangeStart": "81", - "idleTimeoutInMinutes": "4", - "protocol": "Tcp" - } - } - ], - "outboundRules": [], - "inboundNatPools": [] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/publicIPAddresses', parameters('inboundPublicIPName'))]", - "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" - ] - }, - { - "type": "Microsoft.Network/virtualNetworks", - "apiVersion": "[variables('vnetApiVersion')]", - "name": "[parameters('vnetName')]", - "location": "[resourceGroup().location]", - "dependsOn": [ - "[concat('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]", - "[resourceId('Microsoft.Network/natGateways', parameters('natGatewayName'))]" - ], - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[parameters('vnetAddressPrefix')]" - ] - }, - "subnets": [ - { - "name": "[parameters('subnetName')]", - "properties": { - "addressPrefix": "[parameters('subnetAddressPrefix')]", - "serviceEndpoints": [], - "delegations": [ - { - "name": "Microsoft.ContainerInstance.containerGroups", - "id": "[concat(resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('subnetName')), '/delegations/Microsoft.ContainerInstance.containerGroups')]", - "properties": { - "serviceName": "Microsoft.ContainerInstance/containerGroups" - }, - "type": "Microsoft.Network/virtualNetworks/subnets/delegations" - } - ], - "privateEndpointNetworkPolicies": "Disabled", - "privateLinkServiceNetworkPolicies": "Enabled", - "networkSecurityGroup": { - "id": "[resourceId('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]" - }, - "natGateway": { - "id": "[resourceId('Microsoft.Network/natGateways', parameters('natGatewayName'))]" - } - }, - "type": "Microsoft.Network/virtualNetworks/subnets" - } - ], - "virtualNetworkPeerings": [], - "enableDdosProtection": true, - "ddosProtectionPlan": { - "id": "[resourceId('Microsoft.Network/ddosProtectionPlans', variables('ddosProtectionPlanName'))]" - } - } - }, - { - "type": "Microsoft.Network/ddosProtectionPlans", - "apiVersion": "[variables('loadBalancerApiVersion')]", - "name": "[variables('ddosProtectionPlanName')]", - "location": "[resourceGroup().location]" - }, - { - "type": "Microsoft.Network/natGateways", - "apiVersion": "[variables('loadBalancerApiVersion')]", - "name": "[parameters('natGatewayName')]", - "location": "[resourceGroup().location]", - "sku": { - "name": "Standard" - }, - "properties": { - "idleTimeoutInMinutes": 4, - "publicIpAddresses": [ - { - "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('outboundPublicIPName'))]" - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/publicIPAddresses', parameters('outboundPublicIPName'))]" - ] - }, - { - "type": "Microsoft.Network/networkSecurityGroups", - "apiVersion": "[variables('loadBalancerApiVersion')]", - "name": "[parameters('networkSecurityGroupName')]", - "location": "[resourceGroup().location]", - "properties": { - "securityRules": [ - { - "name": "AllowHTTPInbound", - "properties": { - "access": "Allow", - "description": "Allow Internet traffic on port range", - "destinationAddressPrefix": "*", - "destinationPortRanges": ["80-331"], - "direction": "Inbound", - "protocol": "*", - "priority": 100, - "sourceAddressPrefix": "Internet", - "sourcePortRange": "*" - } - } - ] - } - } - ] -} diff --git a/recipe-packs/ACI-Ngroups-basic.json b/recipe-packs/ACI-Ngroups-basic.json deleted file mode 100644 index 1e4a54c1..00000000 --- a/recipe-packs/ACI-Ngroups-basic.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "parameters": { - }, - "variables": { - "cgProfileName": "cgp_1", - "nGroupsName": "ngroup_lin1_basic", - "apiVersion": "2024-09-01-preview", - "desiredCount": 1, - "prefixCG": "cg-lin1-basic-", - "resourcePrefix": "[concat('/subscriptions/', subscription().subscriptionId, '/resourceGroups/', resourceGroup().name, '/providers/')]" - }, - "resources": [ - { - "apiVersion": "[variables('apiVersion')]", - "type": "Microsoft.ContainerInstance/containerGroupProfiles", - "name": "[variables('cgProfileName')]", - "location": "[resourceGroup().location]", - "properties": { - "sku": "Standard", - "containers": [ - { - "name": "aci-helloworld", - "properties": { - "image": "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e", - "ports": [ - { - "protocol": "TCP", - "port": 80 - } - ], - "resources": { - "requests": { - "memoryInGB": 1.0, - "cpu": 1.0 - } - } - } - } - ], - "restartPolicy": "Always", - "ipAddress": { - "ports": [ - { - "protocol": "TCP", - "port": 80 - } - ], - "type": "Public" - }, - "osType": "Linux" - } - }, - { - "apiVersion": "[variables('apiVersion')]", - "type": "Microsoft.ContainerInstance/NGroups", - "name": "[variables('nGroupsName')]", - "location": "[resourceGroup().location]", - "dependsOn": [ - "[concat('Microsoft.ContainerInstance/containerGroupProfiles/', variables('cgProfileName'))]" - ], - "properties": { - "elasticProfile": { - "desiredCount": "[variables('desiredCount')]", - "containerGroupNamingPolicy": { - "guidNamingPolicy":{ - "prefix":"[variables('prefixCG')]" - } - } - }, - "containerGroupProfiles": [ - { - "resource": { - "id": "[concat(variables('resourcePrefix'), 'Microsoft.ContainerInstance/containerGroupProfiles/', variables('cgProfileName'))]" - } - } - ] - }, - "tags": { - "cirrusTestScenario": "lin-1.basic" - } - } - ] -} From f24d77a0080bb4e917420ea475187322029f79da Mon Sep 17 00:00:00 2001 From: Shivam Gupta Date: Mon, 18 Aug 2025 18:31:51 -0700 Subject: [PATCH 05/12] removed unused properties from GW --- recipe-packs/bicep/ACI-NGroups-Gateway.bicep | 2 -- 1 file changed, 2 deletions(-) diff --git a/recipe-packs/bicep/ACI-NGroups-Gateway.bicep b/recipe-packs/bicep/ACI-NGroups-Gateway.bicep index 222d52d7..b8fb570c 100644 --- a/recipe-packs/bicep/ACI-NGroups-Gateway.bicep +++ b/recipe-packs/bicep/ACI-NGroups-Gateway.bicep @@ -253,8 +253,6 @@ resource appGatewaySubnet 'Microsoft.Network/virtualNetworks/subnets@2022-09-01' id: '${applicationGateway.id}/gatewayIPConfigurations/appGatewayIpConfig' } ] - serviceEndpoints: [] - delegations: [] networkSecurityGroup: { id: networkSecurityGroup.id } From b88ca36b136d3c80f2441fb952647660b476a192 Mon Sep 17 00:00:00 2001 From: Shivam Gupta Date: Mon, 15 Sep 2025 17:26:24 -0700 Subject: [PATCH 06/12] updated load balancer bicep --- .../bicep/ACI-NGroups-LoadBalancer.bicep | 111 ++++++++++-------- 1 file changed, 64 insertions(+), 47 deletions(-) diff --git a/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep index 5fe1b9dc..2bd5f2db 100644 --- a/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep +++ b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep @@ -4,7 +4,7 @@ param apiVersion string = '2024-09-01-preview' @description('NGroups parameter name') @maxLength(64) -param nGroupsParamName string = 'nGroups_lin100_reg_lb' +param nGroupsParamName string = 'nGroups_resource_1' @description('Container Group Profile name') @maxLength(64) @@ -38,9 +38,6 @@ param inboundPublicIPName string = 'inboundPublicIP' @maxLength(64) param outboundPublicIPName string = 'outboundPublicIP' -@description('Name of the NAT gateway public IP') -param outboundPublicIPPrefixName string = 'outBoundPublicIPPrefix' - @description('NAT Gateway name') param natGatewayName string = 'natGateway1' @@ -52,20 +49,16 @@ param frontendIPName string = 'loadBalancerFrontend' @maxLength(64) param httpRuleName string = 'httpRule' -@description('Health Probe name') -@maxLength(64) -param healthProbeName string = 'healthProbe' - @description('Virtual Network address prefix') @maxLength(64) -param vnetAddressPrefix string = '172.19.0.0/16' +param vnetAddressPrefix string @description('Subnet address prefix') @maxLength(64) -param subnetAddressPrefix string = '172.19.1.0/24' +param subnetAddressPrefix string @description('Desired container count') -param desiredCount int = 100 +param desiredCount int = 3 @description('Availability zones') param zones array = [] @@ -73,17 +66,15 @@ param zones array = [] @description('Maintain desired count') param maintainDesiredCount bool = true -@description('Domain name label for public IP') -@maxLength(64) -param domainNameLabel string = 'ngroupsdemo' - @description('Inbound NAT Rule name') @maxLength(64) param inboundNatRuleName string = 'inboundNatRule' +@description('Radius ACI Container Context') +param context object + // Variables var cgProfileName = containerGroupProfileName -var prefixCG = 'cg-lin100-regional-lb-' var nGroupsName = nGroupsParamName var resourcePrefix = '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/' var loadBalancerApiVersion = '2022-07-01' @@ -136,9 +127,6 @@ resource inboundPublicIP 'Microsoft.Network/publicIPAddresses@2022-07-01' = { publicIPAllocationMethod: 'Static' idleTimeoutInMinutes: 4 ipTags: [] - dnsSettings: { - domainNameLabel: domainNameLabel - } } } @@ -256,18 +244,32 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' = { } } ] - probes: [ - { - name: healthProbeName - properties: { - protocol: 'Tcp' - port: 80 - intervalInSeconds: 5 - numberOfProbes: 2 - probeThreshold: 1 + probes: union( + context.properties.containers.readinessProbe != null ? [ + { + name: 'readinessProbe' + properties: { + protocol: 'Tcp' + port: context.properties.containers.readinessProbe.tcpSocket.properties.port ?? 80 + intervalInSeconds: context.properties.containers.readinessProbe.periodSeconds ?? 5 + numberOfProbes: context.properties.containers.readinessProbe.failureThreshold ?? 3 + probeThreshold: context.properties.containers.readinessProbe.successThreshold ?? 1 + } } - } - ] + ] : [], + context.properties.containers.livenessProbe != null ? [ + { + name: 'livenessProbe' + properties: { + protocol: 'Tcp' + port: context.properties.containers.livenessProbe.tcpSocket.properties.port ?? 80 + intervalInSeconds: context.properties.containers.livenessProbe.periodSeconds ?? 10 + numberOfProbes: context.properties.containers.livenessProbe.failureThreshold ?? 3 + probeThreshold: context.properties.containers.livenessProbe.successThreshold ?? 1 + } + } + ] : [] + ) loadBalancingRules: [ { name: httpRuleName @@ -288,9 +290,9 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' = { id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', loadBalancerName, backendAddressPoolName) } ] - probe: { - id: resourceId('Microsoft.Network/loadBalancers/probes', loadBalancerName, healthProbeName) - } + probe: context.properties.containers.readinessProbe != null ? { + id: resourceId('Microsoft.Network/loadBalancers/probes', loadBalancerName, 'readinessProbe') + } : null } } ] @@ -323,8 +325,8 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' = { ] } -// Container Group Profile -resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfiles@2024-09-01-preview' = { +// ContainerGroupProfile resource - Create default CGProfile when platformOptions is not provided else use the CGProfile resource provided by the customer. +resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfiles@2024-09-01-preview' = if (context.properties.platformOptions == null) { name: cgProfileName location: resourceGroup().location properties: { @@ -333,22 +335,34 @@ resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfil { name: 'web' properties: { - image: 'mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e' + image: context.properties.containers.image ports: [ { - protocol: 'TCP' - port: 80 + protocol: context.properties.containers.ports != null ? context.properties.containers.ports.additionalProperties.properties.protocol ?? 'TCP' : 'TCP' + port: context.properties.containers.ports != null ? context.properties.containers.ports.additionalProperties.properties.containerPort : 80 } ] resources: { requests: { - memoryInGB: json('1.0') - cpu: json('1.0') + memoryInGB: context.properties.containers.resources.?requests.?memoryInMib/1024 ?? json('1.0') + cpu: context.properties.containers.resources.?requests.?cpu ?? json('1.0') } } + volumeMounts: [ + { + name: 'cacheVolume' + mountPath: '/mnt/cache' // ephemeral volume path in container filesystem + } + ] } } ] + volumes: [ + { + name: 'cacheVolume' + emptyDir: {} // ephemeral volume + } + ] restartPolicy: 'Always' ipAddress: { ports: [ @@ -368,15 +382,16 @@ resource nGroups 'Microsoft.ContainerInstance/NGroups@2024-09-01-preview' = { name: nGroupsName location: resourceGroup().location zones: zones + identity: { + type: 'SystemAssigned' + } properties: { elasticProfile: { desiredCount: desiredCount maintainDesiredCount: maintainDesiredCount - containerGroupNamingPolicy: { - guidNamingPolicy: { - prefix: prefixCG - } - } + } + updateProfile: { + updateMode: 'Rolling' } containerGroupProfiles: [ { @@ -406,8 +421,9 @@ resource nGroups 'Microsoft.ContainerInstance/NGroups@2024-09-01-preview' = { ] } tags: { - cirrusTestScenario: 'lin-100.regional.loadbalancer' 'reprovision.enabled': true + 'metadata.container.environmentVariable.orchestratorId': true + 'rollingupdate.replace.enabled': true } dependsOn: [ containerGroupProfile @@ -422,7 +438,6 @@ output subnetId string = virtualNetwork.properties.subnets[0].id output loadBalancerId string = loadBalancer.id output frontendIPConfigurationId string = loadBalancer.properties.frontendIPConfigurations[0].id output backendAddressPoolId string = loadBalancer.properties.backendAddressPools[0].id -output healthProbeId string = loadBalancer.properties.probes[0].id output inboundPublicIPId string = inboundPublicIP.id output outboundPublicIPId string = outboundPublicIP.id output inboundPublicIPFQDN string = inboundPublicIP.properties.dnsSettings.fqdn @@ -431,3 +446,5 @@ output networkSecurityGroupId string = networkSecurityGroup.id output ddosProtectionPlanId string = ddosProtectionPlan.id output containerGroupProfileId string = containerGroupProfile.id output nGroupsId string = nGroups.id +output readinessProbeId string = context.properties.containers.readinessProbe != null ? resourceId('Microsoft.Network/loadBalancers/probes', loadBalancerName, 'readinessProbe') : '' +output livenessProbeId string = context.properties.containers.livenessProbe != null ? resourceId('Microsoft.Network/loadBalancers/probes', loadBalancerName, 'livenessProbe') : '' From 4cd37f53dc5f8f50e64baaacae7a389f92e5b3fb Mon Sep 17 00:00:00 2001 From: Shivam Gupta Date: Mon, 15 Sep 2025 17:27:42 -0700 Subject: [PATCH 07/12] deleted gateway and basic ngroups template --- recipe-packs/bicep/ACI-NGroups-Gateway.bicep | 517 ------------------ recipe-packs/bicep/Ngroups-basic.bicep | 76 --- .../terraform/aci-ngroups-basic/README.md | 79 --- .../terraform/aci-ngroups-basic/main.tf | 147 ----- .../terraform/aci-ngroups-basic/outputs.tf | 36 -- .../terraform.tfvars.example | 21 - .../terraform/aci-ngroups-basic/variables.tf | 69 --- .../terraform/aci-ngroups-gateway/README.md | 183 ------- .../terraform/aci-ngroups-gateway/main.tf | 400 -------------- .../terraform/aci-ngroups-gateway/outputs.tf | 96 ---- .../terraform.tfvars.example | 39 -- .../aci-ngroups-gateway/variables.tf | 142 ----- 12 files changed, 1805 deletions(-) delete mode 100644 recipe-packs/bicep/ACI-NGroups-Gateway.bicep delete mode 100644 recipe-packs/bicep/Ngroups-basic.bicep delete mode 100644 recipe-packs/terraform/aci-ngroups-basic/README.md delete mode 100644 recipe-packs/terraform/aci-ngroups-basic/main.tf delete mode 100644 recipe-packs/terraform/aci-ngroups-basic/outputs.tf delete mode 100644 recipe-packs/terraform/aci-ngroups-basic/terraform.tfvars.example delete mode 100644 recipe-packs/terraform/aci-ngroups-basic/variables.tf delete mode 100644 recipe-packs/terraform/aci-ngroups-gateway/README.md delete mode 100644 recipe-packs/terraform/aci-ngroups-gateway/main.tf delete mode 100644 recipe-packs/terraform/aci-ngroups-gateway/outputs.tf delete mode 100644 recipe-packs/terraform/aci-ngroups-gateway/terraform.tfvars.example delete mode 100644 recipe-packs/terraform/aci-ngroups-gateway/variables.tf diff --git a/recipe-packs/bicep/ACI-NGroups-Gateway.bicep b/recipe-packs/bicep/ACI-NGroups-Gateway.bicep deleted file mode 100644 index b8fb570c..00000000 --- a/recipe-packs/bicep/ACI-NGroups-Gateway.bicep +++ /dev/null @@ -1,517 +0,0 @@ -@description('Container Instance API version') -@maxLength(32) -param apiVersion string = '2024-09-01-preview' - -@description('NGroups parameter name') -@maxLength(64) -param nGroupsNameParam string = 'nGroups_lin100_regional_ag' - -@description('Container Group Profile name') -@maxLength(64) -param containerGroupProfileName string = 'cgp' - -@description('Application Gateway name') -@maxLength(64) -param applicationGatewayName string = 'agw1' - -@description('Public IP name') -@maxLength(64) -param publicIPName string = 'publicIP' - -@description('Backend Address Pool name') -@maxLength(64) -param backendAddressPoolName string = 'bepool' - -@description('Virtual Network name') -@maxLength(64) -param vnetName string = 'vnet1' - -@description('Network Security Group name') -@maxLength(64) -param networkSecurityGroupName string = 'nsg1' - -@description('Desired container count') -param desiredCount int = 100 - -@description('Maintain desired count') -param maintainDesiredCount bool = true - -@description('Availability zones') -param zones array = [] - -@description('Virtual Network address prefix') -@maxLength(64) -param vnetAddressPrefix string = '172.16.0.0/23' - -@description('ACI Subnet address prefix') -@maxLength(64) -param aciSubnetAddressPrefix string = '172.16.0.0/25' - -@description('Application Gateway Subnet address prefix') -@maxLength(64) -param appGatewaySubnetAddressPrefix string = '172.16.1.0/25' - -@description('ACI Subnet name') -@maxLength(64) -param aciSubnetName string = 'aciSubnet' - -@description('Application Gateway Subnet name') -@maxLength(64) -param appGatewaySubnetName string = 'appgatewaySubnet' - -@description('DDoS Protection Plan name') -@maxLength(64) -param ddosProtectionPlanName string = 'ddosProtectionPlan' - -// Variables -var resourcePrefix = '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/' -var applicationGatewayApiVersion = '2022-09-01' -var prefixCG = 'cg-lin100-regional-ag-' - -// Network Security Group -resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2022-09-01' = { - name: networkSecurityGroupName - location: resourceGroup().location - properties: { - securityRules: [ - { - name: 'AppGatewayV2ProbeInbound' - properties: { - access: 'Allow' - description: 'Allow traffic from GatewayManager. This rule is needed for application gateway probes to work.' - destinationAddressPrefix: '*' - destinationPortRange: '65200-65535' - direction: 'Inbound' - protocol: 'Tcp' - priority: 100 - sourceAddressPrefix: 'GatewayManager' - sourcePortRange: '*' - } - } - { - name: 'AllowHTTPInbound' - properties: { - access: 'Allow' - description: 'Allow Internet traffic on port 80' - destinationAddressPrefix: '*' - destinationPortRange: '80' - direction: 'Inbound' - protocol: 'Tcp' - priority: 110 - sourceAddressPrefix: 'Internet' - sourcePortRange: '*' - } - } - { - name: 'AllowPublicIPAddress' - properties: { - access: 'Allow' - description: 'Allow traffic from public ip address' - destinationAddressPrefix: publicIPAddress.properties.ipAddress - destinationPortRange: '80' - direction: 'Inbound' - protocol: 'Tcp' - priority: 111 - sourceAddressPrefix: 'Internet' - sourcePortRange: '*' - } - } - { - name: 'AllowVirtualNetworkInbound' - properties: { - access: 'Allow' - description: 'Allow Internet traffic to Virtual network' - destinationAddressPrefix: 'VirtualNetwork' - destinationPortRange: '80' - direction: 'Inbound' - protocol: '*' - priority: 112 - sourceAddressPrefix: '*' - sourcePortRange: '*' - } - } - ] - } -} - -// Public IP Address -resource publicIPAddress 'Microsoft.Network/publicIPAddresses@2022-09-01' = { - name: publicIPName - location: resourceGroup().location - sku: { - name: 'Standard' - tier: 'Regional' - } - properties: { - publicIPAddressVersion: 'IPv4' - publicIPAllocationMethod: 'Static' - idleTimeoutInMinutes: 5 - ipTags: [] - ddosSettings: { - protectionMode: 'VirtualNetworkInherited' - } - } -} - -// Virtual Network -resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-09-01' = { - name: vnetName - location: resourceGroup().location - properties: { - addressSpace: { - addressPrefixes: [ - vnetAddressPrefix - ] - } - subnets: [ - { - name: appGatewaySubnetName - id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, appGatewaySubnetName) - properties: { - addressPrefix: appGatewaySubnetAddressPrefix - applicationGatewayIPConfigurations: [ - { - id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/gatewayIPConfigurations/appGatewayIpConfig' - } - ] - serviceEndpoints: [] - delegations: [] - networkSecurityGroup: { - id: networkSecurityGroup.id - } - privateEndpointNetworkPolicies: 'Disabled' - privateLinkServiceNetworkPolicies: 'Enabled' - } - type: 'Microsoft.Network/virtualNetworks/subnets' - } - { - name: aciSubnetName - id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, aciSubnetName) - properties: { - addressPrefix: aciSubnetAddressPrefix - serviceEndpoints: [] - delegations: [ - { - name: 'ACIDelegationService' - id: '${resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, aciSubnetName)}/delegations/ACIDelegationService' - properties: { - serviceName: 'Microsoft.ContainerInstance/containerGroups' - } - type: 'Microsoft.Network/virtualNetworks/subnets/delegations' - } - ] - networkSecurityGroup: { - id: networkSecurityGroup.id - } - privateEndpointNetworkPolicies: 'Disabled' - privateLinkServiceNetworkPolicies: 'Enabled' - } - type: 'Microsoft.Network/virtualNetworks/subnets' - } - ] - virtualNetworkPeerings: [] - enableDdosProtection: false - } - dependsOn: [ - networkSecurityGroup - ] -} - -// ACI Subnet (separate resource for proper dependency management) -resource aciSubnet 'Microsoft.Network/virtualNetworks/subnets@2022-09-01' = { - parent: virtualNetwork - name: aciSubnetName - properties: { - addressPrefix: aciSubnetAddressPrefix - serviceEndpoints: [] - delegations: [ - { - name: 'ACIDelegationService' - id: '${resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, aciSubnetName)}/delegations/ACIDelegationService' - properties: { - serviceName: 'Microsoft.ContainerInstance/containerGroups' - } - type: 'Microsoft.Network/virtualNetworks/subnets/delegations' - } - ] - networkSecurityGroup: { - id: networkSecurityGroup.id - } - privateEndpointNetworkPolicies: 'Disabled' - privateLinkServiceNetworkPolicies: 'Enabled' - } -} - -// Application Gateway Subnet (separate resource for proper dependency management) -resource appGatewaySubnet 'Microsoft.Network/virtualNetworks/subnets@2022-09-01' = { - parent: virtualNetwork - name: appGatewaySubnetName - properties: { - addressPrefix: appGatewaySubnetAddressPrefix - applicationGatewayIPConfigurations: [ - { - id: '${applicationGateway.id}/gatewayIPConfigurations/appGatewayIpConfig' - } - ] - networkSecurityGroup: { - id: networkSecurityGroup.id - } - privateEndpointNetworkPolicies: 'Disabled' - privateLinkServiceNetworkPolicies: 'Enabled' - } -} - -// Application Gateway -resource applicationGateway 'Microsoft.Network/applicationGateways@2022-09-01' = { - name: applicationGatewayName - location: resourceGroup().location - properties: { - sku: { - name: 'Standard_v2' - tier: 'Standard_v2' - } - gatewayIPConfigurations: [ - { - name: 'appGatewayIpConfig' - id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/gatewayIPConfigurations/appGatewayIpConfig' - properties: { - subnet: { - id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, appGatewaySubnetName) - } - } - } - ] - sslCertificates: [] - trustedRootCertificates: [] - trustedClientCertificates: [] - sslProfiles: [] - frontendIPConfigurations: [ - { - name: 'appGwPublicFrontendIpIPv4' - id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/frontendIPConfigurations/appGwPublicFrontendIpIPv4' - properties: { - privateIPAllocationMethod: 'Dynamic' - publicIPAddress: { - id: publicIPAddress.id - } - } - } - ] - frontendPorts: [ - { - name: 'port_80' - id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/frontendPorts/port_80' - properties: { - port: 80 - } - } - ] - backendAddressPools: [ - { - name: backendAddressPoolName - id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/backendAddressPools/${backendAddressPoolName}' - properties: { - backendAddresses: [] - } - } - ] - loadDistributionPolicies: [] - backendHttpSettingsCollection: [ - { - name: '${applicationGatewayName}-be-settings' - id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/backendHttpSettingsCollection/${applicationGatewayName}-be-settings' - properties: { - port: 80 - protocol: 'Http' - cookieBasedAffinity: 'Disabled' - pickHostNameFromBackendAddress: false - requestTimeout: 60 - probe: { - id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/probes/healthprobe' - } - } - } - ] - backendSettingsCollection: [] - httpListeners: [ - { - name: '${applicationGatewayName}-listener' - id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/httpListeners/${applicationGatewayName}-listener' - properties: { - frontendIPConfiguration: { - id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/frontendIPConfigurations/appGwPublicFrontendIpIPv4' - } - frontendPort: { - id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/frontendPorts/port_80' - } - protocol: 'Http' - hostNames: [] - requireServerNameIndication: false - customErrorConfigurations: [] - } - } - ] - listeners: [] - urlPathMaps: [] - requestRoutingRules: [ - { - name: '${applicationGatewayName}-routerule' - id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/requestRoutingRules/${applicationGatewayName}-routerule' - properties: { - ruleType: 'Basic' - priority: 1 - httpListener: { - id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/httpListeners/${applicationGatewayName}-listener' - } - backendAddressPool: { - id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/backendAddressPools/${backendAddressPoolName}' - } - backendHttpSettings: { - id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/backendHttpSettingsCollection/${applicationGatewayName}-be-settings' - } - } - } - ] - routingRules: [] - probes: [ - { - name: 'healthprobe' - id: '${resourceId('Microsoft.Network/applicationGateways', applicationGatewayName)}/probes/healthprobe' - properties: { - protocol: 'Http' - host: '127.0.0.1' - path: '/' - interval: 3600 - timeout: 3600 - unhealthyThreshold: 3 - pickHostNameFromBackendHttpSettings: false - minServers: 0 - match: {} - } - } - ] - rewriteRuleSets: [] - redirectConfigurations: [] - privateLinkConfigurations: [] - enableHttp2: false - autoscaleConfiguration: { - minCapacity: 0 - maxCapacity: 3 - } - } - dependsOn: [ - appGatewaySubnet - publicIPAddress - ] -} - -// Container Group Profile -resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfiles@2024-09-01-preview' = { - name: containerGroupProfileName - location: resourceGroup().location - properties: { - sku: 'Standard' - containers: [ - { - name: 'web' - properties: { - image: 'mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e' - ports: [ - { - protocol: 'TCP' - port: 80 - } - ] - resources: { - requests: { - memoryInGB: json('1.0') - cpu: json('1.0') - } - } - } - } - ] - restartPolicy: 'Always' - ipAddress: { - ports: [ - { - protocol: 'TCP' - port: 80 - } - ] - type: 'Private' - } - osType: 'Linux' - } -} - -// NGroups -resource nGroups 'Microsoft.ContainerInstance/NGroups@2024-09-01-preview' = { - name: nGroupsNameParam - location: resourceGroup().location - zones: zones - properties: { - elasticProfile: { - desiredCount: desiredCount - maintainDesiredCount: maintainDesiredCount - containerGroupNamingPolicy: { - guidNamingPolicy: { - prefix: prefixCG - } - } - } - containerGroupProfiles: [ - { - resource: { - id: '${resourcePrefix}Microsoft.ContainerInstance/containerGroupProfiles/${containerGroupProfileName}' - } - containerGroupProperties: { - subnetIds: [ - { - id: resourceId('Microsoft.Network/virtualNetworks/subnets', vnetName, aciSubnetName) - name: aciSubnetName - } - ] - } - networkProfile: { - applicationGateway: { - resource: { - id: applicationGateway.id - } - backendAddressPools: [ - { - resource: { - id: resourceId('Microsoft.Network/applicationGateways/backendAddressPools', applicationGatewayName, backendAddressPoolName) - } - } - ] - } - } - } - ] - } - tags: { - cirrusTestScenario: 'lin-100.regional.appgateway' - 'reprovision.enabled': true - } - dependsOn: [ - containerGroupProfile - applicationGateway - virtualNetwork - ] -} - -// Outputs -output virtualNetworkId string = virtualNetwork.id -output aciSubnetId string = aciSubnet.id -output appGatewaySubnetId string = appGatewaySubnet.id -output applicationGatewayId string = applicationGateway.id -output backendAddressPoolId string = applicationGateway.properties.backendAddressPools[0].id -output publicIPId string = publicIPAddress.id -output publicIPAddress string = publicIPAddress.properties.ipAddress -output networkSecurityGroupId string = networkSecurityGroup.id -output containerGroupProfileId string = containerGroupProfile.id -output nGroupsId string = nGroups.id -output frontendIPConfigurationId string = applicationGateway.properties.frontendIPConfigurations[0].id -output httpListenerId string = applicationGateway.properties.httpListeners[0].id -output healthProbeId string = applicationGateway.properties.probes[0].id diff --git a/recipe-packs/bicep/Ngroups-basic.bicep b/recipe-packs/bicep/Ngroups-basic.bicep deleted file mode 100644 index 84511864..00000000 --- a/recipe-packs/bicep/Ngroups-basic.bicep +++ /dev/null @@ -1,76 +0,0 @@ -// Variables -var cgProfileName = 'cgp_1' -var nGroupsName = 'ngroup_lin1_basic' -var apiVersion = '2024-09-01-preview' -var desiredCount = 1 -var prefixCG = 'cg-lin1-basic-' -var resourcePrefix = '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/' - -// Container Group Profile -resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfiles@2024-09-01-preview' = { - name: cgProfileName - location: resourceGroup().location - properties: { - sku: 'Standard' - containers: [ - { - name: 'aci-helloworld' - properties: { - image: 'mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e' - ports: [ - { - protocol: 'TCP' - port: 80 - } - ] - resources: { - requests: { - memoryInGB: 1.0 - cpu: 1.0 - } - } - } - } - ] - restartPolicy: 'Always' - ipAddress: { - ports: [ - { - protocol: 'TCP' - port: 80 - } - ] - type: 'Public' - } - osType: 'Linux' - } -} - -// NGroups resource -resource nGroups 'Microsoft.ContainerInstance/NGroups@2024-09-01-preview' = { - name: nGroupsName - location: resourceGroup().location - dependsOn: [ - containerGroupProfile - ] - properties: { - elasticProfile: { - desiredCount: desiredCount - containerGroupNamingPolicy: { - guidNamingPolicy: { - prefix: prefixCG - } - } - } - containerGroupProfiles: [ - { - resource: { - id: '${resourcePrefix}Microsoft.ContainerInstance/containerGroupProfiles/${cgProfileName}' - } - } - ] - } - tags: { - cirrusTestScenario: 'lin-1.basic' - } -} diff --git a/recipe-packs/terraform/aci-ngroups-basic/README.md b/recipe-packs/terraform/aci-ngroups-basic/README.md deleted file mode 100644 index 89086175..00000000 --- a/recipe-packs/terraform/aci-ngroups-basic/README.md +++ /dev/null @@ -1,79 +0,0 @@ -# Azure Container Instance NGroups Terraform Configuration - -This Terraform configuration converts the ARM template for Azure Container Instance NGroups to Terraform format. - -## Prerequisites - -- Terraform >= 0.14 -- Azure CLI installed and authenticated -- An existing Azure Resource Group - -## Resources Created - -1. **Container Group Profile** - Defines the template for container groups -2. **NGroups** - Manages elastic scaling of container groups - -## Usage - -1. Copy the example variables file: - ```bash - cp terraform.tfvars.example terraform.tfvars - ``` - -2. Edit `terraform.tfvars` with your specific values: - ```hcl - resource_group_name = "your-resource-group-name" - ``` - -3. Initialize Terraform: - ```bash - terraform init - ``` - -4. Plan the deployment: - ```bash - terraform plan - ``` - -5. Apply the configuration: - ```bash - terraform apply - ``` - -## Important Notes - -- **Preview Features**: Both Container Group Profiles and NGroups are preview features in Azure -- **ARM Template Deployment**: Since these resources are not yet fully supported in the AzureRM provider, this configuration uses `azurerm_resource_group_template_deployment` to deploy ARM templates within Terraform -- **Dependencies**: The NGroups resource depends on the Container Group Profile being created first - -## Variables - -| Variable | Description | Default | -|----------|-------------|---------| -| `resource_group_name` | Name of the Azure Resource Group | (required) | -| `location` | Azure region | Resource group location | -| `cg_profile_name` | Container Group Profile name | `cgp_1` | -| `ngroups_name` | NGroups resource name | `ngroup_lin1_basic` | -| `desired_count` | Desired container count | `1` | -| `prefix_cg` | Container group prefix | `cg-lin1-basic-` | -| `container_image` | Container image to deploy | ACI Hello World | -| `container_port` | Container port | `80` | -| `memory_gb` | Memory allocation in GB | `1.0` | -| `cpu_cores` | CPU allocation | `1.0` | -| `tags` | Resource tags | Default tags | - -## Outputs - -- `resource_group_name` - Name of the resource group -- `resource_group_location` - Location of the resource group -- `subscription_id` - Azure subscription ID -- `container_group_profile_name` - Name of the container group profile -- `ngroups_name` - Name of the NGroups resource -- Deployment IDs for both resources - -## Clean Up - -To destroy the resources: -```bash -terraform destroy -``` diff --git a/recipe-packs/terraform/aci-ngroups-basic/main.tf b/recipe-packs/terraform/aci-ngroups-basic/main.tf deleted file mode 100644 index aef46571..00000000 --- a/recipe-packs/terraform/aci-ngroups-basic/main.tf +++ /dev/null @@ -1,147 +0,0 @@ -# Configure the Azure Provider -terraform { - required_providers { - azurerm = { - source = "hashicorp/azurerm" - version = "~>3.0" - } - } -} - -# Configure the Microsoft Azure Provider -provider "azurerm" { - features {} -} - -# Data source to get current resource group -data "azurerm_resource_group" "current" { - name = var.resource_group_name -} - -# Data source to get current subscription -data "azurerm_client_config" "current" {} - -# Local variables -locals { - cg_profile_name = "cgp_1" - ngroups_name = "ngroup_lin1_basic" - api_version = "2024-09-01-preview" - desired_count = 1 - prefix_cg = "cg-lin1-basic-" - resource_prefix = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${data.azurerm_resource_group.current.name}/providers/" -} - -# Template deployment for Container Group Profile (using ARM template within Terraform) -resource "azurerm_resource_group_template_deployment" "container_group_profile" { - name = "cgp-deployment" - resource_group_name = data.azurerm_resource_group.current.name - deployment_mode = "Incremental" - - template_content = jsonencode({ - "$schema" = "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" - contentVersion = "1.0.0.0" - parameters = {} - variables = { - cgProfileName = local.cg_profile_name - apiVersion = local.api_version - } - resources = [ - { - apiVersion = local.api_version - type = "Microsoft.ContainerInstance/containerGroupProfiles" - name = local.cg_profile_name - location = data.azurerm_resource_group.current.location - properties = { - sku = "Standard" - containers = [ - { - name = "aci-helloworld" - properties = { - image = "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e" - ports = [ - { - protocol = "TCP" - port = 80 - } - ] - resources = { - requests = { - memoryInGB = 1.0 - cpu = 1.0 - } - } - } - } - ] - restartPolicy = "Always" - ipAddress = { - ports = [ - { - protocol = "TCP" - port = 80 - } - ] - type = "Public" - } - osType = "Linux" - } - } - ] - }) -} - -# Template deployment for NGroups -resource "azurerm_resource_group_template_deployment" "ngroups" { - name = "ngroups-deployment" - resource_group_name = data.azurerm_resource_group.current.name - deployment_mode = "Incremental" - - depends_on = [ - azurerm_resource_group_template_deployment.container_group_profile - ] - - template_content = jsonencode({ - "$schema" = "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" - contentVersion = "1.0.0.0" - parameters = {} - variables = { - cgProfileName = local.cg_profile_name - nGroupsName = local.ngroups_name - apiVersion = local.api_version - desiredCount = local.desired_count - prefixCG = local.prefix_cg - resourcePrefix = local.resource_prefix - } - resources = [ - { - apiVersion = local.api_version - type = "Microsoft.ContainerInstance/NGroups" - name = local.ngroups_name - location = data.azurerm_resource_group.current.location - dependsOn = [ - "Microsoft.ContainerInstance/containerGroupProfiles/${local.cg_profile_name}" - ] - properties = { - elasticProfile = { - desiredCount = local.desired_count - containerGroupNamingPolicy = { - guidNamingPolicy = { - prefix = local.prefix_cg - } - } - } - containerGroupProfiles = [ - { - resource = { - id = "${local.resource_prefix}Microsoft.ContainerInstance/containerGroupProfiles/${local.cg_profile_name}" - } - } - ] - } - tags = { - cirrusTestScenario = "lin-1.basic" - } - } - ] - }) -} diff --git a/recipe-packs/terraform/aci-ngroups-basic/outputs.tf b/recipe-packs/terraform/aci-ngroups-basic/outputs.tf deleted file mode 100644 index dd7735f8..00000000 --- a/recipe-packs/terraform/aci-ngroups-basic/outputs.tf +++ /dev/null @@ -1,36 +0,0 @@ -# Output values for the Terraform configuration - -output "resource_group_name" { - description = "Name of the resource group" - value = data.azurerm_resource_group.current.name -} - -output "resource_group_location" { - description = "Location of the resource group" - value = data.azurerm_resource_group.current.location -} - -output "subscription_id" { - description = "Azure subscription ID" - value = data.azurerm_client_config.current.subscription_id -} - -output "container_group_profile_name" { - description = "Name of the container group profile" - value = local.cg_profile_name -} - -output "ngroups_name" { - description = "Name of the NGroups resource" - value = local.ngroups_name -} - -output "container_group_profile_deployment_id" { - description = "Deployment ID for container group profile" - value = azurerm_resource_group_template_deployment.container_group_profile.id -} - -output "ngroups_deployment_id" { - description = "Deployment ID for NGroups resource" - value = azurerm_resource_group_template_deployment.ngroups.id -} diff --git a/recipe-packs/terraform/aci-ngroups-basic/terraform.tfvars.example b/recipe-packs/terraform/aci-ngroups-basic/terraform.tfvars.example deleted file mode 100644 index 8b3d9663..00000000 --- a/recipe-packs/terraform/aci-ngroups-basic/terraform.tfvars.example +++ /dev/null @@ -1,21 +0,0 @@ -# Example Terraform variables file -# Copy this file to terraform.tfvars and modify the values as needed - -# Required variables -resource_group_name = "my-resource-group" - -# Optional variables (defaults will be used if not specified) -# location = "East US" -# cg_profile_name = "cgp_1" -# ngroups_name = "ngroup_lin1_basic" -# desired_count = 1 -# prefix_cg = "cg-lin1-basic-" -# container_port = 80 -# memory_gb = 1.0 -# cpu_cores = 1.0 - -# tags = { -# cirrusTestScenario = "lin-1.basic" -# environment = "development" -# project = "my-project" -# } diff --git a/recipe-packs/terraform/aci-ngroups-basic/variables.tf b/recipe-packs/terraform/aci-ngroups-basic/variables.tf deleted file mode 100644 index 6e4ab868..00000000 --- a/recipe-packs/terraform/aci-ngroups-basic/variables.tf +++ /dev/null @@ -1,69 +0,0 @@ -# Variable definitions for the Terraform configuration - -variable "resource_group_name" { - description = "Name of the Azure Resource Group" - type = string -} - -variable "location" { - description = "Azure region for resource deployment" - type = string - default = null # Will use resource group location if not specified -} - -variable "cg_profile_name" { - description = "Name of the Container Group Profile" - type = string - default = "cgp_1" -} - -variable "ngroups_name" { - description = "Name of the NGroups resource" - type = string - default = "ngroup_lin1_basic" -} - -variable "desired_count" { - description = "Desired count for elastic profile" - type = number - default = 1 -} - -variable "prefix_cg" { - description = "Prefix for container group naming" - type = string - default = "cg-lin1-basic-" -} - -variable "container_image" { - description = "Container image to deploy" - type = string - default = "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e" -} - -variable "container_port" { - description = "Port to expose on the container" - type = number - default = 80 -} - -variable "memory_gb" { - description = "Memory allocation in GB" - type = number - default = 1.0 -} - -variable "cpu_cores" { - description = "CPU allocation" - type = number - default = 1.0 -} - -variable "tags" { - description = "Tags to apply to resources" - type = map(string) - default = { - cirrusTestScenario = "lin-1.basic" - environment = "development" - } -} diff --git a/recipe-packs/terraform/aci-ngroups-gateway/README.md b/recipe-packs/terraform/aci-ngroups-gateway/README.md deleted file mode 100644 index 1f58dbd1..00000000 --- a/recipe-packs/terraform/aci-ngroups-gateway/README.md +++ /dev/null @@ -1,183 +0,0 @@ -# Azure Container Instance NGroups with Application Gateway - Terraform Configuration - -This Terraform configuration converts the ARM template for Azure Container Instance NGroups with Application Gateway to Terraform format. This is a comprehensive setup that includes networking infrastructure, security groups, and application gateway integration. - -## Architecture Overview - -This configuration deploys: - -1. **Virtual Network** with two subnets: - - ACI Subnet (for container instances) - - Application Gateway Subnet -2. **Network Security Group** with rules for Application Gateway and HTTP traffic -3. **Public IP** for the Application Gateway -4. **Application Gateway** with health probes and routing rules -5. **Container Group Profile** with private networking -6. **NGroups** with Application Gateway integration for elastic scaling - -## Prerequisites - -- Terraform >= 0.14 -- Azure CLI installed and authenticated -- An existing Azure Resource Group -- Sufficient Azure permissions to create networking and container resources - -## Resources Created - -### Networking -- **Virtual Network** (`azurerm_virtual_network`) -- **ACI Subnet** with Container Instance delegation (`azurerm_subnet`) -- **Application Gateway Subnet** (`azurerm_subnet`) -- **Network Security Group** with security rules (`azurerm_network_security_group`) -- **Public IP** for Application Gateway (`azurerm_public_ip`) - -### Application Gateway -- **Application Gateway** with Standard_v2 SKU (`azurerm_application_gateway`) -- Backend address pool for container instances -- HTTP listener on port 80 -- Health probe configuration -- Request routing rules - -### Container Instance Resources -- **Container Group Profile** (via ARM template deployment) -- **NGroups** with Application Gateway integration (via ARM template deployment) - -## Usage - -### 1. Setup Variables - -Copy the example variables file: -```bash -cp ngroups-gateway.tfvars.example ngroups-gateway.tfvars -``` - -Edit `ngroups-gateway.tfvars` with your specific values: -```hcl -resource_group_name = "your-resource-group-name" -desired_count = 50 # Adjust based on your needs -``` - -### 2. Initialize Terraform - -```bash -terraform init -``` - -### 3. Plan the Deployment - -```bash -terraform plan -var-file="ngroups-gateway.tfvars" -``` - -### 4. Apply the Configuration - -```bash -terraform apply -var-file="ngroups-gateway.tfvars" -``` - -## Configuration Details - -### Network Configuration - -- **VNet Address Space**: `172.16.0.0/23` (default) -- **ACI Subnet**: `172.16.0.0/25` (default) -- **App Gateway Subnet**: `172.16.1.0/25` (default) - -### Security Rules - -The Network Security Group includes: -- Application Gateway V2 probe traffic (ports 65200-65535) -- HTTP traffic on port 80 -- Public IP specific access -- Virtual Network internal traffic - -### Application Gateway - -- **SKU**: Standard_v2 with autoscaling (0-3 instances) -- **Frontend**: Public IP configuration -- **Backend**: Integrated with NGroups container instances -- **Health Probe**: HTTP probe on path "/" - -### Container Configuration - -- **Desired Count**: 100 instances (default, configurable) -- **Maintain Desired Count**: Enabled -- **Container Image**: ACI Hello World (configurable) -- **Networking**: Private IP addresses within VNet -- **Auto-scaling**: Managed by NGroups - -## Variables - -| Variable | Description | Default | Required | -|----------|-------------|---------|----------| -| `resource_group_name` | Resource Group name | - | ✅ | -| `api_version` | Container Instance API version | `2024-09-01-preview` | ❌ | -| `desired_count` | Desired container count | `100` | ❌ | -| `vnet_address_prefix` | VNet address space | `172.16.0.0/23` | ❌ | -| `aci_subnet_address_prefix` | ACI subnet CIDR | `172.16.0.0/25` | ❌ | -| `app_gateway_subnet_address_prefix` | App Gateway subnet CIDR | `172.16.1.0/25` | ❌ | -| `container_image` | Container image | ACI Hello World | ❌ | -| `maintain_desired_count` | Maintain desired count | `true` | ❌ | - -See `ngroups-gateway-variables.tf` for complete variable list. - -## Outputs - -Key outputs include: -- Virtual Network and subnet IDs -- Application Gateway ID and public IP -- Backend address pool ID -- Container Group Profile and NGroups deployment IDs -- Network Security Group ID - -## Important Notes - -### Preview Features -- **Container Group Profiles** and **NGroups** are preview features -- These resources use ARM template deployments within Terraform -- Future versions may support native Terraform resources - -### Dependencies -- The configuration properly handles resource dependencies -- Application Gateway is created before NGroups deployment -- Subnets are associated with NSG after creation - -### Scaling -- NGroups automatically manages container instance scaling -- Application Gateway distributes traffic across container instances -- Health probes ensure only healthy instances receive traffic - -## Troubleshooting - -### Common Issues - -1. **Subnet Address Conflicts**: Ensure subnet CIDRs don't overlap -2. **Application Gateway Startup**: May take 10-15 minutes to fully provision -3. **Container Health**: Check that containers respond to health probes - -### Monitoring - -Monitor the deployment through: -- Azure Portal for resource status -- Application Gateway metrics for traffic distribution -- Container Instance logs for application health - -## Clean Up - -To destroy all resources: -```bash -terraform destroy -var-file="ngroups-gateway.tfvars" -``` - -**Note**: This will delete all networking infrastructure and container resources. - -## Migration Path - -When native Terraform support becomes available for Container Group Profiles and NGroups: -1. Replace ARM template deployments with native resources -2. Update variable references -3. Test the migration in a development environment - -## Support - -This configuration is based on the ARM template `ACI-NGroups-Gateway.yaml` and maintains feature parity while providing Terraform workflow benefits. diff --git a/recipe-packs/terraform/aci-ngroups-gateway/main.tf b/recipe-packs/terraform/aci-ngroups-gateway/main.tf deleted file mode 100644 index 356db183..00000000 --- a/recipe-packs/terraform/aci-ngroups-gateway/main.tf +++ /dev/null @@ -1,400 +0,0 @@ -# Configure the Azure Provider -terraform { - required_providers { - azurerm = { - source = "hashicorp/azurerm" - version = "~>3.0" - } - } -} - -# Configure the Microsoft Azure Provider -provider "azurerm" { - features {} -} - -# Data source to get current resource group -data "azurerm_resource_group" "current" { - name = var.resource_group_name -} - -# Data source to get current subscription -data "azurerm_client_config" "current" {} - -# Local variables -locals { - api_version = var.api_version - ngroups_name = var.ngroups_name_param - container_group_profile_name = var.container_group_profile_name - application_gateway_name = var.application_gateway_name - public_ip_name = var.public_ip_name - backend_address_pool_name = var.backend_address_pool_name - vnet_name = var.vnet_name - network_security_group_name = var.network_security_group_name - desired_count = var.desired_count - maintain_desired_count = var.maintain_desired_count - zones = var.zones - vnet_address_prefix = var.vnet_address_prefix - aci_subnet_address_prefix = var.aci_subnet_address_prefix - app_gateway_subnet_address_prefix = var.app_gateway_subnet_address_prefix - aci_subnet_name = var.aci_subnet_name - app_gateway_subnet_name = var.app_gateway_subnet_name - ddos_protection_plan_name = var.ddos_protection_plan_name - - description = "This ARM template is an example template of using an App Gateway with a NGroup." - resource_prefix = "/subscriptions/${data.azurerm_client_config.current.subscription_id}/resourceGroups/${data.azurerm_resource_group.current.name}/providers/" - application_gateway_api_version = "2022-09-01" - prefix_cg = "cg-lin100-regional-ag-" -} - -# Network Security Group -resource "azurerm_network_security_group" "nsg" { - name = local.network_security_group_name - location = data.azurerm_resource_group.current.location - resource_group_name = data.azurerm_resource_group.current.name - - security_rule { - name = "AppGatewayV2ProbeInbound" - priority = 100 - direction = "Inbound" - access = "Allow" - protocol = "Tcp" - source_port_range = "*" - destination_port_range = "65200-65535" - source_address_prefix = "GatewayManager" - destination_address_prefix = "*" - description = "Allow traffic from GatewayManager. This rule is needed for application gateway probes to work." - } - - security_rule { - name = "AllowHTTPInbound" - priority = 110 - direction = "Inbound" - access = "Allow" - protocol = "Tcp" - source_port_range = "*" - destination_port_range = "80" - source_address_prefix = "Internet" - destination_address_prefix = "*" - description = "Allow Internet traffic on port 80" - } - - security_rule { - name = "AllowPublicIPAddress" - priority = 111 - direction = "Inbound" - access = "Allow" - protocol = "Tcp" - source_port_range = "*" - destination_port_range = "80" - source_address_prefix = "Internet" - destination_address_prefix = azurerm_public_ip.public_ip.ip_address - description = "Allow traffic from public ip address" - } - - security_rule { - name = "AllowVirtualNetworkInbound" - priority = 112 - direction = "Inbound" - access = "Allow" - protocol = "*" - source_port_range = "*" - destination_port_range = "80" - source_address_prefix = "*" - destination_address_prefix = "VirtualNetwork" - description = "Allow Internet traffic to Virtual network" - } -} - -# Virtual Network -resource "azurerm_virtual_network" "vnet" { - name = local.vnet_name - location = data.azurerm_resource_group.current.location - resource_group_name = data.azurerm_resource_group.current.name - address_space = [local.vnet_address_prefix] - - depends_on = [azurerm_network_security_group.nsg] -} - -# ACI Subnet -resource "azurerm_subnet" "aci_subnet" { - name = local.aci_subnet_name - resource_group_name = data.azurerm_resource_group.current.name - virtual_network_name = azurerm_virtual_network.vnet.name - address_prefixes = [local.aci_subnet_address_prefix] - - delegation { - name = "ACIDelegationService" - - service_delegation { - name = "Microsoft.ContainerInstance/containerGroups" - } - } - - private_endpoint_network_policies_enabled = false - private_link_service_network_policies_enabled = true - - depends_on = [azurerm_virtual_network.vnet] -} - -# Application Gateway Subnet -resource "azurerm_subnet" "app_gateway_subnet" { - name = local.app_gateway_subnet_name - resource_group_name = data.azurerm_resource_group.current.name - virtual_network_name = azurerm_virtual_network.vnet.name - address_prefixes = [local.app_gateway_subnet_address_prefix] - - private_endpoint_network_policies_enabled = false - private_link_service_network_policies_enabled = true - - depends_on = [azurerm_virtual_network.vnet] -} - -# Associate NSG with ACI Subnet -resource "azurerm_subnet_network_security_group_association" "aci_subnet_nsg" { - subnet_id = azurerm_subnet.aci_subnet.id - network_security_group_id = azurerm_network_security_group.nsg.id -} - -# Associate NSG with Application Gateway Subnet -resource "azurerm_subnet_network_security_group_association" "app_gateway_subnet_nsg" { - subnet_id = azurerm_subnet.app_gateway_subnet.id - network_security_group_id = azurerm_network_security_group.nsg.id -} - -# Public IP for Application Gateway -resource "azurerm_public_ip" "public_ip" { - name = local.public_ip_name - location = data.azurerm_resource_group.current.location - resource_group_name = data.azurerm_resource_group.current.name - allocation_method = "Static" - sku = "Standard" - sku_tier = "Regional" - ip_version = "IPv4" - idle_timeout_in_minutes = 5 - - ddos_protection_mode = "VirtualNetworkInherited" -} - -# Application Gateway -resource "azurerm_application_gateway" "app_gateway" { - name = local.application_gateway_name - location = data.azurerm_resource_group.current.location - resource_group_name = data.azurerm_resource_group.current.name - - sku { - name = "Standard_v2" - tier = "Standard_v2" - } - - autoscale_configuration { - min_capacity = 0 - max_capacity = 3 - } - - gateway_ip_configuration { - name = "appGatewayIpConfig" - subnet_id = azurerm_subnet.app_gateway_subnet.id - } - - frontend_port { - name = "port_80" - port = 80 - } - - frontend_ip_configuration { - name = "appGwPublicFrontendIpIPv4" - public_ip_address_id = azurerm_public_ip.public_ip.id - } - - backend_address_pool { - name = local.backend_address_pool_name - } - - backend_http_settings { - name = "${local.application_gateway_name}-be-settings" - cookie_based_affinity = "Disabled" - port = 80 - protocol = "Http" - request_timeout = 60 - probe_name = "healthprobe" - } - - http_listener { - name = "${local.application_gateway_name}-listener" - frontend_ip_configuration_name = "appGwPublicFrontendIpIPv4" - frontend_port_name = "port_80" - protocol = "Http" - } - - request_routing_rule { - name = "${local.application_gateway_name}-routerule" - rule_type = "Basic" - priority = 1 - http_listener_name = "${local.application_gateway_name}-listener" - backend_address_pool_name = local.backend_address_pool_name - backend_http_settings_name = "${local.application_gateway_name}-be-settings" - } - - probe { - name = "healthprobe" - protocol = "Http" - path = "/" - host = "127.0.0.1" - interval = 3600 - timeout = 3600 - unhealthy_threshold = 3 - } - - depends_on = [ - azurerm_subnet.app_gateway_subnet, - azurerm_public_ip.public_ip - ] -} - -# Template deployment for Container Group Profile (using ARM template within Terraform) -resource "azurerm_resource_group_template_deployment" "container_group_profile" { - name = "cgp-deployment" - resource_group_name = data.azurerm_resource_group.current.name - deployment_mode = "Incremental" - - template_content = jsonencode({ - "$schema" = "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" - contentVersion = "1.0.0.0" - parameters = {} - variables = { - containerGroupProfileName = local.container_group_profile_name - apiVersion = local.api_version - } - resources = [ - { - apiVersion = local.api_version - type = "Microsoft.ContainerInstance/containerGroupProfiles" - name = local.container_group_profile_name - location = data.azurerm_resource_group.current.location - properties = { - sku = "Standard" - containers = [ - { - name = "web" - properties = { - image = var.container_image - ports = [ - { - protocol = "TCP" - port = var.container_port - } - ] - resources = { - requests = { - memoryInGB = var.memory_gb - cpu = var.cpu_cores - } - } - } - } - ] - restartPolicy = "Always" - ipAddress = { - ports = [ - { - protocol = "TCP" - port = var.container_port - } - ] - type = "Private" - } - osType = "Linux" - } - } - ] - }) -} - -# Template deployment for NGroups -resource "azurerm_resource_group_template_deployment" "ngroups" { - name = "ngroups-deployment" - resource_group_name = data.azurerm_resource_group.current.name - deployment_mode = "Incremental" - - depends_on = [ - azurerm_resource_group_template_deployment.container_group_profile, - azurerm_application_gateway.app_gateway, - azurerm_virtual_network.vnet - ] - - template_content = jsonencode({ - "$schema" = "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" - contentVersion = "1.0.0.0" - parameters = {} - variables = { - containerGroupProfileName = local.container_group_profile_name - nGroupsNameParam = local.ngroups_name - applicationGatewayName = local.application_gateway_name - vnetName = local.vnet_name - aciSubnetName = local.aci_subnet_name - backendAddressPoolName = local.backend_address_pool_name - apiVersion = local.api_version - desiredCount = local.desired_count - maintainDesiredCount = local.maintain_desired_count - prefixCG = local.prefix_cg - resourcePrefix = local.resource_prefix - } - resources = [ - { - apiVersion = local.api_version - type = "Microsoft.ContainerInstance/NGroups" - name = local.ngroups_name - location = data.azurerm_resource_group.current.location - dependsOn = [ - "Microsoft.ContainerInstance/containerGroupProfiles/${local.container_group_profile_name}" - ] - properties = { - elasticProfile = { - desiredCount = local.desired_count - maintainDesiredCount = local.maintain_desired_count - containerGroupNamingPolicy = { - guidNamingPolicy = { - prefix = local.prefix_cg - } - } - } - containerGroupProfiles = [ - { - resource = { - id = "${local.resource_prefix}Microsoft.ContainerInstance/containerGroupProfiles/${local.container_group_profile_name}" - } - containerGroupProperties = { - subnetIds = [ - { - id = azurerm_subnet.aci_subnet.id - name = local.aci_subnet_name - } - ] - } - networkProfile = { - applicationGateway = { - resource = { - id = azurerm_application_gateway.app_gateway.id - } - backendAddressPools = [ - { - resource = { - id = "${azurerm_application_gateway.app_gateway.id}/backendAddressPools/${local.backend_address_pool_name}" - } - } - ] - } - } - } - ] - } - zones = local.zones - tags = { - "cirrusTestScenario" = "lin-100.regional.appgateway" - "reprovision.enabled" = "true" - } - } - ] - }) -} diff --git a/recipe-packs/terraform/aci-ngroups-gateway/outputs.tf b/recipe-packs/terraform/aci-ngroups-gateway/outputs.tf deleted file mode 100644 index ad2994c5..00000000 --- a/recipe-packs/terraform/aci-ngroups-gateway/outputs.tf +++ /dev/null @@ -1,96 +0,0 @@ -# Output values for the NGroups Gateway Terraform configuration - -output "resource_group_name" { - description = "Name of the resource group" - value = data.azurerm_resource_group.current.name -} - -output "resource_group_location" { - description = "Location of the resource group" - value = data.azurerm_resource_group.current.location -} - -output "subscription_id" { - description = "Azure subscription ID" - value = data.azurerm_client_config.current.subscription_id -} - -output "virtual_network_id" { - description = "ID of the Virtual Network" - value = azurerm_virtual_network.vnet.id -} - -output "virtual_network_name" { - description = "Name of the Virtual Network" - value = azurerm_virtual_network.vnet.name -} - -output "aci_subnet_id" { - description = "ID of the ACI subnet" - value = azurerm_subnet.aci_subnet.id -} - -output "app_gateway_subnet_id" { - description = "ID of the Application Gateway subnet" - value = azurerm_subnet.app_gateway_subnet.id -} - -output "network_security_group_id" { - description = "ID of the Network Security Group" - value = azurerm_network_security_group.nsg.id -} - -output "public_ip_address" { - description = "Public IP address" - value = azurerm_public_ip.public_ip.ip_address -} - -output "public_ip_id" { - description = "ID of the Public IP" - value = azurerm_public_ip.public_ip.id -} - -output "application_gateway_id" { - description = "ID of the Application Gateway" - value = azurerm_application_gateway.app_gateway.id -} - -output "application_gateway_name" { - description = "Name of the Application Gateway" - value = azurerm_application_gateway.app_gateway.name -} - -output "backend_address_pool_id" { - description = "ID of the backend address pool" - value = "${azurerm_application_gateway.app_gateway.id}/backendAddressPools/${local.backend_address_pool_name}" -} - -output "container_group_profile_name" { - description = "Name of the container group profile" - value = local.container_group_profile_name -} - -output "ngroups_name" { - description = "Name of the NGroups resource" - value = local.ngroups_name -} - -output "container_group_profile_deployment_id" { - description = "Deployment ID for container group profile" - value = azurerm_resource_group_template_deployment.container_group_profile.id -} - -output "ngroups_deployment_id" { - description = "Deployment ID for NGroups resource" - value = azurerm_resource_group_template_deployment.ngroups.id -} - -output "application_gateway_frontend_ip" { - description = "Frontend IP configuration of the Application Gateway" - value = azurerm_application_gateway.app_gateway.frontend_ip_configuration[0].name -} - -output "application_gateway_backend_pool" { - description = "Backend address pool name" - value = local.backend_address_pool_name -} diff --git a/recipe-packs/terraform/aci-ngroups-gateway/terraform.tfvars.example b/recipe-packs/terraform/aci-ngroups-gateway/terraform.tfvars.example deleted file mode 100644 index 737df049..00000000 --- a/recipe-packs/terraform/aci-ngroups-gateway/terraform.tfvars.example +++ /dev/null @@ -1,39 +0,0 @@ -# Example Terraform variables file for NGroups Gateway configuration -# Copy this file to ngroups-gateway.tfvars and modify the values as needed - -# Required variables -resource_group_name = "my-ngroups-gateway-rg" - -# Optional variables (defaults will be used if not specified) -# api_version = "2024-09-01-preview" -# ngroups_name_param = "nGroups_lin100_regional_ag" -# container_group_profile_name = "cgp" -# application_gateway_name = "agw1" -# public_ip_name = "publicIP" -# backend_address_pool_name = "bepool" -# vnet_name = "vnet1" -# network_security_group_name = "nsg1" -# desired_count = 100 -# maintain_desired_count = true -# zones = [] - -# Network configuration -# vnet_address_prefix = "172.16.0.0/23" -# aci_subnet_address_prefix = "172.16.0.0/25" -# app_gateway_subnet_address_prefix = "172.16.1.0/25" -# aci_subnet_name = "aciSubnet" -# app_gateway_subnet_name = "appgatewaySubnet" - -# Container configuration -# container_image = "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e" -# container_port = 80 -# memory_gb = 1.0 -# cpu_cores = 1.0 - -# Tags -# tags = { -# cirrusTestScenario = "lin-100.regional.appgateway" -# "reprovision.enabled" = "true" -# environment = "development" -# project = "my-project" -# } diff --git a/recipe-packs/terraform/aci-ngroups-gateway/variables.tf b/recipe-packs/terraform/aci-ngroups-gateway/variables.tf deleted file mode 100644 index ba65b918..00000000 --- a/recipe-packs/terraform/aci-ngroups-gateway/variables.tf +++ /dev/null @@ -1,142 +0,0 @@ -# Variable definitions for the NGroups Gateway Terraform configuration - -variable "resource_group_name" { - description = "Name of the Azure Resource Group" - type = string -} - -variable "api_version" { - description = "API version for Container Instance resources" - type = string - default = "2024-09-01-preview" -} - -variable "ngroups_name_param" { - description = "Name of the NGroups resource" - type = string - default = "nGroups_lin100_regional_ag" -} - -variable "container_group_profile_name" { - description = "Name of the Container Group Profile" - type = string - default = "cgp" -} - -variable "application_gateway_name" { - description = "Name of the Application Gateway" - type = string - default = "agw1" -} - -variable "public_ip_name" { - description = "Name of the Public IP address" - type = string - default = "publicIP" -} - -variable "backend_address_pool_name" { - description = "Name of the backend address pool" - type = string - default = "bepool" -} - -variable "vnet_name" { - description = "Name of the Virtual Network" - type = string - default = "vnet1" -} - -variable "network_security_group_name" { - description = "Name of the Network Security Group" - type = string - default = "nsg1" -} - -variable "desired_count" { - description = "Desired number of container instances" - type = number - default = 100 -} - -variable "maintain_desired_count" { - description = "Whether to maintain the desired count" - type = bool - default = true -} - -variable "zones" { - description = "Availability zones for the resources" - type = list(string) - default = [] -} - -variable "vnet_address_prefix" { - description = "Address prefix for the Virtual Network" - type = string - default = "172.16.0.0/23" -} - -variable "aci_subnet_address_prefix" { - description = "Address prefix for the ACI subnet" - type = string - default = "172.16.0.0/25" -} - -variable "app_gateway_subnet_address_prefix" { - description = "Address prefix for the Application Gateway subnet" - type = string - default = "172.16.1.0/25" -} - -variable "aci_subnet_name" { - description = "Name of the ACI subnet" - type = string - default = "aciSubnet" -} - -variable "app_gateway_subnet_name" { - description = "Name of the Application Gateway subnet" - type = string - default = "appgatewaySubnet" -} - -variable "ddos_protection_plan_name" { - description = "Name of the DDoS protection plan" - type = string - default = "ddosProtectionPlan" -} - -variable "container_image" { - description = "Container image to deploy" - type = string - default = "mcr.microsoft.com/azuredocs/aci-helloworld@sha256:565dba8ce20ca1a311c2d9485089d7ddc935dd50140510050345a1b0ea4ffa6e" -} - -variable "container_port" { - description = "Port to expose on the container" - type = number - default = 80 -} - -variable "memory_gb" { - description = "Memory allocation in GB" - type = number - default = 1.0 -} - -variable "cpu_cores" { - description = "CPU allocation" - type = number - default = 1.0 -} - -variable "tags" { - description = "Tags to apply to resources" - type = map(string) - default = { - cirrusTestScenario = "lin-100.regional.appgateway" - "reprovision.enabled" = "true" - environment = "development" - } -} From a39683c7613bdfa69179a19634f1ac078f13ac6f Mon Sep 17 00:00:00 2001 From: Shivam Gupta Date: Tue, 14 Oct 2025 13:45:32 -0700 Subject: [PATCH 08/12] updated desiredCount param --- .../bicep/ACI-NGroups-LoadBalancer.bicep | 83 +-- .../bicep/ACI-NGroups-LoadBalancer.json | 556 ++++++++++++++++++ 2 files changed, 600 insertions(+), 39 deletions(-) create mode 100644 recipe-packs/bicep/ACI-NGroups-LoadBalancer.json diff --git a/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep index 2bd5f2db..fb169961 100644 --- a/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep +++ b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep @@ -1,6 +1,6 @@ @description('Container Instance API version') @maxLength(32) -param apiVersion string = '2024-09-01-preview' +param apiVersion string = '2024-11-01-preview' @description('NGroups parameter name') @maxLength(64) @@ -57,8 +57,8 @@ param vnetAddressPrefix string @maxLength(64) param subnetAddressPrefix string -@description('Desired container count') -param desiredCount int = 3 +// @description('Desired container count') +// param desiredCount int = 3 @description('Availability zones') param zones array = [] @@ -73,15 +73,26 @@ param inboundNatRuleName string = 'inboundNatRule' @description('Radius ACI Container Context') param context object +// Output the context object for debugging +output contextObject object = context + // Variables var cgProfileName = containerGroupProfileName var nGroupsName = nGroupsParamName var resourcePrefix = '/subscriptions/${subscription().subscriptionId}/resourceGroups/${resourceGroup().name}/providers/' -var loadBalancerApiVersion = '2022-07-01' -var vnetApiVersion = '2022-07-01' -var publicIPVersion = '2022-07-01' +// var loadBalancerApiVersion = '2022-07-01' +// var vnetApiVersion = '2022-07-01' +// var publicIPVersion = '2022-07-01' var ddosProtectionPlanName = 'ddosProtectionPlan' +// Helper variables for probes +var hasReadinessProbe = contains(context.properties.containers, 'readinessProbe') && context.properties.containers.readinessProbe != null +var hasLivenessProbe = contains(context.properties.containers, 'livenessProbe') && context.properties.containers.livenessProbe != null + +// Get probe port with safe navigation +var readinessProbePort = hasReadinessProbe && contains(context.properties.containers.readinessProbe, 'tcpSocket') && context.properties.containers.readinessProbe.tcpSocket != null && contains(context.properties.containers.readinessProbe.tcpSocket, 'properties') && contains(context.properties.containers.readinessProbe.tcpSocket.properties, 'port') ? context.properties.containers.readinessProbe.tcpSocket.properties.port : 80 +var livenessProbePort = hasLivenessProbe && contains(context.properties.containers.livenessProbe, 'tcpSocket') && context.properties.containers.livenessProbe.tcpSocket != null && contains(context.properties.containers.livenessProbe.tcpSocket, 'properties') && contains(context.properties.containers.livenessProbe.tcpSocket.properties, 'port') ? context.properties.containers.livenessProbe.tcpSocket.properties.port : 80 + // DDoS Protection Plan resource ddosProtectionPlan 'Microsoft.Network/ddosProtectionPlans@2022-07-01' = { name: ddosProtectionPlanName @@ -161,9 +172,7 @@ resource natGateway 'Microsoft.Network/natGateways@2022-07-01' = { } ] } - dependsOn: [ - outboundPublicIP - ] + } // Virtual Network @@ -210,10 +219,7 @@ resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-07-01' = { id: ddosProtectionPlan.id } } - dependsOn: [ - networkSecurityGroup - natGateway - ] + } // Load Balancer @@ -245,27 +251,27 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' = { } ] probes: union( - context.properties.containers.readinessProbe != null ? [ + hasReadinessProbe ? [ { name: 'readinessProbe' properties: { protocol: 'Tcp' - port: context.properties.containers.readinessProbe.tcpSocket.properties.port ?? 80 - intervalInSeconds: context.properties.containers.readinessProbe.periodSeconds ?? 5 - numberOfProbes: context.properties.containers.readinessProbe.failureThreshold ?? 3 - probeThreshold: context.properties.containers.readinessProbe.successThreshold ?? 1 + port: readinessProbePort + intervalInSeconds: context.properties.containers.readinessProbe.?periodSeconds ?? 5 + numberOfProbes: context.properties.containers.readinessProbe.?failureThreshold ?? 3 + probeThreshold: context.properties.containers.readinessProbe.?successThreshold ?? 1 } } ] : [], - context.properties.containers.livenessProbe != null ? [ + hasLivenessProbe ? [ { name: 'livenessProbe' properties: { protocol: 'Tcp' - port: context.properties.containers.livenessProbe.tcpSocket.properties.port ?? 80 - intervalInSeconds: context.properties.containers.livenessProbe.periodSeconds ?? 10 - numberOfProbes: context.properties.containers.livenessProbe.failureThreshold ?? 3 - probeThreshold: context.properties.containers.livenessProbe.successThreshold ?? 1 + port: livenessProbePort + intervalInSeconds: context.properties.containers.livenessProbe.?periodSeconds ?? 10 + numberOfProbes: context.properties.containers.livenessProbe.?failureThreshold ?? 3 + probeThreshold: context.properties.containers.livenessProbe.?successThreshold ?? 1 } } ] : [] @@ -290,7 +296,7 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' = { id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', loadBalancerName, backendAddressPoolName) } ] - probe: context.properties.containers.readinessProbe != null ? { + probe: hasReadinessProbe ? { id: resourceId('Microsoft.Network/loadBalancers/probes', loadBalancerName, 'readinessProbe') } : null } @@ -303,15 +309,15 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' = { backendAddressPool: { id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', loadBalancerName, backendAddressPoolName) } - backendPort: '80' - enableFloatingIP: 'false' - enableTcpReset: 'false' + backendPort: 80 + enableFloatingIP: false + enableTcpReset: false frontendIPConfiguration: { id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', loadBalancerName, frontendIPName) } - frontendPortRangeEnd: '331' - frontendPortRangeStart: '81' - idleTimeoutInMinutes: '4' + frontendPortRangeEnd: 331 + frontendPortRangeStart: 81 + idleTimeoutInMinutes: 4 protocol: 'Tcp' } } @@ -320,13 +326,12 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' = { inboundNatPools: [] } dependsOn: [ - inboundPublicIP virtualNetwork ] } // ContainerGroupProfile resource - Create default CGProfile when platformOptions is not provided else use the CGProfile resource provided by the customer. -resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfiles@2024-09-01-preview' = if (context.properties.platformOptions == null) { +resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfiles@2024-09-01-preview' = { name: cgProfileName location: resourceGroup().location properties: { @@ -387,7 +392,7 @@ resource nGroups 'Microsoft.ContainerInstance/NGroups@2024-09-01-preview' = { } properties: { elasticProfile: { - desiredCount: desiredCount + desiredCount: context.properties.replicas ?? 2 maintainDesiredCount: maintainDesiredCount } updateProfile: { @@ -421,9 +426,9 @@ resource nGroups 'Microsoft.ContainerInstance/NGroups@2024-09-01-preview' = { ] } tags: { - 'reprovision.enabled': true - 'metadata.container.environmentVariable.orchestratorId': true - 'rollingupdate.replace.enabled': true + 'reprovision.enabled': 'true' + 'metadata.container.environmentVariable.orchestratorId': 'true' + 'rollingupdate.replace.enabled': 'true' } dependsOn: [ containerGroupProfile @@ -440,11 +445,11 @@ output frontendIPConfigurationId string = loadBalancer.properties.frontendIPConf output backendAddressPoolId string = loadBalancer.properties.backendAddressPools[0].id output inboundPublicIPId string = inboundPublicIP.id output outboundPublicIPId string = outboundPublicIP.id -output inboundPublicIPFQDN string = inboundPublicIP.properties.dnsSettings.fqdn +output inboundPublicIPFQDN string = contains(inboundPublicIP.properties, 'dnsSettings') && inboundPublicIP.properties.dnsSettings != null ? inboundPublicIP.properties.dnsSettings.fqdn : '' output natGatewayId string = natGateway.id output networkSecurityGroupId string = networkSecurityGroup.id output ddosProtectionPlanId string = ddosProtectionPlan.id output containerGroupProfileId string = containerGroupProfile.id output nGroupsId string = nGroups.id -output readinessProbeId string = context.properties.containers.readinessProbe != null ? resourceId('Microsoft.Network/loadBalancers/probes', loadBalancerName, 'readinessProbe') : '' -output livenessProbeId string = context.properties.containers.livenessProbe != null ? resourceId('Microsoft.Network/loadBalancers/probes', loadBalancerName, 'livenessProbe') : '' +output readinessProbeId string = hasReadinessProbe ? resourceId('Microsoft.Network/loadBalancers/probes', loadBalancerName, 'readinessProbe') : '' +output livenessProbeId string = hasLivenessProbe ? resourceId('Microsoft.Network/loadBalancers/probes', loadBalancerName, 'livenessProbe') : '' diff --git a/recipe-packs/bicep/ACI-NGroups-LoadBalancer.json b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.json new file mode 100644 index 00000000..b1e67a21 --- /dev/null +++ b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.json @@ -0,0 +1,556 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.38.33.27573", + "templateHash": "6336561542319590498" + } + }, + "parameters": { + "apiVersion": { + "type": "string", + "defaultValue": "2024-11-01-preview", + "maxLength": 32, + "metadata": { + "description": "Container Instance API version" + } + }, + "nGroupsParamName": { + "type": "string", + "defaultValue": "nGroups_resource_1", + "maxLength": 64, + "metadata": { + "description": "NGroups parameter name" + } + }, + "containerGroupProfileName": { + "type": "string", + "defaultValue": "cgp_1", + "maxLength": 64, + "metadata": { + "description": "Container Group Profile name" + } + }, + "loadBalancerName": { + "type": "string", + "defaultValue": "slb_1", + "maxLength": 64, + "metadata": { + "description": "Load Balancer name" + } + }, + "backendAddressPoolName": { + "type": "string", + "defaultValue": "bepool_1", + "maxLength": 64, + "metadata": { + "description": "Backend Address Pool name" + } + }, + "vnetName": { + "type": "string", + "defaultValue": "vnet_1", + "maxLength": 64, + "metadata": { + "description": "Virtual Network name" + } + }, + "subnetName": { + "type": "string", + "defaultValue": "subnet_1", + "maxLength": 64, + "metadata": { + "description": "Subnet name" + } + }, + "networkSecurityGroupName": { + "type": "string", + "defaultValue": "nsg_1", + "maxLength": 64, + "metadata": { + "description": "Network Security Group name" + } + }, + "inboundPublicIPName": { + "type": "string", + "defaultValue": "inboundPublicIP", + "maxLength": 64, + "metadata": { + "description": "Inbound Public IP name" + } + }, + "outboundPublicIPName": { + "type": "string", + "defaultValue": "outboundPublicIP", + "maxLength": 64, + "metadata": { + "description": "Outbound Public IP name" + } + }, + "natGatewayName": { + "type": "string", + "defaultValue": "natGateway1", + "metadata": { + "description": "NAT Gateway name" + } + }, + "frontendIPName": { + "type": "string", + "defaultValue": "loadBalancerFrontend", + "maxLength": 64, + "metadata": { + "description": "Frontend IP name" + } + }, + "httpRuleName": { + "type": "string", + "defaultValue": "httpRule", + "maxLength": 64, + "metadata": { + "description": "HTTP Rule name" + } + }, + "vnetAddressPrefix": { + "type": "string", + "maxLength": 64, + "metadata": { + "description": "Virtual Network address prefix" + } + }, + "subnetAddressPrefix": { + "type": "string", + "maxLength": 64, + "metadata": { + "description": "Subnet address prefix" + } + }, + "zones": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "Availability zones" + } + }, + "maintainDesiredCount": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Maintain desired count" + } + }, + "inboundNatRuleName": { + "type": "string", + "defaultValue": "inboundNatRule", + "maxLength": 64, + "metadata": { + "description": "Inbound NAT Rule name" + } + }, + "context": { + "type": "object", + "metadata": { + "description": "Radius ACI Container Context" + } + } + }, + "variables": { + "cgProfileName": "[parameters('containerGroupProfileName')]", + "nGroupsName": "[parameters('nGroupsParamName')]", + "resourcePrefix": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/', subscription().subscriptionId, resourceGroup().name)]", + "ddosProtectionPlanName": "ddosProtectionPlan", + "hasReadinessProbe": "[and(contains(parameters('context').properties.containers, 'readinessProbe'), not(equals(parameters('context').properties.containers.readinessProbe, null())))]", + "hasLivenessProbe": "[and(contains(parameters('context').properties.containers, 'livenessProbe'), not(equals(parameters('context').properties.containers.livenessProbe, null())))]", + "readinessProbePort": "[if(and(and(and(and(variables('hasReadinessProbe'), contains(parameters('context').properties.containers.readinessProbe, 'tcpSocket')), not(equals(parameters('context').properties.containers.readinessProbe.tcpSocket, null()))), contains(parameters('context').properties.containers.readinessProbe.tcpSocket, 'properties')), contains(parameters('context').properties.containers.readinessProbe.tcpSocket.properties, 'port')), parameters('context').properties.containers.readinessProbe.tcpSocket.properties.port, 80)]", + "livenessProbePort": "[if(and(and(and(and(variables('hasLivenessProbe'), contains(parameters('context').properties.containers.livenessProbe, 'tcpSocket')), not(equals(parameters('context').properties.containers.livenessProbe.tcpSocket, null()))), contains(parameters('context').properties.containers.livenessProbe.tcpSocket, 'properties')), contains(parameters('context').properties.containers.livenessProbe.tcpSocket.properties, 'port')), parameters('context').properties.containers.livenessProbe.tcpSocket.properties.port, 80)]" + }, + "resources": [ + { + "type": "Microsoft.Network/ddosProtectionPlans", + "apiVersion": "2022-07-01", + "name": "[variables('ddosProtectionPlanName')]", + "location": "[resourceGroup().location]" + }, + { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2022-07-01", + "name": "[parameters('networkSecurityGroupName')]", + "location": "[resourceGroup().location]", + "properties": { + "securityRules": [ + { + "name": "AllowHTTPInbound", + "properties": { + "access": "Allow", + "description": "Allow Internet traffic on port range", + "destinationAddressPrefix": "*", + "destinationPortRanges": [ + "80-331" + ], + "direction": "Inbound", + "protocol": "*", + "priority": 100, + "sourceAddressPrefix": "Internet", + "sourcePortRange": "*" + } + } + ] + } + }, + { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2022-07-01", + "name": "[parameters('inboundPublicIPName')]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard", + "tier": "Regional" + }, + "properties": { + "publicIPAddressVersion": "IPv4", + "publicIPAllocationMethod": "Static", + "idleTimeoutInMinutes": 4, + "ipTags": [] + } + }, + { + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2022-07-01", + "name": "[parameters('outboundPublicIPName')]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard", + "tier": "Regional" + }, + "properties": { + "publicIPAddressVersion": "IPv4", + "publicIPAllocationMethod": "Static", + "idleTimeoutInMinutes": 4, + "ipTags": [] + } + }, + { + "type": "Microsoft.Network/natGateways", + "apiVersion": "2022-07-01", + "name": "[parameters('natGatewayName')]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard" + }, + "properties": { + "idleTimeoutInMinutes": 4, + "publicIpAddresses": [ + { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('outboundPublicIPName'))]" + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/publicIPAddresses', parameters('outboundPublicIPName'))]" + ] + }, + { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2022-07-01", + "name": "[parameters('vnetName')]", + "location": "[resourceGroup().location]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[parameters('vnetAddressPrefix')]" + ] + }, + "subnets": [ + { + "name": "[parameters('subnetName')]", + "properties": { + "addressPrefix": "[parameters('subnetAddressPrefix')]", + "serviceEndpoints": [], + "delegations": [ + { + "name": "Microsoft.ContainerInstance.containerGroups", + "id": "[format('{0}/delegations/Microsoft.ContainerInstance.containerGroups', resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('subnetName')))]", + "properties": { + "serviceName": "Microsoft.ContainerInstance/containerGroups" + }, + "type": "Microsoft.Network/virtualNetworks/subnets/delegations" + } + ], + "privateEndpointNetworkPolicies": "Disabled", + "privateLinkServiceNetworkPolicies": "Enabled", + "networkSecurityGroup": { + "id": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]" + }, + "natGateway": { + "id": "[resourceId('Microsoft.Network/natGateways', parameters('natGatewayName'))]" + } + }, + "type": "Microsoft.Network/virtualNetworks/subnets" + } + ], + "virtualNetworkPeerings": [], + "enableDdosProtection": true, + "ddosProtectionPlan": { + "id": "[resourceId('Microsoft.Network/ddosProtectionPlans', variables('ddosProtectionPlanName'))]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/ddosProtectionPlans', variables('ddosProtectionPlanName'))]", + "[resourceId('Microsoft.Network/natGateways', parameters('natGatewayName'))]", + "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]" + ] + }, + { + "type": "Microsoft.Network/loadBalancers", + "apiVersion": "2022-07-01", + "name": "[parameters('loadBalancerName')]", + "location": "[resourceGroup().location]", + "sku": { + "name": "Standard" + }, + "properties": { + "frontendIPConfigurations": [ + { + "properties": { + "publicIPAddress": { + "id": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('inboundPublicIPName'))]" + }, + "privateIPAllocationMethod": "Dynamic" + }, + "name": "[parameters('frontendIPName')]" + } + ], + "backendAddressPools": [ + { + "name": "[parameters('backendAddressPoolName')]", + "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('loadBalancerName'), parameters('backendAddressPoolName'))]", + "properties": { + "loadBalancerBackendAddresses": [] + } + } + ], + "probes": "[union(if(variables('hasReadinessProbe'), createArray(createObject('name', 'readinessProbe', 'properties', createObject('protocol', 'Tcp', 'port', variables('readinessProbePort'), 'intervalInSeconds', coalesce(tryGet(parameters('context').properties.containers.readinessProbe, 'periodSeconds'), 5), 'numberOfProbes', coalesce(tryGet(parameters('context').properties.containers.readinessProbe, 'failureThreshold'), 3), 'probeThreshold', coalesce(tryGet(parameters('context').properties.containers.readinessProbe, 'successThreshold'), 1)))), createArray()), if(variables('hasLivenessProbe'), createArray(createObject('name', 'livenessProbe', 'properties', createObject('protocol', 'Tcp', 'port', variables('livenessProbePort'), 'intervalInSeconds', coalesce(tryGet(parameters('context').properties.containers.livenessProbe, 'periodSeconds'), 10), 'numberOfProbes', coalesce(tryGet(parameters('context').properties.containers.livenessProbe, 'failureThreshold'), 3), 'probeThreshold', coalesce(tryGet(parameters('context').properties.containers.livenessProbe, 'successThreshold'), 1)))), createArray()))]", + "loadBalancingRules": [ + { + "name": "[parameters('httpRuleName')]", + "properties": { + "frontendIPConfiguration": { + "id": "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('loadBalancerName'), parameters('frontendIPName'))]" + }, + "frontendPort": 80, + "backendPort": 80, + "enableFloatingIP": false, + "idleTimeoutInMinutes": 15, + "protocol": "Tcp", + "enableTcpReset": true, + "loadDistribution": "Default", + "disableOutboundSnat": false, + "backendAddressPools": [ + { + "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('loadBalancerName'), parameters('backendAddressPoolName'))]" + } + ], + "probe": "[if(variables('hasReadinessProbe'), createObject('id', resourceId('Microsoft.Network/loadBalancers/probes', parameters('loadBalancerName'), 'readinessProbe')), null())]" + } + } + ], + "inboundNatRules": [ + { + "name": "[parameters('inboundNatRuleName')]", + "properties": { + "backendAddressPool": { + "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('loadBalancerName'), parameters('backendAddressPoolName'))]" + }, + "backendPort": 80, + "enableFloatingIP": false, + "enableTcpReset": false, + "frontendIPConfiguration": { + "id": "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', parameters('loadBalancerName'), parameters('frontendIPName'))]" + }, + "frontendPortRangeEnd": 331, + "frontendPortRangeStart": 81, + "idleTimeoutInMinutes": 4, + "protocol": "Tcp" + } + } + ], + "outboundRules": [], + "inboundNatPools": [] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/publicIPAddresses', parameters('inboundPublicIPName'))]", + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ] + }, + { + "type": "Microsoft.ContainerInstance/containerGroupProfiles", + "apiVersion": "2024-09-01-preview", + "name": "[variables('cgProfileName')]", + "location": "[resourceGroup().location]", + "properties": { + "sku": "Standard", + "containers": [ + { + "name": "web", + "properties": { + "image": "[parameters('context').properties.containers.image]", + "ports": [ + { + "protocol": "[if(not(equals(parameters('context').properties.containers.ports, null())), coalesce(parameters('context').properties.containers.ports.additionalProperties.properties.protocol, 'TCP'), 'TCP')]", + "port": "[if(not(equals(parameters('context').properties.containers.ports, null())), parameters('context').properties.containers.ports.additionalProperties.properties.containerPort, 80)]" + } + ], + "resources": { + "requests": { + "memoryInGB": "[coalesce(div(tryGet(tryGet(parameters('context').properties.containers.resources, 'requests'), 'memoryInMib'), 1024), json('1.0'))]", + "cpu": "[coalesce(tryGet(tryGet(parameters('context').properties.containers.resources, 'requests'), 'cpu'), json('1.0'))]" + } + }, + "volumeMounts": [ + { + "name": "cacheVolume", + "mountPath": "/mnt/cache" + } + ] + } + } + ], + "volumes": [ + { + "name": "cacheVolume", + "emptyDir": {} + } + ], + "restartPolicy": "Always", + "ipAddress": { + "ports": [ + { + "protocol": "TCP", + "port": 80 + } + ], + "type": "Private" + }, + "osType": "Linux" + } + }, + { + "type": "Microsoft.ContainerInstance/ngroups", + "apiVersion": "2024-09-01-preview", + "name": "[variables('nGroupsName')]", + "location": "[resourceGroup().location]", + "zones": "[parameters('zones')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "elasticProfile": { + "desiredCount": "[coalesce(parameters('context').properties.replicas, 2)]", + "maintainDesiredCount": "[parameters('maintainDesiredCount')]" + }, + "updateProfile": { + "updateMode": "Rolling" + }, + "containerGroupProfiles": [ + { + "resource": { + "id": "[format('{0}Microsoft.ContainerInstance/containerGroupProfiles/{1}', variables('resourcePrefix'), variables('cgProfileName'))]" + }, + "containerGroupProperties": { + "subnetIds": [ + { + "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), parameters('subnetName'))]", + "name": "[parameters('subnetName')]" + } + ] + }, + "networkProfile": { + "loadBalancer": { + "backendAddressPools": [ + { + "resource": { + "id": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', parameters('loadBalancerName'), parameters('backendAddressPoolName'))]" + } + } + ] + } + } + } + ] + }, + "tags": { + "reprovision.enabled": "true", + "metadata.container.environmentVariable.orchestratorId": "true", + "rollingupdate.replace.enabled": "true" + }, + "dependsOn": [ + "[resourceId('Microsoft.ContainerInstance/containerGroupProfiles', variables('cgProfileName'))]", + "[resourceId('Microsoft.Network/loadBalancers', parameters('loadBalancerName'))]", + "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + ] + } + ], + "outputs": { + "virtualNetworkId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName'))]" + }, + "subnetId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Network/virtualNetworks', parameters('vnetName')), '2022-07-01').subnets[0].id]" + }, + "loadBalancerId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/loadBalancers', parameters('loadBalancerName'))]" + }, + "frontendIPConfigurationId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Network/loadBalancers', parameters('loadBalancerName')), '2022-07-01').frontendIPConfigurations[0].id]" + }, + "backendAddressPoolId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Network/loadBalancers', parameters('loadBalancerName')), '2022-07-01').backendAddressPools[0].id]" + }, + "inboundPublicIPId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('inboundPublicIPName'))]" + }, + "outboundPublicIPId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('outboundPublicIPName'))]" + }, + "inboundPublicIPFQDN": { + "type": "string", + "value": "[if(and(contains(reference(resourceId('Microsoft.Network/publicIPAddresses', parameters('inboundPublicIPName')), '2022-07-01'), 'dnsSettings'), not(equals(reference(resourceId('Microsoft.Network/publicIPAddresses', parameters('inboundPublicIPName')), '2022-07-01').dnsSettings, null()))), reference(resourceId('Microsoft.Network/publicIPAddresses', parameters('inboundPublicIPName')), '2022-07-01').dnsSettings.fqdn, '')]" + }, + "natGatewayId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/natGateways', parameters('natGatewayName'))]" + }, + "networkSecurityGroupId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]" + }, + "ddosProtectionPlanId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/ddosProtectionPlans', variables('ddosProtectionPlanName'))]" + }, + "containerGroupProfileId": { + "type": "string", + "value": "[resourceId('Microsoft.ContainerInstance/containerGroupProfiles', variables('cgProfileName'))]" + }, + "nGroupsId": { + "type": "string", + "value": "[resourceId('Microsoft.ContainerInstance/ngroups', variables('nGroupsName'))]" + }, + "readinessProbeId": { + "type": "string", + "value": "[if(variables('hasReadinessProbe'), resourceId('Microsoft.Network/loadBalancers/probes', parameters('loadBalancerName'), 'readinessProbe'), '')]" + }, + "livenessProbeId": { + "type": "string", + "value": "[if(variables('hasLivenessProbe'), resourceId('Microsoft.Network/loadBalancers/probes', parameters('loadBalancerName'), 'livenessProbe'), '')]" + } + } +} \ No newline at end of file From aa423296741bd10ab325714093e931eb97070d43 Mon Sep 17 00:00:00 2001 From: Shivam Gupta Date: Tue, 14 Oct 2025 14:04:49 -0700 Subject: [PATCH 09/12] updated context.properties to context.resource.properties --- .../bicep/ACI-NGroups-LoadBalancer.bicep | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep index fb169961..5e1f6f1a 100644 --- a/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep +++ b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep @@ -86,12 +86,12 @@ var resourcePrefix = '/subscriptions/${subscription().subscriptionId}/resourceGr var ddosProtectionPlanName = 'ddosProtectionPlan' // Helper variables for probes -var hasReadinessProbe = contains(context.properties.containers, 'readinessProbe') && context.properties.containers.readinessProbe != null -var hasLivenessProbe = contains(context.properties.containers, 'livenessProbe') && context.properties.containers.livenessProbe != null +var hasReadinessProbe = contains(context.resource.properties.containers, 'readinessProbe') && context.resource.properties.containers.readinessProbe != null +var hasLivenessProbe = contains(context.resource.properties.containers, 'livenessProbe') && context.resource.properties.containers.livenessProbe != null // Get probe port with safe navigation -var readinessProbePort = hasReadinessProbe && contains(context.properties.containers.readinessProbe, 'tcpSocket') && context.properties.containers.readinessProbe.tcpSocket != null && contains(context.properties.containers.readinessProbe.tcpSocket, 'properties') && contains(context.properties.containers.readinessProbe.tcpSocket.properties, 'port') ? context.properties.containers.readinessProbe.tcpSocket.properties.port : 80 -var livenessProbePort = hasLivenessProbe && contains(context.properties.containers.livenessProbe, 'tcpSocket') && context.properties.containers.livenessProbe.tcpSocket != null && contains(context.properties.containers.livenessProbe.tcpSocket, 'properties') && contains(context.properties.containers.livenessProbe.tcpSocket.properties, 'port') ? context.properties.containers.livenessProbe.tcpSocket.properties.port : 80 +var readinessProbePort = hasReadinessProbe && contains(context.resource.properties.containers.readinessProbe, 'tcpSocket') && context.resource.properties.containers.readinessProbe.tcpSocket != null && contains(context.resource.properties.containers.readinessProbe.tcpSocket, 'properties') && contains(context.resource.properties.containers.readinessProbe.tcpSocket.properties, 'port') ? context.resource.properties.containers.readinessProbe.tcpSocket.properties.port : 80 +var livenessProbePort = hasLivenessProbe && contains(context.resource.properties.containers.livenessProbe, 'tcpSocket') && context.resource.properties.containers.livenessProbe.tcpSocket != null && contains(context.resource.properties.containers.livenessProbe.tcpSocket, 'properties') && contains(context.resource.properties.containers.livenessProbe.tcpSocket.properties, 'port') ? context.resource.properties.containers.livenessProbe.tcpSocket.properties.port : 80 // DDoS Protection Plan resource ddosProtectionPlan 'Microsoft.Network/ddosProtectionPlans@2022-07-01' = { @@ -257,9 +257,9 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' = { properties: { protocol: 'Tcp' port: readinessProbePort - intervalInSeconds: context.properties.containers.readinessProbe.?periodSeconds ?? 5 - numberOfProbes: context.properties.containers.readinessProbe.?failureThreshold ?? 3 - probeThreshold: context.properties.containers.readinessProbe.?successThreshold ?? 1 + intervalInSeconds: context.resource.properties.containers.readinessProbe.?periodSeconds ?? 5 + numberOfProbes: context.resource.properties.containers.readinessProbe.?failureThreshold ?? 3 + probeThreshold: context.resource.properties.containers.readinessProbe.?successThreshold ?? 1 } } ] : [], @@ -269,9 +269,9 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' = { properties: { protocol: 'Tcp' port: livenessProbePort - intervalInSeconds: context.properties.containers.livenessProbe.?periodSeconds ?? 10 - numberOfProbes: context.properties.containers.livenessProbe.?failureThreshold ?? 3 - probeThreshold: context.properties.containers.livenessProbe.?successThreshold ?? 1 + intervalInSeconds: context.resource.properties.containers.livenessProbe.?periodSeconds ?? 10 + numberOfProbes: context.resource.properties.containers.livenessProbe.?failureThreshold ?? 3 + probeThreshold: context.resource.properties.containers.livenessProbe.?successThreshold ?? 1 } } ] : [] @@ -340,17 +340,17 @@ resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfil { name: 'web' properties: { - image: context.properties.containers.image + image: context.resource.properties.containers.image ports: [ { - protocol: context.properties.containers.ports != null ? context.properties.containers.ports.additionalProperties.properties.protocol ?? 'TCP' : 'TCP' - port: context.properties.containers.ports != null ? context.properties.containers.ports.additionalProperties.properties.containerPort : 80 + protocol: context.resource.properties.containers.ports != null ? context.resource.properties.containers.ports.additionalProperties.properties.protocol ?? 'TCP' : 'TCP' + port: context.resource.properties.containers.ports != null ? context.resource.properties.containers.ports.additionalProperties.properties.containerPort : 80 } ] resources: { requests: { - memoryInGB: context.properties.containers.resources.?requests.?memoryInMib/1024 ?? json('1.0') - cpu: context.properties.containers.resources.?requests.?cpu ?? json('1.0') + memoryInGB: context.resource.properties.containers.resource.?requests.?memoryInMib/1024 ?? json('1.0') + cpu: context.resource.properties.containers.resource.?requests.?cpu ?? json('1.0') } } volumeMounts: [ @@ -392,7 +392,7 @@ resource nGroups 'Microsoft.ContainerInstance/NGroups@2024-09-01-preview' = { } properties: { elasticProfile: { - desiredCount: context.properties.replicas ?? 2 + desiredCount: context.resource.properties.replicas ?? 2 maintainDesiredCount: maintainDesiredCount } updateProfile: { From 598379b2ff29333601c313b757ec55b24462a525 Mon Sep 17 00:00:00 2001 From: Shivam Gupta Date: Tue, 14 Oct 2025 16:25:29 -0700 Subject: [PATCH 10/12] updated template - fixed context properties and null checks --- .../bicep/ACI-NGroups-LoadBalancer.bicep | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep index 5e1f6f1a..96fe2066 100644 --- a/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep +++ b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep @@ -86,12 +86,13 @@ var resourcePrefix = '/subscriptions/${subscription().subscriptionId}/resourceGr var ddosProtectionPlanName = 'ddosProtectionPlan' // Helper variables for probes -var hasReadinessProbe = contains(context.resource.properties.containers, 'readinessProbe') && context.resource.properties.containers.readinessProbe != null -var hasLivenessProbe = contains(context.resource.properties.containers, 'livenessProbe') && context.resource.properties.containers.livenessProbe != null +var hasReadinessProbe = contains(context.resource.properties.containers, 'readinessProbe') && context.resource.properties.containers.demo.readinessProbe != null +var hasLivenessProbe = contains(context.resource.properties.containers, 'livenessProbe') && context.resource.properties.containers.demo.livenessProbe != null // Get probe port with safe navigation -var readinessProbePort = hasReadinessProbe && contains(context.resource.properties.containers.readinessProbe, 'tcpSocket') && context.resource.properties.containers.readinessProbe.tcpSocket != null && contains(context.resource.properties.containers.readinessProbe.tcpSocket, 'properties') && contains(context.resource.properties.containers.readinessProbe.tcpSocket.properties, 'port') ? context.resource.properties.containers.readinessProbe.tcpSocket.properties.port : 80 -var livenessProbePort = hasLivenessProbe && contains(context.resource.properties.containers.livenessProbe, 'tcpSocket') && context.resource.properties.containers.livenessProbe.tcpSocket != null && contains(context.resource.properties.containers.livenessProbe.tcpSocket, 'properties') && contains(context.resource.properties.containers.livenessProbe.tcpSocket.properties, 'port') ? context.resource.properties.containers.livenessProbe.tcpSocket.properties.port : 80 +var readinessProbePort = hasReadinessProbe && contains(context.resource.properties.containers.demo.readinessProbe, 'tcpSocket') && context.resource.properties.containers.demo.readinessProbe.tcpSocket != null && contains(context.resource.properties.containers.demo.readinessProbe.tcpSocket, 'properties') && contains(context.resource.properties.containers.demo.readinessProbe.tcpSocket.properties, 'port') ? context.resource.properties.containers.demo.readinessProbe.tcpSocket.properties.port : 80 +var livenessProbePort = hasLivenessProbe && contains(context.resource.properties.containers.demo.livenessProbe, 'tcpSocket') && context.resource.properties.containers.demo.livenessProbe.tcpSocket != null && contains(context.resource.properties.containers.demo.livenessProbe.tcpSocket, 'properties') && contains(context.resource.properties.containers.demo.livenessProbe.tcpSocket.properties, 'port') ? context.resource.properties.containers.demo.livenessProbe.tcpSocket.properties.port : 80 + // DDoS Protection Plan resource ddosProtectionPlan 'Microsoft.Network/ddosProtectionPlans@2022-07-01' = { @@ -257,9 +258,9 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' = { properties: { protocol: 'Tcp' port: readinessProbePort - intervalInSeconds: context.resource.properties.containers.readinessProbe.?periodSeconds ?? 5 - numberOfProbes: context.resource.properties.containers.readinessProbe.?failureThreshold ?? 3 - probeThreshold: context.resource.properties.containers.readinessProbe.?successThreshold ?? 1 + intervalInSeconds: context.resource.properties.containers.demo.readinessProbe.?periodSeconds ?? 5 + numberOfProbes: context.resource.properties.containers.demo.readinessProbe.?failureThreshold ?? 3 + probeThreshold: context.resource.properties.containers.demo.readinessProbe.?successThreshold ?? 1 } } ] : [], @@ -269,9 +270,9 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' = { properties: { protocol: 'Tcp' port: livenessProbePort - intervalInSeconds: context.resource.properties.containers.livenessProbe.?periodSeconds ?? 10 - numberOfProbes: context.resource.properties.containers.livenessProbe.?failureThreshold ?? 3 - probeThreshold: context.resource.properties.containers.livenessProbe.?successThreshold ?? 1 + intervalInSeconds: context.resource.properties.containers.demo.livenessProbe.?periodSeconds ?? 10 + numberOfProbes: context.resource.properties.containers.demo.livenessProbe.?failureThreshold ?? 3 + probeThreshold: context.resource.properties.containers.demo.livenessProbe.?successThreshold ?? 1 } } ] : [] @@ -340,22 +341,22 @@ resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfil { name: 'web' properties: { - image: context.resource.properties.containers.image + image: context.resource.properties.containers.demo.image ports: [ { - protocol: context.resource.properties.containers.ports != null ? context.resource.properties.containers.ports.additionalProperties.properties.protocol ?? 'TCP' : 'TCP' - port: context.resource.properties.containers.ports != null ? context.resource.properties.containers.ports.additionalProperties.properties.containerPort : 80 + protocol: context.resource.properties.containers.demo.ports.?http.?protocol ?? 'TCP' + port: context.resource.properties.containers.demo.ports.?http.?containerPort ?? 80 } ] resources: { requests: { - memoryInGB: context.resource.properties.containers.resource.?requests.?memoryInMib/1024 ?? json('1.0') - cpu: context.resource.properties.containers.resource.?requests.?cpu ?? json('1.0') + memoryInGB: context.resource.properties.containers.demo.?resources.?requests.?memoryInMib != null ? context.resource.properties.containers.demo.?resources.?requests.?memoryInMib /1024 : json('1.0') + cpu: context.resource.properties.containers.demo.?resources.?requests.?cpu ?? json('1.0') } } volumeMounts: [ { - name: 'cacheVolume' + name: 'cachevolume' mountPath: '/mnt/cache' // ephemeral volume path in container filesystem } ] @@ -364,7 +365,7 @@ resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfil ] volumes: [ { - name: 'cacheVolume' + name: 'cachevolume' emptyDir: {} // ephemeral volume } ] @@ -373,7 +374,7 @@ resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfil ports: [ { protocol: 'TCP' - port: 80 + port: context.resource.properties.containers.demo.ports.?http.?containerPort ?? 80 } ] type: 'Private' @@ -392,7 +393,7 @@ resource nGroups 'Microsoft.ContainerInstance/NGroups@2024-09-01-preview' = { } properties: { elasticProfile: { - desiredCount: context.resource.properties.replicas ?? 2 + desiredCount: context.resource.?properties.?replicas ?? 2 maintainDesiredCount: maintainDesiredCount } updateProfile: { From e961656a7bd169beeb3b2521e3a7b860840e8001 Mon Sep 17 00:00:00 2001 From: Shivam Gupta Date: Thu, 16 Oct 2025 11:52:18 -0700 Subject: [PATCH 11/12] fixed LB --- .../bicep/ACI-NGroups-LoadBalancer.bicep | 63 +++++++++---------- 1 file changed, 30 insertions(+), 33 deletions(-) diff --git a/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep index 96fe2066..7066b99d 100644 --- a/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep +++ b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep @@ -90,8 +90,8 @@ var hasReadinessProbe = contains(context.resource.properties.containers, 'readin var hasLivenessProbe = contains(context.resource.properties.containers, 'livenessProbe') && context.resource.properties.containers.demo.livenessProbe != null // Get probe port with safe navigation -var readinessProbePort = hasReadinessProbe && contains(context.resource.properties.containers.demo.readinessProbe, 'tcpSocket') && context.resource.properties.containers.demo.readinessProbe.tcpSocket != null && contains(context.resource.properties.containers.demo.readinessProbe.tcpSocket, 'properties') && contains(context.resource.properties.containers.demo.readinessProbe.tcpSocket.properties, 'port') ? context.resource.properties.containers.demo.readinessProbe.tcpSocket.properties.port : 80 -var livenessProbePort = hasLivenessProbe && contains(context.resource.properties.containers.demo.livenessProbe, 'tcpSocket') && context.resource.properties.containers.demo.livenessProbe.tcpSocket != null && contains(context.resource.properties.containers.demo.livenessProbe.tcpSocket, 'properties') && contains(context.resource.properties.containers.demo.livenessProbe.tcpSocket.properties, 'port') ? context.resource.properties.containers.demo.livenessProbe.tcpSocket.properties.port : 80 +// var readinessProbePort = context.resource.properties.containers.?demo.?readinessProbe.?tcpSocket.?properties.?port ?? 80 +var livenessProbePort = context.resource.properties.containers.?demo.?livenessProbe.?tcpSocket.?properties.?port ?? 80 // DDoS Protection Plan @@ -122,6 +122,22 @@ resource networkSecurityGroup 'Microsoft.Network/networkSecurityGroups@2022-07-0 sourcePortRange: '*' } } + { + name: 'AzureCloudInbound' + properties: { + access: 'Allow' + description: 'Allow Azure Cloud traffic on port range' + destinationAddressPrefix: '*' + destinationPortRanges: [ + '3000' + ] + direction: 'Inbound' + protocol: '*' + priority: 110 + sourceAddressPrefix: 'AzureCloud' + sourcePortRange: '*' + } + } ] } } @@ -252,18 +268,18 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' = { } ] probes: union( - hasReadinessProbe ? [ + [ { name: 'readinessProbe' properties: { protocol: 'Tcp' - port: readinessProbePort - intervalInSeconds: context.resource.properties.containers.demo.readinessProbe.?periodSeconds ?? 5 - numberOfProbes: context.resource.properties.containers.demo.readinessProbe.?failureThreshold ?? 3 - probeThreshold: context.resource.properties.containers.demo.readinessProbe.?successThreshold ?? 1 + port: context.resource.properties.containers.demo.ports.?http.?containerPort ?? 80 + intervalInSeconds: context.resource.properties.containers.?demo.?readinessProbe.?periodSeconds ?? 5 + numberOfProbes: context.resource.properties.containers.?demo.?readinessProbe.?failureThreshold ?? 1 + probeThreshold: context.resource.properties.containers.?demo.?readinessProbe.?successThreshold ?? 1 } } - ] : [], + ], hasLivenessProbe ? [ { name: 'livenessProbe' @@ -284,45 +300,26 @@ resource loadBalancer 'Microsoft.Network/loadBalancers@2022-07-01' = { frontendIPConfiguration: { id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', loadBalancerName, frontendIPName) } - frontendPort: 80 - backendPort: 80 + frontendPort: context.resource.properties.containers.demo.ports.?http.?containerPort ?? 80 + backendPort: context.resource.properties.containers.demo.ports.?http.?containerPort ?? 80 enableFloatingIP: false - idleTimeoutInMinutes: 15 + idleTimeoutInMinutes: 5 protocol: 'Tcp' enableTcpReset: true loadDistribution: 'Default' - disableOutboundSnat: false + disableOutboundSnat: true backendAddressPools: [ { id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', loadBalancerName, backendAddressPoolName) } ] - probe: hasReadinessProbe ? { + probe: { id: resourceId('Microsoft.Network/loadBalancers/probes', loadBalancerName, 'readinessProbe') - } : null - } - } - ] - inboundNatRules: [ - { - name: inboundNatRuleName - properties: { - backendAddressPool: { - id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', loadBalancerName, backendAddressPoolName) } - backendPort: 80 - enableFloatingIP: false - enableTcpReset: false - frontendIPConfiguration: { - id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', loadBalancerName, frontendIPName) - } - frontendPortRangeEnd: 331 - frontendPortRangeStart: 81 - idleTimeoutInMinutes: 4 - protocol: 'Tcp' } } ] + inboundNatRules: [] outboundRules: [] inboundNatPools: [] } From 366e7f605780c51044170d0a27a771f6e4614316 Mon Sep 17 00:00:00 2001 From: Shivam Gupta Date: Fri, 17 Oct 2025 10:41:44 -0700 Subject: [PATCH 12/12] added UserAssignedIdentity and redis cache in app.bicep --- .../bicep/ACI-NGroups-LoadBalancer.bicep | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep index 7066b99d..a82cf072 100644 --- a/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep +++ b/recipe-packs/bicep/ACI-NGroups-LoadBalancer.bicep @@ -70,6 +70,10 @@ param maintainDesiredCount bool = true @maxLength(64) param inboundNatRuleName string = 'inboundNatRule' +@description('User Assigned Identity name') +@maxLength(64) +param userAssignedIdentityName string = 'uai_1' + @description('Radius ACI Container Context') param context object @@ -93,6 +97,21 @@ var hasLivenessProbe = contains(context.resource.properties.containers, 'livenes // var readinessProbePort = context.resource.properties.containers.?demo.?readinessProbe.?tcpSocket.?properties.?port ?? 80 var livenessProbePort = context.resource.properties.containers.?demo.?livenessProbe.?tcpSocket.?properties.?port ?? 80 +// Check if there's a Redis connection configured +var resourceProperties = context.resource.properties ?? {} +var connectionsConfig = resourceProperties.connections ?? {} +var hasRedisConnection = contains(connectionsConfig, 'redis') +// Get Redis connection outputs if available +var resourceConnections = context.resource.connections ?? {} +var redisOutputs = hasRedisConnection && contains(resourceConnections, 'redis') ? resourceConnections.redis : {} +var redisHost = hasRedisConnection && contains(redisOutputs, 'host') ? redisOutputs.host : '' +var redisPort = hasRedisConnection && contains(redisOutputs, 'port') ? redisOutputs.port : 0 + +// User Assigned Managed Identity +resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { + name: userAssignedIdentityName + location: resourceGroup().location +} // DDoS Protection Plan resource ddosProtectionPlan 'Microsoft.Network/ddosProtectionPlans@2022-07-01' = { @@ -339,6 +358,20 @@ resource containerGroupProfile 'Microsoft.ContainerInstance/containerGroupProfil name: 'web' properties: { image: context.resource.properties.containers.demo.image + environmentVariables: [ + { + name: 'CONNECTION_REDIS_HOST' + value: redisHost + } + { + name: 'CONNECTION_REDIS_PORT' + value: redisPort + } + { + name: 'CONNECTION_REDIS_CONNECTIONSTRING' + secureValue: 'redis://${redisHost}:${redisPort},abortConnect=False' + } + ] ports: [ { protocol: context.resource.properties.containers.demo.ports.?http.?protocol ?? 'TCP' @@ -386,7 +419,10 @@ resource nGroups 'Microsoft.ContainerInstance/NGroups@2024-09-01-preview' = { location: resourceGroup().location zones: zones identity: { - type: 'SystemAssigned' + type: 'UserAssigned' + userAssignedIdentities: { + '${resourcePrefix}Microsoft.ManagedIdentity/userAssignedIdentities/${userAssignedIdentityName}': {} + } } properties: { elasticProfile: { @@ -432,6 +468,7 @@ resource nGroups 'Microsoft.ContainerInstance/NGroups@2024-09-01-preview' = { containerGroupProfile loadBalancer virtualNetwork + userAssignedIdentity ] } @@ -451,3 +488,6 @@ output containerGroupProfileId string = containerGroupProfile.id output nGroupsId string = nGroups.id output readinessProbeId string = hasReadinessProbe ? resourceId('Microsoft.Network/loadBalancers/probes', loadBalancerName, 'readinessProbe') : '' output livenessProbeId string = hasLivenessProbe ? resourceId('Microsoft.Network/loadBalancers/probes', loadBalancerName, 'livenessProbe') : '' +output userAssignedIdentityId string = userAssignedIdentity.id +output userAssignedIdentityClientId string = userAssignedIdentity.properties.clientId +output userAssignedIdentityPrincipalId string = userAssignedIdentity.properties.principalId