|
| 1 | +--- |
| 2 | +title: ADE extensibility model for custom Terraform images |
| 3 | +titleSuffix: Azure Deployment Environments |
| 4 | +description: Learn how to use the ADE extensibility model to build and utilize custom Terraform images within your environment definitions for deployment environments. |
| 5 | +ms.service: deployment-environments |
| 6 | +author: RoseHJM |
| 7 | +ms.author: rosemalcolm |
| 8 | +ms.date: 04/15/2024 |
| 9 | +ms.topic: how-to |
| 10 | + |
| 11 | +#customer intent: As a developer, I want to learn how to build and utilize custom images within my environment definitions for deployment environments. |
| 12 | +--- |
| 13 | + |
| 14 | +# Configure a container image to execute deployments with Terraform |
| 15 | + |
| 16 | +In this article, you learn how to build and utilize a custom image within your environment definitions for deployments in Azure Deployment Environments (ADE). You learn how to configure a custom image to provision infrastructure using the Terraform Infrastructure-as-Code (IaC) framework. |
| 17 | + |
| 18 | +ADE supports an extensibility model that enables you to create custom images that you can use in your environment definitions. To leverage this extensibility model, you can create your own custom images, and store them in a public container registry. You can then reference these images in your environment definitions to deploy your environments. |
| 19 | + |
| 20 | +The ADE team provides a selection of images to get you started, which you can see in the [Runner-Images](https://github.com/Azure/deployment-environments/tree/custom-runner-private-preview/Runner-Images) folder. |
| 21 | + |
| 22 | +The ADE CLI is a tool that allows you to build custom images by using ADE base images. You can use the ADE CLI to customize your deployments and deletions to fit your workflow. The ADE CLI is preinstalled on the sample images. To learn more about the ADE CLI, see the [CLI Custom Runner Image reference](https://aka.ms/deployment-environments/ade-cli-reference). |
| 23 | + |
| 24 | +## Prerequisites |
| 25 | + |
| 26 | +- An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). |
| 27 | + |
| 28 | +## Create and build a Docker image by using Terraform |
| 29 | + |
| 30 | +In this example, you learn how to build a Docker image to utilize ADE deployments and access the ADE CLI, basing your image on one of the ADE authored images. |
| 31 | + |
| 32 | +### FROM statement |
| 33 | + |
| 34 | +Include a FROM statement within a created DockerFile for your new image pointing to a sample image hosted on Microsoft Artifact Registry. |
| 35 | + |
| 36 | +Here's an example FROM statement, referencing the sample core image: |
| 37 | + |
| 38 | +```docker |
| 39 | +FROM mcr.microsoft.com/deployment-environments/runners/core:latest |
| 40 | +``` |
| 41 | + |
| 42 | +This statement pulls the most recently published core image, and makes it a basis for your custom image. |
| 43 | + |
| 44 | +### Install Terraform in a Dockerfile |
| 45 | + |
| 46 | +You can install the Terraform CLI to an executable location so that it can be used in your deployment and deletion scripts. Here's an example of that process, installing version 1.5.5 of the Terraform CLI: |
| 47 | + |
| 48 | +```azure cli |
| 49 | +RUN wget -O terraform.zip https://releases.hashicorp.com/terraform/1.7.4/terraform_1.5.5_linux_amd64.zip |
| 50 | +RUN unzip terraform.zip && rm terraform.zip |
| 51 | +RUN mv terraform /usr/bin/terraform |
| 52 | +``` |
| 53 | + |
| 54 | +The ADE sample images are based on the Azure CLI image, and have the ADE CLI and JQ packages preinstalled. You can learn more about the [Azure CLI](/cli/azure/), and the [JQ package](https://devdocs.io/jq/). |
| 55 | + |
| 56 | +To install any more packages you need within your image, use the RUN statement. |
| 57 | + |
| 58 | +### Execute operation shell scripts |
| 59 | + |
| 60 | +Within the sample images, operations are determined and executed based on the operation name. Currently, the two operation names supported are *deploy* and *delete*. |
| 61 | + |
| 62 | +To set up your custom image to utilize this structure, specify a folder at the level of your Dockerfile named *scripts*, and specify two files, *deploy.sh*, and *delete.sh*. The deploy shell script runs when your environment is created or redeployed, and the delete shell script runs when your environment is deleted. You can see examples of shell scripts in the repository under the [Runner-Images folder for the ARM-Bicep](https://github.com/Azure/deployment-environments/tree/custom-runner-private-preview/Runner-Images/ARM-Bicep) image. |
| 63 | + |
| 64 | +To ensure these shell scripts are executable, add the following lines to your Dockerfile: |
| 65 | + |
| 66 | +```docker |
| 67 | +COPY scripts/* /scripts/ |
| 68 | +RUN find /scripts/ -type f -iname "*.sh" -exec dos2unix '{}' '+' |
| 69 | +RUN find /scripts/ -type f -iname "*.sh" -exec chmod +x {} \; |
| 70 | +``` |
| 71 | + |
| 72 | +### Author operation shell scripts to use the Terraform CLI |
| 73 | +There are three steps to deploy infrastructure via Terraform: |
| 74 | +1. `terraform init` - initializes the Terraform CLI to perform actions within the working directory |
| 75 | +1. `terraform plan` - develops a plan based on the incoming Terraform infrastructure files and variables, and any existing state files, and develops steps needed to create or update infrastructure specified in the *.tf* files |
| 76 | +1. `terraform apply` - applies the plan to create new or update existing infrastructure in Azure |
| 77 | + |
| 78 | +During the core image's entrypoint, any existing state files are pulled into the container and the directory saved under the environment variable ```$ADE_STORAGE```. Additionally, any parameters set for the current environment stored under the variable ```$ADE_OPERATION_PARAMETERS```. In order to access the existing state file, and set your variables within a *.tfvars.json* file, run the following commands: |
| 79 | +```bash |
| 80 | +EnvironmentState="$ADE_STORAGE/environment.tfstate" |
| 81 | +EnvironmentPlan="/environment.tfplan" |
| 82 | +EnvironmentVars="/environment.tfvars.json" |
| 83 | + |
| 84 | +echo "$ADE_OPERATION_PARAMETERS" > $EnvironmentVars |
| 85 | +``` |
| 86 | + |
| 87 | +Additionally, to utilize ADE's privileges to deploy infrastructure inside your subscription, your script needs to use ADE's Managed Service Identity (MSI) when provisioning infrastructure by using the Terraform AzureRM provider. If your deployment needs special permissions to complete your deployment, such as particular roles, assign those permissions to the project environment type's identity that is being used for your environment deployment. ADE sets the relevant environment variables, such as the client, tenant, and subscription IDs within the core image's entrypoint, so run the following commands to ensure the provider uses ADE's MSI: |
| 88 | +```bash |
| 89 | +export ARM_USE_MSI=true |
| 90 | +export ARM_CLIENT_ID=$ADE_CLIENT_ID |
| 91 | +export ARM_TENANT_ID=$ADE_TENANT_ID |
| 92 | +export ARM_SUBSCRIPTION_ID=$ADE_SUBSCRIPTION_ID |
| 93 | +``` |
| 94 | + |
| 95 | +If you have other variables to reference within your template that aren't specified in your environment's parameters, set environment variables using the prefix *TF_VAR*. A list of provided ADE environment variables is provided [here](insert link). An example of those commands could be; |
| 96 | +```bash |
| 97 | +export TF_VAR_resource_group_name=$ADE_RESOURCE_GROUP_NAME |
| 98 | +export TF_VAR_ade_env_name=$ADE_ENVIRONMENT_NAME |
| 99 | +export TF_VAR_env_name=$ADE_ENVIRONMENT_NAME |
| 100 | +export TF_VAR_ade_subscription=$ADE_SUBSCRIPTION_ID |
| 101 | +export TF_VAR_ade_location=$ADE_ENVIRONMENT_LOCATION |
| 102 | +export TF_VAR_ade_environment_type=$ADE_ENVIRONMENT_TYPE |
| 103 | +``` |
| 104 | + |
| 105 | +Now, you can run the steps listed previously to initialize the Terraform CLI, generate a plan for provisioning infrastructure, and apply a plan during your deployment script: |
| 106 | +```bash |
| 107 | +terraform init |
| 108 | +terraform plan -no-color -compact-warnings -refresh=true -lock=true -state=$EnvironmentState -out=$EnvironmentPlan -var-file="$EnvironmentVars" |
| 109 | +terraform apply -no-color -compact-warnings -auto-approve -lock=true -state=$EnvironmentState $EnvironmentPlan |
| 110 | +``` |
| 111 | + |
| 112 | +During your deletion script, you can add the `destroy` flag to your plan generation to delete the existing resources, as shown in the following example: |
| 113 | +```bash |
| 114 | +terraform init |
| 115 | +terraform plan -no-color -compact-warnings -destroy -refresh=true -lock=true -state=$EnvironmentState -out=$EnvironmentPlan -var-file="$EnvironmentVars" |
| 116 | +terraform apply -no-color -compact-warnings -auto-approve -lock=true -state=$EnvironmentState $EnvironmentPlan |
| 117 | +``` |
| 118 | + |
| 119 | +Finally, to make the outputs of your deployment uploaded and accessible when accessing your environment via the Azure CLI, transform the output object from Terraform to the ADE-specified format through the JQ package. Set the value to the $ADE_OUTPUTS environment variable, as shown in the following example: |
| 120 | +```bash |
| 121 | +tfOutputs=$(terraform output -state=$EnvironmentState -json) |
| 122 | +# Convert Terraform output format to ADE format. |
| 123 | +tfOutputs=$(jq 'walk(if type == "object" then |
| 124 | + if .type == "bool" then .type = "boolean" |
| 125 | + elif .type == "list" then .type = "array" |
| 126 | + elif .type == "map" then .type = "object" |
| 127 | + elif .type == "set" then .type = "array" |
| 128 | + elif (.type | type) == "array" then |
| 129 | + if .type[0] == "tuple" then .type = "array" |
| 130 | + elif .type[0] == "object" then .type = "object" |
| 131 | + elif .type[0] == "set" then .type = "array" |
| 132 | + else . |
| 133 | + end |
| 134 | + else . |
| 135 | + end |
| 136 | + else . |
| 137 | + end)' <<< "$tfOutputs") |
| 138 | + |
| 139 | +echo "{\"outputs\": $tfOutputs}" > $ADE_OUTPUTS |
| 140 | +``` |
| 141 | + |
| 142 | +### Build the image |
| 143 | + |
| 144 | +Before you build the image to be pushed to your registry, ensure the [Docker Engine is installed](https://docs.docker.com/desktop/) on your computer. Then, navigate to the directory of your Dockerfile, and run the following command: |
| 145 | + |
| 146 | +```docker |
| 147 | +docker build . -t {YOUR_REGISTRY}.azurecr.io/{YOUR_REPOSITORY}:{YOUR_TAG} |
| 148 | +``` |
| 149 | + |
| 150 | +For example, if you want to save your image under a repository within your registry named `customImage`, and upload with the tag version of `1.0.0`, you would run: |
| 151 | + |
| 152 | +```docker |
| 153 | +docker build . -t {YOUR_REGISTRY}.azurecr.io/customImage:1.0.0 |
| 154 | +``` |
| 155 | + |
| 156 | +## Push the Docker image to a registry |
| 157 | + |
| 158 | +In order to use custom images, you need to set up a publicly accessible image registry with anonymous image pull enabled. This way, Azure Deployment Environments can access your custom image to execute in our container. |
| 159 | + |
| 160 | +Azure Container Registry is an Azure offering that stores container images and similar artifacts. |
| 161 | + |
| 162 | +To create a registry, which can be done through the Azure CLI, the Azure portal, PowerShell commands, and more, follow one of the [quickstarts](/azure/container-registry/container-registry-get-started-azure-cli). |
| 163 | + |
| 164 | +To set up your registry to have anonymous image pull enabled, run the following commands in the Azure CLI: |
| 165 | + |
| 166 | +```azurecli |
| 167 | +az login |
| 168 | +az acr login -n {YOUR_REGISTRY} |
| 169 | +az acr update -n {YOUR_REGISTRY} --public-network-enabled true |
| 170 | +az acr update -n {YOUR_REGISTRY} --anonymous-pull-enabled true |
| 171 | +``` |
| 172 | + |
| 173 | +When you're ready to push your image to your registry, run the following command: |
| 174 | + |
| 175 | +```docker |
| 176 | +docker push {YOUR_REGISTRY}.azurecr.io/{YOUR_IMAGE_LOCATION}:{YOUR_TAG} |
| 177 | +``` |
| 178 | + |
| 179 | +## Connect the image to your environment definition |
| 180 | + |
| 181 | +When authoring environment definitions to use your custom image in their deployment, edit the `runner` property on the manifest file (environment.yaml or manifest.yaml). |
| 182 | + |
| 183 | +```yaml |
| 184 | +runner: "{YOUR_REGISTRY}.azurecr.io/{YOUR_REPOSITORY}:{YOUR_TAG}" |
| 185 | +``` |
| 186 | +
|
| 187 | +## Access operation logs and error details |
| 188 | +
|
| 189 | +ADE stores error details for a failed deployment the *$ADE_ERROR_LOG* file. |
| 190 | +
|
| 191 | +To troubleshoot a failed deployment: |
| 192 | +
|
| 193 | +1. Sign in to the [Developer Portal](https://devportal.microsoft.com/). |
| 194 | +1. Identify the environment that failed to deploy, and select **See details**. |
| 195 | +
|
| 196 | + :::image type="content" source="media/how-to-configure-extensibility-terraform-container-image/failed-deployment-card.png" alt-text="Screenshot showing failed deployment error details, specifically an invalid name for a storage account." lightbox="media/how-to-configure-extensibility-terraform-container-image/failed-deployment-card.png"::: |
| 197 | +
|
| 198 | +1. Review the error details in the **Error Details** section. |
| 199 | +
|
| 200 | + :::image type="content" source="media/how-to-configure-extensibility-terraform-container-image/deployment-error-details.png" alt-text="Screenshot showing a failed deployment of an environment with the See Details button displayed." lightbox="media/how-to-configure-extensibility-terraform-container-image/deployment-error-details.png"::: |
| 201 | +
|
| 202 | +Additionally, you can use the Azure CLI to view an environment's error details using the following command: |
| 203 | +```bash |
| 204 | +az devcenter dev environment show --environment-name {YOUR_ENVIRONMENT_NAME} --project {YOUR_PROJECT_NAME} |
| 205 | +``` |
| 206 | + |
| 207 | +To view the operation logs for an environment deployment or deletion, use the Azure CLI to retrieve the latest operation for your environment, and then view the logs for that operation ID. |
| 208 | + |
| 209 | +```bash |
| 210 | +# Get list of operations on the environment, choose the latest operation |
| 211 | +az devcenter dev environment list-operation --environment-name {YOUR_ENVIRONMENT_NAME} --project {YOUR_PROJECT_NAME} |
| 212 | +# Using the latest operation ID, view the operation logs |
| 213 | +az devcenter dev environment show-logs-by-operation --environment-name {YOUR_ENVIRONMENT_NAME} --project {YOUR_PROJECT_NAME} --operation-id {LATEST_OPERATION_ID} |
| 214 | +``` |
| 215 | + |
| 216 | + |
| 217 | +## Related content |
| 218 | + |
| 219 | +- [ADE CLI Custom Runner Image reference](https://aka.ms/deployment-environments/ade-cli-reference) |
0 commit comments