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:
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:
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:
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:
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:
- Configure
azd
pipelines to integrate Terraform with GitHub Actions. - Set up authentication to allow CI/CD workflows to interact with Azure.
- 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:
- Run
azd pipeline config
to set up an automated pipeline. - Choose between GitHub Actions or Azure DevOps for automation.
- 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.
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 | bash
- name: Install Terraform
run: |
sudo apt-get update && sudo apt-get install -y unzip
curl -fsSL -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! 🎉
Original Article Source: Configure Azure Developer CLI (azd) for HashiCorp Terraform Infrastructure Deployment written by Chris Pietschmann (If you’re reading this somewhere other than Build5Nines.com, it was republished without permission.)