Skip to content

Commit 093557a

Browse files
rfk-ncsaliceti
andauthored
feat: New Container App Job module plus Container App updates (#178)
* New Container App Job module plus Container App updates * Add ADO template to start job * [DTOSS-8589] Fix container app RG data source error (#179) * Add lowercase virtual network name New resources should be created with lowercase name * Fix error container app error when the RG is not created The data source was relying on the resource group existence and failing when building an environment from scratch * Add logging and exit only once job completes * Ammend variables in line with other modules * Update docs * Update terraform docs (#181) * Merge with main * Docs updates --------- Co-authored-by: Colin Saliceti <[email protected]>
1 parent 21b774d commit 093557a

File tree

14 files changed

+484
-56
lines changed

14 files changed

+484
-56
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
---
2+
3+
parameters:
4+
- name: serviceConnection
5+
type: string
6+
- name: targetSubscriptionId
7+
type: string
8+
- name: resourceGroupName
9+
type: string
10+
- name: jobName
11+
type: string
12+
- name: waitForCompletion
13+
type: boolean
14+
default: true
15+
16+
steps:
17+
- task: AzureCLI@2
18+
name: apply_database_changes
19+
displayName: Apply database changes
20+
inputs:
21+
azureSubscription: ${{ parameters.serviceConnection }}
22+
addSpnToEnvironment: true
23+
failOnStandardError: false
24+
scriptType: bash
25+
scriptLocation: inlineScript
26+
inlineScript: |
27+
# Exit immediately if a command exits with a non-zero status
28+
set -eo pipefail
29+
30+
az account set -s ${{ parameters.targetSubscriptionId }}
31+
32+
JOB_NAME="${{ parameters.jobName }}"
33+
RESOURCE_GROUP_NAME="${{ parameters.resourceGroupName }}"
34+
35+
POLLING_INTERVAL_SECONDS=5
36+
MAX_POLLING_ATTEMPTS=60
37+
CURRENT_ATTEMPT=0
38+
39+
echo "##[section]Starting Container App Job: ${JOB_NAME} in Resource Group: ${RESOURCE_GROUP_NAME}"
40+
41+
# Start the container app job and capture the execution name
42+
JOB_EXECUTION_NAME=$(az containerapp job start --name ${JOB_NAME} --resource-group ${RESOURCE_GROUP_NAME} --query name -o tsv)
43+
44+
# If we need to wait for job completion, poll for job execution status and only exit step once completed
45+
if [[ "${{ parameters.waitForCompletion }}" == "True" ]]; then
46+
while true; do
47+
JOB_STATUS=$(az containerapp job execution show --name ${JOB_NAME} --job-execution-name ${JOB_EXECUTION_NAME} --resource-group ${RESOURCE_GROUP_NAME} --query properties.status -o tsv)
48+
49+
echo "Job execution status after $((CURRENT_ATTEMPT * POLLING_INTERVAL_SECONDS)) seconds: $JOB_STATUS"
50+
51+
if [[ "$JOB_STATUS" == "Succeeded" ]]; then
52+
break
53+
elif [[ "$JOB_STATUS" == "Failed" ]]; then
54+
echo "##[error]Job failed."
55+
exit 1
56+
elif [[ "$JOB_STATUS" == "Cancelled" ]]; then
57+
echo "##[warning]Job was cancelled."
58+
exit 1
59+
fi
60+
sleep $POLLING_INTERVAL_SECONDS
61+
62+
# Increment the current attempt counter to ensure we don't exceed the maximum polling attempts
63+
CURRENT_ATTEMPT=$((CURRENT_ATTEMPT + 1))
64+
if [[ $CURRENT_ATTEMPT -ge $MAX_POLLING_ATTEMPTS ]]; then
65+
echo "##[error]Max polling attempts of $MAX_POLLING_ATTEMPTS reached. Exiting."
66+
exit 1
67+
fi
68+
done
69+
fi

.azuredevops/templates/steps/apply-database-changes.yaml

Lines changed: 0 additions & 47 deletions
This file was deleted.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This repository contains terraform modules and Azure devops pipeline steps to de
1111
- [baseline](infrastructure/modules/baseline/README.md)
1212
- [container-app](infrastructure/modules/container-app/README.md)
1313
- [container-app-environment](infrastructure/modules/container-app-environment/README.md)
14+
- [container-app-job](infrastructure/modules/container-app-job/README.md)
1415
- [container-registry](infrastructure/modules/container-registry/README.md)
1516
- [diagnostic-settings](infrastructure/modules/diagnostic-settings/README.md)
1617
- [dns-a-record](infrastructure/modules/dns-a-record/README.md)

infrastructure/modules/container-app-environment/main.tf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ resource "azurerm_container_app_environment" "main" {
99
}
1010

1111
module "apex-record" {
12+
count = var.private_dns_zone_rg_name != null ? 1 : 0
13+
1214
source = "../private-dns-a-record"
1315
providers = {
1416
azurerm = azurerm.dns
@@ -22,6 +24,8 @@ module "apex-record" {
2224
}
2325

2426
module "wildcard-record" {
27+
count = var.private_dns_zone_rg_name != null ? 1 : 0
28+
2529
source = "../private-dns-a-record"
2630
providers = {
2731
azurerm = azurerm.dns

infrastructure/modules/container-app-environment/tfdocs.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,6 @@ Description: Name of the container app environment.
1616

1717
Type: `string`
1818

19-
### <a name="input_private_dns_zone_rg_name"></a> [private\_dns\_zone\_rg\_name](#input\_private\_dns\_zone\_rg\_name)
20-
21-
Description: Name of the hub resource group where the private DNS zone is located.
22-
23-
Type: `string`
24-
2519
### <a name="input_resource_group_name"></a> [resource\_group\_name](#input\_resource\_group\_name)
2620

2721
Description: Name of the resource group to create the container app environment in.
@@ -46,6 +40,14 @@ Type: `string`
4640

4741
Default: `"UK South"`
4842

43+
### <a name="input_private_dns_zone_rg_name"></a> [private\_dns\_zone\_rg\_name](#input\_private\_dns\_zone\_rg\_name)
44+
45+
Description: Name of the hub resource group where the private DNS zone is located. This is only required if adding custom DNS records, for instance when hosting container apps with an HTTP ingress..
46+
47+
Type: `string`
48+
49+
Default: `null`
50+
4951
### <a name="input_zone_redundancy_enabled"></a> [zone\_redundancy\_enabled](#input\_zone\_redundancy\_enabled)
5052

5153
Description: Enable availability zone redundancy for the container app environment. Should be set to true in production.

infrastructure/modules/container-app-environment/variables.tf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ variable "vnet_integration_subnet_id" {
2626

2727
variable "private_dns_zone_rg_name" {
2828
type = string
29-
description = "Name of the hub resource group where the private DNS zone is located."
29+
description = "Name of the hub resource group where the private DNS zone is located. This is only required if adding custom DNS records, for instance when hosting container apps with an HTTP ingress."
30+
default = null
3031
}
3132

3233
variable "zone_redundancy_enabled" {
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# container-app
2+
3+
Deploy an [Azure container app job](https://learn.microsoft.com/en-us/azure/container-apps/jobs) to a [Container app environment](https://learn.microsoft.com/en-us/azure/container-apps/environment) in order to run container workloads that do not require ingress or web access. The container app job can be triggered manually or via an event source, such as a timer or message queue. The job will not automatically run following deployment or update of the resources and the job can stop upon completion if the application code contains an exit code. It can be run again on demand via the Azure portal, CLI, or API.
4+
5+
Integrates with the [container-app-environment module](../container-app-environment/).
6+
7+
See also the following ADO template step that can be used to trigger a container app job run: [app-container-job-start.yaml](../../../.azuredevops/templates/steps/app-container-job-start.yaml).
8+
9+
## Terraform documentation
10+
For the list of inputs, outputs, resources... check the [terraform module documentation](tfdocs.md).
11+
12+
## Usage
13+
Create the common container app environment:
14+
```hcl
15+
module "container-app-environment" {
16+
source = "../../../dtos-devops-templates/infrastructure/modules/container-app-environment"
17+
18+
name = "manage-breast-screening-${var.environment}"
19+
resource_group_name = var.resource_group_name
20+
location = var.location
21+
log_analytics_workspace_id = data.terraform_remote_state.audit.outputs.log_analytics_workspace_id
22+
vnet_integration_subnet_id = module.container_app_subnet.id
23+
}
24+
```
25+
26+
Create a container app job to run an image pulled from Azure Container Registry (ACR). The job will run in the container app environment and can be triggered manually or via an event source:
27+
```hcl
28+
module "container-app-job" {
29+
30+
source = "../../../dtos-devops-templates/infrastructure/modules/container-app-job"
31+
32+
name = "ca-workload-name-${var.environment}"
33+
resource_group_name = var.resource_group_name
34+
location = var.location
35+
container_app_environment_id = module.container-app-environment.id
36+
37+
# List of user assigned managed identities to assign to the container app job so it can authenticate against e.g. database, key vault, etc:
38+
user_assigned_identity_ids = var.user_assigned_identity_ids
39+
40+
acr_login_server = data.azurerm_container_registry.acr.login_server
41+
acr_managed_identity_id = var.acr_managed_identity_id
42+
docker_image = "${data.azurerm_container_registry.acr.login_server}/${var.docker_image}:${var.docker_env_tag}"
43+
44+
environment_variables = {
45+
"ENVIRONMENT_VAR" = var.environment_var
46+
}
47+
}
48+
```
49+
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Merge the identities passed in via a variables:
2+
locals {
3+
all_identity_ids = concat([var.acr_managed_identity_id], var.user_assigned_identity_ids)
4+
}
5+
6+
resource "azurerm_container_app_job" "this" {
7+
name = var.name
8+
location = var.location
9+
resource_group_name = var.resource_group_name
10+
11+
container_app_environment_id = var.container_app_environment_id
12+
replica_timeout_in_seconds = var.replica_timeout_in_seconds
13+
replica_retry_limit = var.replica_retry_limit
14+
15+
identity {
16+
type = "UserAssigned"
17+
identity_ids = local.all_identity_ids
18+
}
19+
20+
# Configure manual trigger for on-demand execution via CLI
21+
manual_trigger_config {
22+
parallelism = var.job_parallelism
23+
replica_completion_count = var.replica_completion_count
24+
}
25+
26+
# Define the container template for the job.
27+
template {
28+
29+
container {
30+
name = var.name
31+
image = var.docker_image
32+
cpu = local.cpu
33+
memory = local.memory
34+
35+
# Optional: Command to execute in the container.
36+
command = var.container_command
37+
args = var.container_args
38+
39+
# Optional: Environment variables for the container.
40+
dynamic "env" {
41+
for_each = var.environment_variables
42+
content {
43+
name = env.key
44+
value = env.value
45+
}
46+
}
47+
}
48+
}
49+
50+
# Optional: Configure private registry access using Managed Identity
51+
dynamic "registry" {
52+
for_each = var.acr_login_server != null && var.acr_managed_identity_id != null ? [1] : []
53+
54+
content {
55+
server = var.acr_login_server
56+
identity = var.acr_managed_identity_id
57+
}
58+
}
59+
}

0 commit comments

Comments
 (0)