diff --git a/avm/res/app/container-app/README.md b/avm/res/app/container-app/README.md index 8fb2b89697..e07c1f8f48 100644 --- a/avm/res/app/container-app/README.md +++ b/avm/res/app/container-app/README.md @@ -59,8 +59,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { ] environmentResourceId: '' name: 'acamin001' - // Non-required parameters - location: '' } } ``` @@ -95,10 +93,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { }, "name": { "value": "acamin001" - }, - // Non-required parameters - "location": { - "value": "" } } } @@ -127,8 +121,6 @@ param containers = [ ] param environmentResourceId = '' param name = 'acamin001' -// Non-required parameters -param location = '' ``` @@ -162,7 +154,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { name: 'acapriv001' // Non-required parameters disableIngress: true - location: '' } } ``` @@ -201,9 +192,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { // Non-required parameters "disableIngress": { "value": true - }, - "location": { - "value": "" } } } @@ -234,7 +222,6 @@ param environmentResourceId = '' param name = 'acapriv001' // Non-required parameters param disableIngress = true -param location = '' ``` @@ -335,19 +322,17 @@ module containerApp 'br/public:avm/res/app/container-app:' = { ] } } - secrets: { - secureList: [ - { - name: 'containerappstoredsecret' - value: '' - } - { - identity: '' - keyVaultUrl: '' - name: 'keyvaultstoredsecret' - } - ] - } + secrets: [ + { + name: 'containerappstoredsecret' + value: '' + } + { + identity: '' + keyVaultUrl: '' + name: 'keyvaultstoredsecret' + } + ] tags: { Env: 'test' 'hidden-title': 'This is visible in the resource name' @@ -467,19 +452,17 @@ module containerApp 'br/public:avm/res/app/container-app:' = { } }, "secrets": { - "value": { - "secureList": [ - { - "name": "containerappstoredsecret", - "value": "" - }, - { - "identity": "", - "keyVaultUrl": "", - "name": "keyvaultstoredsecret" - } - ] - } + "value": [ + { + "name": "containerappstoredsecret", + "value": "" + }, + { + "identity": "", + "keyVaultUrl": "", + "name": "keyvaultstoredsecret" + } + ] }, "tags": { "value": { @@ -583,19 +566,17 @@ param runtime = { ] } } -param secrets = { - secureList: [ - { - name: 'containerappstoredsecret' - value: '' - } - { - identity: '' - keyVaultUrl: '' - name: 'keyvaultstoredsecret' - } - ] -} +param secrets = [ + { + name: 'containerappstoredsecret' + value: '' + } + { + identity: '' + keyVaultUrl: '' + name: 'keyvaultstoredsecret' + } +] param tags = { Env: 'test' 'hidden-title': 'This is visible in the resource name' @@ -643,7 +624,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { ingressExternal: false ingressTargetPort: 80 ingressTransport: 'tcp' - location: '' } } ``` @@ -700,9 +680,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { }, "ingressTransport": { "value": "tcp" - }, - "location": { - "value": "" } } } @@ -743,7 +720,6 @@ param ingressAllowInsecure = false param ingressExternal = false param ingressTargetPort = 80 param ingressTransport = 'tcp' -param location = '' ``` @@ -795,11 +771,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { // Non-required parameters ingressAllowInsecure: false ingressExternal: false - location: '' - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } managedIdentities: { userAssignedResourceIds: [ '' @@ -868,15 +839,6 @@ module containerApp 'br/public:avm/res/app/container-app:' = { "ingressExternal": { "value": false }, - "location": { - "value": "" - }, - "lock": { - "value": { - "kind": "CanNotDelete", - "name": "myCustomLockName" - } - }, "managedIdentities": { "value": { "userAssignedResourceIds": [ @@ -937,11 +899,6 @@ param name = 'acawaf001' // Non-required parameters param ingressAllowInsecure = false param ingressExternal = false -param location = '' -param lock = { - kind: 'CanNotDelete' - name: 'myCustomLockName' -} param managedIdentities = { userAssignedResourceIds: [ '' @@ -997,7 +954,7 @@ param tags = { | [`scaleMaxReplicas`](#parameter-scalemaxreplicas) | int | Maximum number of container replicas. Defaults to 10 if not set. | | [`scaleMinReplicas`](#parameter-scaleminreplicas) | int | Minimum number of container replicas. Defaults to 3 if not set. | | [`scaleRules`](#parameter-scalerules) | array | Scaling rules. | -| [`secrets`](#parameter-secrets) | secureObject | The secrets of the Container App. | +| [`secrets`](#parameter-secrets) | array | The secrets of the Container App. | | [`service`](#parameter-service) | object | Dev ContainerApp service type. | | [`serviceBinds`](#parameter-servicebinds) | array | List of container app services bound to the app. | | [`stickySessionsAffinity`](#parameter-stickysessionsaffinity) | string | Bool indicating if the Container App should enable session affinity. | @@ -1970,8 +1927,49 @@ Scaling rules. The secrets of the Container App. - Required: No -- Type: secureObject -- Default: `{}` +- Type: array + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`keyVaultUrl`](#parameter-secretskeyvaulturl) | string | Azure Key Vault URL pointing to the secret referenced by the Container App Job. Required if `value` is null. | +| [`value`](#parameter-secretsvalue) | securestring | The secret value, if not fetched from Key Vault. Required if `keyVaultUrl` is not null. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`identity`](#parameter-secretsidentity) | string | Resource ID of a managed identity to authenticate with Azure Key Vault, or System to use a system-assigned identity. | +| [`name`](#parameter-secretsname) | string | The name of the secret. | + +### Parameter: `secrets.keyVaultUrl` + +Azure Key Vault URL pointing to the secret referenced by the Container App Job. Required if `value` is null. + +- Required: No +- Type: string + +### Parameter: `secrets.value` + +The secret value, if not fetched from Key Vault. Required if `keyVaultUrl` is not null. + +- Required: No +- Type: securestring + +### Parameter: `secrets.identity` + +Resource ID of a managed identity to authenticate with Azure Key Vault, or System to use a system-assigned identity. + +- Required: No +- Type: string + +### Parameter: `secrets.name` + +The name of the secret. + +- Required: No +- Type: string ### Parameter: `service` diff --git a/avm/res/app/container-app/main.bicep b/avm/res/app/container-app/main.bicep index 201ba8da5b..3f6e2fcd83 100644 --- a/avm/res/app/container-app/main.bicep +++ b/avm/res/app/container-app/main.bicep @@ -135,8 +135,7 @@ param containers containerType[] param initContainersTemplate array = [] @description('Optional. The secrets of the Container App.') -@secure() -param secrets object = {} +param secrets secretType[]? @description('Optional. User friendly suffix that is appended to the revision name.') param revisionSuffix string = '' @@ -147,8 +146,6 @@ param volumes array = [] @description('Optional. Workload profile name to pin for container app execution.') param workloadProfileName string = '' -var secretList = !empty(secrets) ? secrets.secureList : [] - var formattedUserAssignedIdentities = reduce( map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), {}, @@ -261,20 +258,24 @@ resource containerApp 'Microsoft.App/containerApps@2024-10-02-preview' = { service: (includeAddOns && !empty(service)) ? service : null maxInactiveRevisions: maxInactiveRevisions registries: !empty(registries) ? registries : null - secrets: secretList + secrets: secrets runtime: { - dotnet: !empty(runtime.?dotnet) ? { - autoConfigureDataProtection: runtime.?dotnet.autoConfigureDataProtection - } : null - java: !empty(runtime.?java) ? { - enableMetrics: runtime.?java.enableMetrics - javaAgent: { - enabled: runtime.?java.enableJavaAgent - logging: { - loggerSettings: runtime.?java.?loggerSettings + dotnet: !empty(runtime.?dotnet) + ? { + autoConfigureDataProtection: runtime.?dotnet.autoConfigureDataProtection } - } - } : null + : null + java: !empty(runtime.?java) + ? { + enableMetrics: runtime.?java.enableMetrics + javaAgent: { + enabled: runtime.?java.enableJavaAgent + logging: { + loggerSettings: runtime.?java.?loggerSettings + } + } + } + : null } } template: { @@ -544,3 +545,20 @@ type runtimeType = { }[]? }? }? + +@export() +@description('The type for a secret.') +type secretType = { + @description('Optional. Resource ID of a managed identity to authenticate with Azure Key Vault, or System to use a system-assigned identity.') + identity: string? + + @description('Conditional. Azure Key Vault URL pointing to the secret referenced by the Container App Job. Required if `value` is null.') + keyVaultUrl: string? + + @description('Optional. The name of the secret.') + name: string? + + @description('Conditional. The secret value, if not fetched from Key Vault. Required if `keyVaultUrl` is not null.') + @secure() + value: string? +} diff --git a/avm/res/app/container-app/main.json b/avm/res/app/container-app/main.json index 2cf631732b..88cf79e58a 100644 --- a/avm/res/app/container-app/main.json +++ b/avm/res/app/container-app/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.33.13.18514", - "templateHash": "10576304396294722473" + "templateHash": "7075159430454310469" }, "name": "Container Apps", "description": "This module deploys a Container App." @@ -512,6 +512,43 @@ "description": "Optional. App runtime configuration for the Container App." } }, + "secretType": { + "type": "object", + "properties": { + "identity": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Resource ID of a managed identity to authenticate with Azure Key Vault, or System to use a system-assigned identity." + } + }, + "keyVaultUrl": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Azure Key Vault URL pointing to the secret referenced by the Container App Job. Required if `value` is null." + } + }, + "name": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The name of the secret." + } + }, + "value": { + "type": "securestring", + "nullable": true, + "metadata": { + "description": "Conditional. The secret value, if not fetched from Key Vault. Required if `keyVaultUrl` is not null." + } + } + }, + "metadata": { + "__bicep_export!": true, + "description": "The type for a secret." + } + }, "lockType": { "type": "object", "properties": { @@ -934,8 +971,11 @@ } }, "secrets": { - "type": "secureObject", - "defaultValue": {}, + "type": "array", + "items": { + "$ref": "#/definitions/secretType" + }, + "nullable": true, "metadata": { "description": "Optional. The secrets of the Container App." } @@ -970,7 +1010,6 @@ "input": "[union(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')], createObject('roleDefinitionId', coalesce(tryGet(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName), if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex('formattedRoleAssignments')].roleDefinitionIdOrName)))))]" } ], - "secretList": "[if(not(empty(parameters('secrets'))), parameters('secrets').secureList, createArray())]", "formattedUserAssignedIdentities": "[reduce(map(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createArray()), lambda('id', createObject(format('{0}', lambdaVariables('id')), createObject()))), createObject(), lambda('cur', 'next', union(lambdaVariables('cur'), lambdaVariables('next'))))]", "identity": "[if(not(empty(parameters('managedIdentities'))), createObject('type', if(coalesce(tryGet(parameters('managedIdentities'), 'systemAssigned'), false()), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'SystemAssigned,UserAssigned', 'SystemAssigned'), if(not(empty(coalesce(tryGet(parameters('managedIdentities'), 'userAssignedResourceIds'), createObject()))), 'UserAssigned', 'None')), 'userAssignedIdentities', if(not(empty(variables('formattedUserAssignedIdentities'))), variables('formattedUserAssignedIdentities'), null())), null())]", "builtInRoleNames": { @@ -1019,7 +1058,7 @@ "service": "[if(and(parameters('includeAddOns'), not(empty(parameters('service')))), parameters('service'), null())]", "maxInactiveRevisions": "[parameters('maxInactiveRevisions')]", "registries": "[if(not(empty(parameters('registries'))), parameters('registries'), null())]", - "secrets": "[variables('secretList')]", + "secrets": "[parameters('secrets')]", "runtime": { "dotnet": "[if(not(empty(tryGet(parameters('runtime'), 'dotnet'))), createObject('autoConfigureDataProtection', tryGet(parameters('runtime'), 'dotnet', 'autoConfigureDataProtection')), null())]", "java": "[if(not(empty(tryGet(parameters('runtime'), 'java'))), createObject('enableMetrics', tryGet(parameters('runtime'), 'java', 'enableMetrics'), 'javaAgent', createObject('enabled', tryGet(parameters('runtime'), 'java', 'enableJavaAgent'), 'logging', createObject('loggerSettings', tryGet(tryGet(parameters('runtime'), 'java'), 'loggerSettings')))), null())]" diff --git a/avm/res/app/container-app/tests/e2e/defaults/main.test.bicep b/avm/res/app/container-app/tests/e2e/defaults/main.test.bicep index c534e47cb3..bfc7880b4a 100644 --- a/avm/res/app/container-app/tests/e2e/defaults/main.test.bicep +++ b/avm/res/app/container-app/tests/e2e/defaults/main.test.bicep @@ -52,7 +52,6 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId - location: resourceLocation containers: [ { name: 'simple-hello-world-container' @@ -65,8 +64,5 @@ module testDeployment '../../../main.bicep' = [ } ] } - dependsOn: [ - nestedDependencies - ] } ] diff --git a/avm/res/app/container-app/tests/e2e/disable-ingress/main.test.bicep b/avm/res/app/container-app/tests/e2e/disable-ingress/main.test.bicep index ffe04696b9..694d877bab 100644 --- a/avm/res/app/container-app/tests/e2e/disable-ingress/main.test.bicep +++ b/avm/res/app/container-app/tests/e2e/disable-ingress/main.test.bicep @@ -52,7 +52,6 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId - location: resourceLocation disableIngress: true containers: [ { diff --git a/avm/res/app/container-app/tests/e2e/max/main.test.bicep b/avm/res/app/container-app/tests/e2e/max/main.test.bicep index b2b2d87fd4..67a05f663f 100644 --- a/avm/res/app/container-app/tests/e2e/max/main.test.bicep +++ b/avm/res/app/container-app/tests/e2e/max/main.test.bicep @@ -95,19 +95,17 @@ module testDeployment '../../../main.bicep' = [ nestedDependencies.outputs.managedIdentityResourceId ] } - secrets: { - secureList: [ - { - name: 'containerappstoredsecret' - value: myCustomContainerAppSecret - } - { - name: 'keyvaultstoredsecret' - keyVaultUrl: nestedDependencies.outputs.keyVaultSecretURI - identity: nestedDependencies.outputs.managedIdentityResourceId - } - ] - } + secrets: [ + { + name: 'containerappstoredsecret' + value: myCustomContainerAppSecret + } + { + name: 'keyvaultstoredsecret' + keyVaultUrl: nestedDependencies.outputs.keyVaultSecretURI + identity: nestedDependencies.outputs.managedIdentityResourceId + } + ] containers: [ { name: 'simple-hello-world-container' @@ -150,7 +148,7 @@ module testDeployment '../../../main.bicep' = [ java: { enableJavaAgent: true enableMetrics: false - loggerSettings:[ + loggerSettings: [ { level: 'info' logger: 'test' diff --git a/avm/res/app/container-app/tests/e2e/vnet/main.test.bicep b/avm/res/app/container-app/tests/e2e/vnet/main.test.bicep index 3c34893f8c..dd909e518e 100644 --- a/avm/res/app/container-app/tests/e2e/vnet/main.test.bicep +++ b/avm/res/app/container-app/tests/e2e/vnet/main.test.bicep @@ -52,7 +52,6 @@ module testDeployment '../../../main.bicep' = [ params: { name: '${namePrefix}${serviceShort}001' environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId - location: resourceLocation ingressExternal: false ingressTransport: 'tcp' ingressAllowInsecure: false diff --git a/avm/res/app/container-app/tests/e2e/waf-aligned/main.test.bicep b/avm/res/app/container-app/tests/e2e/waf-aligned/main.test.bicep index a25b1ec60d..0bbe6788df 100644 --- a/avm/res/app/container-app/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/app/container-app/tests/e2e/waf-aligned/main.test.bicep @@ -59,11 +59,6 @@ module testDeployment '../../../main.bicep' = [ Env: 'test' } environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId - location: resourceLocation - lock: { - kind: 'CanNotDelete' - name: 'myCustomLockName' - } managedIdentities: { userAssignedResourceIds: [ nestedDependencies.outputs.managedIdentityResourceId @@ -98,8 +93,5 @@ module testDeployment '../../../main.bicep' = [ } ] } - dependsOn: [ - nestedDependencies - ] } ] diff --git a/avm/res/app/container-app/version.json b/avm/res/app/container-app/version.json index 15548e8d1a..7466cbe674 100644 --- a/avm/res/app/container-app/version.json +++ b/avm/res/app/container-app/version.json @@ -1,6 +1,6 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.12", + "version": "0.13", "pathFilters": [ "./main.json" ]