The Azure Developer CLI (azd) is a powerful tool for accelerating application deployment to Microsoft Azure. While azd defaults to Azure Bicep for Infrastructure as Code (IaC) deployment, you can configure it to work with HashiCorp Terraform too! This enables azd to be configured not just for Terraform deployments, but also to support cloud-agnostic IaC deployments that offer modular, reusable, and scalable infrastructure configurations.

This article walks through the steps to configure an azd template to use Terraform IaC, store Terraform state locally or in Azure Storage, integrate outputs/variables from Terraform to azd, and how to automate Terraform azd deployments using GitHub Actions workflows.

Prerequisites

Before configuring Azure Developer CLI (azd) to work with HashiCorp Terraform, you need to ensure that your development environment is properly set up. This involves installing the necessary tools and configuring authentication to interact with Microsoft Azure.

Before we get started, be sure you have all the required tools installed:

Set Up azd for Terraform

Now that we have all the necessary prerequisites in place, the next step is to configure Azure Developer CLI (azd) to work with HashiCorp Terraform for infrastructure deployment. By default, azd uses Azure Bicep as its Infrastructure as Code (IaC) provider, but we can easily override this setting to use Terraform, which offers greater flexibility, modularity, and cloud-agnostic capabilities.

Step 1: Create an azd Project

Before configuring azd to deploy infrastructure using Terraform, we first need to set up an Azure Developer CLI (azd) project. azd provides a structured way to manage infrastructure, application code, and deployment pipelines in a single repository. This setup ensures that all components of your cloud-based solution are consistently deployed and maintained.

By creating an azd project, you gain:

  • A standardized project structure that aligns with Azure best practices.
  • Built-in environment management for handling configurations across different environments (e.g., dev, staging, prod).
  • Support for multiple infrastructure providers, including Terraform.

In this step, we will initialize a new azd project, which will serve as the foundation for deploying and managing our Terraform-based infrastructure.

If you don’t have an azd project yet, create one using the following command to initialize the project structure:

azd init

Step 2: Configure azure.yaml to use HashiCorp Terraform

Once the azd project is initialized, the next step is to configure it to use HashiCorp Terraform as the Infrastructure as Code (IaC) provider. By default, azd assumes Azure Bicep for resource provisioning, but we can override this setting to leverage Terraform instead.

The azure.yaml file is the core configuration file for azd, defining how the project interacts with Azure services, manages environments, and provisions infrastructure. By modifying this file, we can instruct azd to recognize Terraform as the preferred deployment tool for the infrastructure layer.

In this step, we will:

  • Update azure.yaml to set Terraform as the infrastructure provider.
  • Ensure that azd correctly integrates with Terraform for deploying resources.
  • Lay the groundwork for managing infrastructure-as-code efficiently within the azd framework.

This configuration ensures that all Terraform operations—such as state management, variable handling, and resource provisioning—are seamlessly managed within azd.

In the root folder of your project, locate the azure.yaml file and modify it to specify the terraform infrastructure provider:

infra:
  provider: terraform

The azd template will now be configured to use Terraform instead of Azure Bicep.

Step 3: Organize Terraform infra Files

With azd configured to use Terraform, the next step is to properly structure your Terraform configuration files within the project. A well-organized folder structure ensures clarity, maintainability, and scalability, especially when working with teams or deploying complex cloud infrastructures.

Terraform follows a modular approach, where each .tf file defines specific components of the infrastructure, such as resource definitions, variables, and outputs. Keeping these files in a dedicated infra/ folder within your azd project aligns with best practices and makes it easier for azd to detect and deploy infrastructure resources efficiently.

In this step, we will:

  • Create a dedicated infra/ folder to store Terraform files.
  • Ensure Terraform files are logically organized for better readability and modularity.
  • Lay the foundation for efficient state management and resource provisioning.

By structuring your Terraform files effectively, you enable seamless integration with azd, making infrastructure deployments more predictable, scalable, and manageable.

Create an infra/ folder inside your project and move all Terraform (.tf) files there:

mkdir -p infra
mv *.tf infra/

Your project structure should look similar to something like this:

/my-azd-project
  ├── azure.yaml
  ├── infra/
      ├── main.tf
      ├── variables.tf
      ├── outputs.tf
      ├── provider.tf

Keep in mind that any Terraform modules the project contains will also be within the infra/ folder too.

Step 3: Create .tfvars.json Parameters File

HashiCorp Terraform allows parameterization through variable files (.tfvars.json), making it easier to manage configurations across different environments. By defining a .tfvars.json file, we can store and reuse key infrastructure parameters such as resource names, locations, and sizes, ensuring consistent deployments.

