Introduction
Welcome back to our journey of creating a DevOps-engineered website from scratch. In our last article, we laid out the strategy and the tools. Now, it's time to roll up our sleeves and delve into the technical setup. We'll be purchasing domain names, setting up cloud services, and configuring our CI/CD pipeline. Let's dive in!
NOTE: This article is the second part of a series. If you haven't read the first part, you can find it here.
NOTE: The source code for this project is available on GitHub: here
Part 1: Establishing Prerequisites
Before we initiate the deployment process, it's essential to set up the foundational elements that our website's infrastructure will rely on. Each component plays a critical role in the seamless creation and operation of the website.
- Azure Subscription: A subscription is your gateway to accessing all Azure cloud services. It's a prerequisite for creating and managing Azure resources. If you don't have one already, you can sign up for a free subscription. Get an Azure Subscription.
- Domain Name: The domain name serves as your website's digital address, essential for branding and making the site accessible to visitors. OVH is one of the platforms where you can secure a domain name that fits your brand. Register a domain on OVH.
- Terraform: This tool allows you to define, provision, and configure Azure infrastructure using easy-to-understand declarative code. It's an indispensable part of Infrastructure as Code (IaC), making your infrastructure management more efficient and scalable. Install Terraform.
- SendGrid Account: Reliable email communication is a pillar of user engagement. SendGrid provides robust email services that can be integrated into your website for sending transactional and marketing emails. Sign up for SendGrid.
NOTE: The SendGrid account is optional. If you don't want to use SendGrid, you can remove the SendGrid-related code from the Terraform configuration. It will be used to send emails to newsletter subscribers.
Part 2: Infrastructure Setup with Terraform
Terraform Installation
To manage our Azure resources effectively, we begin by installing Terraform. This powerful tool allows us to define our infrastructure as code, making it easier to deploy and version our web application infrastructure. Detailed instructions for installing Terraform can be found on the official Terraform website.
Terraform Background
The foundation of Terraform usage is its ability to manage state. We set up a Terraform backend that stores the state of our resources, ensuring that we can track and collaborate on changes made to our infrastructure. Here is an example of how we might configure our Terraform backend using an Azure Storage Account:
terraform {
backend "azurerm" {
resource_group_name = "your_resource_group"
storage_account_name = "your_storage_account"
container_name = "your_container"
key = "terraform.tfstate"
}
}
For this configuration to work, we first need to create the resource group, the storage account and the storage container. We can do this manually wth the fowwowing script:
NOTE: The backend configuration is stored in a file called `backend-config.txt`. This file is not included in the repository for security reasons.
set website_name=bhsitconsulting
set region=westeurope
## Create an resource group
az group create -n "%website_name%-tfdata" -l %region%
## Create a storage account
az storage account create --name "%website_name%tfdata" --resource-group "%website_name%-tfdata" --location %region% --sku Standard_LRS
## Create a storage account container
az storage container create -n tfdata --account-name "%website_name%tfdata" --resource-group "%website_name%-tfdata"
## Set up backend config file
echo resource_group_name="%website_name%-tfdata" > backend-config.txt
echo storage_account_name="%website_name%tfdata" >> backend-config.txt
echo container_name="tfdata" >> backend-config.txt
Custom Domain and DNS Configuration
To connect a custom domain to our Azure Static Web App, we need to configure DNS settings. This is handled by creating an Azure DNS zone and defining the necessary records. Here's how we set up the DNS resources in Terraform:
DNS Zone: This resource is where we define our DNS zone, which will contain all our DNS records.
resource "azurerm_dns_zone" "swa" {
name = var.custom_domain_name
resource_group_name = azurerm_resource_group.swa.name
tags = var.common_tags
}
TXT Record: A TXT record is used for various purposes, including domain ownership verification and email sender verification. We add a TXT record to our DNS zone with the necessary validation token.
resource "azurerm_dns_txt_record" "txt" {
name = "@"
zone_name = azurerm_dns_zone.swa.name
resource_group_name = azurerm_resource_group.swa.name
ttl = 300
record {
value = azurerm_static_site_custom_domain.txt.validation_token == "" ? "validated" : azurerm_static_site_custom_domain.txt.validation_token
}
}
NOTE: The validation_token
is used to
verify domain ownership. It is generated by Azure Static Web App and
stored in the azurerm_static_site_custom_domain.txt
resource.
The validation_token
is only available after the domain has
been validated. This is why we use a conditional statement to check if
the token is available. If it is, we use it as the value for the TXT
record. Otherwise, we use the string "validated".
A Record: The A record maps our domain name to the IP address of our static site. The "@" symbol represents the root domain.
resource "azurerm_dns_a_record" "alias" {
name = "@"
zone_name = azurerm_dns_zone.swa.name
resource_group_name = azurerm_resource_group.swa.name
ttl = 300
target_resource_id = azurerm_static_site.swa.id
}
Outputs Configuration
After deploying our infrastructure with Terraform, we receive output that includes sensitive information such as the API token for our static site and the name servers for our DNS zone. These outputs are crucial for configuring our domain's settings in OVH and for managing our static site:
output "api_token" {
value = azurerm_static_site.swa.api_key
sensitive = true
}
output "name_servers" {
value = azurerm_dns_zone.swa.name_servers
sensitive = false
}
Each output has a specific utility:
- API Token: This token is used to authenticate GitHub Actions or other CI/CD pipelines with Azure, allowing for automated deployment of our web application. It is marked as sensitive because it grants access to control your Azure resources.
- Name Servers: The list of name servers is crucial for configuring your domain name system (DNS) settings. You'll need to enter these servers at your domain registrar, like OVH, to point your domain to the Azure infrastructure we've established.
- Storage Connection String: This connection string provides necessary credentials to access the Azure Table Storage, enabling our application to store and retrieve data securely. It is vital for backend operations such as managing newsletter subscriptions.
It's imperative to securely store the api_token
and
storage_connection_string
as they contain sensitive information
that can affect the integrity and security of your web application.
The api_token
will be used in our Azure DevOps deployment file
(e.g., azure-static-web-apps-deployment.yml
) to enable
continuous deployment through Azure Pipelines.
Azure Static Web App Configuration
The Azure Static Web App is a pivotal resource in our project, hosting the static content that makes up the frontend of our website. It's a scalable and cost-effective solution that integrates with Azure functions for backend services. Here's how we define it in Terraform:
resource "azurerm_static_site" "swa" {
name = var.website_name
resource_group_name = azurerm_resource_group.swa.name
location = var.region
sku_tier = var.static_web_app_sku
app_settings = {
"TableServiceConnectionString" = module.storage.primary_connection_string
"SendGridApiKey" = var.sendgrid_api_key
}
}
In this configuration:
- TableServiceConnectionString: The connection string to Azure Table Storage, enabling the app to store and access data like email addresses for the newsletter.
- SendGridApiKey: The API key for SendGrid, authorizing the app to send emails via SendGrid's email delivery service.
This Terraform configuration ensures our web app has all it needs to operate smoothly, with connections to data storage and email services set up right from the start.
Set up Azure DevOps
Azure Repos
Azure Repos provides Git repositories for source control of your code. It's a scalable, secure place that stores your code and branches, and supports collaborative pull requests and advanced file management. To start using Azure Repos, you first need to create a new repo where you will store the code for your web application.
Azure Boards for Planning
Azure Boards is a service for managing your development projects with agile tools, work item tracking, and Kanban boards. You can plan your work, track progress, and discuss issues directly from within the board. Check out Azure Boards documentation for detailed instructions on setting up and using boards for planning your web app development.
CI Pipeline for Continuous Delivery
The Continuous Integration (CI) pipeline in Azure DevOps helps to automate the build and deployment of your application. The following YAML file defines a CI pipeline for an Azure Static Web App, specifying the build and deployment process triggered by changes in the main branch:
name: Azure Static Web Apps CI/CD
pr:
branches:
include:
- main
trigger:
branches:
include:
- main
jobs:
- job: build_and_deploy_job
displayName: Build and Deploy Job
condition: or(eq(variables['Build.Reason'], 'Manual'), eq(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Reason'], 'IndividualCI'))
pool:
vmImage: ubuntu-latest
variables:
- group: bhs-it-consulting-variable-group
steps:
- checkout: self
submodules: true
- task: AzureStaticWebApp@0
inputs:
azure_static_web_apps_api_token: $(AZURE_STATIC_WEB_APPS_API_TOKEN)
app_location: "/Website" # App source code path
api_location: "/Website/api" # Api source code path - optional
skip_app_build: true # Skip build - optional
# output_location: "" # Built app content directory - optional
This pipeline uses Azure Static Web Apps task to deploy your web application, using the API token defined in the Azure DevOps variable group for authentication. The pipeline is configured to trigger on pull requests and commits to the main branch, ensuring that updates are continuously deployed to your static web app.
Conclusion
This article has taken us through the foundational steps of setting up a robust DevOps infrastructure using Terraform, detailing the process of creating Azure resources, configuring custom domains, and setting up a CI/CD pipeline. By leveraging IaC, we've laid down a scalable and maintainable framework for our web application.
The forthcoming article will shift focus from infrastructure to development, discussing the intricacies of building the website itself, from HTML construction to implementing essential web services like SendGrid. We'll also reflect on the learning process, the importance of SEO, and the revelations about the distinction between development and design.