diff --git a/avm/res/app/job/README.md b/avm/res/app/job/README.md index ccca0ee810..d4bd78a58a 100644 --- a/avm/res/app/job/README.md +++ b/avm/res/app/job/README.md @@ -15,7 +15,7 @@ This module deploys a Container App Job. | Resource Type | API Version | | :-- | :-- | -| `Microsoft.App/jobs` | [2023-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/2023-05-01/jobs) | +| `Microsoft.App/jobs` | [2024-03-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.App/jobs) | | `Microsoft.Authorization/locks` | [2020-05-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-05-01/locks) | | `Microsoft.Authorization/roleAssignments` | [2022-04-01](https://learn.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2022-04-01/roleAssignments) | @@ -50,7 +50,7 @@ module job 'br/public:avm/res/app/job:' = { image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' name: 'simple-hello-world-container' resources: { - cpu: '' + cpu: '0.25' memory: '0.5Gi' } } @@ -60,10 +60,7 @@ module job 'br/public:avm/res/app/job:' = { triggerType: 'Manual' // Non-required parameters location: '' - manualTriggerConfig: { - parallelism: 1 - replicaCompletionCount: 1 - } + manualTriggerConfig: {} } } ``` @@ -87,7 +84,7 @@ module job 'br/public:avm/res/app/job:' = { "image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest", "name": "simple-hello-world-container", "resources": { - "cpu": "", + "cpu": "0.25", "memory": "0.5Gi" } } @@ -107,10 +104,7 @@ module job 'br/public:avm/res/app/job:' = { "value": "" }, "manualTriggerConfig": { - "value": { - "parallelism": 1, - "replicaCompletionCount": 1 - } + "value": {} } } } @@ -135,6 +129,16 @@ module job 'br/public:avm/res/app/job:' = { // Required parameters containers: [ { + env: [ + { + name: 'AZURE_STORAGE_QUEUE_NAME' + value: '' + } + { + name: 'AZURE_STORAGE_CONNECTION_STRING' + secretRef: 'connection-string' + } + ] image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' name: 'simple-hello-world-container' probes: [ @@ -155,15 +159,66 @@ module job 'br/public:avm/res/app/job:' = { } ] resources: { - cpu: '' - memory: '0.5Gi' + cpu: '1.25' + memory: '1.5Gi' } + volumeMounts: [ + { + mountPath: '/mnt/data' + volumeName: 'ajmaxemptydir' + } + ] + } + { + args: [ + 'arg1' + 'arg2' + ] + command: [ + '-c' + '/bin/bash' + 'echo hello' + 'sleep 100000' + ] + env: [ + { + name: 'SOME_ENV_VAR' + value: 'some-value' + } + ] + image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + name: 'second-simple-container' } ] environmentResourceId: '' name: 'ajmax001' - triggerType: 'Manual' + triggerType: 'Event' // Non-required parameters + eventTriggerConfig: { + parallelism: 1 + replicaCompletionCount: 1 + scale: { + maxExecutions: 1 + minExecutions: 1 + pollingInterval: 55 + rules: [ + { + auth: [ + { + secretRef: 'connectionString' + triggerParameter: 'connection' + } + ] + metadata: { + queueName: '' + storageAccountResourceId: '' + } + name: 'queue' + type: 'azure-queue' + } + ] + } + } location: '' lock: { kind: 'CanNotDelete' @@ -175,10 +230,6 @@ module job 'br/public:avm/res/app/job:' = { '' ] } - manualTriggerConfig: { - parallelism: 1 - replicaCompletionCount: 1 - } roleAssignments: [ { principalId: '' @@ -196,18 +247,22 @@ module job 'br/public:avm/res/app/job:' = { roleDefinitionIdOrName: '' } ] - secrets: { - secureList: [ - { - name: 'customtest' - value: '' - } - ] - } + secrets: [ + { + name: 'connection-string' + value: '' + } + ] tags: { Env: 'test' 'hidden-title': 'This is visible in the resource name' } + volumes: [ + { + name: 'ajmaxemptydir' + storageType: 'EmptyDir' + } + ] workloadProfileName: '' } } @@ -229,6 +284,16 @@ module job 'br/public:avm/res/app/job:' = { "containers": { "value": [ { + "env": [ + { + "name": "AZURE_STORAGE_QUEUE_NAME", + "value": "" + }, + { + "name": "AZURE_STORAGE_CONNECTION_STRING", + "secretRef": "connection-string" + } + ], "image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest", "name": "simple-hello-world-container", "probes": [ @@ -249,9 +314,35 @@ module job 'br/public:avm/res/app/job:' = { } ], "resources": { - "cpu": "", - "memory": "0.5Gi" - } + "cpu": "1.25", + "memory": "1.5Gi" + }, + "volumeMounts": [ + { + "mountPath": "/mnt/data", + "volumeName": "ajmaxemptydir" + } + ] + }, + { + "args": [ + "arg1", + "arg2" + ], + "command": [ + "-c", + "/bin/bash", + "echo hello", + "sleep 100000" + ], + "env": [ + { + "name": "SOME_ENV_VAR", + "value": "some-value" + } + ], + "image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest", + "name": "second-simple-container" } ] }, @@ -262,9 +353,36 @@ module job 'br/public:avm/res/app/job:' = { "value": "ajmax001" }, "triggerType": { - "value": "Manual" + "value": "Event" }, // Non-required parameters + "eventTriggerConfig": { + "value": { + "parallelism": 1, + "replicaCompletionCount": 1, + "scale": { + "maxExecutions": 1, + "minExecutions": 1, + "pollingInterval": 55, + "rules": [ + { + "auth": [ + { + "secretRef": "connectionString", + "triggerParameter": "connection" + } + ], + "metadata": { + "queueName": "", + "storageAccountResourceId": "" + }, + "name": "queue", + "type": "azure-queue" + } + ] + } + } + }, "location": { "value": "" }, @@ -282,12 +400,6 @@ module job 'br/public:avm/res/app/job:' = { ] } }, - "manualTriggerConfig": { - "value": { - "parallelism": 1, - "replicaCompletionCount": 1 - } - }, "roleAssignments": { "value": [ { @@ -308,14 +420,12 @@ module job 'br/public:avm/res/app/job:' = { ] }, "secrets": { - "value": { - "secureList": [ - { - "name": "customtest", - "value": "" - } - ] - } + "value": [ + { + "name": "connection-string", + "value": "" + } + ] }, "tags": { "value": { @@ -323,6 +433,14 @@ module job 'br/public:avm/res/app/job:' = { "hidden-title": "This is visible in the resource name" } }, + "volumes": { + "value": [ + { + "name": "ajmaxemptydir", + "storageType": "EmptyDir" + } + ] + }, "workloadProfileName": { "value": "" } @@ -369,19 +487,18 @@ module job 'br/public:avm/res/app/job:' = { } ] resources: { - cpu: '' + cpu: '0.25' memory: '0.5Gi' } } ] environmentResourceId: '' name: 'ajwaf001' - triggerType: 'Manual' + triggerType: 'Schedule' // Non-required parameters location: '' - manualTriggerConfig: { - parallelism: 1 - replicaCompletionCount: 1 + scheduleTriggerConfig: { + cronExpression: '0 0 * * *' } tags: { Env: 'test' @@ -428,7 +545,7 @@ module job 'br/public:avm/res/app/job:' = { } ], "resources": { - "cpu": "", + "cpu": "0.25", "memory": "0.5Gi" } } @@ -441,16 +558,15 @@ module job 'br/public:avm/res/app/job:' = { "value": "ajwaf001" }, "triggerType": { - "value": "Manual" + "value": "Schedule" }, // Non-required parameters "location": { "value": "" }, - "manualTriggerConfig": { + "scheduleTriggerConfig": { "value": { - "parallelism": 1, - "replicaCompletionCount": 1 + "cronExpression": "0 0 * * *" } }, "tags": { @@ -477,27 +593,32 @@ module job 'br/public:avm/res/app/job:' = { | Parameter | Type | Description | | :-- | :-- | :-- | | [`containers`](#parameter-containers) | array | List of container definitions for the Container App. | -| [`environmentResourceId`](#parameter-environmentresourceid) | string | Resource ID of environment. | +| [`environmentResourceId`](#parameter-environmentresourceid) | string | Resource ID of Container Apps Environment. | | [`name`](#parameter-name) | string | Name of the Container App. | | [`triggerType`](#parameter-triggertype) | string | Trigger type of the job. | +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`eventTriggerConfig`](#parameter-eventtriggerconfig) | object | Configuration of an event driven job. Required if `TriggerType` is `Event`. | +| [`manualTriggerConfig`](#parameter-manualtriggerconfig) | object | Configuration of a manually triggered job. Required if `TriggerType` is `Manual`. | +| [`scheduleTriggerConfig`](#parameter-scheduletriggerconfig) | object | Configuration of a schedule based job. Required if `TriggerType` is `Schedule`. | + **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | | [`enableTelemetry`](#parameter-enabletelemetry) | bool | Enable/Disable usage telemetry for module. | -| [`eventTriggerConfig`](#parameter-eventtriggerconfig) | object | Required if TriggerType is Event. Configuration of an event driven job. | -| [`initContainersTemplate`](#parameter-initcontainerstemplate) | array | List of specialized containers that run before app containers. | -| [`location`](#parameter-location) | string | Location for all Resources. | +| [`initContainers`](#parameter-initcontainers) | array | List of specialized containers that run before app containers. | +| [`location`](#parameter-location) | string | Location for all Resources. Defaults to the location of the Resource Group. | | [`lock`](#parameter-lock) | object | The lock settings of the service. | | [`managedIdentities`](#parameter-managedidentities) | object | The managed identity definition for this resource. | -| [`manualTriggerConfig`](#parameter-manualtriggerconfig) | object | Required if TriggerType is Manual. Configuration of a manual job. | | [`registries`](#parameter-registries) | array | Collection of private container registry credentials for containers used by the Container app. | | [`replicaRetryLimit`](#parameter-replicaretrylimit) | int | The maximum number of times a replica can be retried. | | [`replicaTimeout`](#parameter-replicatimeout) | int | Maximum number of seconds a replica is allowed to run. | | [`roleAssignments`](#parameter-roleassignments) | array | Array of role assignments to create. | -| [`scheduleTriggerConfig`](#parameter-scheduletriggerconfig) | object | Required if TriggerType is Schedule. Configuration of a schedule based job. | -| [`secrets`](#parameter-secrets) | secureObject | The secrets of the Container App. | +| [`secrets`](#parameter-secrets) | array | The secrets of the Container App. | | [`tags`](#parameter-tags) | object | Tags of the resource. | | [`volumes`](#parameter-volumes) | array | List of volume definitions for the Container App. | | [`workloadProfileName`](#parameter-workloadprofilename) | string | The name of the workload profile to use. | @@ -509,203 +630,968 @@ List of container definitions for the Container App. - Required: Yes - Type: array -### Parameter: `environmentResourceId` +**Required parameters** -Resource ID of environment. +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`image`](#parameter-containersimage) | string | The image of the container. | +| [`name`](#parameter-containersname) | string | The name of the container. | -- Required: Yes -- Type: string +**Optional parameters** -### Parameter: `name` +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`args`](#parameter-containersargs) | array | Container start command arguments. | +| [`command`](#parameter-containerscommand) | array | The command to run in the container. | +| [`env`](#parameter-containersenv) | array | The environment variables to set in the container. | +| [`probes`](#parameter-containersprobes) | array | The probes of the container. | +| [`resources`](#parameter-containersresources) | object | The resources to allocate to the container. | +| [`volumeMounts`](#parameter-containersvolumemounts) | array | The volume mounts to attach to the container. | -Name of the Container App. +### Parameter: `containers.image` + +The image of the container. - Required: Yes - Type: string -### Parameter: `triggerType` +### Parameter: `containers.name` -Trigger type of the job. +The name of the container. - Required: Yes - Type: string -- Allowed: - ```Bicep - [ - 'Event' - 'Manual' - 'Schedule' - ] - ``` -### Parameter: `enableTelemetry` +### Parameter: `containers.args` -Enable/Disable usage telemetry for module. +Container start command arguments. - Required: No -- Type: bool -- Default: `True` +- Type: array -### Parameter: `eventTriggerConfig` +### Parameter: `containers.command` -Required if TriggerType is Event. Configuration of an event driven job. +The command to run in the container. - Required: No -- Type: object +- Type: array -### Parameter: `initContainersTemplate` +### Parameter: `containers.env` -List of specialized containers that run before app containers. +The environment variables to set in the container. - Required: No - Type: array -### Parameter: `location` +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-containersenvname) | string | The environment variable name. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`secretRef`](#parameter-containersenvsecretref) | string | The name of the Container App secret from which to pull the envrionment variable value. Required if `value` is null. | +| [`value`](#parameter-containersenvvalue) | string | The environment variable value. Required if `secretRef` is null. | + +### Parameter: `containers.env.name` + +The environment variable name. + +- Required: Yes +- Type: string -Location for all Resources. +### Parameter: `containers.env.secretRef` + +The name of the Container App secret from which to pull the envrionment variable value. Required if `value` is null. - Required: No - Type: string -- Default: `[resourceGroup().location]` -### Parameter: `lock` +### Parameter: `containers.env.value` -The lock settings of the service. +The environment variable value. Required if `secretRef` is null. - Required: No -- Type: object +- Type: string + +### Parameter: `containers.probes` + +The probes of the container. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`type`](#parameter-containersprobestype) | string | The type of probe. | **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | -| [`name`](#parameter-lockname) | string | Specify the name of lock. | +| [`failureThreshold`](#parameter-containersprobesfailurethreshold) | int | Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. | +| [`httpGet`](#parameter-containersprobeshttpget) | object | HTTPGet specifies the http request to perform. | +| [`initialDelaySeconds`](#parameter-containersprobesinitialdelayseconds) | int | Number of seconds after the container has started before liveness probes are initiated. Defaults to 0 seconds. | +| [`periodSeconds`](#parameter-containersprobesperiodseconds) | int | How often (in seconds) to perform the probe. Defaults to 10 seconds. | +| [`successThreshold`](#parameter-containersprobessuccessthreshold) | int | Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. | +| [`tcpSocket`](#parameter-containersprobestcpsocket) | object | TCPSocket specifies an action involving a TCP port. | +| [`terminationGracePeriodSeconds`](#parameter-containersprobesterminationgraceperiodseconds) | int | Duration in seconds the pod needs to terminate gracefully upon probe failure. This is an alpha field and requires enabling ProbeTerminationGracePeriod feature gate. | +| [`timeoutSeconds`](#parameter-containersprobestimeoutseconds) | int | Number of seconds after which the probe times out. Defaults to 1 second. | -### Parameter: `lock.kind` +### Parameter: `containers.probes.type` -Specify the type of lock. +The type of probe. -- Required: No +- Required: Yes - Type: string - Allowed: ```Bicep [ - 'CanNotDelete' - 'None' - 'ReadOnly' + 'Liveness' + 'Readiness' + 'Startup' ] ``` -### Parameter: `lock.name` +### Parameter: `containers.probes.failureThreshold` -Specify the name of lock. +Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3. - Required: No -- Type: string +- Type: int -### Parameter: `managedIdentities` +### Parameter: `containers.probes.httpGet` -The managed identity definition for this resource. +HTTPGet specifies the http request to perform. - Required: No - Type: object +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`path`](#parameter-containersprobeshttpgetpath) | string | Path to access on the HTTP server. | +| [`port`](#parameter-containersprobeshttpgetport) | int | Name of the port to access on the container. If not specified, the containerPort is used. | +| [`scheme`](#parameter-containersprobeshttpgetscheme) | string | Scheme to use for connecting to the host. Defaults to HTTP. | + **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`systemAssigned`](#parameter-managedidentitiessystemassigned) | bool | Enables system assigned managed identity on the resource. | -| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. | +| [`host`](#parameter-containersprobeshttpgethost) | string | Host name to connect to, defaults to the pod IP. | +| [`httpHeaders`](#parameter-containersprobeshttpgethttpheaders) | array | Custom headers to set in the request. | -### Parameter: `managedIdentities.systemAssigned` +### Parameter: `containers.probes.httpGet.path` -Enables system assigned managed identity on the resource. +Path to access on the HTTP server. -- Required: No -- Type: bool +- Required: Yes +- Type: string -### Parameter: `managedIdentities.userAssignedResourceIds` +### Parameter: `containers.probes.httpGet.port` -The resource ID(s) to assign to the resource. +Name of the port to access on the container. If not specified, the containerPort is used. + +- Required: Yes +- Type: int + +### Parameter: `containers.probes.httpGet.scheme` + +Scheme to use for connecting to the host. Defaults to HTTP. - Required: No -- Type: array +- Type: string +- Allowed: + ```Bicep + [ + 'HTTP' + 'HTTPS' + ] + ``` -### Parameter: `manualTriggerConfig` +### Parameter: `containers.probes.httpGet.host` -Required if TriggerType is Manual. Configuration of a manual job. +Host name to connect to, defaults to the pod IP. - Required: No -- Type: object +- Type: string -### Parameter: `registries` +### Parameter: `containers.probes.httpGet.httpHeaders` -Collection of private container registry credentials for containers used by the Container app. +Custom headers to set in the request. - Required: No - Type: array -### Parameter: `replicaRetryLimit` +**Required parameters** -The maximum number of times a replica can be retried. +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-containersprobeshttpgethttpheadersname) | string | The header field name. | +| [`value`](#parameter-containersprobeshttpgethttpheadersvalue) | string | The header field value. | + +### Parameter: `containers.probes.httpGet.httpHeaders.name` + +The header field name. + +- Required: Yes +- Type: string + +### Parameter: `containers.probes.httpGet.httpHeaders.value` + +The header field value. + +- Required: Yes +- Type: string + +### Parameter: `containers.probes.initialDelaySeconds` + +Number of seconds after the container has started before liveness probes are initiated. Defaults to 0 seconds. - Required: No - Type: int -- Default: `0` -### Parameter: `replicaTimeout` +### Parameter: `containers.probes.periodSeconds` -Maximum number of seconds a replica is allowed to run. +How often (in seconds) to perform the probe. Defaults to 10 seconds. - Required: No - Type: int -- Default: `1800` -### Parameter: `roleAssignments` +### Parameter: `containers.probes.successThreshold` -Array of role assignments to create. +Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1. - Required: No -- Type: array +- Type: int + +### Parameter: `containers.probes.tcpSocket` + +TCPSocket specifies an action involving a TCP port. + +- Required: No +- Type: object **Required parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | -| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| [`port`](#parameter-containersprobestcpsocketport) | int | Name of the port to access on the container. If not specified, the containerPort is used. | **Optional parameters** | Parameter | Type | Description | | :-- | :-- | :-- | -| [`condition`](#parameter-roleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". | -| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | -| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | -| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | -| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | +| [`host`](#parameter-containersprobestcpsockethost) | string | Host name to connect to, defaults to the pod IP. | -### Parameter: `roleAssignments.principalId` +### Parameter: `containers.probes.tcpSocket.port` -The principal ID of the principal (user/group/identity) to assign the role to. +Name of the port to access on the container. If not specified, the containerPort is used. - Required: Yes -- Type: string +- Type: int -### Parameter: `roleAssignments.roleDefinitionIdOrName` +### Parameter: `containers.probes.tcpSocket.host` -The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. +Host name to connect to, defaults to the pod IP. - Required: Yes - Type: string -### Parameter: `roleAssignments.condition` +### Parameter: `containers.probes.terminationGracePeriodSeconds` -The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". +Duration in seconds the pod needs to terminate gracefully upon probe failure. This is an alpha field and requires enabling ProbeTerminationGracePeriod feature gate. - Required: No -- Type: string +- Type: int + +### Parameter: `containers.probes.timeoutSeconds` + +Number of seconds after which the probe times out. Defaults to 1 second. + +- Required: No +- Type: int + +### Parameter: `containers.resources` + +The resources to allocate to the container. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`cpu`](#parameter-containersresourcescpu) | string | The CPU limit of the container in cores. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`memory`](#parameter-containersresourcesmemory) | string | The required memory. | + +### Parameter: `containers.resources.cpu` + +The CPU limit of the container in cores. + +- Required: Yes +- Type: string + +### Parameter: `containers.resources.memory` + +The required memory. + +- Required: Yes +- Type: string + +### Parameter: `containers.volumeMounts` + +The volume mounts to attach to the container. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`mountPath`](#parameter-containersvolumemountsmountpath) | string | The path within the container at which the volume should be mounted. Must not contain ':'. | +| [`volumeName`](#parameter-containersvolumemountsvolumename) | string | This must match the Name of a Volume. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`subPath`](#parameter-containersvolumemountssubpath) | string | Path within the volume from which the container's volume should be mounted. | + +### Parameter: `containers.volumeMounts.mountPath` + +The path within the container at which the volume should be mounted. Must not contain ':'. + +- Required: Yes +- Type: string + +### Parameter: `containers.volumeMounts.volumeName` + +This must match the Name of a Volume. + +- Required: Yes +- Type: string + +### Parameter: `containers.volumeMounts.subPath` + +Path within the volume from which the container's volume should be mounted. + +- Required: No +- Type: string + +### Parameter: `environmentResourceId` + +Resource ID of Container Apps Environment. + +- Required: Yes +- Type: string + +### Parameter: `name` + +Name of the Container App. + +- Required: Yes +- Type: string + +### Parameter: `triggerType` + +Trigger type of the job. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'Event' + 'Manual' + 'Schedule' + ] + ``` + +### Parameter: `eventTriggerConfig` + +Configuration of an event driven job. Required if `TriggerType` is `Event`. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`scale`](#parameter-eventtriggerconfigscale) | object | Scaling configurations for event driven jobs. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`parallelism`](#parameter-eventtriggerconfigparallelism) | int | Number of parallel replicas of a job that can run at a given time. Defaults to 1. | +| [`replicaCompletionCount`](#parameter-eventtriggerconfigreplicacompletioncount) | int | Minimum number of successful replica completions before overall job completion. Must be equal or or less than the parallelism. Defaults to 1. | + +### Parameter: `eventTriggerConfig.scale` + +Scaling configurations for event driven jobs. + +- Required: Yes +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`maxExecutions`](#parameter-eventtriggerconfigscalemaxexecutions) | int | Maximum number of job executions that are created for a trigger, default 100. | +| [`minExecutions`](#parameter-eventtriggerconfigscaleminexecutions) | int | Minimum number of job executions that are created for a trigger, default 0. | +| [`pollingInterval`](#parameter-eventtriggerconfigscalepollinginterval) | int | Interval to check each event source in seconds. Defaults to 30s. | +| [`rules`](#parameter-eventtriggerconfigscalerules) | array | Scaling rules for the job. | + +### Parameter: `eventTriggerConfig.scale.maxExecutions` + +Maximum number of job executions that are created for a trigger, default 100. + +- Required: No +- Type: int + +### Parameter: `eventTriggerConfig.scale.minExecutions` + +Minimum number of job executions that are created for a trigger, default 0. + +- Required: No +- Type: int + +### Parameter: `eventTriggerConfig.scale.pollingInterval` + +Interval to check each event source in seconds. Defaults to 30s. + +- Required: No +- Type: int + +### Parameter: `eventTriggerConfig.scale.rules` + +Scaling rules for the job. + +- Required: Yes +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`auth`](#parameter-eventtriggerconfigscalerulesauth) | array | Authentication secrets for the scale rule. | +| [`name`](#parameter-eventtriggerconfigscalerulesname) | string | The name of the scale rule. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`metadata`](#parameter-eventtriggerconfigscalerulesmetadata) | object | Metadata properties to describe the scale rule. | +| [`type`](#parameter-eventtriggerconfigscalerulestype) | string | The type of the rule. | + +### Parameter: `eventTriggerConfig.scale.rules.auth` + +Authentication secrets for the scale rule. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`secretRef`](#parameter-eventtriggerconfigscalerulesauthsecretref) | string | Name of the secret from which to pull the auth params. | +| [`triggerParameter`](#parameter-eventtriggerconfigscalerulesauthtriggerparameter) | string | Trigger Parameter that uses the secret. | + +### Parameter: `eventTriggerConfig.scale.rules.auth.secretRef` + +Name of the secret from which to pull the auth params. + +- Required: Yes +- Type: string + +### Parameter: `eventTriggerConfig.scale.rules.auth.triggerParameter` + +Trigger Parameter that uses the secret. + +- Required: Yes +- Type: string + +### Parameter: `eventTriggerConfig.scale.rules.name` + +The name of the scale rule. + +- Required: Yes +- Type: string + +### Parameter: `eventTriggerConfig.scale.rules.metadata` + +Metadata properties to describe the scale rule. + +- Required: Yes +- Type: object + +### Parameter: `eventTriggerConfig.scale.rules.type` + +The type of the rule. + +- Required: Yes +- Type: string + +### Parameter: `eventTriggerConfig.parallelism` + +Number of parallel replicas of a job that can run at a given time. Defaults to 1. + +- Required: No +- Type: int + +### Parameter: `eventTriggerConfig.replicaCompletionCount` + +Minimum number of successful replica completions before overall job completion. Must be equal or or less than the parallelism. Defaults to 1. + +- Required: No +- Type: int + +### Parameter: `manualTriggerConfig` + +Configuration of a manually triggered job. Required if `TriggerType` is `Manual`. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`parallelism`](#parameter-manualtriggerconfigparallelism) | int | Number of parallel replicas of a job that can run at a given time. Defaults to 1. | +| [`replicaCompletionCount`](#parameter-manualtriggerconfigreplicacompletioncount) | int | Minimum number of successful replica completions before overall job completion. Must be equal or or less than the parallelism. Defaults to 1. | + +### Parameter: `manualTriggerConfig.parallelism` + +Number of parallel replicas of a job that can run at a given time. Defaults to 1. + +- Required: No +- Type: int + +### Parameter: `manualTriggerConfig.replicaCompletionCount` + +Minimum number of successful replica completions before overall job completion. Must be equal or or less than the parallelism. Defaults to 1. + +- Required: No +- Type: int + +### Parameter: `scheduleTriggerConfig` + +Configuration of a schedule based job. Required if `TriggerType` is `Schedule`. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`cronExpression`](#parameter-scheduletriggerconfigcronexpression) | string | Cron formatted repeating schedule ("* * * * *") of a Cron Job. It supports the standard [cron](https://en.wikipedia.org/wiki/Cron) expression syntax. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`parallelism`](#parameter-scheduletriggerconfigparallelism) | int | Number of parallel replicas of a job that can run at a given time. Defaults to 1. | +| [`replicaCompletionCount`](#parameter-scheduletriggerconfigreplicacompletioncount) | int | Number of successful completions of a job that are necessary to consider the job complete. Must be equal or or less than the parallelism. Defaults to 1. | + +### Parameter: `scheduleTriggerConfig.cronExpression` + +Cron formatted repeating schedule ("* * * * *") of a Cron Job. It supports the standard [cron](https://en.wikipedia.org/wiki/Cron) expression syntax. + +- Required: Yes +- Type: string + +### Parameter: `scheduleTriggerConfig.parallelism` + +Number of parallel replicas of a job that can run at a given time. Defaults to 1. + +- Required: No +- Type: int + +### Parameter: `scheduleTriggerConfig.replicaCompletionCount` + +Number of successful completions of a job that are necessary to consider the job complete. Must be equal or or less than the parallelism. Defaults to 1. + +- Required: No +- Type: int + +### Parameter: `enableTelemetry` + +Enable/Disable usage telemetry for module. + +- Required: No +- Type: bool +- Default: `True` + +### Parameter: `initContainers` + +List of specialized containers that run before app containers. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`image`](#parameter-initcontainersimage) | string | The image of the container. | +| [`name`](#parameter-initcontainersname) | string | The name of the container. | +| [`resources`](#parameter-initcontainersresources) | object | Container resource requirements. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`args`](#parameter-initcontainersargs) | array | Container start command arguments. | +| [`command`](#parameter-initcontainerscommand) | array | Container start command. | +| [`env`](#parameter-initcontainersenv) | array | The environment variables to set in the container. | +| [`volumeMounts`](#parameter-initcontainersvolumemounts) | array | The volume mounts to attach to the container. | + +### Parameter: `initContainers.image` + +The image of the container. + +- Required: Yes +- Type: string + +### Parameter: `initContainers.name` + +The name of the container. + +- Required: Yes +- Type: string + +### Parameter: `initContainers.resources` + +Container resource requirements. + +- Required: No +- Type: object + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`cpu`](#parameter-initcontainersresourcescpu) | string | The CPU limit of the container in cores. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`memory`](#parameter-initcontainersresourcesmemory) | string | The required memory. | + +### Parameter: `initContainers.resources.cpu` + +The CPU limit of the container in cores. + +- Required: Yes +- Type: string + +### Parameter: `initContainers.resources.memory` + +The required memory. + +- Required: Yes +- Type: string + +### Parameter: `initContainers.args` + +Container start command arguments. + +- Required: Yes +- Type: array + +### Parameter: `initContainers.command` + +Container start command. + +- Required: Yes +- Type: array + +### Parameter: `initContainers.env` + +The environment variables to set in the container. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-initcontainersenvname) | string | The environment variable name. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`secretRef`](#parameter-initcontainersenvsecretref) | string | The name of the Container App secret from which to pull the envrionment variable value. Required if `value` is null. | +| [`value`](#parameter-initcontainersenvvalue) | string | The environment variable value. Required if `secretRef` is null. | + +### Parameter: `initContainers.env.name` + +The environment variable name. + +- Required: Yes +- Type: string + +### Parameter: `initContainers.env.secretRef` + +The name of the Container App secret from which to pull the envrionment variable value. Required if `value` is null. + +- Required: No +- Type: string + +### Parameter: `initContainers.env.value` + +The environment variable value. Required if `secretRef` is null. + +- Required: No +- Type: string + +### Parameter: `initContainers.volumeMounts` + +The volume mounts to attach to the container. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`mountPath`](#parameter-initcontainersvolumemountsmountpath) | string | The path within the container at which the volume should be mounted. Must not contain ':'. | +| [`volumeName`](#parameter-initcontainersvolumemountsvolumename) | string | This must match the Name of a Volume. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`subPath`](#parameter-initcontainersvolumemountssubpath) | string | Path within the volume from which the container's volume should be mounted. | + +### Parameter: `initContainers.volumeMounts.mountPath` + +The path within the container at which the volume should be mounted. Must not contain ':'. + +- Required: Yes +- Type: string + +### Parameter: `initContainers.volumeMounts.volumeName` + +This must match the Name of a Volume. + +- Required: Yes +- Type: string + +### Parameter: `initContainers.volumeMounts.subPath` + +Path within the volume from which the container's volume should be mounted. + +- Required: No +- Type: string + +### Parameter: `location` + +Location for all Resources. Defaults to the location of the Resource Group. + +- Required: No +- Type: string +- Default: `[resourceGroup().location]` + +### Parameter: `lock` + +The lock settings of the service. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`kind`](#parameter-lockkind) | string | Specify the type of lock. | +| [`name`](#parameter-lockname) | string | Specify the name of lock. | + +### Parameter: `lock.kind` + +Specify the type of lock. + +- Required: No +- Type: string +- Allowed: + ```Bicep + [ + 'CanNotDelete' + 'None' + 'ReadOnly' + ] + ``` + +### Parameter: `lock.name` + +Specify the name of lock. + +- Required: No +- Type: string + +### Parameter: `managedIdentities` + +The managed identity definition for this resource. + +- Required: No +- Type: object + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`systemAssigned`](#parameter-managedidentitiessystemassigned) | bool | Enables system assigned managed identity on the resource. | +| [`userAssignedResourceIds`](#parameter-managedidentitiesuserassignedresourceids) | array | The resource ID(s) to assign to the resource. | + +### Parameter: `managedIdentities.systemAssigned` + +Enables system assigned managed identity on the resource. + +- Required: No +- Type: bool + +### Parameter: `managedIdentities.userAssignedResourceIds` + +The resource ID(s) to assign to the resource. + +- Required: No +- Type: array + +### Parameter: `registries` + +Collection of private container registry credentials for containers used by the Container app. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`server`](#parameter-registriesserver) | string | The FQDN name of the container registry. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`passwordSecretRef`](#parameter-registriespasswordsecretref) | string | The name of the secret contains the login password. Required if `username` is not null. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`identity`](#parameter-registriesidentity) | string | The resource ID of the (user) managed identity, which is used to access the Azure Container Registry. | +| [`username`](#parameter-registriesusername) | string | The username for the container registry. | + +### Parameter: `registries.server` + +The FQDN name of the container registry. + +- Required: Yes +- Type: string + +### Parameter: `registries.passwordSecretRef` + +The name of the secret contains the login password. Required if `username` is not null. + +- Required: No +- Type: string + +### Parameter: `registries.identity` + +The resource ID of the (user) managed identity, which is used to access the Azure Container Registry. + +- Required: No +- Type: string + +### Parameter: `registries.username` + +The username for the container registry. + +- Required: No +- Type: string + +### Parameter: `replicaRetryLimit` + +The maximum number of times a replica can be retried. + +- Required: No +- Type: int +- Default: `0` + +### Parameter: `replicaTimeout` + +Maximum number of seconds a replica is allowed to run. + +- Required: No +- Type: int +- Default: `1800` + +### Parameter: `roleAssignments` + +Array of role assignments to create. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`principalId`](#parameter-roleassignmentsprincipalid) | string | The principal ID of the principal (user/group/identity) to assign the role to. | +| [`roleDefinitionIdOrName`](#parameter-roleassignmentsroledefinitionidorname) | string | The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`condition`](#parameter-roleassignmentscondition) | string | The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". | +| [`conditionVersion`](#parameter-roleassignmentsconditionversion) | string | Version of the condition. | +| [`delegatedManagedIdentityResourceId`](#parameter-roleassignmentsdelegatedmanagedidentityresourceid) | string | The Resource Id of the delegated managed identity resource. | +| [`description`](#parameter-roleassignmentsdescription) | string | The description of the role assignment. | +| [`principalType`](#parameter-roleassignmentsprincipaltype) | string | The principal type of the assigned principal ID. | + +### Parameter: `roleAssignments.principalId` + +The principal ID of the principal (user/group/identity) to assign the role to. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.roleDefinitionIdOrName` + +The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. + +- Required: Yes +- Type: string + +### Parameter: `roleAssignments.condition` + +The conditions on the role assignment. This limits the resources it can be assigned to. e.g.: @Resource[Microsoft.Storage/storageAccounts/blobServices/containers:ContainerName] StringEqualsIgnoreCase "foo_storage_container". + +- Required: No +- Type: string ### Parameter: `roleAssignments.conditionVersion` @@ -751,19 +1637,54 @@ The principal type of the assigned principal ID. ] ``` -### Parameter: `scheduleTriggerConfig` +### Parameter: `secrets` -Required if TriggerType is Schedule. Configuration of a schedule based job. +The secrets of the Container App. - Required: No -- Type: object +- Type: array -### Parameter: `secrets` +**Conditional parameters** -The secrets of the Container App. +| 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: secureObject +- Type: string ### Parameter: `tags` @@ -779,6 +1700,91 @@ List of volume definitions for the Container App. - Required: No - Type: array +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`name`](#parameter-volumesname) | string | The name of the volume. | +| [`storageType`](#parameter-volumesstoragetype) | string | The container name. | + +**Conditional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`mountOptions`](#parameter-volumesmountoptions) | string | Mount options used while mounting the Azure file share or NFS Azure file share. Must be a comma-separated string. Required if `storageType` is not `EmptyDir`. | +| [`storageName`](#parameter-volumesstoragename) | string | The storage account name. Not needed for EmptyDir and Secret. Required if `storageType` is `AzureFile` or `NfsAzureFile`. | + +**Optional parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`secrets`](#parameter-volumessecrets) | array | List of secrets to be added in volume. If no secrets are provided, all secrets in collection will be added to volume. | + +### Parameter: `volumes.name` + +The name of the volume. + +- Required: Yes +- Type: string + +### Parameter: `volumes.storageType` + +The container name. + +- Required: Yes +- Type: string +- Allowed: + ```Bicep + [ + 'AzureFile' + 'EmptyDir' + 'NfsAzureFile' + 'Secret' + ] + ``` + +### Parameter: `volumes.mountOptions` + +Mount options used while mounting the Azure file share or NFS Azure file share. Must be a comma-separated string. Required if `storageType` is not `EmptyDir`. + +- Required: No +- Type: string + +### Parameter: `volumes.storageName` + +The storage account name. Not needed for EmptyDir and Secret. Required if `storageType` is `AzureFile` or `NfsAzureFile`. + +- Required: No +- Type: string + +### Parameter: `volumes.secrets` + +List of secrets to be added in volume. If no secrets are provided, all secrets in collection will be added to volume. + +- Required: No +- Type: array + +**Required parameters** + +| Parameter | Type | Description | +| :-- | :-- | :-- | +| [`path`](#parameter-volumessecretspath) | string | Path to project secret to. If no path is provided, path defaults to name of secret listed in secretRef. | +| [`secretRef`](#parameter-volumessecretssecretref) | string | Name of the Container App secret from which to pull the secret value. | + +### Parameter: `volumes.secrets.path` + +Path to project secret to. If no path is provided, path defaults to name of secret listed in secretRef. + +- Required: Yes +- Type: string + +### Parameter: `volumes.secrets.secretRef` + +Name of the Container App secret from which to pull the secret value. + +- Required: Yes +- Type: string + ### Parameter: `workloadProfileName` The name of the workload profile to use. diff --git a/avm/res/app/job/main.bicep b/avm/res/app/job/main.bicep index 2d9a0f6ba6..442cb4b8b2 100644 --- a/avm/res/app/job/main.bicep +++ b/avm/res/app/job/main.bicep @@ -5,22 +5,61 @@ metadata owner = 'Azure/module-maintainers' @description('Required. Name of the Container App.') param name string -@description('Optional. Location for all Resources.') +@description('Optional. Location for all Resources. Defaults to the location of the Resource Group.') param location string = resourceGroup().location -@description('Required. Resource ID of environment.') +@description('Required. Resource ID of Container Apps Environment.') param environmentResourceId string @description('Optional. The lock settings of the service.') -param lock lockType +param lock lockType? @description('Optional. Tags of the resource.') +@metadata({ + example: ''' + { + "key1": "value1", + "key2": "value2" + } + ''' +}) param tags object? @description('Optional. Collection of private container registry credentials for containers used by the Container app.') -param registries array? +@metadata({ + example: '''[ + { + "server": "myregistry.azurecr.io", + "identity": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity" + }, + { + "server": "myregistry2.azurecr.io", + "identity": "system" + } + , + { + "server": "myregistry3.azurecr.io", + "username": "myusername", + "passwordSecretRef": "secret-name" + } +]''' +}) +param registries registryType? @description('Optional. The managed identity definition for this resource.') +@metadata({ + example: ''' + { + "systemAssigned": true, + "userAssignedResourceIds": [ + "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity" + ] + }, + { + "systemAssigned": true + } + ''' +}) param managedIdentities managedIdentitiesType @description('Optional. Array of role assignments to create.') @@ -30,19 +69,19 @@ param roleAssignments roleAssignmentType param enableTelemetry bool = true @description('Required. List of container definitions for the Container App.') -param containers array +param containers containerType @description('Optional. List of specialized containers that run before app containers.') -param initContainersTemplate array? +param initContainers initContainerType? -@description('Optional. Required if TriggerType is Event. Configuration of an event driven job.') -param eventTriggerConfig object? +@description('Conditional. Configuration of an event driven job. Required if `TriggerType` is `Event`.') +param eventTriggerConfig eventTriggerConfigType? -@description('Optional. Required if TriggerType is Schedule. Configuration of a schedule based job.') -param scheduleTriggerConfig object? +@description('Conditional. Configuration of a schedule based job. Required if `TriggerType` is `Schedule`.') +param scheduleTriggerConfig scheduleTriggerconfigType? -@description('Optional. Required if TriggerType is Manual. Configuration of a manual job.') -param manualTriggerConfig object? +@description('Conditional. Configuration of a manually triggered job. Required if `TriggerType` is `Manual`.') +param manualTriggerConfig manualTriggerConfigType? @description('Optional. The maximum number of times a replica can be retried.') param replicaRetryLimit int = 0 @@ -51,11 +90,32 @@ param replicaRetryLimit int = 0 param workloadProfileName string = 'Consumption' @description('Optional. The secrets of the Container App.') -@secure() -param secrets object? +@metadata({ + example: ''' +{ + "name": "mysecret" + "identity": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity", + "keyVaultUrl": "https://myvault${environment().suffixes.keyvaultDns}/secrets/mysecret", +}, +{ + "name": "mysecret" + "identity": "system", + "keyVaultUrl": "https://myvault${environment().suffixes.keyvaultDns}/secrets/mysecret", +}, +{ + "name": "mysecret", + "value": "mysecretvalue" +} +{ + name: 'connection-string' + value: listKeys('/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/myStorageAccount', '2023-04-01').keys[0].value +} +''' +}) +param secrets secretType? @description('Optional. List of volume definitions for the Container App.') -param volumes array? +param volumes volumeType? @description('Optional. Maximum number of seconds a replica is allowed to run.') param replicaTimeout int = 1800 @@ -68,8 +128,6 @@ param replicaTimeout int = 1800 @description('Required. Trigger type of the job.') param triggerType string -var secretList = secrets.?secureList - var formattedUserAssignedIdentities = reduce( map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }), {}, @@ -121,7 +179,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableT } } -resource job 'Microsoft.App/jobs@2023-05-01' = { +resource job 'Microsoft.App/jobs@2024-03-01' = { name: name tags: tags location: location @@ -129,18 +187,18 @@ resource job 'Microsoft.App/jobs@2023-05-01' = { properties: { environmentId: environmentResourceId configuration: { + triggerType: triggerType eventTriggerConfig: triggerType == 'Event' ? eventTriggerConfig : null manualTriggerConfig: triggerType == 'Manual' ? manualTriggerConfig : null scheduleTriggerConfig: triggerType == 'Schedule' ? scheduleTriggerConfig : null replicaRetryLimit: replicaRetryLimit replicaTimeout: replicaTimeout registries: registries - secrets: secretList - triggerType: triggerType + secrets: secrets } template: { containers: containers - initContainers: initContainersTemplate + initContainers: initContainers volumes: volumes } workloadProfileName: workloadProfileName @@ -177,19 +235,40 @@ resource automationAccount_roleAssignments 'Microsoft.Authorization/roleAssignme scope: job } ] + +// ============ // +// Outputs // +// ============ // + @description('The resource ID of the Container App Job.') +@metadata({ + example: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.App/jobs/myJob' +}) output resourceId string = job.id @description('The name of the resource group the Container App Job was deployed into.') +@metadata({ + example: 'myResourceGroup' +}) output resourceGroupName string = resourceGroup().name @description('The name of the Container App Job.') +@metadata({ + example: 'myJob' +}) output name string = job.name @description('The location the resource was deployed into.') +@metadata({ + example: 'Germany West Central' +}) output location string = job.location @description('The principal ID of the system assigned identity.') +@metadata({ + example: ''''00000000-0000-0000-0000-000000000000' + ''''' +}) output systemAssignedMIPrincipalId string = job.?identity.?principalId ?? '' // =============== // @@ -210,7 +289,7 @@ type lockType = { @description('Optional. Specify the type of lock.') kind: ('CanNotDelete' | 'ReadOnly' | 'None')? -}? +} type roleAssignmentType = { @description('Required. The role to assign. You can provide either the display name of the role definition, the role definition GUID, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') @@ -234,3 +313,358 @@ type roleAssignmentType = { @description('Optional. The Resource Id of the delegated managed identity resource.') delegatedManagedIdentityResourceId: string? }[]? + +type registryType = { + @description('Required. The FQDN name of the container registry.') + @metadata({ example: 'myregistry.azurecr.io' }) + server: string + + @description('Optional. The resource ID of the (user) managed identity, which is used to access the Azure Container Registry.') + @metadata({ + example: ''' + user-assigned identity: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity + system-assigned identity: system + ''' + }) + identity: string? + + @description('Optional. The username for the container registry.') + username: string? + + @description('Conditional. The name of the secret contains the login password. Required if `username` is not null.') + passwordSecretRef: string? +}[]? + +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.') + @metadata({ + example: ''' + https://myvault${environment().suffixes.keyvaultDns}/secrets/mysecret + ''' + }) + 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? +}[]? + +type volumeType = { + @description('Required. The name of the volume.') + name: string + + @description('Conditional. Mount options used while mounting the Azure file share or NFS Azure file share. Must be a comma-separated string. Required if `storageType` is not `EmptyDir`.') + mountOptions: string? + + @description('Optional. List of secrets to be added in volume. If no secrets are provided, all secrets in collection will be added to volume.') + secrets: { + @description('Required. Path to project secret to. If no path is provided, path defaults to name of secret listed in secretRef.') + path: string + + @description('Required. Name of the Container App secret from which to pull the secret value.') + secretRef: string + }[]? + + @description('Conditional. The storage account name. Not needed for EmptyDir and Secret. Required if `storageType` is `AzureFile` or `NfsAzureFile`.') + storageName: string? + + @description('Required. The container name.') + storageType: ('AzureFile' | 'EmptyDir' | 'NfsAzureFile' | 'Secret') +}[]? + +type containerEnvironmentVariablesType = { + @description('Required. The environment variable name.') + name: string + + @description('Conditional. The name of the Container App secret from which to pull the envrionment variable value. Required if `value` is null.') + secretRef: string? + + @description('Conditional. The environment variable value. Required if `secretRef` is null.') + value: string? +}[]? + +type containerProbeHttpGetHttpHeadersItem = { + @description('Required. The header field name.') + name: string + + @description('Required. The header field value.') + value: string +}[]? + +type containerProbeType = { + @description('Optional. Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3.') + @minValue(1) + @maxValue(10) + failureThreshold: int? + + @description('Optional. HTTPGet specifies the http request to perform.') + httpGet: { + @description('Optional. Host name to connect to, defaults to the pod IP.') + host: string? + + @description('Optional. Custom headers to set in the request.') + httpHeaders: { + @description('Required. The header field name.') + name: string + + @description('Required. The header field value.') + value: string + }[]? + + @description('Required. Path to access on the HTTP server.') + path: string + + @description('Required. Name of the port to access on the container. If not specified, the containerPort is used.') + @minValue(1) + @maxValue(65535) + port: int + + @description('Required. Scheme to use for connecting to the host. Defaults to HTTP.') + scheme: ('HTTP' | 'HTTPS')? + }? + + @description('Optional. Number of seconds after the container has started before liveness probes are initiated. Defaults to 0 seconds.') + @minValue(1) + @maxValue(60) + initialDelaySeconds: int? + + @description('Optional. How often (in seconds) to perform the probe. Defaults to 10 seconds.') + @minValue(1) + @maxValue(60) + periodSeconds: int? + + @description('Optional. Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1.') + @minValue(1) + @maxValue(10) + successThreshold: int? + + @description('Optional. TCPSocket specifies an action involving a TCP port.') + tcpSocket: { + @description('Optional. Host name to connect to, defaults to the pod IP.') + host: string + + @description('Required. Name of the port to access on the container. If not specified, the containerPort is used.') + @minValue(1) + @maxValue(65535) + port: int + }? + + @description('Optional. Duration in seconds the pod needs to terminate gracefully upon probe failure. This is an alpha field and requires enabling ProbeTerminationGracePeriod feature gate.') + @minValue(0) + @maxValue(3600) + terminationGracePeriodSeconds: int? + + @description('Optional. Number of seconds after which the probe times out. Defaults to 1 second.') + @minValue(1) + @maxValue(240) + timeoutSeconds: int? + + @description('Required. The type of probe.') + type: ('Liveness' | 'Readiness' | 'Startup') +}[]? + +type containerResourceType = { + @description('Required. The CPU limit of the container in cores.') + @metadata({ + example: ''' + '0.25' + '1' + ''' + }) + cpu: string + + @description('Optional. The required memory.') + @metadata({ + example: ''' + '250Mb' + '1.5Gi' + '1500Mi' + ''' + }) + memory: string +}? + +type containerVolumeMountType = { + @description('Required. The path within the container at which the volume should be mounted. Must not contain \':\'.') + mountPath: string + + @description('Optional. Path within the volume from which the container\'s volume should be mounted.') + subPath: string? + + @description('Required. This must match the Name of a Volume.') + volumeName: string +}[]? + +type manualTriggerConfigType = { + @description('Optional. Number of parallel replicas of a job that can run at a given time. Defaults to 1.') + parallelism: int? + + @description('Optional. Minimum number of successful replica completions before overall job completion. Must be equal or or less than the parallelism. Defaults to 1.') + replicaCompletionCount: int? +} + +type scheduleTriggerconfigType = { + @description('Required. Cron formatted repeating schedule ("* * * * *") of a Cron Job. It supports the standard [cron](https://en.wikipedia.org/wiki/Cron) expression syntax.') + @metadata({ + example: ''' + '* * * * *' // Every minute, every hour, every day + '0 0 * * *' // at 00:00 UTC every day + ''' + }) + cronExpression: string + + @description('Optional. Number of parallel replicas of a job that can run at a given time. Defaults to 1.') + parallelism: int? + + @description('Optional. Number of successful completions of a job that are necessary to consider the job complete. Must be equal or or less than the parallelism. Defaults to 1.') + replicaCompletionCount: int? +} + +type eventTriggerConfigType = { + @description('Optional. Number of parallel replicas of a job that can run at a given time. Defaults to 1.') + parallelism: int? + + @description('Optional. Minimum number of successful replica completions before overall job completion. Must be equal or or less than the parallelism. Defaults to 1.') + replicaCompletionCount: int? + + @description('Required. Scaling configurations for event driven jobs.') + scale: jobScaleType +} + +type jobScaleType = { + @description('Optional. Maximum number of job executions that are created for a trigger, default 100.') + maxExecutions: int? + + @description('Optional. Minimum number of job executions that are created for a trigger, default 0.') + minExecutions: int? + + @description('Optional. Interval to check each event source in seconds. Defaults to 30s.') + pollingInterval: int? + + @description('Optional. Scaling rules for the job.') + @metadata({ + example: ''' + // for type azure-queue + { + name: 'myrule' + type: 'azure-queue' + metadata: { + queueName: 'default' + storageAccountResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/myStorageAccount' + } + auth: { + secretRef: 'mysecret' + triggerParameter: 'queueName' + } + } + ''' + }) + rules: { + @description('Required. Authentication secrets for the scale rule.') + auth: { + @description('Required. Name of the secret from which to pull the auth params.') + secretRef: string + + @description('Required. Trigger Parameter that uses the secret.') + triggerParameter: string + }[]? + + @description('Optional. Metadata properties to describe the scale rule.') + @metadata({ + example: ''' + { + "// for type azure-queue + { + queueName: 'default' + storageAccountResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/myStorageAccount' + }" + } + ''' + }) + 'metadata': object + + @description('Required. The name of the scale rule.') + name: string + + @description('Optional. The type of the rule.') + @metadata({ + example: ''' + { + "azure-servicebus" + "azure-queue" + "redis" + } + ''' + }) + 'type': string + }[] +} + +type initContainerType = { + @description('Optional. Container start command arguments.') + args: string[] + + @description('Optional. Container start command.') + command: string[] + + @description('Optional. The environment variables to set in the container.') + @metadata({ + example: ''' + [ + { + name: 'AZURE_STORAGE_QUEUE_NAME' + value: '' + } + { + name: 'AZURE_STORAGE_CONNECTION_STRING' + secretRef: 'connection-string' + } + ] + ''' + }) + env: containerEnvironmentVariablesType + + @description('Required. The image of the container.') + image: string + + @description('Required. The name of the container.') + name: string + + @description('Required. Container resource requirements.') + resources: containerResourceType + + @description('Optional. The volume mounts to attach to the container.') + volumeMounts: containerVolumeMountType +}[] + +type containerType = { + @description('Optional. Container start command arguments.') + args: string[]? + + @description('Optional. The command to run in the container.') + command: string[]? + + @description('Optional. The environment variables to set in the container.') + env: containerEnvironmentVariablesType + + @description('Required. The image of the container.') + image: string + + @description('Required. The name of the container.') + name: string + + @description('Optional. The probes of the container.') + probes: containerProbeType + + @description('Optional. The resources to allocate to the container.') + resources: containerResourceType + + @description('Optional. The volume mounts to attach to the container.') + volumeMounts: containerVolumeMountType +}[] diff --git a/avm/res/app/job/main.json b/avm/res/app/job/main.json index 58fed4a24e..8af9c58237 100644 --- a/avm/res/app/job/main.json +++ b/avm/res/app/job/main.json @@ -5,8 +5,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.26.170.59819", - "templateHash": "3096359783958038878" + "version": "0.27.1.19265", + "templateHash": "15082663564910990848" }, "name": "Container App Jobs", "description": "This module deploys a Container App Job.", @@ -58,8 +58,7 @@ "description": "Optional. Specify the type of lock." } } - }, - "nullable": true + } }, "roleAssignmentType": { "type": "array", @@ -126,6 +125,680 @@ } }, "nullable": true + }, + "registryType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "server": { + "type": "string", + "metadata": { + "example": "myregistry.azurecr.io", + "description": "Required. The FQDN name of the container registry." + } + }, + "identity": { + "type": "string", + "nullable": true, + "metadata": { + "example": " user-assigned identity: /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity\n system-assigned identity: system\n ", + "description": "Optional. The resource ID of the (user) managed identity, which is used to access the Azure Container Registry." + } + }, + "username": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. The username for the container registry." + } + }, + "passwordSecretRef": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The name of the secret contains the login password. Required if `username` is not null." + } + } + } + }, + "nullable": true + }, + "secretType": { + "type": "array", + "items": { + "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": { + "example": " https://myvault${environment().suffixes.keyvaultDns}/secrets/mysecret\n ", + "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." + } + } + } + }, + "nullable": true + }, + "volumeType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the volume." + } + }, + "mountOptions": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. Mount options used while mounting the Azure file share or NFS Azure file share. Must be a comma-separated string. Required if `storageType` is not `EmptyDir`." + } + }, + "secrets": { + "type": "array", + "items": { + "type": "object", + "properties": { + "path": { + "type": "string", + "metadata": { + "description": "Required. Path to project secret to. If no path is provided, path defaults to name of secret listed in secretRef." + } + }, + "secretRef": { + "type": "string", + "metadata": { + "description": "Required. Name of the Container App secret from which to pull the secret value." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. List of secrets to be added in volume. If no secrets are provided, all secrets in collection will be added to volume." + } + }, + "storageName": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The storage account name. Not needed for EmptyDir and Secret. Required if `storageType` is `AzureFile` or `NfsAzureFile`." + } + }, + "storageType": { + "type": "string", + "allowedValues": [ + "AzureFile", + "EmptyDir", + "NfsAzureFile", + "Secret" + ], + "metadata": { + "description": "Required. The container name." + } + } + } + }, + "nullable": true + }, + "containerEnvironmentVariablesType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The environment variable name." + } + }, + "secretRef": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The name of the Container App secret from which to pull the envrionment variable value. Required if `value` is null." + } + }, + "value": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Conditional. The environment variable value. Required if `secretRef` is null." + } + } + } + }, + "nullable": true + }, + "containerProbeHttpGetHttpHeadersItem": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The header field name." + } + }, + "value": { + "type": "string", + "metadata": { + "description": "Required. The header field value." + } + } + } + }, + "nullable": true + }, + "containerProbeType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "failureThreshold": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 10, + "metadata": { + "description": "Optional. Minimum consecutive failures for the probe to be considered failed after having succeeded. Defaults to 3." + } + }, + "httpGet": { + "type": "object", + "properties": { + "host": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Host name to connect to, defaults to the pod IP." + } + }, + "httpHeaders": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "metadata": { + "description": "Required. The header field name." + } + }, + "value": { + "type": "string", + "metadata": { + "description": "Required. The header field value." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. Custom headers to set in the request." + } + }, + "path": { + "type": "string", + "metadata": { + "description": "Required. Path to access on the HTTP server." + } + }, + "port": { + "type": "int", + "minValue": 1, + "maxValue": 65535, + "metadata": { + "description": "Required. Name of the port to access on the container. If not specified, the containerPort is used." + } + }, + "scheme": { + "type": "string", + "allowedValues": [ + "HTTP", + "HTTPS" + ], + "nullable": true, + "metadata": { + "description": "Required. Scheme to use for connecting to the host. Defaults to HTTP." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. HTTPGet specifies the http request to perform." + } + }, + "initialDelaySeconds": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 60, + "metadata": { + "description": "Optional. Number of seconds after the container has started before liveness probes are initiated. Defaults to 0 seconds." + } + }, + "periodSeconds": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 60, + "metadata": { + "description": "Optional. How often (in seconds) to perform the probe. Defaults to 10 seconds." + } + }, + "successThreshold": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 10, + "metadata": { + "description": "Optional. Minimum consecutive successes for the probe to be considered successful after having failed. Defaults to 1." + } + }, + "tcpSocket": { + "type": "object", + "properties": { + "host": { + "type": "string", + "metadata": { + "description": "Optional. Host name to connect to, defaults to the pod IP." + } + }, + "port": { + "type": "int", + "minValue": 1, + "maxValue": 65535, + "metadata": { + "description": "Required. Name of the port to access on the container. If not specified, the containerPort is used." + } + } + }, + "nullable": true, + "metadata": { + "description": "Optional. TCPSocket specifies an action involving a TCP port." + } + }, + "terminationGracePeriodSeconds": { + "type": "int", + "nullable": true, + "minValue": 0, + "maxValue": 3600, + "metadata": { + "description": "Optional. Duration in seconds the pod needs to terminate gracefully upon probe failure. This is an alpha field and requires enabling ProbeTerminationGracePeriod feature gate." + } + }, + "timeoutSeconds": { + "type": "int", + "nullable": true, + "minValue": 1, + "maxValue": 240, + "metadata": { + "description": "Optional. Number of seconds after which the probe times out. Defaults to 1 second." + } + }, + "type": { + "type": "string", + "allowedValues": [ + "Liveness", + "Readiness", + "Startup" + ], + "metadata": { + "description": "Required. The type of probe." + } + } + } + }, + "nullable": true + }, + "containerResourceType": { + "type": "object", + "properties": { + "cpu": { + "type": "string", + "metadata": { + "example": " '0.25'\n '1'\n ", + "description": "Required. The CPU limit of the container in cores." + } + }, + "memory": { + "type": "string", + "metadata": { + "example": " '250Mb'\n '1.5Gi'\n '1500Mi'\n ", + "description": "Optional. The required memory." + } + } + }, + "nullable": true + }, + "containerVolumeMountType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "mountPath": { + "type": "string", + "metadata": { + "description": "Required. The path within the container at which the volume should be mounted. Must not contain ':'." + } + }, + "subPath": { + "type": "string", + "nullable": true, + "metadata": { + "description": "Optional. Path within the volume from which the container's volume should be mounted." + } + }, + "volumeName": { + "type": "string", + "metadata": { + "description": "Required. This must match the Name of a Volume." + } + } + } + }, + "nullable": true + }, + "manualTriggerConfigType": { + "type": "object", + "properties": { + "parallelism": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Number of parallel replicas of a job that can run at a given time. Defaults to 1." + } + }, + "replicaCompletionCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Minimum number of successful replica completions before overall job completion. Must be equal or or less than the parallelism. Defaults to 1." + } + } + } + }, + "scheduleTriggerconfigType": { + "type": "object", + "properties": { + "cronExpression": { + "type": "string", + "metadata": { + "example": " '* * * * *' // Every minute, every hour, every day\n '0 0 * * *' // at 00:00 UTC every day\n ", + "description": "Required. Cron formatted repeating schedule (\"* * * * *\") of a Cron Job. It supports the standard [cron](https://en.wikipedia.org/wiki/Cron) expression syntax." + } + }, + "parallelism": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Number of parallel replicas of a job that can run at a given time. Defaults to 1." + } + }, + "replicaCompletionCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Number of successful completions of a job that are necessary to consider the job complete. Must be equal or or less than the parallelism. Defaults to 1." + } + } + } + }, + "eventTriggerConfigType": { + "type": "object", + "properties": { + "parallelism": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Number of parallel replicas of a job that can run at a given time. Defaults to 1." + } + }, + "replicaCompletionCount": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Minimum number of successful replica completions before overall job completion. Must be equal or or less than the parallelism. Defaults to 1." + } + }, + "scale": { + "$ref": "#/definitions/jobScaleType", + "metadata": { + "description": "Required. Scaling configurations for event driven jobs." + } + } + } + }, + "jobScaleType": { + "type": "object", + "properties": { + "maxExecutions": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Maximum number of job executions that are created for a trigger, default 100." + } + }, + "minExecutions": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Minimum number of job executions that are created for a trigger, default 0." + } + }, + "pollingInterval": { + "type": "int", + "nullable": true, + "metadata": { + "description": "Optional. Interval to check each event source in seconds. Defaults to 30s." + } + }, + "rules": { + "type": "array", + "items": { + "type": "object", + "properties": { + "auth": { + "type": "array", + "items": { + "type": "object", + "properties": { + "secretRef": { + "type": "string", + "metadata": { + "description": "Required. Name of the secret from which to pull the auth params." + } + }, + "triggerParameter": { + "type": "string", + "metadata": { + "description": "Required. Trigger Parameter that uses the secret." + } + } + } + }, + "nullable": true, + "metadata": { + "description": "Required. Authentication secrets for the scale rule." + } + }, + "metadata": { + "type": "object", + "metadata": { + "example": " {\n \"// for type azure-queue\n {\n queueName: 'default'\n storageAccountResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/myStorageAccount'\n }\"\n }\n ", + "description": "Optional. Metadata properties to describe the scale rule." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the scale rule." + } + }, + "type": { + "type": "string", + "metadata": { + "example": " {\n \"azure-servicebus\"\n \"azure-queue\"\n \"redis\"\n }\n ", + "description": "Optional. The type of the rule." + } + } + } + }, + "metadata": { + "example": " // for type azure-queue\n {\n name: 'myrule'\n type: 'azure-queue'\n metadata: {\n queueName: 'default'\n storageAccountResourceId: '/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/myStorageAccount'\n }\n auth: {\n secretRef: 'mysecret'\n triggerParameter: 'queueName'\n }\n }\n ", + "description": "Optional. Scaling rules for the job." + } + } + } + }, + "initContainerType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "args": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Optional. Container start command arguments." + } + }, + "command": { + "type": "array", + "items": { + "type": "string" + }, + "metadata": { + "description": "Optional. Container start command." + } + }, + "env": { + "$ref": "#/definitions/containerEnvironmentVariablesType", + "metadata": { + "example": " [\n {\n name: 'AZURE_STORAGE_QUEUE_NAME'\n value: ''\n }\n {\n name: 'AZURE_STORAGE_CONNECTION_STRING'\n secretRef: 'connection-string'\n }\n ]\n ", + "description": "Optional. The environment variables to set in the container." + } + }, + "image": { + "type": "string", + "metadata": { + "description": "Required. The image of the container." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the container." + } + }, + "resources": { + "$ref": "#/definitions/containerResourceType", + "metadata": { + "description": "Required. Container resource requirements." + } + }, + "volumeMounts": { + "$ref": "#/definitions/containerVolumeMountType", + "metadata": { + "description": "Optional. The volume mounts to attach to the container." + } + } + } + } + }, + "containerType": { + "type": "array", + "items": { + "type": "object", + "properties": { + "args": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. Container start command arguments." + } + }, + "command": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true, + "metadata": { + "description": "Optional. The command to run in the container." + } + }, + "env": { + "$ref": "#/definitions/containerEnvironmentVariablesType", + "metadata": { + "description": "Optional. The environment variables to set in the container." + } + }, + "image": { + "type": "string", + "metadata": { + "description": "Required. The image of the container." + } + }, + "name": { + "type": "string", + "metadata": { + "description": "Required. The name of the container." + } + }, + "probes": { + "$ref": "#/definitions/containerProbeType", + "metadata": { + "description": "Optional. The probes of the container." + } + }, + "resources": { + "$ref": "#/definitions/containerResourceType", + "metadata": { + "description": "Optional. The resources to allocate to the container." + } + }, + "volumeMounts": { + "$ref": "#/definitions/containerVolumeMountType", + "metadata": { + "description": "Optional. The volume mounts to attach to the container." + } + } + } + } } }, "parameters": { @@ -139,17 +812,18 @@ "type": "string", "defaultValue": "[resourceGroup().location]", "metadata": { - "description": "Optional. Location for all Resources." + "description": "Optional. Location for all Resources. Defaults to the location of the Resource Group." } }, "environmentResourceId": { "type": "string", "metadata": { - "description": "Required. Resource ID of environment." + "description": "Required. Resource ID of Container Apps Environment." } }, "lock": { "$ref": "#/definitions/lockType", + "nullable": true, "metadata": { "description": "Optional. The lock settings of the service." } @@ -158,19 +832,22 @@ "type": "object", "nullable": true, "metadata": { + "example": " {\n \"key1\": \"value1\",\n \"key2\": \"value2\"\n }\n ", "description": "Optional. Tags of the resource." } }, "registries": { - "type": "array", + "$ref": "#/definitions/registryType", "nullable": true, "metadata": { + "example": "[[\n {\n \"server\": \"myregistry.azurecr.io\",\n \"identity\": \"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity\"\n },\n {\n \"server\": \"myregistry2.azurecr.io\",\n \"identity\": \"system\"\n }\n ,\n {\n \"server\": \"myregistry3.azurecr.io\",\n \"username\": \"myusername\",\n \"passwordSecretRef\": \"secret-name\"\n }\n]", "description": "Optional. Collection of private container registry credentials for containers used by the Container app." } }, "managedIdentities": { "$ref": "#/definitions/managedIdentitiesType", "metadata": { + "example": " {\n \"systemAssigned\": true,\n \"userAssignedResourceIds\": [\n \"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity\"\n ]\n },\n {\n \"systemAssigned\": true\n }\n ", "description": "Optional. The managed identity definition for this resource." } }, @@ -188,37 +865,37 @@ } }, "containers": { - "type": "array", + "$ref": "#/definitions/containerType", "metadata": { "description": "Required. List of container definitions for the Container App." } }, - "initContainersTemplate": { - "type": "array", + "initContainers": { + "$ref": "#/definitions/initContainerType", "nullable": true, "metadata": { "description": "Optional. List of specialized containers that run before app containers." } }, "eventTriggerConfig": { - "type": "object", + "$ref": "#/definitions/eventTriggerConfigType", "nullable": true, "metadata": { - "description": "Optional. Required if TriggerType is Event. Configuration of an event driven job." + "description": "Conditional. Configuration of an event driven job. Required if `TriggerType` is `Event`." } }, "scheduleTriggerConfig": { - "type": "object", + "$ref": "#/definitions/scheduleTriggerconfigType", "nullable": true, "metadata": { - "description": "Optional. Required if TriggerType is Schedule. Configuration of a schedule based job." + "description": "Conditional. Configuration of a schedule based job. Required if `TriggerType` is `Schedule`." } }, "manualTriggerConfig": { - "type": "object", + "$ref": "#/definitions/manualTriggerConfigType", "nullable": true, "metadata": { - "description": "Optional. Required if TriggerType is Manual. Configuration of a manual job." + "description": "Conditional. Configuration of a manually triggered job. Required if `TriggerType` is `Manual`." } }, "replicaRetryLimit": { @@ -236,14 +913,15 @@ } }, "secrets": { - "type": "secureObject", + "$ref": "#/definitions/secretType", "nullable": true, "metadata": { + "example": "{\n \"name\": \"mysecret\"\n \"identity\": \"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myManagedIdentity\",\n \"keyVaultUrl\": \"https://myvault${environment().suffixes.keyvaultDns}/secrets/mysecret\",\n},\n{\n \"name\": \"mysecret\"\n \"identity\": \"system\",\n \"keyVaultUrl\": \"https://myvault${environment().suffixes.keyvaultDns}/secrets/mysecret\",\n},\n{\n \"name\": \"mysecret\",\n \"value\": \"mysecretvalue\"\n}\n{\n name: 'connection-string'\n value: listKeys('/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.Storage/storageAccounts/myStorageAccount', '2023-04-01').keys[0].value\n}\n", "description": "Optional. The secrets of the Container App." } }, "volumes": { - "type": "array", + "$ref": "#/definitions/volumeType", "nullable": true, "metadata": { "description": "Optional. List of volume definitions for the Container App." @@ -269,7 +947,6 @@ } }, "variables": { - "secretList": "[tryGet(parameters('secrets'), 'secureList')]", "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": { @@ -304,7 +981,7 @@ }, "job": { "type": "Microsoft.App/jobs", - "apiVersion": "2023-05-01", + "apiVersion": "2024-03-01", "name": "[parameters('name')]", "tags": "[parameters('tags')]", "location": "[parameters('location')]", @@ -312,18 +989,18 @@ "properties": { "environmentId": "[parameters('environmentResourceId')]", "configuration": { + "triggerType": "[parameters('triggerType')]", "eventTriggerConfig": "[if(equals(parameters('triggerType'), 'Event'), parameters('eventTriggerConfig'), null())]", "manualTriggerConfig": "[if(equals(parameters('triggerType'), 'Manual'), parameters('manualTriggerConfig'), null())]", "scheduleTriggerConfig": "[if(equals(parameters('triggerType'), 'Schedule'), parameters('scheduleTriggerConfig'), null())]", "replicaRetryLimit": "[parameters('replicaRetryLimit')]", "replicaTimeout": "[parameters('replicaTimeout')]", "registries": "[parameters('registries')]", - "secrets": "[variables('secretList')]", - "triggerType": "[parameters('triggerType')]" + "secrets": "[parameters('secrets')]" }, "template": { "containers": "[parameters('containers')]", - "initContainers": "[parameters('initContainersTemplate')]", + "initContainers": "[parameters('initContainers')]", "volumes": "[parameters('volumes')]" }, "workloadProfileName": "[parameters('workloadProfileName')]" @@ -370,6 +1047,7 @@ "resourceId": { "type": "string", "metadata": { + "example": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/myResourceGroup/providers/Microsoft.App/jobs/myJob", "description": "The resource ID of the Container App Job." }, "value": "[resourceId('Microsoft.App/jobs', parameters('name'))]" @@ -377,6 +1055,7 @@ "resourceGroupName": { "type": "string", "metadata": { + "example": "myResourceGroup", "description": "The name of the resource group the Container App Job was deployed into." }, "value": "[resourceGroup().name]" @@ -384,6 +1063,7 @@ "name": { "type": "string", "metadata": { + "example": "myJob", "description": "The name of the Container App Job." }, "value": "[parameters('name')]" @@ -391,16 +1071,18 @@ "location": { "type": "string", "metadata": { + "example": "Germany West Central", "description": "The location the resource was deployed into." }, - "value": "[reference('job', '2023-05-01', 'full').location]" + "value": "[reference('job', '2024-03-01', 'full').location]" }, "systemAssignedMIPrincipalId": { "type": "string", "metadata": { + "example": "'00000000-0000-0000-0000-000000000000'\n ''", "description": "The principal ID of the system assigned identity." }, - "value": "[coalesce(tryGet(tryGet(reference('job', '2023-05-01', 'full'), 'identity'), 'principalId'), '')]" + "value": "[coalesce(tryGet(tryGet(reference('job', '2024-03-01', 'full'), 'identity'), 'principalId'), '')]" } } } \ No newline at end of file diff --git a/avm/res/app/job/tests/e2e/defaults/main.test.bicep b/avm/res/app/job/tests/e2e/defaults/main.test.bicep index 546fa7c4c8..9f4d8fab23 100644 --- a/avm/res/app/job/tests/e2e/defaults/main.test.bicep +++ b/avm/res/app/job/tests/e2e/defaults/main.test.bicep @@ -54,17 +54,13 @@ module testDeployment '../../../main.bicep' = [ environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId location: resourceLocation triggerType: 'Manual' - manualTriggerConfig: { - replicaCompletionCount: 1 - parallelism: 1 - } + manualTriggerConfig: {} containers: [ { name: 'simple-hello-world-container' image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' resources: { - // workaround as 'float' values are not supported in Bicep, yet the resource providers expects them. Related issue: https://github.com/Azure/bicep/issues/1386 - cpu: json('0.25') + cpu: '0.25' memory: '0.5Gi' } } diff --git a/avm/res/app/job/tests/e2e/max/dependencies.bicep b/avm/res/app/job/tests/e2e/max/dependencies.bicep index b03d4aca93..1955015c44 100644 --- a/avm/res/app/job/tests/e2e/max/dependencies.bicep +++ b/avm/res/app/job/tests/e2e/max/dependencies.bicep @@ -10,6 +10,9 @@ param managedIdentityName string @description('Required. The name of the workload profile to create.') param workloadProfileName string +@description('Required. The name of the storage account to create.') +param storageAccountName string + resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = { name: managedEnvironmentName location: location @@ -30,6 +33,36 @@ resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2022- location: location } +resource storageAccount 'Microsoft.Storage/storageAccounts@2023-04-01' = { + name: storageAccountName + location: location + kind: 'StorageV2' + sku: { + name: 'Standard_LRS' + } + properties: { + allowSharedKeyAccess: true + allowBlobPublicAccess: false + minimumTlsVersion: 'TLS1_2' + networkAcls: { + bypass: 'AzureServices' + defaultAction: 'Deny' + } + } + tags: { + 'hidden-title': 'This is visible in the resource name' + Env: 'test' + } + + resource storageQueueService 'queueServices@2023-04-01' = { + name: 'default' + + resource storageQueue 'queues@2023-04-01' = { + name: 'jobs-queue' + } + } +} + @description('The resource ID of the created Managed Identity.') output managedIdentityResourceId string = managedIdentity.id @@ -38,3 +71,12 @@ output managedEnvironmentResourceId string = managedEnvironment.id @description('The principal ID of the created Managed Identity.') output managedIdentityPrincipalId string = managedIdentity.properties.principalId + +@description('The resource ID of the created storage account') +output storageAccountResourceId string = storageAccount.id + +@description('The name of the storage account created.') +output storageAccountName string = storageAccount.name + +@description('The name of the storage queue created.') +output storageQueueName string = storageAccount::storageQueueService::storageQueue.name diff --git a/avm/res/app/job/tests/e2e/max/main.test.bicep b/avm/res/app/job/tests/e2e/max/main.test.bicep index 18ae51956b..b31e72f824 100644 --- a/avm/res/app/job/tests/e2e/max/main.test.bicep +++ b/avm/res/app/job/tests/e2e/max/main.test.bicep @@ -20,6 +20,9 @@ param serviceShort string = 'ajmax' @description('Optional. A token to inject into the name of each resource.') param namePrefix string = '#_namePrefix_#' +// needed for the storage account itself and for using listKeys in the secrets, as the storage account is created in the nested deployment and the value needs to exist at the time of deployment +var storageAccountName = uniqueString('dep-${namePrefix}-menv-${serviceShort}storage') + // =========== // // Deployments // // =========== // @@ -39,6 +42,7 @@ module nestedDependencies 'dependencies.bicep' = { managedEnvironmentName: 'dep-${namePrefix}-menv-${serviceShort}' managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}' workloadProfileName: serviceShort + storageAccountName: storageAccountName } } @@ -70,27 +74,49 @@ module testDeployment '../../../main.bicep' = [ nestedDependencies.outputs.managedIdentityResourceId ] } - secrets: { - secureList: [ - { - name: 'customtest' - value: guid(deployment().name) - } - ] - } - triggerType: 'Manual' - manualTriggerConfig: { - replicaCompletionCount: 1 + triggerType: 'Event' + eventTriggerConfig: { parallelism: 1 + replicaCompletionCount: 1 + scale: { + minExecutions: 1 + maxExecutions: 1 + pollingInterval: 55 + rules: [ + { + name: 'queue' + type: 'azure-queue' + metadata: { + queueName: nestedDependencies.outputs.storageQueueName + storageAccountResourceId: nestedDependencies.outputs.storageAccountResourceId + } + auth: [ + { + secretRef: 'connectionString' + triggerParameter: 'connection' + } + ] + } + ] + } } + secrets: [ + { + name: 'connection-string' + // needed for using listKeys in the secrets, as the storage account is created in the nested deployment and the value needs to exist at the time of deployment + value: listKeys( + '${resourceGroup.id}/providers/Microsoft.Storage/storageAccounts/${storageAccountName}', + '2023-04-01' + ).keys[0].value + } + ] containers: [ { name: 'simple-hello-world-container' image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' resources: { - // workaround as 'float' values are not supported in Bicep, yet the resource providers expects them. Related issue: https://github.com/Azure/bicep/issues/1386 - cpu: json('0.25') - memory: '0.5Gi' + cpu: '1.25' + memory: '1.5Gi' } probes: [ { @@ -109,6 +135,48 @@ module testDeployment '../../../main.bicep' = [ periodSeconds: 3 } ] + env: [ + { + name: 'AZURE_STORAGE_QUEUE_NAME' + value: nestedDependencies.outputs.storageQueueName + } + { + name: 'AZURE_STORAGE_CONNECTION_STRING' + secretRef: 'connection-string' + } + ] + volumeMounts: [ + { + volumeName: '${namePrefix}${serviceShort}emptydir' + mountPath: '/mnt/data' + } + ] + } + { + name: 'second-simple-container' + image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' + env: [ + { + name: 'SOME_ENV_VAR' + value: 'some-value' + } + ] + args: [ + 'arg1' + 'arg2' + ] + command: [ + '/bin/bash' + '-c' + 'echo hello' + 'sleep 100000' + ] + } + ] + volumes: [ + { + storageType: 'EmptyDir' + name: '${namePrefix}${serviceShort}emptydir' } ] roleAssignments: [ diff --git a/avm/res/app/job/tests/e2e/waf-aligned/main.test.bicep b/avm/res/app/job/tests/e2e/waf-aligned/main.test.bicep index 93b2d344ab..6f73900195 100644 --- a/avm/res/app/job/tests/e2e/waf-aligned/main.test.bicep +++ b/avm/res/app/job/tests/e2e/waf-aligned/main.test.bicep @@ -60,18 +60,16 @@ module testDeployment '../../../main.bicep' = [ environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId workloadProfileName: serviceShort location: resourceLocation - triggerType: 'Manual' - manualTriggerConfig: { - replicaCompletionCount: 1 - parallelism: 1 + triggerType: 'Schedule' + scheduleTriggerConfig: { + cronExpression: '0 0 * * *' } containers: [ { name: 'simple-hello-world-container' image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest' resources: { - // workaround as 'float' values are not supported in Bicep, yet the resource providers expects them. Related issue: https://github.com/Azure/bicep/issues/1386 - cpu: json('0.25') + cpu: '0.25' memory: '0.5Gi' } probes: [ diff --git a/avm/res/app/job/version.json b/avm/res/app/job/version.json index 7fa401bdf7..1c035df49f 100644 --- a/avm/res/app/job/version.json +++ b/avm/res/app/job/version.json @@ -1,7 +1,7 @@ { "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#", - "version": "0.1", + "version": "0.2", "pathFilters": [ "./main.json" ] -} +} \ No newline at end of file