Define Terraform Variables in variables.tf

Before creating the .tfvars.json file, ensure that your Terraform variables are properly declared in variables.tf:

variable "resource_group_name" {
  type        = string
  description = "The name of the resource group"
}

variable "location" {
  type        = string
  description = "Azure region for resource deployment"
  default     = "eastus"
}

variable "storage_account_name" {
  type        = string
  description = "Name of the Azure Storage account"
}

Create the .tfvars.json File

Next, create a terraform.tfvars.json file inside the infra/ directory to define the input parameters to the Terraform template for these variables:

{
  "resource_group_name": "azd-rg-dev",
  "location": "eastus",
  "storage_account_name": "azdstorageaccountdev"
}

If your Terraform project doesn’t have any any variables defined, then you can create an empty terraform.tfvars.json file. It’s important to know that azd will require the .tfvars.json file exists and contain valid JSON. Here’s an example of an empty .tfvars.json file that can be used if no variables are defined in the Terraform project:

{ }

By using .tfvars.json, we enable repeatable and environment-specific deployments, ensuring that Terraform and azd work together seamlessly. This approach simplifies configuration management, making it easier to deploy infrastructure across multiple environments.

Configure Terraform State Management

Terraform uses a state file (terraform.tfstate) to keep track of infrastructure resources deployed in Azure. This state file is critical because it allows Terraform to determine what changes need to be applied to match the desired configuration. Without proper state management, deployments can become inconsistent, leading to resource duplication or misconfiguration.

Terraform provides two main approaches for storing state:

  • Remote State (Azure Storage) – Stored in an Azure Storage Account, recommended for team collaboration and CI/CD pipelines.
  • Local State – Stored on your local machine, suitable for development or testing.

Proper state management is essential for:

Consistency – Ensures Terraform knows which resources exist and prevents duplication.
Collaboration – Enables multiple team members to work on the same infrastructure.
Security – Keeps state files safe and prevents accidental changes.

By implementing state management correctly, we ensure a reliable and scalable infrastructure deployment process.

Option 1: Local Terraform State (For Testing)

By default, Terraform stores state locally in the infra/ folder. This is not recommended for team collaboration, but useful for quick testing.

Here’s how to modify your provider.tf file to specify the local state file explicitly:

terraform {
  backend "local" {
    path = "terraform.tfstate"
  }
}

Remember, if you don’t specify any Terraform backend, then Terraform will always default to local state.

Option 2: Azure Storage Backend (Recommended for Teams)

For teams working collaboratively on Terraform deployments, using Azure Storage as a remote backend is the best practice. Unlike local state storage, which is limited to a single developer’s machine, Azure Storage provides a secure and centralized location for storing the Terraform state file (terraform.tfstate).

By leveraging Azure Storage for Terraform state management, you gain several advantages:

Collaboration – Multiple team members can work on the same infrastructure without conflicts.
Security – State files are stored securely in an Azure Storage Account, reducing the risk of data loss.
Automation – Enables integration with CI/CD pipelines for continuous deployment.
Locking & Versioning – Azure Storage supports state locking and history tracking, preventing simultaneous updates and allowing rollbacks.

Using Azure Storage for Terraform state ensures that your infrastructure deployments are scalable, secure, and team-friendly.

Step 1: Create an Azure Storage Account

The following commands can be run to create an Azure Storage Account and Container using the Azure CLI:

az group create --name azd-tf-rg --location eastus
az storage account create --name azdtfstate --resource-group azd-tf-rg --location eastus --sku Standard_LRS
az storage container create --name tfstate --account-name azdtfstate

Step 2: Configure Remote State in provider.tf

After setting up an Azure Storage Account for Terraform state management, the next step is to configure Terraform to use this remote backend. This is done by defining the backend configuration in the provider.tf file. With this configuration, Terraform will automatically store and retrieve infrastructure state from Azure Storage, improving security, collaboration, and operational efficiency.

Modify provider.tf to use Azure Storage for Terraform state:

terraform {
  backend "azurerm" {
    resource_group_name  = "azd-tf-rg"
    storage_account_name = "azdtfstate"
    container_name       = "tfstate"
    key                  = "terraform.tfstate"
  }
}

Run terraform init to initialize the configured backend:

terraform init

Deploy Infrastructure with azd

Now that we have configured azd to use Terraform and set up state management, it’s time to deploy our infrastructure to Azure. azd simplifies the deployment process by orchestrating Terraform commands, managing environment variables, and ensuring a streamlined infrastructure deployment workflow.

