.If no value is provided, a new temporary VNET and subnet will be created in the staging resource group and will be deleted along with the remaining temporary resources. |
| [`tags`](#parameter-tags) | object | Tags of the resource. |
+| [`validationProcess`](#parameter-validationprocess) | object | Configuration options and list of validations to be performed on the resulting image. |
| [`vmSize`](#parameter-vmsize) | string | Specifies the size for the VM. |
-| [`vmUserAssignedIdentities`](#parameter-vmuserassignedidentities) | array | List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts.Be aware, the user assigned identities specified in the \'managedIdentities\' parameter must have the \'Managed Identity Operator\' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM.
|
+| [`vmUserAssignedIdentities`](#parameter-vmuserassignedidentities) | array | List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts. Be aware, the user assigned identities specified in the 'managedIdentities' parameter must have the 'Managed Identity Operator' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM. |
**Generated parameters**
@@ -641,6 +675,20 @@ Specify the name of lock.
- Required: No
- Type: string
+### Parameter: `optimizeVmBoot`
+
+The optimize property can be enabled while creating a VM image and allows VM optimization to improve image creation time.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'Disabled'
+ 'Enabled'
+ ]
+ ```
+
### Parameter: `osDiskSizeGB`
Specifies the size of OS disk.
@@ -759,6 +807,140 @@ Tags of the resource.
- Required: No
- Type: object
+### Parameter: `validationProcess`
+
+Configuration options and list of validations to be performed on the resulting image.
+
+- Required: No
+- Type: object
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`continueDistributeOnFailure`](#parameter-validationprocesscontinuedistributeonfailure) | bool | If validation fails and this field is set to false, output image(s) will not be distributed. This is the default behavior. If validation fails and this field is set to true, output image(s) will still be distributed. Please use this option with caution as it may result in bad images being distributed for use. In either case (true or false), the end to end image run will be reported as having failed in case of a validation failure. [Note: This field has no effect if validation succeeds.]. |
+| [`inVMValidations`](#parameter-validationprocessinvmvalidations) | array | A list of validators that will be performed on the image. Azure Image Builder supports File, PowerShell and Shell validators. |
+| [`sourceValidationOnly`](#parameter-validationprocesssourcevalidationonly) | bool | If this field is set to true, the image specified in the 'source' section will directly be validated. No separate build will be run to generate and then validate a customized image. Not supported when performing customizations, validations or distributions on the image. |
+
+### Parameter: `validationProcess.continueDistributeOnFailure`
+
+If validation fails and this field is set to false, output image(s) will not be distributed. This is the default behavior. If validation fails and this field is set to true, output image(s) will still be distributed. Please use this option with caution as it may result in bad images being distributed for use. In either case (true or false), the end to end image run will be reported as having failed in case of a validation failure. [Note: This field has no effect if validation succeeds.].
+
+- Required: No
+- Type: bool
+
+### Parameter: `validationProcess.inVMValidations`
+
+A list of validators that will be performed on the image. Azure Image Builder supports File, PowerShell and Shell validators.
+
+- Required: No
+- Type: array
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`type`](#parameter-validationprocessinvmvalidationstype) | string | The type of validation. |
+
+**Optional parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`destination`](#parameter-validationprocessinvmvalidationsdestination) | string | Destination of the file. |
+| [`inline`](#parameter-validationprocessinvmvalidationsinline) | array | Array of commands to be run, separated by commas. |
+| [`name`](#parameter-validationprocessinvmvalidationsname) | string | Friendly Name to provide context on what this validation step does. |
+| [`runAsSystem`](#parameter-validationprocessinvmvalidationsrunassystem) | bool | If specified, the PowerShell script will be run with elevated privileges using the Local System user. Can only be true when the runElevated field above is set to true. |
+| [`runElevated`](#parameter-validationprocessinvmvalidationsrunelevated) | bool | If specified, the PowerShell script will be run with elevated privileges. |
+| [`scriptUri`](#parameter-validationprocessinvmvalidationsscripturi) | string | URI of the PowerShell script to be run for validation. It can be a github link, Azure Storage URI, etc. |
+| [`sha256Checksum`](#parameter-validationprocessinvmvalidationssha256checksum) | string | Value of sha256 checksum of the file, you generate this locally, and then Image Builder will checksum and validate. |
+| [`sourceUri`](#parameter-validationprocessinvmvalidationssourceuri) | string | The source URI of the file. |
+| [`validExitCodes`](#parameter-validationprocessinvmvalidationsvalidexitcodes) | array | Valid codes that can be returned from the script/inline command, this avoids reported failure of the script/inline command. |
+
+### Parameter: `validationProcess.inVMValidations.type`
+
+The type of validation.
+
+- Required: Yes
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'File'
+ 'PowerShell'
+ 'Shell'
+ ]
+ ```
+
+### Parameter: `validationProcess.inVMValidations.destination`
+
+Destination of the file.
+
+- Required: No
+- Type: string
+
+### Parameter: `validationProcess.inVMValidations.inline`
+
+Array of commands to be run, separated by commas.
+
+- Required: No
+- Type: array
+
+### Parameter: `validationProcess.inVMValidations.name`
+
+Friendly Name to provide context on what this validation step does.
+
+- Required: No
+- Type: string
+
+### Parameter: `validationProcess.inVMValidations.runAsSystem`
+
+If specified, the PowerShell script will be run with elevated privileges using the Local System user. Can only be true when the runElevated field above is set to true.
+
+- Required: No
+- Type: bool
+
+### Parameter: `validationProcess.inVMValidations.runElevated`
+
+If specified, the PowerShell script will be run with elevated privileges.
+
+- Required: No
+- Type: bool
+
+### Parameter: `validationProcess.inVMValidations.scriptUri`
+
+URI of the PowerShell script to be run for validation. It can be a github link, Azure Storage URI, etc.
+
+- Required: No
+- Type: string
+
+### Parameter: `validationProcess.inVMValidations.sha256Checksum`
+
+Value of sha256 checksum of the file, you generate this locally, and then Image Builder will checksum and validate.
+
+- Required: No
+- Type: string
+
+### Parameter: `validationProcess.inVMValidations.sourceUri`
+
+The source URI of the file.
+
+- Required: No
+- Type: string
+
+### Parameter: `validationProcess.inVMValidations.validExitCodes`
+
+Valid codes that can be returned from the script/inline command, this avoids reported failure of the script/inline command.
+
+- Required: No
+- Type: array
+
+### Parameter: `validationProcess.sourceValidationOnly`
+
+If this field is set to true, the image specified in the 'source' section will directly be validated. No separate build will be run to generate and then validate a customized image. Not supported when performing customizations, validations or distributions on the image.
+
+- Required: No
+- Type: bool
+
### Parameter: `vmSize`
Specifies the size for the VM.
@@ -769,7 +951,7 @@ Specifies the size for the VM.
### Parameter: `vmUserAssignedIdentities`
-List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts.
Be aware, the user assigned identities specified in the \'managedIdentities\' parameter must have the \'Managed Identity Operator\' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM.
+List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts. Be aware, the user assigned identities specified in the 'managedIdentities' parameter must have the 'Managed Identity Operator' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM.
- Required: No
- Type: array
diff --git a/avm/res/virtual-machine-images/image-template/main.bicep b/avm/res/virtual-machine-images/image-template/main.bicep
index 4c46be1680..2a61c05386 100644
--- a/avm/res/virtual-machine-images/image-template/main.bicep
+++ b/avm/res/virtual-machine-images/image-template/main.bicep
@@ -49,15 +49,22 @@ param roleAssignments roleAssignmentType
@description('Required. The distribution targets where the image output needs to go to.')
param distributions distributionType[]
-@description('''
-Optional. List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts.
-Be aware, the user assigned identities specified in the \'managedIdentities\' parameter must have the \'Managed Identity Operator\' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM.
-''')
+@description('Optional. List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts. Be aware, the user assigned identities specified in the \'managedIdentities\' parameter must have the \'Managed Identity Operator\' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM.')
param vmUserAssignedIdentities array = []
@description('Required. The managed identity definition for this resource.')
param managedIdentities managedIdentitiesType
+@description('Optional. Configuration options and list of validations to be performed on the resulting image.')
+param validationProcess validationProcessType
+
+@allowed([
+ 'Enabled'
+ 'Disabled'
+])
+@description('Optional. The optimize property can be enabled while creating a VM image and allows VM optimization to improve image creation time.')
+param optimizeVmBoot string?
+
var identity = {
type: 'UserAssigned'
userAssignedIdentities: reduce(
@@ -100,7 +107,7 @@ resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' =
}
}
-resource imageTemplate 'Microsoft.VirtualMachineImages/imageTemplates@2022-02-14' = {
+resource imageTemplate 'Microsoft.VirtualMachineImages/imageTemplates@2023-07-01' = {
#disable-next-line use-stable-resource-identifiers // Disabling as ImageTemplates are not idempotent and hence always must have new name
name: '${name}-${baseTime}'
location: location
@@ -164,6 +171,14 @@ resource imageTemplate 'Microsoft.VirtualMachineImages/imageTemplates@2022-02-14
: {})
)
]
+ validate: validationProcess
+ optimize: optimizeVmBoot != null
+ ? {
+ vmBoot: {
+ state: optimizeVmBoot
+ }
+ }
+ : null
}
}
@@ -316,6 +331,47 @@ type managedImageDistributionType = {
@description('Required. The resource ID of the managed image. Defaults to a compute image with name \'imageName-baseTime\' in the current resource group.')
imageResourceId: string?
- @description('Conditional. Name of the managed or unmanaged image that will be created..')
+ @description('Conditional. Name of the managed or unmanaged image that will be created.')
imageName: string
}
+
+type validationProcessType = {
+ @description('Optional. If validation fails and this field is set to false, output image(s) will not be distributed. This is the default behavior. If validation fails and this field is set to true, output image(s) will still be distributed. Please use this option with caution as it may result in bad images being distributed for use. In either case (true or false), the end to end image run will be reported as having failed in case of a validation failure. [Note: This field has no effect if validation succeeds.].')
+ continueDistributeOnFailure: bool?
+
+ @description('Optional. A list of validators that will be performed on the image. Azure Image Builder supports File, PowerShell and Shell validators.')
+ inVMValidations: {
+ @description('Required. The type of validation.')
+ type: ('PowerShell' | 'Shell' | 'File')
+
+ @description('Optional. Friendly Name to provide context on what this validation step does.')
+ name: string?
+
+ @description('Optional. URI of the PowerShell script to be run for validation. It can be a github link, Azure Storage URI, etc.')
+ scriptUri: string?
+
+ @description('Optional. Array of commands to be run, separated by commas.')
+ inline: string[]?
+
+ @description('Optional. Valid codes that can be returned from the script/inline command, this avoids reported failure of the script/inline command.')
+ validExitCodes: int[]?
+
+ @description('Optional. Value of sha256 checksum of the file, you generate this locally, and then Image Builder will checksum and validate.')
+ sha256Checksum: string?
+
+ @description('Optional. The source URI of the file.')
+ sourceUri: string?
+
+ @description('Optional. Destination of the file.')
+ destination: string?
+
+ @description('Optional. If specified, the PowerShell script will be run with elevated privileges using the Local System user. Can only be true when the runElevated field above is set to true.')
+ runAsSystem: bool?
+
+ @description('Optional. If specified, the PowerShell script will be run with elevated privileges.')
+ runElevated: bool?
+ }[]?
+
+ @description('Optional. If this field is set to true, the image specified in the \'source\' section will directly be validated. No separate build will be run to generate and then validate a customized image. Not supported when performing customizations, validations or distributions on the image.')
+ sourceValidationOnly: bool?
+}?
diff --git a/avm/res/virtual-machine-images/image-template/main.json b/avm/res/virtual-machine-images/image-template/main.json
index e2bef8c3df..f3894b7f80 100644
--- a/avm/res/virtual-machine-images/image-template/main.json
+++ b/avm/res/virtual-machine-images/image-template/main.json
@@ -5,8 +5,8 @@
"metadata": {
"_generator": {
"name": "bicep",
- "version": "0.26.54.24096",
- "templateHash": "4620280027037163005"
+ "version": "0.26.170.59819",
+ "templateHash": "16618215702378872010"
},
"name": "Virtual Machine Image Templates",
"description": "This module deploys a Virtual Machine Image Template that can be consumed by Azure Image Builder (AIB).",
@@ -281,10 +281,122 @@
"imageName": {
"type": "string",
"metadata": {
- "description": "Conditional. Name of the managed or unmanaged image that will be created.."
+ "description": "Conditional. Name of the managed or unmanaged image that will be created."
}
}
}
+ },
+ "validationProcessType": {
+ "type": "object",
+ "properties": {
+ "continueDistributeOnFailure": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. If validation fails and this field is set to false, output image(s) will not be distributed. This is the default behavior. If validation fails and this field is set to true, output image(s) will still be distributed. Please use this option with caution as it may result in bad images being distributed for use. In either case (true or false), the end to end image run will be reported as having failed in case of a validation failure. [Note: This field has no effect if validation succeeds.]."
+ }
+ },
+ "inVMValidations": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "allowedValues": [
+ "File",
+ "PowerShell",
+ "Shell"
+ ],
+ "metadata": {
+ "description": "Required. The type of validation."
+ }
+ },
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Friendly Name to provide context on what this validation step does."
+ }
+ },
+ "scriptUri": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. URI of the PowerShell script to be run for validation. It can be a github link, Azure Storage URI, etc."
+ }
+ },
+ "inline": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Array of commands to be run, separated by commas."
+ }
+ },
+ "validExitCodes": {
+ "type": "array",
+ "items": {
+ "type": "int"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Valid codes that can be returned from the script/inline command, this avoids reported failure of the script/inline command."
+ }
+ },
+ "sha256Checksum": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Value of sha256 checksum of the file, you generate this locally, and then Image Builder will checksum and validate."
+ }
+ },
+ "sourceUri": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The source URI of the file."
+ }
+ },
+ "destination": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Destination of the file."
+ }
+ },
+ "runAsSystem": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. If specified, the PowerShell script will be run with elevated privileges using the Local System user. Can only be true when the runElevated field above is set to true."
+ }
+ },
+ "runElevated": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. If specified, the PowerShell script will be run with elevated privileges."
+ }
+ }
+ }
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. A list of validators that will be performed on the image. Azure Image Builder supports File, PowerShell and Shell validators."
+ }
+ },
+ "sourceValidationOnly": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. If this field is set to true, the image specified in the 'source' section will directly be validated. No separate build will be run to generate and then validate a customized image. Not supported when performing customizations, validations or distributions on the image."
+ }
+ }
+ },
+ "nullable": true
}
},
"parameters": {
@@ -396,7 +508,7 @@
"type": "array",
"defaultValue": [],
"metadata": {
- "description": "Optional. List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts.\nBe aware, the user assigned identities specified in the \\'managedIdentities\\' parameter must have the \\'Managed Identity Operator\\' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM.\n"
+ "description": "Optional. List of User-Assigned Identities associated to the Build VM for accessing Azure resources such as Key Vaults from your customizer scripts. Be aware, the user assigned identities specified in the 'managedIdentities' parameter must have the 'Managed Identity Operator' role assignment on all the user assigned identities specified in this parameter for Azure Image Builder to be able to associate them to the build VM."
}
},
"managedIdentities": {
@@ -404,6 +516,23 @@
"metadata": {
"description": "Required. The managed identity definition for this resource."
}
+ },
+ "validationProcess": {
+ "$ref": "#/definitions/validationProcessType",
+ "metadata": {
+ "description": "Optional. Configuration options and list of validations to be performed on the resulting image."
+ }
+ },
+ "optimizeVmBoot": {
+ "type": "string",
+ "nullable": true,
+ "allowedValues": [
+ "Enabled",
+ "Disabled"
+ ],
+ "metadata": {
+ "description": "Optional. The optimize property can be enabled while creating a VM image and allows VM optimization to improve image creation time."
+ }
}
},
"variables": {
@@ -442,7 +571,7 @@
},
"imageTemplate": {
"type": "Microsoft.VirtualMachineImages/imageTemplates",
- "apiVersion": "2022-02-14",
+ "apiVersion": "2023-07-01",
"name": "[format('{0}-{1}', parameters('name'), parameters('baseTime'))]",
"location": "[parameters('location')]",
"tags": "[parameters('tags')]",
@@ -464,7 +593,9 @@
},
"source": "[parameters('imageSource')]",
"customize": "[parameters('customizationSteps')]",
- "stagingResourceGroup": "[parameters('stagingResourceGroup')]"
+ "stagingResourceGroup": "[parameters('stagingResourceGroup')]",
+ "validate": "[parameters('validationProcess')]",
+ "optimize": "[if(not(equals(parameters('optimizeVmBoot'), null())), createObject('vmBoot', createObject('state', parameters('optimizeVmBoot'))), null())]"
}
},
"imageTemplate_lock": {
@@ -545,7 +676,7 @@
"metadata": {
"description": "The location the resource was deployed into."
},
- "value": "[reference('imageTemplate', '2022-02-14', 'full').location]"
+ "value": "[reference('imageTemplate', '2023-07-01', 'full').location]"
}
}
}
\ No newline at end of file
diff --git a/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep b/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep
index 2bd4869dd5..49eba449c6 100644
--- a/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep
+++ b/avm/res/virtual-machine-images/image-template/tests/e2e/max/main.test.bicep
@@ -81,6 +81,20 @@ module testDeployment '../../../main.bicep' = {
]
}
]
+ validationProcess: {
+ continueDistributeOnFailure: true
+ sourceValidationOnly: false
+ inVMValidations: [
+ {
+ type: 'Shell'
+ name: 'Validate-Software'
+ inline: [
+ 'echo "Software validation successful."'
+ ]
+ }
+ ]
+ }
+ optimizeVmBoot: 'Enabled'
imageSource: {
type: 'PlatformImage'
publisher: 'canonical'
diff --git a/avm/res/virtual-machine-images/image-template/version.json b/avm/res/virtual-machine-images/image-template/version.json
index 7fa401bdf7..9481fea58e 100644
--- a/avm/res/virtual-machine-images/image-template/version.json
+++ b/avm/res/virtual-machine-images/image-template/version.json
@@ -1,6 +1,6 @@
{
"$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
- "version": "0.1",
+ "version": "0.2",
"pathFilters": [
"./main.json"
]
From a302384a779c9b32d15ba8378ac902d8df5b5778 Mon Sep 17 00:00:00 2001
From: Alexander Sehr
Date: Sat, 11 May 2024 18:43:33 +0200
Subject: [PATCH 8/9] feat: Migrated module App/Jobs from CARML -
`avm/res/app/job` (#1823)
## Description
Migrated `Microsoft.App/jobs` from CARML & updated to latest specs
cc: @MrRoundRobin fyi
## Pipeline Reference
| Pipeline |
| -------- |
|
[![avm.res.app.job](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.job.yml/badge.svg?branch=users%2Falsehr%2FappJobsModule&event=workflow_dispatch)](https://github.com/AlexanderSehr/bicep-registry-modules/actions/workflows/avm.res.app.job.yml)
|
## Type of Change
- [ ] Update to CI Environment or utlities (Non-module effecting
changes)
- [x] Azure Verified Module updates:
- [ ] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [ ] Update to documentation
---------
Co-authored-by: Erika Gressi <56914614+eriqua@users.noreply.github.com>
---
.github/CODEOWNERS | 2 +-
.github/ISSUE_TEMPLATE/avm_module_issue.yml | 1 +
.github/workflows/avm.res.app.job.yml | 90 ++
avm/res/app/job/ORPHANED.md | 4 +
avm/res/app/job/README.md | 812 ++++++++++++++++++
avm/res/app/job/main.bicep | 236 +++++
avm/res/app/job/main.json | 406 +++++++++
.../job/tests/e2e/defaults/dependencies.bicep | 21 +
.../job/tests/e2e/defaults/main.test.bicep | 74 ++
.../app/job/tests/e2e/max/dependencies.bicep | 40 +
avm/res/app/job/tests/e2e/max/main.test.bicep | 136 +++
.../tests/e2e/waf-aligned/dependencies.bicep | 40 +
.../job/tests/e2e/waf-aligned/main.test.bicep | 98 +++
avm/res/app/job/version.json | 7 +
14 files changed, 1966 insertions(+), 1 deletion(-)
create mode 100644 .github/workflows/avm.res.app.job.yml
create mode 100644 avm/res/app/job/ORPHANED.md
create mode 100644 avm/res/app/job/README.md
create mode 100644 avm/res/app/job/main.bicep
create mode 100644 avm/res/app/job/main.json
create mode 100644 avm/res/app/job/tests/e2e/defaults/dependencies.bicep
create mode 100644 avm/res/app/job/tests/e2e/defaults/main.test.bicep
create mode 100644 avm/res/app/job/tests/e2e/max/dependencies.bicep
create mode 100644 avm/res/app/job/tests/e2e/max/main.test.bicep
create mode 100644 avm/res/app/job/tests/e2e/waf-aligned/dependencies.bicep
create mode 100644 avm/res/app/job/tests/e2e/waf-aligned/main.test.bicep
create mode 100644 avm/res/app/job/version.json
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index a35c3158ea..893e030b61 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -7,10 +7,10 @@
/avm/ptn/authorization/role-assignment/ @Azure/avm-ptn-authorization-roleassignment-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/ptn/security/security-center/ @Azure/avm-ptn-security-securitycenter-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/aad/domain-service/ @Azure/avm-res-aad-domainservice-module-owners-bicep @Azure/avm-core-team-technical-bicep
-#/avm/res/aad/domain-service/ @Azure/avm-res-aad-domainservice-module-owners-bicep
/avm/res/analysis-services/server/ @Azure/avm-res-analysisservices-server-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/api-management/service/ @Azure/avm-res-apimanagement-service-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/app/container-app/ @Azure/avm-res-app-containerapp-module-owners-bicep @Azure/avm-core-team-technical-bicep
+/avm/res/app/job/ @Azure/avm-res-app-job-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/app/managed-environment/ @Azure/avm-res-app-managedenvironment-module-owners-bicep @Azure/avm-core-team-technical-bicep
/avm/res/app-configuration/configuration-store/ @Azure/avm-res-appconfiguration-configurationstore-module-owners-bicep @Azure/avm-core-team-technical-bicep
#/avm/res/authorization/lock/ @Azure/avm-res-authorization-lock-module-owners-bicep @Azure/avm-core-team-technical-bicep
diff --git a/.github/ISSUE_TEMPLATE/avm_module_issue.yml b/.github/ISSUE_TEMPLATE/avm_module_issue.yml
index 459c075404..bd18fad337 100644
--- a/.github/ISSUE_TEMPLATE/avm_module_issue.yml
+++ b/.github/ISSUE_TEMPLATE/avm_module_issue.yml
@@ -51,6 +51,7 @@ body:
- "avm/res/api-management/service"
- "avm/res/app-configuration/configuration-store"
- "avm/res/app/container-app"
+ - "avm/res/app/job"
- "avm/res/app/managed-environment"
- "avm/res/automation/automation-account"
- "avm/res/batch/batch-account"
diff --git a/.github/workflows/avm.res.app.job.yml b/.github/workflows/avm.res.app.job.yml
new file mode 100644
index 0000000000..1d91926f99
--- /dev/null
+++ b/.github/workflows/avm.res.app.job.yml
@@ -0,0 +1,90 @@
+name: "avm.res.app.job"
+
+on:
+ schedule:
+ - cron: "0 12 1/15 * *" # Bi-Weekly Test (on 1st & 15th of month)
+ workflow_dispatch:
+ inputs:
+ staticValidation:
+ type: boolean
+ description: "Execute static validation"
+ required: false
+ default: true
+ deploymentValidation:
+ type: boolean
+ description: "Execute deployment validation"
+ required: false
+ default: true
+ removeDeployment:
+ type: boolean
+ description: "Remove deployed module"
+ required: false
+ default: true
+ customLocation:
+ type: string
+ description: "Default location overwrite (e.g., eastus)"
+ required: false
+ push:
+ branches:
+ - main
+ paths:
+ - ".github/actions/templates/avm-**"
+ - ".github/workflows/avm.template.module.yml"
+ - ".github/workflows/avm.res.app.job.yml"
+ - "avm/res/app/job/**"
+ - "avm/utilities/pipelines/**"
+ - "!avm/utilities/pipelines/platform/**"
+ - "!*/**/README.md"
+
+env:
+ modulePath: "avm/res/app/job"
+ workflowPath: ".github/workflows/avm.res.app.job.yml"
+
+concurrency:
+ group: ${{ github.workflow }}
+
+jobs:
+ ###########################
+ # Initialize pipeline #
+ ###########################
+ job_initialize_pipeline:
+ runs-on: ubuntu-latest
+ name: "Initialize pipeline"
+ steps:
+ - name: "Checkout"
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: "Set input parameters to output variables"
+ id: get-workflow-param
+ uses: ./.github/actions/templates/avm-getWorkflowInput
+ with:
+ workflowPath: "${{ env.workflowPath}}"
+ - name: "Get module test file paths"
+ id: get-module-test-file-paths
+ uses: ./.github/actions/templates/avm-getModuleTestFiles
+ with:
+ modulePath: "${{ env.modulePath }}"
+ outputs:
+ workflowInput: ${{ steps.get-workflow-param.outputs.workflowInput }}
+ moduleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.moduleTestFilePaths }}
+ psRuleModuleTestFilePaths: ${{ steps.get-module-test-file-paths.outputs.psRuleModuleTestFilePaths }}
+ modulePath: "${{ env.modulePath }}"
+
+ ##############################
+ # Call reusable workflow #
+ ##############################
+ call-workflow-passing-data:
+ name: "Run"
+ permissions:
+ id-token: write # For OIDC
+ contents: write # For release tags
+ needs:
+ - job_initialize_pipeline
+ uses: ./.github/workflows/avm.template.module.yml
+ with:
+ workflowInput: "${{ needs.job_initialize_pipeline.outputs.workflowInput }}"
+ moduleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.moduleTestFilePaths }}"
+ psRuleModuleTestFilePaths: "${{ needs.job_initialize_pipeline.outputs.psRuleModuleTestFilePaths }}"
+ modulePath: "${{ needs.job_initialize_pipeline.outputs.modulePath}}"
+ secrets: inherit
diff --git a/avm/res/app/job/ORPHANED.md b/avm/res/app/job/ORPHANED.md
new file mode 100644
index 0000000000..ef8fa911d2
--- /dev/null
+++ b/avm/res/app/job/ORPHANED.md
@@ -0,0 +1,4 @@
+⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️
+
+- Only security and bug fixes are being handled by the AVM core team at present.
+- If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)!
\ No newline at end of file
diff --git a/avm/res/app/job/README.md b/avm/res/app/job/README.md
new file mode 100644
index 0000000000..93e9c6e55a
--- /dev/null
+++ b/avm/res/app/job/README.md
@@ -0,0 +1,812 @@
+# Container App Jobs `[Microsoft.App/jobs]`
+
+> ⚠️THIS MODULE IS CURRENTLY ORPHANED.⚠️
+>
+> - Only security and bug fixes are being handled by the AVM core team at present.
+> - If interested in becoming the module owner of this orphaned module (must be Microsoft FTE), please look for the related "orphaned module" GitHub issue [here](https://aka.ms/AVM/OrphanedModules)!
+
+This module deploys a Container App Job.
+
+## Navigation
+
+- [Resource Types](#Resource-Types)
+- [Usage examples](#Usage-examples)
+- [Parameters](#Parameters)
+- [Outputs](#Outputs)
+- [Cross-referenced modules](#Cross-referenced-modules)
+- [Data Collection](#Data-Collection)
+
+## Resource Types
+
+| 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.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) |
+
+## Usage examples
+
+The following section provides usage examples for the module, which were used to validate and deploy the module successfully. For a full reference, please review the module's test folder in its repository.
+
+>**Note**: Each example lists all the required parameters first, followed by the rest - each in alphabetical order.
+
+>**Note**: To reference the module, please use the following syntax `br/public:avm/res/app/job:`.
+
+- [Using only defaults](#example-1-using-only-defaults)
+- [Using large parameter set](#example-2-using-large-parameter-set)
+- [WAF-aligned](#example-3-waf-aligned)
+
+### Example 1: _Using only defaults_
+
+This instance deploys the module with the minimum set of required parameters.
+
+
+
+
+via Bicep module
+
+```bicep
+module job 'br/public:avm/res/app/job:' = {
+ name: 'jobDeployment'
+ params: {
+ // Required parameters
+ containers: [
+ {
+ image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
+ name: 'simple-hello-world-container'
+ resources: {
+ cpu: ''
+ memory: '0.5Gi'
+ }
+ }
+ ]
+ environmentResourceId: ''
+ name: 'ajmin001'
+ triggerType: 'Manual'
+ // Non-required parameters
+ location: ''
+ manualTriggerConfig: {
+ parallelism: 1
+ replicaCompletionCount: 1
+ }
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "containers": {
+ "value": [
+ {
+ "image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest",
+ "name": "simple-hello-world-container",
+ "resources": {
+ "cpu": "",
+ "memory": "0.5Gi"
+ }
+ }
+ ]
+ },
+ "environmentResourceId": {
+ "value": ""
+ },
+ "name": {
+ "value": "ajmin001"
+ },
+ "triggerType": {
+ "value": "Manual"
+ },
+ // Non-required parameters
+ "location": {
+ "value": ""
+ },
+ "manualTriggerConfig": {
+ "value": {
+ "parallelism": 1,
+ "replicaCompletionCount": 1
+ }
+ }
+ }
+}
+```
+
+
+
+
+### Example 2: _Using large parameter set_
+
+This instance deploys the module with most of its features enabled.
+
+
+
+
+via Bicep module
+
+```bicep
+module job 'br/public:avm/res/app/job:' = {
+ name: 'jobDeployment'
+ params: {
+ // Required parameters
+ containers: [
+ {
+ image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
+ name: 'simple-hello-world-container'
+ probes: [
+ {
+ httpGet: {
+ httpHeaders: [
+ {
+ name: 'Custom-Header'
+ value: 'Awesome'
+ }
+ ]
+ path: '/health'
+ port: 8080
+ }
+ initialDelaySeconds: 3
+ periodSeconds: 3
+ type: 'Liveness'
+ }
+ ]
+ resources: {
+ cpu: ''
+ memory: '0.5Gi'
+ }
+ }
+ ]
+ environmentResourceId: ''
+ name: 'ajmax001'
+ triggerType: 'Manual'
+ // Non-required parameters
+ location: ''
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ managedIdentities: {
+ systemAssigned: true
+ userAssignedResourceIds: [
+ ''
+ ]
+ }
+ manualTriggerConfig: {
+ parallelism: 1
+ replicaCompletionCount: 1
+ }
+ roleAssignments: [
+ {
+ principalId: ''
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: 'Owner'
+ }
+ {
+ principalId: ''
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c'
+ }
+ {
+ principalId: ''
+ principalType: 'ServicePrincipal'
+ roleDefinitionIdOrName: ''
+ }
+ ]
+ secrets: {
+ secureList: [
+ {
+ name: 'customtest'
+ value: ''
+ }
+ ]
+ }
+ tags: {
+ Env: 'test'
+ 'hidden-title': 'This is visible in the resource name'
+ }
+ workloadProfileName: ''
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "containers": {
+ "value": [
+ {
+ "image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest",
+ "name": "simple-hello-world-container",
+ "probes": [
+ {
+ "httpGet": {
+ "httpHeaders": [
+ {
+ "name": "Custom-Header",
+ "value": "Awesome"
+ }
+ ],
+ "path": "/health",
+ "port": 8080
+ },
+ "initialDelaySeconds": 3,
+ "periodSeconds": 3,
+ "type": "Liveness"
+ }
+ ],
+ "resources": {
+ "cpu": "",
+ "memory": "0.5Gi"
+ }
+ }
+ ]
+ },
+ "environmentResourceId": {
+ "value": ""
+ },
+ "name": {
+ "value": "ajmax001"
+ },
+ "triggerType": {
+ "value": "Manual"
+ },
+ // Non-required parameters
+ "location": {
+ "value": ""
+ },
+ "lock": {
+ "value": {
+ "kind": "CanNotDelete",
+ "name": "myCustomLockName"
+ }
+ },
+ "managedIdentities": {
+ "value": {
+ "systemAssigned": true,
+ "userAssignedResourceIds": [
+ ""
+ ]
+ }
+ },
+ "manualTriggerConfig": {
+ "value": {
+ "parallelism": 1,
+ "replicaCompletionCount": 1
+ }
+ },
+ "roleAssignments": {
+ "value": [
+ {
+ "principalId": "",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": "Owner"
+ },
+ {
+ "principalId": "",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": "b24988ac-6180-42a0-ab88-20f7382dd24c"
+ },
+ {
+ "principalId": "",
+ "principalType": "ServicePrincipal",
+ "roleDefinitionIdOrName": ""
+ }
+ ]
+ },
+ "secrets": {
+ "value": {
+ "secureList": [
+ {
+ "name": "customtest",
+ "value": ""
+ }
+ ]
+ }
+ },
+ "tags": {
+ "value": {
+ "Env": "test",
+ "hidden-title": "This is visible in the resource name"
+ }
+ },
+ "workloadProfileName": {
+ "value": ""
+ }
+ }
+}
+```
+
+
+
+
+### Example 3: _WAF-aligned_
+
+This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.
+
+
+
+
+via Bicep module
+
+```bicep
+module job 'br/public:avm/res/app/job:' = {
+ name: 'jobDeployment'
+ params: {
+ // Required parameters
+ containers: [
+ {
+ image: 'mcr.microsoft.com/azuredocs/containerapps-helloworld:latest'
+ name: 'simple-hello-world-container'
+ probes: [
+ {
+ httpGet: {
+ httpHeaders: [
+ {
+ name: 'Custom-Header'
+ value: 'Awesome'
+ }
+ ]
+ path: '/health'
+ port: 8080
+ }
+ initialDelaySeconds: 3
+ periodSeconds: 3
+ type: 'Liveness'
+ }
+ ]
+ resources: {
+ cpu: ''
+ memory: '0.5Gi'
+ }
+ }
+ ]
+ environmentResourceId: ''
+ name: 'ajwaf001'
+ triggerType: 'Manual'
+ // Non-required parameters
+ location: ''
+ manualTriggerConfig: {
+ parallelism: 1
+ replicaCompletionCount: 1
+ }
+ tags: {
+ Env: 'test'
+ 'hidden-title': 'This is visible in the resource name'
+ }
+ workloadProfileName: ''
+ }
+}
+```
+
+
+
+
+
+
+via JSON Parameter file
+
+```json
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
+ "contentVersion": "1.0.0.0",
+ "parameters": {
+ // Required parameters
+ "containers": {
+ "value": [
+ {
+ "image": "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest",
+ "name": "simple-hello-world-container",
+ "probes": [
+ {
+ "httpGet": {
+ "httpHeaders": [
+ {
+ "name": "Custom-Header",
+ "value": "Awesome"
+ }
+ ],
+ "path": "/health",
+ "port": 8080
+ },
+ "initialDelaySeconds": 3,
+ "periodSeconds": 3,
+ "type": "Liveness"
+ }
+ ],
+ "resources": {
+ "cpu": "",
+ "memory": "0.5Gi"
+ }
+ }
+ ]
+ },
+ "environmentResourceId": {
+ "value": ""
+ },
+ "name": {
+ "value": "ajwaf001"
+ },
+ "triggerType": {
+ "value": "Manual"
+ },
+ // Non-required parameters
+ "location": {
+ "value": ""
+ },
+ "manualTriggerConfig": {
+ "value": {
+ "parallelism": 1,
+ "replicaCompletionCount": 1
+ }
+ },
+ "tags": {
+ "value": {
+ "Env": "test",
+ "hidden-title": "This is visible in the resource name"
+ }
+ },
+ "workloadProfileName": {
+ "value": ""
+ }
+ }
+}
+```
+
+
+
+
+
+## Parameters
+
+**Required parameters**
+
+| Parameter | Type | Description |
+| :-- | :-- | :-- |
+| [`containers`](#parameter-containers) | array | List of container definitions for the Container App. |
+| [`environmentResourceId`](#parameter-environmentresourceid) | string | Resource ID of environment. |
+| [`name`](#parameter-name) | string | Name of the Container App. |
+| [`triggerType`](#parameter-triggertype) | string | Trigger type of the job. |
+
+**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. |
+| [`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. |
+| [`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. |
+
+### Parameter: `containers`
+
+List of container definitions for the Container App.
+
+- Required: Yes
+- Type: array
+
+### Parameter: `environmentResourceId`
+
+Resource ID of 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: `enableTelemetry`
+
+Enable/Disable usage telemetry for module.
+
+- Required: No
+- Type: bool
+- Default: `True`
+
+### Parameter: `eventTriggerConfig`
+
+Required if TriggerType is Event. Configuration of an event driven job.
+
+- Required: No
+- Type: object
+
+### Parameter: `initContainersTemplate`
+
+List of specialized containers that run before app containers.
+
+- Required: No
+- Type: array
+
+### Parameter: `location`
+
+Location for all Resources.
+
+- 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: `manualTriggerConfig`
+
+Required if TriggerType is Manual. Configuration of a manual job.
+
+- Required: No
+- Type: object
+
+### Parameter: `registries`
+
+Collection of private container registry credentials for containers used by the Container app.
+
+- Required: No
+- Type: array
+
+### 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`
+
+Version of the condition.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ '2.0'
+ ]
+ ```
+
+### Parameter: `roleAssignments.delegatedManagedIdentityResourceId`
+
+The Resource Id of the delegated managed identity resource.
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments.description`
+
+The description of the role assignment.
+
+- Required: No
+- Type: string
+
+### Parameter: `roleAssignments.principalType`
+
+The principal type of the assigned principal ID.
+
+- Required: No
+- Type: string
+- Allowed:
+ ```Bicep
+ [
+ 'Device'
+ 'ForeignGroup'
+ 'Group'
+ 'ServicePrincipal'
+ 'User'
+ ]
+ ```
+
+### Parameter: `scheduleTriggerConfig`
+
+Required if TriggerType is Schedule. Configuration of a schedule based job.
+
+- Required: No
+- Type: object
+
+### Parameter: `secrets`
+
+The secrets of the Container App.
+
+- Required: No
+- Type: secureObject
+
+### Parameter: `tags`
+
+Tags of the resource.
+
+- Required: No
+- Type: object
+
+### Parameter: `volumes`
+
+List of volume definitions for the Container App.
+
+- Required: No
+- Type: array
+
+### Parameter: `workloadProfileName`
+
+The name of the workload profile to use.
+
+- Required: No
+- Type: string
+- Default: `'Consumption'`
+
+
+## Outputs
+
+| Output | Type | Description |
+| :-- | :-- | :-- |
+| `location` | string | The location the resource was deployed into. |
+| `name` | string | The name of the Container App Job. |
+| `resourceGroupName` | string | The name of the resource group the Container App Job was deployed into. |
+| `resourceId` | string | The resource ID of the Container App Job. |
+| `systemAssignedMIPrincipalId` | string | The principal ID of the system assigned identity. |
+
+## Cross-referenced modules
+
+_None_
+
+## Data Collection
+
+The software may collect information about you and your use of the software and send it to Microsoft. Microsoft may use this information to provide services and improve our products and services. You may turn off the telemetry as described in the [repository](https://aka.ms/avm/telemetry). There are also some features in the software that may enable you and Microsoft to collect data from users of your applications. If you use these features, you must comply with applicable law, including providing appropriate notices to users of your applications together with a copy of Microsoft’s privacy statement. Our privacy statement is located at . You can learn more about data collection and use in the help documentation and our privacy statement. Your use of the software operates as your consent to these practices.
diff --git a/avm/res/app/job/main.bicep b/avm/res/app/job/main.bicep
new file mode 100644
index 0000000000..2d9a0f6ba6
--- /dev/null
+++ b/avm/res/app/job/main.bicep
@@ -0,0 +1,236 @@
+metadata name = 'Container App Jobs'
+metadata description = 'This module deploys a Container App Job.'
+metadata owner = 'Azure/module-maintainers'
+
+@description('Required. Name of the Container App.')
+param name string
+
+@description('Optional. Location for all Resources.')
+param location string = resourceGroup().location
+
+@description('Required. Resource ID of environment.')
+param environmentResourceId string
+
+@description('Optional. The lock settings of the service.')
+param lock lockType
+
+@description('Optional. Tags of the resource.')
+param tags object?
+
+@description('Optional. Collection of private container registry credentials for containers used by the Container app.')
+param registries array?
+
+@description('Optional. The managed identity definition for this resource.')
+param managedIdentities managedIdentitiesType
+
+@description('Optional. Array of role assignments to create.')
+param roleAssignments roleAssignmentType
+
+@description('Optional. Enable/Disable usage telemetry for module.')
+param enableTelemetry bool = true
+
+@description('Required. List of container definitions for the Container App.')
+param containers array
+
+@description('Optional. List of specialized containers that run before app containers.')
+param initContainersTemplate array?
+
+@description('Optional. Required if TriggerType is Event. Configuration of an event driven job.')
+param eventTriggerConfig object?
+
+@description('Optional. Required if TriggerType is Schedule. Configuration of a schedule based job.')
+param scheduleTriggerConfig object?
+
+@description('Optional. Required if TriggerType is Manual. Configuration of a manual job.')
+param manualTriggerConfig object?
+
+@description('Optional. The maximum number of times a replica can be retried.')
+param replicaRetryLimit int = 0
+
+@description('Optional. The name of the workload profile to use.')
+param workloadProfileName string = 'Consumption'
+
+@description('Optional. The secrets of the Container App.')
+@secure()
+param secrets object?
+
+@description('Optional. List of volume definitions for the Container App.')
+param volumes array?
+
+@description('Optional. Maximum number of seconds a replica is allowed to run.')
+param replicaTimeout int = 1800
+
+@allowed([
+ 'Event'
+ 'Manual'
+ 'Schedule'
+])
+@description('Required. Trigger type of the job.')
+param triggerType string
+
+var secretList = secrets.?secureList
+
+var formattedUserAssignedIdentities = reduce(
+ map((managedIdentities.?userAssignedResourceIds ?? []), (id) => { '${id}': {} }),
+ {},
+ (cur, next) => union(cur, next)
+) // Converts the flat array to an object like { '${id1}': {}, '${id2}': {} }
+
+var identity = !empty(managedIdentities)
+ ? {
+ type: (managedIdentities.?systemAssigned ?? false)
+ ? (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'SystemAssigned,UserAssigned' : 'SystemAssigned')
+ : (!empty(managedIdentities.?userAssignedResourceIds ?? {}) ? 'UserAssigned' : 'None')
+ userAssignedIdentities: !empty(formattedUserAssignedIdentities) ? formattedUserAssignedIdentities : null
+ }
+ : null
+
+var builtInRoleNames = {
+ 'ContainerApp Reader': subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b'
+ )
+ Contributor: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')
+ Owner: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')
+ Reader: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')
+ 'Role Based Access Control Administrator (Preview)': subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ 'f58310d9-a9f6-439a-9e8d-f62e7b41a168'
+ )
+ 'User Access Administrator': subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9'
+ )
+}
+
+resource avmTelemetry 'Microsoft.Resources/deployments@2023-07-01' = if (enableTelemetry) {
+ name: '46d3xbcp.res.app-job.${replace('-..--..-', '.', '-')}.${substring(uniqueString(deployment().name, location), 0, 4)}'
+ properties: {
+ mode: 'Incremental'
+ template: {
+ '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#'
+ contentVersion: '1.0.0.0'
+ resources: []
+ outputs: {
+ telemetry: {
+ type: 'String'
+ value: 'For more information, see https://aka.ms/avm/TelemetryInfo'
+ }
+ }
+ }
+ }
+}
+
+resource job 'Microsoft.App/jobs@2023-05-01' = {
+ name: name
+ tags: tags
+ location: location
+ identity: identity
+ properties: {
+ environmentId: environmentResourceId
+ configuration: {
+ 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
+ }
+ template: {
+ containers: containers
+ initContainers: initContainersTemplate
+ volumes: volumes
+ }
+ workloadProfileName: workloadProfileName
+ }
+}
+
+resource job_lock 'Microsoft.Authorization/locks@2020-05-01' = if (!empty(lock ?? {}) && lock.?kind != 'None') {
+ name: lock.?name ?? 'lock-${name}'
+ properties: {
+ level: lock.?kind ?? ''
+ notes: lock.?kind == 'CanNotDelete'
+ ? 'Cannot delete resource or child resources.'
+ : 'Cannot delete or modify the resource or child resources.'
+ }
+ scope: job
+}
+
+resource automationAccount_roleAssignments 'Microsoft.Authorization/roleAssignments@2022-04-01' = [
+ for (roleAssignment, index) in (roleAssignments ?? []): {
+ name: guid(job.id, roleAssignment.principalId, roleAssignment.roleDefinitionIdOrName)
+ properties: {
+ roleDefinitionId: contains(builtInRoleNames, roleAssignment.roleDefinitionIdOrName)
+ ? builtInRoleNames[roleAssignment.roleDefinitionIdOrName]
+ : contains(roleAssignment.roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/')
+ ? roleAssignment.roleDefinitionIdOrName
+ : subscriptionResourceId('Microsoft.Authorization/roleDefinitions', roleAssignment.roleDefinitionIdOrName)
+ principalId: roleAssignment.principalId
+ description: roleAssignment.?description
+ principalType: roleAssignment.?principalType
+ condition: roleAssignment.?condition
+ conditionVersion: !empty(roleAssignment.?condition) ? (roleAssignment.?conditionVersion ?? '2.0') : null // Must only be set if condtion is set
+ delegatedManagedIdentityResourceId: roleAssignment.?delegatedManagedIdentityResourceId
+ }
+ scope: job
+ }
+]
+@description('The resource ID of the Container App Job.')
+output resourceId string = job.id
+
+@description('The name of the resource group the Container App Job was deployed into.')
+output resourceGroupName string = resourceGroup().name
+
+@description('The name of the Container App Job.')
+output name string = job.name
+
+@description('The location the resource was deployed into.')
+output location string = job.location
+
+@description('The principal ID of the system assigned identity.')
+output systemAssignedMIPrincipalId string = job.?identity.?principalId ?? ''
+
+// =============== //
+// Definitions //
+// =============== //
+
+type managedIdentitiesType = {
+ @description('Optional. Enables system assigned managed identity on the resource.')
+ systemAssigned: bool?
+
+ @description('Optional. The resource ID(s) to assign to the resource.')
+ userAssignedResourceIds: string[]?
+}?
+
+type lockType = {
+ @description('Optional. Specify the name of lock.')
+ name: string?
+
+ @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\'.')
+ roleDefinitionIdOrName: string
+
+ @description('Required. The principal ID of the principal (user/group/identity) to assign the role to.')
+ principalId: string
+
+ @description('Optional. The principal type of the assigned principal ID.')
+ principalType: ('ServicePrincipal' | 'Group' | 'User' | 'ForeignGroup' | 'Device')?
+
+ @description('Optional. The description of the role assignment.')
+ description: string?
+
+ @description('Optional. 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".')
+ condition: string?
+
+ @description('Optional. Version of the condition.')
+ conditionVersion: '2.0'?
+
+ @description('Optional. The Resource Id of the delegated managed identity resource.')
+ delegatedManagedIdentityResourceId: string?
+}[]?
diff --git a/avm/res/app/job/main.json b/avm/res/app/job/main.json
new file mode 100644
index 0000000000..6d8ee06c25
--- /dev/null
+++ b/avm/res/app/job/main.json
@@ -0,0 +1,406 @@
+{
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "languageVersion": "2.0",
+ "contentVersion": "1.0.0.0",
+ "metadata": {
+ "_generator": {
+ "name": "bicep",
+ "version": "0.27.1.19265",
+ "templateHash": "11649443218681434280"
+ },
+ "name": "Container App Jobs",
+ "description": "This module deploys a Container App Job.",
+ "owner": "Azure/module-maintainers"
+ },
+ "definitions": {
+ "managedIdentitiesType": {
+ "type": "object",
+ "properties": {
+ "systemAssigned": {
+ "type": "bool",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Enables system assigned managed identity on the resource."
+ }
+ },
+ "userAssignedResourceIds": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The resource ID(s) to assign to the resource."
+ }
+ }
+ },
+ "nullable": true
+ },
+ "lockType": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the name of lock."
+ }
+ },
+ "kind": {
+ "type": "string",
+ "allowedValues": [
+ "CanNotDelete",
+ "None",
+ "ReadOnly"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Specify the type of lock."
+ }
+ }
+ },
+ "nullable": true
+ },
+ "roleAssignmentType": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "roleDefinitionIdOrName": {
+ "type": "string",
+ "metadata": {
+ "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'."
+ }
+ },
+ "principalId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. The principal ID of the principal (user/group/identity) to assign the role to."
+ }
+ },
+ "principalType": {
+ "type": "string",
+ "allowedValues": [
+ "Device",
+ "ForeignGroup",
+ "Group",
+ "ServicePrincipal",
+ "User"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The principal type of the assigned principal ID."
+ }
+ },
+ "description": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The description of the role assignment."
+ }
+ },
+ "condition": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. 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": {
+ "type": "string",
+ "allowedValues": [
+ "2.0"
+ ],
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Version of the condition."
+ }
+ },
+ "delegatedManagedIdentityResourceId": {
+ "type": "string",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The Resource Id of the delegated managed identity resource."
+ }
+ }
+ }
+ },
+ "nullable": true
+ }
+ },
+ "parameters": {
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Name of the Container App."
+ }
+ },
+ "location": {
+ "type": "string",
+ "defaultValue": "[resourceGroup().location]",
+ "metadata": {
+ "description": "Optional. Location for all Resources."
+ }
+ },
+ "environmentResourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "Required. Resource ID of environment."
+ }
+ },
+ "lock": {
+ "$ref": "#/definitions/lockType",
+ "metadata": {
+ "description": "Optional. The lock settings of the service."
+ }
+ },
+ "tags": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Tags of the resource."
+ }
+ },
+ "registries": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Collection of private container registry credentials for containers used by the Container app."
+ }
+ },
+ "managedIdentities": {
+ "$ref": "#/definitions/managedIdentitiesType",
+ "metadata": {
+ "description": "Optional. The managed identity definition for this resource."
+ }
+ },
+ "roleAssignments": {
+ "$ref": "#/definitions/roleAssignmentType",
+ "metadata": {
+ "description": "Optional. Array of role assignments to create."
+ }
+ },
+ "enableTelemetry": {
+ "type": "bool",
+ "defaultValue": true,
+ "metadata": {
+ "description": "Optional. Enable/Disable usage telemetry for module."
+ }
+ },
+ "containers": {
+ "type": "array",
+ "metadata": {
+ "description": "Required. List of container definitions for the Container App."
+ }
+ },
+ "initContainersTemplate": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of specialized containers that run before app containers."
+ }
+ },
+ "eventTriggerConfig": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Required if TriggerType is Event. Configuration of an event driven job."
+ }
+ },
+ "scheduleTriggerConfig": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Required if TriggerType is Schedule. Configuration of a schedule based job."
+ }
+ },
+ "manualTriggerConfig": {
+ "type": "object",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. Required if TriggerType is Manual. Configuration of a manual job."
+ }
+ },
+ "replicaRetryLimit": {
+ "type": "int",
+ "defaultValue": 0,
+ "metadata": {
+ "description": "Optional. The maximum number of times a replica can be retried."
+ }
+ },
+ "workloadProfileName": {
+ "type": "string",
+ "defaultValue": "Consumption",
+ "metadata": {
+ "description": "Optional. The name of the workload profile to use."
+ }
+ },
+ "secrets": {
+ "type": "secureObject",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. The secrets of the Container App."
+ }
+ },
+ "volumes": {
+ "type": "array",
+ "nullable": true,
+ "metadata": {
+ "description": "Optional. List of volume definitions for the Container App."
+ }
+ },
+ "replicaTimeout": {
+ "type": "int",
+ "defaultValue": 1800,
+ "metadata": {
+ "description": "Optional. Maximum number of seconds a replica is allowed to run."
+ }
+ },
+ "triggerType": {
+ "type": "string",
+ "allowedValues": [
+ "Event",
+ "Manual",
+ "Schedule"
+ ],
+ "metadata": {
+ "description": "Required. Trigger type of the job."
+ }
+ }
+ },
+ "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": {
+ "ContainerApp Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'ad2dd5fb-cd4b-4fd4-a9b6-4fed3630980b')]",
+ "Contributor": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]",
+ "Owner": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635')]",
+ "Reader": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7')]",
+ "Role Based Access Control Administrator (Preview)": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'f58310d9-a9f6-439a-9e8d-f62e7b41a168')]",
+ "User Access Administrator": "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9')]"
+ }
+ },
+ "resources": {
+ "avmTelemetry": {
+ "condition": "[parameters('enableTelemetry')]",
+ "type": "Microsoft.Resources/deployments",
+ "apiVersion": "2023-07-01",
+ "name": "[format('46d3xbcp.res.app-job.{0}.{1}', replace('-..--..-', '.', '-'), substring(uniqueString(deployment().name, parameters('location')), 0, 4))]",
+ "properties": {
+ "mode": "Incremental",
+ "template": {
+ "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
+ "contentVersion": "1.0.0.0",
+ "resources": [],
+ "outputs": {
+ "telemetry": {
+ "type": "String",
+ "value": "For more information, see https://aka.ms/avm/TelemetryInfo"
+ }
+ }
+ }
+ }
+ },
+ "job": {
+ "type": "Microsoft.App/jobs",
+ "apiVersion": "2023-05-01",
+ "name": "[parameters('name')]",
+ "tags": "[parameters('tags')]",
+ "location": "[parameters('location')]",
+ "identity": "[variables('identity')]",
+ "properties": {
+ "environmentId": "[parameters('environmentResourceId')]",
+ "configuration": {
+ "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')]"
+ },
+ "template": {
+ "containers": "[parameters('containers')]",
+ "initContainers": "[parameters('initContainersTemplate')]",
+ "volumes": "[parameters('volumes')]"
+ },
+ "workloadProfileName": "[parameters('workloadProfileName')]"
+ }
+ },
+ "job_lock": {
+ "condition": "[and(not(empty(coalesce(parameters('lock'), createObject()))), not(equals(tryGet(parameters('lock'), 'kind'), 'None')))]",
+ "type": "Microsoft.Authorization/locks",
+ "apiVersion": "2020-05-01",
+ "scope": "[format('Microsoft.App/jobs/{0}', parameters('name'))]",
+ "name": "[coalesce(tryGet(parameters('lock'), 'name'), format('lock-{0}', parameters('name')))]",
+ "properties": {
+ "level": "[coalesce(tryGet(parameters('lock'), 'kind'), '')]",
+ "notes": "[if(equals(tryGet(parameters('lock'), 'kind'), 'CanNotDelete'), 'Cannot delete resource or child resources.', 'Cannot delete or modify the resource or child resources.')]"
+ },
+ "dependsOn": [
+ "job"
+ ]
+ },
+ "automationAccount_roleAssignments": {
+ "copy": {
+ "name": "automationAccount_roleAssignments",
+ "count": "[length(coalesce(parameters('roleAssignments'), createArray()))]"
+ },
+ "type": "Microsoft.Authorization/roleAssignments",
+ "apiVersion": "2022-04-01",
+ "scope": "[format('Microsoft.App/jobs/{0}', parameters('name'))]",
+ "name": "[guid(resourceId('Microsoft.App/jobs', parameters('name')), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId, coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)]",
+ "properties": {
+ "roleDefinitionId": "[if(contains(variables('builtInRoleNames'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName), variables('builtInRoleNames')[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName], if(contains(coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, '/providers/Microsoft.Authorization/roleDefinitions/'), coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName, subscriptionResourceId('Microsoft.Authorization/roleDefinitions', coalesce(parameters('roleAssignments'), createArray())[copyIndex()].roleDefinitionIdOrName)))]",
+ "principalId": "[coalesce(parameters('roleAssignments'), createArray())[copyIndex()].principalId]",
+ "description": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'description')]",
+ "principalType": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'principalType')]",
+ "condition": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition')]",
+ "conditionVersion": "[if(not(empty(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'condition'))), coalesce(tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'conditionVersion'), '2.0'), null())]",
+ "delegatedManagedIdentityResourceId": "[tryGet(coalesce(parameters('roleAssignments'), createArray())[copyIndex()], 'delegatedManagedIdentityResourceId')]"
+ },
+ "dependsOn": [
+ "job"
+ ]
+ }
+ },
+ "outputs": {
+ "resourceId": {
+ "type": "string",
+ "metadata": {
+ "description": "The resource ID of the Container App Job."
+ },
+ "value": "[resourceId('Microsoft.App/jobs', parameters('name'))]"
+ },
+ "resourceGroupName": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the resource group the Container App Job was deployed into."
+ },
+ "value": "[resourceGroup().name]"
+ },
+ "name": {
+ "type": "string",
+ "metadata": {
+ "description": "The name of the Container App Job."
+ },
+ "value": "[parameters('name')]"
+ },
+ "location": {
+ "type": "string",
+ "metadata": {
+ "description": "The location the resource was deployed into."
+ },
+ "value": "[reference('job', '2023-05-01', 'full').location]"
+ },
+ "systemAssignedMIPrincipalId": {
+ "type": "string",
+ "metadata": {
+ "description": "The principal ID of the system assigned identity."
+ },
+ "value": "[coalesce(tryGet(tryGet(reference('job', '2023-05-01', 'full'), 'identity'), 'principalId'), '')]"
+ }
+ }
+}
\ No newline at end of file
diff --git a/avm/res/app/job/tests/e2e/defaults/dependencies.bicep b/avm/res/app/job/tests/e2e/defaults/dependencies.bicep
new file mode 100644
index 0000000000..bb2af3d0f8
--- /dev/null
+++ b/avm/res/app/job/tests/e2e/defaults/dependencies.bicep
@@ -0,0 +1,21 @@
+@description('Required. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Environment to create.')
+param managedEnvironmentName string
+
+resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
+ name: managedEnvironmentName
+ location: location
+ properties: {
+ workloadProfiles: [
+ {
+ workloadProfileType: 'Consumption'
+ name: 'Consumption'
+ }
+ ]
+ }
+}
+
+@description('The resource ID of the created Managed Environment.')
+output managedEnvironmentResourceId string = managedEnvironment.id
diff --git a/avm/res/app/job/tests/e2e/defaults/main.test.bicep b/avm/res/app/job/tests/e2e/defaults/main.test.bicep
new file mode 100644
index 0000000000..546fa7c4c8
--- /dev/null
+++ b/avm/res/app/job/tests/e2e/defaults/main.test.bicep
@@ -0,0 +1,74 @@
+targetScope = 'subscription'
+
+metadata name = 'Using only defaults'
+metadata description = 'This instance deploys the module with the minimum set of required parameters.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-app.job-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param resourceLocation string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ajmin'
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '#_namePrefix_#'
+
+// =========== //
+// Deployments //
+// =========== //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: resourceLocation
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-paramNested'
+ params: {
+ location: resourceLocation
+ managedEnvironmentName: 'dep-${namePrefix}-menv-${serviceShort}'
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [
+ for iteration in ['init', 'idem']: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId
+ location: resourceLocation
+ triggerType: 'Manual'
+ manualTriggerConfig: {
+ replicaCompletionCount: 1
+ parallelism: 1
+ }
+ 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'
+ }
+ }
+ ]
+ }
+ }
+]
diff --git a/avm/res/app/job/tests/e2e/max/dependencies.bicep b/avm/res/app/job/tests/e2e/max/dependencies.bicep
new file mode 100644
index 0000000000..b03d4aca93
--- /dev/null
+++ b/avm/res/app/job/tests/e2e/max/dependencies.bicep
@@ -0,0 +1,40 @@
+@description('Required. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Environment for Container Apps to create.')
+param managedEnvironmentName string
+
+@description('Required. The name of the managed identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the workload profile to create.')
+param workloadProfileName string
+
+resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
+ name: managedEnvironmentName
+ location: location
+ properties: {
+ workloadProfiles: [
+ {
+ name: workloadProfileName
+ workloadProfileType: 'D4'
+ maximumCount: 1
+ minimumCount: 1
+ }
+ ]
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2022-01-31-preview' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The resource ID of the created Managed Environment.')
+output managedEnvironmentResourceId string = managedEnvironment.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
diff --git a/avm/res/app/job/tests/e2e/max/main.test.bicep b/avm/res/app/job/tests/e2e/max/main.test.bicep
new file mode 100644
index 0000000000..18ae51956b
--- /dev/null
+++ b/avm/res/app/job/tests/e2e/max/main.test.bicep
@@ -0,0 +1,136 @@
+targetScope = 'subscription'
+
+metadata name = 'Using large parameter set'
+metadata description = 'This instance deploys the module with most of its features enabled.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-app.job-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param resourceLocation string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ajmax'
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '#_namePrefix_#'
+
+// =========== //
+// Deployments //
+// =========== //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: resourceLocation
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-paramNested'
+ params: {
+ location: resourceLocation
+ managedEnvironmentName: 'dep-${namePrefix}-menv-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ workloadProfileName: serviceShort
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [
+ for iteration in ['init', 'idem']: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Env: 'test'
+ }
+ environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId
+ workloadProfileName: serviceShort
+ location: resourceLocation
+ lock: {
+ kind: 'CanNotDelete'
+ name: 'myCustomLockName'
+ }
+ managedIdentities: {
+ systemAssigned: true
+ userAssignedResourceIds: [
+ nestedDependencies.outputs.managedIdentityResourceId
+ ]
+ }
+ secrets: {
+ secureList: [
+ {
+ name: 'customtest'
+ value: guid(deployment().name)
+ }
+ ]
+ }
+ triggerType: 'Manual'
+ manualTriggerConfig: {
+ replicaCompletionCount: 1
+ parallelism: 1
+ }
+ 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'
+ }
+ probes: [
+ {
+ type: 'Liveness'
+ httpGet: {
+ path: '/health'
+ port: 8080
+ httpHeaders: [
+ {
+ name: 'Custom-Header'
+ value: 'Awesome'
+ }
+ ]
+ }
+ initialDelaySeconds: 3
+ periodSeconds: 3
+ }
+ ]
+ }
+ ]
+ roleAssignments: [
+ {
+ roleDefinitionIdOrName: 'Owner'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ {
+ roleDefinitionIdOrName: 'b24988ac-6180-42a0-ab88-20f7382dd24c'
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ {
+ roleDefinitionIdOrName: subscriptionResourceId(
+ 'Microsoft.Authorization/roleDefinitions',
+ 'acdd72a7-3385-48ef-bd42-f606fba81ae7'
+ )
+ principalId: nestedDependencies.outputs.managedIdentityPrincipalId
+ principalType: 'ServicePrincipal'
+ }
+ ]
+ }
+ }
+]
diff --git a/avm/res/app/job/tests/e2e/waf-aligned/dependencies.bicep b/avm/res/app/job/tests/e2e/waf-aligned/dependencies.bicep
new file mode 100644
index 0000000000..b03d4aca93
--- /dev/null
+++ b/avm/res/app/job/tests/e2e/waf-aligned/dependencies.bicep
@@ -0,0 +1,40 @@
+@description('Required. The location to deploy resources to.')
+param location string = resourceGroup().location
+
+@description('Required. The name of the Managed Environment for Container Apps to create.')
+param managedEnvironmentName string
+
+@description('Required. The name of the managed identity to create.')
+param managedIdentityName string
+
+@description('Required. The name of the workload profile to create.')
+param workloadProfileName string
+
+resource managedEnvironment 'Microsoft.App/managedEnvironments@2023-05-01' = {
+ name: managedEnvironmentName
+ location: location
+ properties: {
+ workloadProfiles: [
+ {
+ name: workloadProfileName
+ workloadProfileType: 'D4'
+ maximumCount: 1
+ minimumCount: 1
+ }
+ ]
+ }
+}
+
+resource managedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2022-01-31-preview' = {
+ name: managedIdentityName
+ location: location
+}
+
+@description('The resource ID of the created Managed Identity.')
+output managedIdentityResourceId string = managedIdentity.id
+
+@description('The resource ID of the created Managed Environment.')
+output managedEnvironmentResourceId string = managedEnvironment.id
+
+@description('The principal ID of the created Managed Identity.')
+output managedIdentityPrincipalId string = managedIdentity.properties.principalId
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
new file mode 100644
index 0000000000..93b2d344ab
--- /dev/null
+++ b/avm/res/app/job/tests/e2e/waf-aligned/main.test.bicep
@@ -0,0 +1,98 @@
+targetScope = 'subscription'
+
+metadata name = 'WAF-aligned'
+metadata description = 'This instance deploys the module in alignment with the best-practices of the Azure Well-Architected Framework.'
+
+// ========== //
+// Parameters //
+// ========== //
+
+@description('Optional. The name of the resource group to deploy for testing purposes.')
+@maxLength(90)
+param resourceGroupName string = 'dep-${namePrefix}-app.job-${serviceShort}-rg'
+
+@description('Optional. The location to deploy resources to.')
+param resourceLocation string = deployment().location
+
+@description('Optional. A short identifier for the kind of deployment. Should be kept short to not run into resource-name length-constraints.')
+param serviceShort string = 'ajwaf'
+
+@description('Optional. A token to inject into the name of each resource.')
+param namePrefix string = '#_namePrefix_#'
+
+// =========== //
+// Deployments //
+// =========== //
+
+// General resources
+// =================
+resource resourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' = {
+ name: resourceGroupName
+ location: resourceLocation
+}
+
+module nestedDependencies 'dependencies.bicep' = {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-paramNested'
+ params: {
+ location: resourceLocation
+ managedEnvironmentName: 'dep-${namePrefix}-menv-${serviceShort}'
+ managedIdentityName: 'dep-${namePrefix}-msi-${serviceShort}'
+ workloadProfileName: serviceShort
+ }
+}
+
+// ============== //
+// Test Execution //
+// ============== //
+
+@batchSize(1)
+module testDeployment '../../../main.bicep' = [
+ for iteration in ['init', 'idem']: {
+ scope: resourceGroup
+ name: '${uniqueString(deployment().name, resourceLocation)}-test-${serviceShort}-${iteration}'
+ params: {
+ name: '${namePrefix}${serviceShort}001'
+ tags: {
+ 'hidden-title': 'This is visible in the resource name'
+ Env: 'test'
+ }
+ environmentResourceId: nestedDependencies.outputs.managedEnvironmentResourceId
+ workloadProfileName: serviceShort
+ location: resourceLocation
+ triggerType: 'Manual'
+ manualTriggerConfig: {
+ replicaCompletionCount: 1
+ parallelism: 1
+ }
+ 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'
+ }
+ probes: [
+ {
+ type: 'Liveness'
+ httpGet: {
+ path: '/health'
+ port: 8080
+ httpHeaders: [
+ {
+ name: 'Custom-Header'
+ value: 'Awesome'
+ }
+ ]
+ }
+ initialDelaySeconds: 3
+ periodSeconds: 3
+ }
+ ]
+ }
+ ]
+ }
+ }
+]
diff --git a/avm/res/app/job/version.json b/avm/res/app/job/version.json
new file mode 100644
index 0000000000..7fa401bdf7
--- /dev/null
+++ b/avm/res/app/job/version.json
@@ -0,0 +1,7 @@
+{
+ "$schema": "https://aka.ms/bicep-registry-module-version-file-schema#",
+ "version": "0.1",
+ "pathFilters": [
+ "./main.json"
+ ]
+}
From 25b69089e72243b2b5e4acb668350c7dbe44444e Mon Sep 17 00:00:00 2001
From: Erika Gressi <56914614+eriqua@users.noreply.github.com>
Date: Sun, 12 May 2024 20:59:32 +0200
Subject: [PATCH 9/9] fix: Remove ptn readme (#1916)
## Description
Remove ptn readme after the folder has been populated
## Pipeline Reference
| Pipeline |
| -------- |
| |
## Type of Change
- [x] Update to CI Environment or utlities (Non-module effecting
changes)
- [ ] Azure Verified Module updates:
- [ ] Bugfix containing backwards compatible bug fixes, and I have NOT
bumped the MAJOR or MINOR version in `version.json`:
- [ ] Someone has opened a bug report issue, and I have included "Closes
#{bug_report_issue_number}" in the PR description.
- [ ] The bug was found by the module author, and no one has opened an
issue to report it yet.
- [ ] Feature update backwards compatible feature updates, and I have
bumped the MINOR version in `version.json`.
- [ ] Breaking changes and I have bumped the MAJOR version in
`version.json`.
- [ ] Update to documentation
## Checklist
- [x] I'm sure there are no other open Pull Requests for the same
update/change
- [ ] I have run `Set-AVMModule` locally to generate the supporting
module files.
- [ ] My corresponding pipelines / checks run clean and green without
any errors or warnings
---
avm/ptn/readme.md | 1 -
1 file changed, 1 deletion(-)
delete mode 100644 avm/ptn/readme.md
diff --git a/avm/ptn/readme.md b/avm/ptn/readme.md
deleted file mode 100644
index 933815b8f8..0000000000
--- a/avm/ptn/readme.md
+++ /dev/null
@@ -1 +0,0 @@
-TODO: Add patterns