Click on each book below to review & buy on Amazon.
As an Amazon Associate, I earn from qualifying purchases.
Terraform - Azure Static Web App - Custom Domain - Azure DevOps Pipelines
Guide to deploying Azure Static Web Apps with a custom domain using Terraform.
Azure DevOps Pipelines will also be created using Terraform.
Terraform Version | 1.4.5 |
Azurerm Version | 3.54.0 |
Azuredevops Version | 0.5.0 |
Prerequisites
The prerequisites are:
-
Your website needs to be held within an Azure DevOps repository.
-
Azure DevOps Personal Access Token with Permissions:
- Code: Read, Write & Manage
- Variable Groups: Read, Create, & Manage
- Build: Read & execute
- Project and Team: Read
-
Azure DevOps parallel jobs are required. Request Parallel Jobs (Free Tier).
-
Ability to add CNAME records for your domain.
-
Have authenticated to Azure, for example, by running
az login
. -
Have authenticated to Azure DevOps, for example, by running
az devops login --organization https://dev.azure.com/<org name>
Terraform Block
The Terrafrom block requires two arguments:
- required_version: Set to the desired Terraform version.
- required_providers: A required providers block. (See below for details).
For help on declaring versions, see Version Constraints
required_providers
There are two required providers:
- azurerm: Manages Azure resources.
- azuredevops: Manages Azure DevOps resources.
azurerm
This provider requires two arguments:
- source: Set this to
"azurerm"
. - version: Set to the desired provider version.
azuredevops
This provider requires two arguments:
- source: Set this to
"microsoft/azuredevops"
. - version: Set to the desired provider version.
Example Code
terraform {
required_version = "=1.4.5"
required_providers {
azurerm = {
source = "azurerm"
version = "=3.54"
}
azuredevops = {
source = "microsoft/azuredevops"
version = "=0.5.0"
}
}
}
Providers
The azurerm & azuredevops providers require configuration.
azurerm Provider
There is are two arguments that may be useful to configure:
- subscription_id: Declaring the Azure subscription_id is useful to save switching subscriptions manually at the command line.
- prevent_deletion_if_contains_resources: This will be under the resource_group features as shown below. It ensures Terraform will not delete the resource group if any resources are present within it.
true
orfalse
.
azuredevops Provider
To ensure the Personal Access Token (PAT) is not stored in code, a variable can be set which will prompt for the token at runtime:
variable "ado_personal_access_token" {
sensitive = true
}
There are two required arguments:
- org_service_url: The DevOps organisation.
https://dev.azure.com/<org name>
. - personal_access_token: Reference the PAT variable.
sensitive(var.ado_personal_access_token)
.
Example Code
provider "azurerm" {
subscription_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
features {
resource_group {
prevent_deletion_if_contains_resources = true
}
}
}
variable "ado_personal_access_token" {
sensitive = true
}
provider "azuredevops" {
org_service_url = "https://dev.azure.com/demo-org"
personal_access_token = sensitive(var.ado_personal_access_token)
}
Azure Resource Group
The azurerm_resource_group resource is used to manage Azure resource groups.
Required Arguments
The required arguments are:
- location: The location for the resource groups meta data.
- name: The name of the resource group.
rg-<app or service name>-<subscription purpose>-<###>
Run az account list-locations -o table
to get a list of locations.
Optional Arguments
The optional arguments are:
- tags: The tags to be assigned to the resource.
Example Code
resource "azurerm_resource_group" "rg-stapp-001" {
name = "rg-stapp-demo-001"
location = "ukwest"
tags = {
environment = "demo"
source = "terraform"
}
}
Azurerm Static Site
The azurerm_static_site resource is used to manage Azure Static Web Apps.
Required Arguments
The required arguments are:
- name: The name for the Azure Static Site.
- location: The region for Azure Functions API/Staging Environments. Valid options are:
Central US
East US 2
East Asia
West Europe
West US 2
- resource_group_name: Reference the name of the azurerm_resource_group resource declared earlier.
azurerm_resource_group.rg-stapp-001.name
Optional Arguments
The optional arguments are:
- sku_tier: Specifies the SKU tier.
Free
orStandard
. Default isFree
. - sku_size: Specifies the SKU size.
Free
orStandard
. Default isFree
. - tags: The tags to be assigned to the resource.
Example Code
resource "azurerm_static_site" "stapp-dtvlinux-001" {
name = "stapp-dtvlinux-demo-001"
location = "West Europe"
resource_group_name = azurerm_resource_group.rg-stapp-001.name
sku_tier = "Free"
sku_size = "Free"
tags = {
environment = "demo"
source = "terraform"
}
}
Azure Static Site Custom Domain
The azurerm_static_site_custom_domain resource is used to manage custom domains for your site. i.e. www.example.com
instead of random-name.azurestaticapps.net
.
Required Arguments
The required arguments are:
- static_site_id: Reference the ID of the azurerm_static_site resource declared earlier.
azurerm_static_site.stapp-dtvlinux-001.id
- domain_name: The custom domain name for the Azure Static Site.
www.example.com
- validation_type: Whether to validate your domain via CNAME or TXT records.
cname-delegation
ordns-txt-token
. Usecname-delegation
.
Example Code
resource "azurerm_static_site_custom_domain" "stapp-dtvlinux-001" {
static_site_id = azurerm_static_site.stapp-dtvlinux-001.id
domain_name = "www.example.com"
validation_type = "cname-delegation"
}
Azure DevOps Variable Group
The azuredevops_variable_group resource manages variable groups within the Azure DevOps Pipeline Library.
Required Arguments
The required arguments are:
- project_id: The ID of your Azure DevOps Project.
- name: The name of the variable group. Azure uses the naming convention
azure-static-web-apps-<webapp hostname>-variable-group
. - allow_access: Whether all pipelines in the project can use the variable group.
true
orfalse
. Usetrue
. - variable: This block configures the variable options to use.
- name: The variable name.
- secret_value: The variables value.
azurerm_static_site.stapp-dtvlinux-001.api_key
. - is_secret: Whether the variable is a secret.
true
orfalse
. Usetrue
.
To find your project ID, run az repos list --query "[].{ProjectName:project.name, ProjectID:project.id}" --output table
Optional Argument
The optional argument is:
- description: Give a description for your variable group.
Example Code
resource "azuredevops_variable_group" "var-grp-dtvlinux-001" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxx"
name = "azure-static-web-apps-dtvlinux-variable-group"
allow_access = true
description = "Static Web App Variable Group"
variable {
name = "AZURE_STATIC_WEB_APPS_API_TOKEN_DTVLINUX"
secret_value = azurerm_static_site.stapp-dtvlinux-001.api_key
is_secret = true
}
}
Azure DevOps Repository File
The azuredevops_git_repository_file resource is used to manage the azure-pipelines.yml file.
Required Arguments
The required arguments are:
- repository_id: The ID of your Azure DevOps repository.
- file: The file to manage.
azure-pipelines.yml
. - content: The contents of the azure-pipelines.yml file.
- branch: Specify the branch the file should exist on.
refs/head/master
is the default. - overwrite_on_create: Enables overwritting if file already exists.
true
orfalse
. Usetrue
.
To find your repository ID, run az repos list --query "[].{RepoName:name, RepoID:id}" --output table
Example Code
resource "azuredevops_git_repository_file" "repo-file-dtvlinux-001" {
repository_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxx"
file = "azure-pipelines.yml"
content = <<-EOT
trigger:
- refs/heads/main
pool:
vmImage: ubuntu-latest
variables:
- group: ${azuredevops_variable_group.var-grp-dtvlinux-001.name}
steps:
- checkout: self
submodules: true
- task: AzureStaticWebApp@0
inputs:
app_location: 'site'
skip_app_build: true
skip_api_build: true
verbose: true
azure_static_web_apps_api_token: $(AZURE_STATIC_WEB_APPS_API_TOKEN_DTVLINUX)
EOT
branch = "refs/heads/main"
overwrite_on_create = true
}
Azure DevOps Build Definition
The azuredevops_build_definition resource is used to manage the build process for the Web App.
Required Arguments
The required arguments are:
- project_id: The ID of your Azure DevOps Project.
- ci_trigger: This block manages continuous integration options.
- use_yaml: Whether to use the Azure pipelines file.
true
orfalse
. Usetrue
.
- use_yaml: Whether to use the Azure pipelines file.
- repository: This block configures the repository options to use.
- repo_type: The repository type.
GitHub
,TfsGit
,Bitbucket
orGitHub Enterprise
. UseTfsGit
. - repo_id: The ID of your Azure DevOps repository.
- branch_name: Specify the branch the file should exist on.
master
is the default. - yml_path: Set this to
azure-pipelines.yml
- repo_type: The repository type.
- variable_groups: This is a list of any variable groups to link to the build definition.
[azuredevops_variable_group.var-grp-dtvlinux-001.id]
.
Example Code
resource "azuredevops_build_definition" "build-def-dtvlinux-001" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxx"
name = "Static Web App Build Definition"
ci_trigger {
use_yaml = true
}
repository {
repo_type = "TfsGit"
repo_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxx"
branch_name = "refs/heads/main"
yml_path = "azure-pipelines.yml"
}
variable_groups = [
azuredevops_variable_group.var-grp-dtvlinux-001.id
]
}
Static Site Hostname Output
The Static Web Apps hostname is a required output. Set as below:
output "static_site_default_hostname" {
value = "${azurerm_static_site.stapp-dtvlinux-001.default_host_name}."
}
Terraform - 1st Run
It is now time to run Terraform commands.
Enter your Azure DevOps PAT token when prompted.
Terraform Initialization
To initialise the Terraform and provider versions, run terraform init
.
Terraform Apply
Run terraform apply
and review the changes/new resources Terraform will implement.
Answer yes
if you wish for Terraform to apply those changes, or no
to cancel the run.
On the first run you will receive Error: creating Static Site Custom Domain
. A later step in this guide adds a CNAME record to your domain before we run terraform apply again.
Custom Domain
You will need to add a CNAME record to your domain.
Azure Static Web App Default Hostname
Get the Static Web App default hostname by running:
terraform output static_site_default_hostname
Create CNAME Record
Follow your domain registrars instructions on how to add a CNAME record.
An example record would be the below, where the data field contains the output from the previous step:
Make sure to exclude the quotes and include the final dot (.)
Type | Name | Data |
---|---|---|
CNAME | www | black-sea-xxxxxxxxx.x.azurestaticapps.net. |
Terraform - 2nd Run
Now the CNAME record has been added, Terraform can run again to add the custom domain to the Web App.
Terraform Apply
Run terraform apply
and review the changes/new resources Terraform will implement.
Answer yes
if you wish for Terraform to apply those changes, or no
to cancel the run.
Visit The Web App via Custom Domain
You can now visit you Web App using your custom domain.
Type it into a browser to give it a try!
Full Example Code
terraform {
required_version = "=1.4.5"
required_providers {
azurerm = {
source = "azurerm"
version = "=3.54"
}
azuredevops = {
source = "microsoft/azuredevops"
version = "=0.5.0"
}
}
}
provider "azurerm" {
subscription_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
features {
resource_group {
prevent_deletion_if_contains_resources = true
}
}
}
variable "ado_personal_access_token" {
sensitive = true
}
provider "azuredevops" {
org_service_url = "https://dev.azure.com/demo-org"
personal_access_token = sensitive(var.ado_personal_access_token)
}
resource "azurerm_resource_group" "rg-stapp-001" {
name = "rg-stapp-demo-001"
location = "ukwest"
tags = {
environment = "demo"
source = "terraform"
}
}
resource "azurerm_static_site" "stapp-dtvlinux-001" {
name = "stapp-dtvlinux-demo-001"
location = "West Europe"
resource_group_name = azurerm_resource_group.rg-stapp-001.name
sku_tier = "Free"
sku_size = "Free"
tags = {
environment = "demo"
source = "terraform"
}
}
resource "azurerm_static_site_custom_domain" "stapp-dtvlinux-001" {
static_site_id = azurerm_static_site.stapp-dtvlinux-001.id
domain_name = "www.example.com"
validation_type = "cname-delegation"
}
resource "azuredevops_variable_group" "var-grp-dtvlinux-001" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxx"
name = "azure-static-web-apps-dtvlinux-variable-group"
allow_access = true
description = "Static Web App Variable Group"
variable {
name = "AZURE_STATIC_WEB_APPS_API_TOKEN_DTVLINUX"
secret_value = azurerm_static_site.stapp-dtvlinux-001.api_key
is_secret = true
}
}
resource "azuredevops_git_repository_file" "repo-file-dtvlinux-001" {
repository_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxx"
file = "azure-pipelines.yml"
content = <<-EOT
trigger:
- refs/heads/main
pool:
vmImage: ubuntu-latest
variables:
- group: ${azuredevops_variable_group.var-grp-dtvlinux-001.name}
steps:
- checkout: self
submodules: true
- task: AzureStaticWebApp@0
inputs:
app_location: 'site'
skip_app_build: true
skip_api_build: true
verbose: true
azure_static_web_apps_api_token: $(AZURE_STATIC_WEB_APPS_API_TOKEN_DTVLINUX)
EOT
branch = "refs/heads/main"
overwrite_on_create = true
}
resource "azuredevops_build_definition" "build-def-dtvlinux-001" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxx"
name = "Static Web App Build Definition"
ci_trigger {
use_yaml = true
}
repository {
repo_type = "TfsGit"
repo_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxx"
branch_name = "refs/heads/main"
yml_path = "azure-pipelines.yml"
}
variable_groups = [
azuredevops_variable_group.var-grp-dtvlinux-001.id
]
}
output "static_site_default_hostname" {
value = "${azurerm_static_site.stapp-dtvlinux-001.default_host_name}."
}
Support DTV Linux
Click on each book below to review & buy on Amazon. As an Amazon Associate, I earn from qualifying purchases.
NordVPN ®: Elevate your online privacy and security. Grab our Special Offer to safeguard your data on public Wi-Fi and secure your devices. I may earn a commission on purchases made through this link.