Once Terraform is configured, deploy your infrastructure with the following azd command:

azd up

This will:

  • Initialize the Terraform project (terraform init)
  • Apply Terraform configuration (terraform apply)
  • Deploy resources to Microsoft Azure as configured in the Terraform IaC code

To destroy the infrastructure and clean up the environment, use the following command:

azd down

This will:

  • Confirm the cleanup (unless --force argument is specified)
  • Destroy the Terraform-managed Azure resources (terraform destroy)
  • Remove associated environment configurations
  • Preserve Terraform state (if using remote state)
  • Validate cleanup completion

By leveraging azd for Terraform deployments, we ensure that infrastructure is repeatable, scalable, and easily manageable, making cloud resource provisioning efficient and robust.


Return Terraform Outputs to azd

When deploying infrastructure with Terraform, it’s often necessary to retrieve specific values, such as resource names, connection strings, or endpoint URLs, for use in your application or deployment scripts. Terraform outputs allow us to extract these values after provisioning infrastructure and pass them to azd for further use.

Integrating Terraform outputs with azd provides several advantages:

Simplifies Application Configuration – Pass dynamically created resource values (e.g., database connection strings) to the application layer.
Enhances Automation – Automatically reference Terraform-managed resources without manual lookups.
Improves Deployment Consistency – Ensures all components of the stack are aware of relevant infrastructure details.

By exposing Terraform outputs to azd, we enable a fully automated, end-to-end infrastructure and application deployment workflow, ensuring all resources are accessible where needed.

Step 1: Define Outputs in Terraform

Modify outputs.tf to return variables from Terraform:

output "resource_group_name" {
  value = azurerm_resource_group.my_rg.name
}

output "storage_account_name" {
  value = azurerm_storage_account.my_storage.name
}

Step 2: Access Terraform Outputs in azd

After defining Terraform outputs in outputs.tf, we need to retrieve these values and make them accessible within azd. Terraform provides a built-in command to extract outputs in JSON format, which can then be mapped to azd environment variables.

Now, when azd up command is run, azd will map Terraform outputs to environment variables:

echo $resource_group_name
echo $storage_account_name

These environment variables can then be referenced within services (aka applications) deployed by the azd template, similar to the following:

services:
  my-app:
    env:
      STORAGE_ACCOUNT_NAME: ${resource_group_name}
      RESOURCE_GROUP_NAME: ${storage_account_name}

By integrating Terraform outputs into azd, we ensure that application services can dynamically reference deployed infrastructure resources without manual configuration. This streamlines deployments, making them scalable, automated, and environment-aware.

Configure CI/CD for Terraform with azd

Automating infrastructure deployments through Continuous Integration and Continuous Deployment (CI/CD) is essential for ensuring reliable, repeatable, and scalable Terraform deployments. By integrating Terraform with azd in a CI/CD pipeline, we can automate the process of provisioning, updating, and managing cloud infrastructure directly from a version-controlled repository.

Using GitHub Actions, Azure DevOps, or other CI/CD tools, we can:

Automatically validate and apply Terraform changes – Ensuring infrastructure updates are reviewed and tested before deployment.
Enforce infrastructure best practices – Running security and compliance checks (terraform validate, tflint, tfsec).
Enable team collaboration – Multiple developers can contribute to infrastructure changes while maintaining consistency.
Seamlessly integrate application and infrastructure deployments – Deploying both the infrastructure and application code in a single pipeline.

In this section, we will:

  1. Configure azd pipelines to integrate Terraform with GitHub Actions.
  2. Set up authentication to allow CI/CD workflows to interact with Azure.
  3. Define a Terraform deployment workflow to automate infrastructure provisioning.

By implementing a CI/CD pipeline for Terraform with azd, we enable fully automated, version-controlled infrastructure deployments, reducing manual errors and increasing efficiency.

Step 1: Enable azd Pipelines

To fully automate Terraform deployments, we need to integrate azd with a CI/CD pipeline. azd provides built-in support for GitHub Actions and Azure DevOps Pipelines, enabling seamless deployment of both infrastructure and application code. By enabling azd pipelines, we ensure that every infrastructure change is automatically validated, tested, and applied, reducing manual intervention and increasing deployment reliability.

Why Enable azd Pipelines?

Automates Infrastructure Deployment – Reduces manual Terraform execution.
Improves Consistency – Ensures all deployments follow the same process across environments.
Enforces Security & Compliance – Automates security checks (terraform validate, tfsec).
Integrates with CI/CD Workflows – Streamlines infrastructure and application deployments in a single pipeline.

