
CI/CD Azure Web app deployed via Terraform
Website CI/CD Pipeline
Terraform Pipeline
Terraform deployment of the full infrastructure needed for a web app - deploying the web app, supporting resources such as the CDN & Application Insights and verifying DNS records for custom domains bound to the web app & CDN
Architecture
Summary
Improvements made to an existing web app deployed using CI/CD including:
- Full Terraform deployment through a DevOps Pipeline, including DNS verification of custom domains (for the web app & CDN) directly from the pipeline
- Addition of Application Insights to monitor the web app
- Addition of a KV to store secrets
Terraform
Modules:
Creates an RG based off the project_name & environment variables passed through
After creation the RG name is outputted
Creates a LAW and links an Application Insights resource to it
APPINSIGHTS_INSTRUMENTATIONKEY & APPLICATIONINSIGHTS_CONNECTION_STRING are outputted to be used to bind a web app to the App Insights resource
The ID of the App Insight is also outputted
Creates a key vault to store secrets needed for the devops pipeline
The Key Vault ID is outputted
Creates a Container Registry to hold the Docker containers created as part of the Web App CI/CD pipeline
Also assigns the managed identity of the ASP created later the AcrPull role so it can pull the latest Docker image
Creates an App Service Plan with the B1 SKU
A Linux Web App is also created under the ASP and also passes through the outputted Application Insight variables, as well as the Container Registry information
The following variables are outputted:
-
app_service_plan.id
-
web_app_name
-
app_service_plan_managed_id
Passed through to be used by the containerRegistry module to assign the Managed Identity the AcrPull role -
web_app_domain_verification_id
Used by the cloudflare module to verify the custom domain used -
web_app_default_domain
Also used by the cloudflare module to verify the custom domain -
storageAccount
Creates a new RG to hold a storage account under - the storage account will later be used by the CDN module to configure it as an origin
Creates a CDN profile and a CDN endpoint, the storage account created by the storageAccount module is linked as a origin so images stored in the storage account can both be cached by the CDN to reduce costs, allow images to have a custom domain instead of the default blob endpoint and be encrypted over HTTPS
The endpoint FQDN is outputted to be later used by the cloudflare module to verify the second custom domain
Using the Cloudflare Terraform provider 3 DNS records are created
-
cdn_cname
Creates a CNAME record using the cdn_hostname and cdn_endpoint_uri, this is used to verify the custom domain linked to the CDN, in my case this will be images.andriusdalgeda.uk -
web_app_txt
Creates a TXT record using the web_app_domain_verification_id & asuid. plus the website_hostname
This is used to verify my andriusdalgeda.uk custom domain linked to the web app -
web_app_a
Creates an A record using the website_hostname and the IP address returned by the DNS Terraform provider
This is needed as Azure doesn’t return the IP address linked to the web_app directly so the provider is called to resolve the record to an IP address
The IP is then passed through to create the A record
After creation of these 3 records a 2min sleep is started to ensure that the DNS changes propagate so that the next modules won’t fail
After adding the DNS records the module links the custom domain to the web app (andriusdalgeda.uk)
An Azure managed certificate is also generated to encrypt traffic over HTTPS to the site
After DNS verification a custom domain is passed through to the CDN (images.andriusdalgeda.uk)
This starts domain verification which can take up to 8 hours so the deployment time can vary
Providers:
-
hashicorp/azurerm
-
cloudflare/cloudflare
Creates DNS records needed to verify the custom domains of both the web app & CDN -
hashicorp/time
Used for a time-sleep statement in the cloudflare module -
hashicorp/dns
Used to resolve the web apps address to an IP, the IP is then passed through and used for domain verification using an A record
Deployment from scratch:
Specify variables where empty:
First run backend.ps1 to create a resource group and storage account to store the Terraform remote backend
Populate the backend.tf using the variables specified in the .ps1 script
Uncomment the use_azuread_auth if you are running this from a pipeline (using a service principal)
Populate template.tfvars
terraform init
terraform plan var-file=“template.tfvars”
terraform apply var-file=“template.tfvars”
Full Deployment:
Using the Terraform code above and azure-pipelines.yml
The pipeline was run and completed after 21mins - the bulk of it being during CDN domain verification
After deploying the infrastructure I had to edit my existing pipeline that deploys the web app to point to the new web app and container registry
I then migrated all of my existing images to the new storage account using the azcopy tool
After it ran I verified that both the website and CDN were working, and configured with HTTPS
Further Improvements
Currently if the Terraform code is deployed the existing pipeline that deploys the web app the code has to be manually updated to point to the new resource
A way to improve this is to use the Azure Devops Terraform provider to write variables directly to the pipelines library (storing the secrets/values)
The KV could also be integrated into the pipeline to store and return the secrets/values