What We’ll Do in This Step:

  1. Run azd pipeline config to set up an automated pipeline.
  2. Choose between GitHub Actions or Azure DevOps for automation.
  3. Configure authentication for the pipeline to deploy Terraform infrastructure securely.

By enabling azd pipelines, we establish a fully automated and scalable deployment workflow, ensuring that all Terraform changes are tracked, tested, and applied efficiently.

azd pipeline config

Follow the prompts given by azd pipeline config to configure GitHub Actions.

Step 2: Add Terraform Workflow

With azd pipelines enabled, the next step is to define a Terraform workflow that automates infrastructure provisioning within the CI/CD pipeline. This workflow will ensure that Terraform operations—such as initialization, validation, plan, and apply—are executed consistently every time changes are pushed to the repository.

Why Automate Terraform with a CI/CD Workflow?

Ensures consistency – Standardizes how Terraform is applied across different environments.
Enhances security & compliance – Automatically runs Terraform validation (terraform validate) and security scans (tfsec).
Speeds up deployments – Eliminates manual Terraform execution, enabling faster infrastructure provisioning.
Enables collaboration – Ensures infrastructure changes are reviewed and deployed through version control.

By adding a Terraform workflow to azd, we create a fully automated, repeatable, and secure deployment pipeline, ensuring that infrastructure changes are managed efficiently.

To setup the GitHub Actions workflow to deploy the Terraform project using azd up, create a .github/workflows/terraform.yml file with the following steps:

name: Terraform Deployment

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout Code
        uses: actions/checkout@v3

      - name: Install azd
        run: curl -fsSL https://aka.ms/install-azd.sh | bash

      - name: Install Terraform
        run: |
          sudo apt-get update && sudo apt-get install -y unzip
          curl -fsSL https://releases.hashicorp.com/terraform/1.3.0/terraform_1.3.0_linux_amd64.zip -o terraform.zip
          unzip terraform.zip && sudo mv terraform /usr/local/bin/

      - name: Authenticate with Azure
        run: az login --service-principal -u ${{ secrets.AZURE_CLIENT_ID }} -p ${{ secrets.AZURE_CLIENT_SECRET }} --tenant ${{ secrets.AZURE_TENANT_ID }}

      - name: Deploy Terraform
        run: |
          azd up

Commit and push this workflow, and GitHub Actions will automate Terraform deployments.

Conclusion

By following this guide, you have successfully configured Azure Developer CLI (azd) to work with HashiCorp Terraform, enabling a streamlined and automated approach to infrastructure provisioning in Azure. We covered:

Setting up azd to use Terraform – Configuring azure.yaml and organizing Terraform files.
Managing Terraform state – Choosing between local and Azure Storage backends for state management.
Deploying infrastructure with azd – Using azd up and azd down to manage deployments.
Handling Terraform outputs – Passing resource information to applications using azd environment variables.
Automating deployments with CI/CD – Integrating Terraform with azd pipelines using GitHub Actions.

Key Benefits of Using azd with Terraform:

🚀 Automated Deployments – Reduce manual work with fully scripted infrastructure provisioning.
🔄 Repeatable and Consistent – Ensure every deployment follows best practices and remains consistent across environments.
🔒 Secure State Management – Protect Terraform state with Azure Storage, ensuring reliability in team workflows.
🛠 Seamless Integration – Use Terraform outputs in azd applications, linking infrastructure and code deployment.
📈 Scalability & CI/CD Ready – Enable infrastructure as code with automated validation and deployment pipelines.

Next Steps:

Now that your azd project is fully configured with HashiCorp Terraform, you can:

  • Expand your infrastructure by adding additional Terraform modules (e.g., databases, networking).
  • Enhance security by integrating Terraform with Azure Policy and RBAC.
  • Optimize cost and performance by leveraging Terraform’s resource lifecycle management.
  • Monitor and maintain deployments using Azure Monitor and Log Analytics.

By combining azd and HashiCorp Terraform, you unlock a powerful, DevOps-ready approach to managing cloud infrastructure in Microsoft Azure. 🚀 Happy deploying! 🎉

Chris Pietschmann is a Microsoft MVP, HashiCorp Ambassador, and Microsoft Certified Trainer (MCT) with 20+ years of experience designing and building Cloud & Enterprise systems. He has worked with companies of all sizes from startups to large enterprises. He has a passion for technology and sharing what he learns with others to help enable them to learn faster and be more productive.
Microsoft MVP HashiCorp Ambassador

Discover more from Build5Nines

Subscribe now to keep reading and get access to the full archive.

Continue reading