
Deploying static assets like HTML, CSS, and JavaScript files is a core part of serverless architecture, and Azure provides a powerful solution with its CDN and Storage Account services. This post will guide you through deploying these assets via Azure CDN, integrating CORS, and setting up a custom domain using Route 53. I'll use Terraform to manage the entire setup, making it repeatable and scalable.
Step 1: Setting Up Azure CDN and Storage Account with Terraform
My architecture involves storing static files in an Azure Storage Account and delivering them to end-users via Azure CDN. This combination reduces latency, leverages HTTPS for secure content delivery, and is optimized for performance.
Terraform Code - CDN and Storage Account Configuration
The following Terraform code provisions an Azure CDN profile and endpoint, a storage account, and storage containers for my static files. It also sets up a custom domain with HTTPS.
resource "azurerm_cdn_profile" "this" {
  name                = local.project
  location            = azurerm_resource_group.this.location
  resource_group_name = azurerm_resource_group.this.name
  sku                 = var.cdn_profile_sku
}
resource "azurerm_cdn_endpoint" "this" {
  name                = local.project
  location            = azurerm_resource_group.this.location
  resource_group_name = azurerm_resource_group.this.name
  optimization_type   = "GeneralWebDelivery"
  origin_host_header  = azurerm_storage_account.this.primary_blob_host
  origin_path         = "/${local.storage_sitefiles_path}"
  profile_name        = azurerm_cdn_profile.this.name
  origin {
    name       = local.project
    host_name  = azurerm_storage_account.this.primary_blob_host
    http_port  = 80
    https_port = 443
  }
  is_compression_enabled    = true
  content_types_to_compress = ["text/html", "text/css", "application/javascript"]
  is_http_allowed  = false
  is_https_allowed = true
}
resource "azurerm_cdn_endpoint_custom_domain" "this" {
  name            = replace(local.site_domain, ".", "-")
  cdn_endpoint_id = azurerm_cdn_endpoint.this.id
  host_name       = local.site_domain
  cdn_managed_https {
    certificate_type = "Dedicated"
    protocol_type    = "ServerNameIndication"
    tls_version      = "TLS12"
  }
}
Step 2: Uploading Static Content to Azure Storage Account
I need a storage account to host my static files, with blobs for each file type. This code sets up the storage account, containers, and blob uploads for assets like HTML, CSS, JavaScript, and images.
Terraform Code - Storage Account and Blob Configuration
resource "azurerm_storage_account" "this" {
  name                     = replace(var.project, "_", "")
  resource_group_name      = azurerm_resource_group.this.name
  location                 = azurerm_resource_group.this.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
}
resource "azurerm_storage_blob" "index_html" {
  name                   = "index.html"
  storage_account_name   = azurerm_storage_account.this.name
  storage_container_name = azurerm_storage_container.sitefiles.name
  type                   = "Block"
  content_type           = "text/html"
  source_content = templatefile("${local.site_files_index_template_dir}/index.html.tmpl", {
    function_app_url = azurerm_linux_function_app.this.default_hostname
    function_name    = azurerm_linux_function_app.this.name
  })
}
resource "azurerm_storage_blob" "site_files" {
  for_each = fileset(local.site_files_dir, "**/*")
  name     = each.value
  content_type           = lookup(local.mime_types, split(".", each.value)[length(split(".", each.value)) - 1])
  source                 = "${local.site_files_dir}/${each.value}"
  storage_account_name   = azurerm_storage_account.this.name
  storage_container_name = azurerm_storage_container.sitefiles.name
  type                   = "Block"
}
resource "azurerm_storage_container" "sitefiles" {
  name                  = local.storage_sitefiles_path
  storage_account_name  = azurerm_storage_account.this.name
  container_access_type = "blob"
}

Step 3: Setting Up CORS for the Storage Account
Cross-Origin Resource Sharing (CORS) allows the CDN to access the storage account securely, preventing unauthorized sites from making requests. This Terraform code sets up CORS for my storage account.
Terraform Code - CORS Setup
resource "azurerm_storage_account_cors_rule" "this" {
  storage_account_name = azurerm_storage_account.this.name
  cors_rule {
    allowed_origins = ["*"]
    allowed_methods = ["GET"]
    max_age_in_seconds = 3600
    allowed_headers = ["*"]
    exposed_headers = ["*"]
  }
}
index.html.tmpl for Static Content
Below is a Terraform template file for the HTML page that interacts with an Azure Function App. This file would be uploaded to the storage account and served via the CDN.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Fun Facts</title>
</head>
<body>
    <h1>Welcome to Fun Facts Page!</h1>
    <!-- Button to Fetch Fun Fact -->
    <button id="get-fact">Get a Fun Fact</button>
    <!-- Display Fun Fact -->
    <p id="fun-fact"></p>
    <script>
        document.getElementById("get-fact").addEventListener("click", async () => {
            try {
                const response = await fetch("https://${function_app_url}/api/${function_name}");
                const fact = await response.text();
                document.getElementById("fun-fact").innerText = fact;
            } catch (error) {
                document.getElementById("fun-fact").innerText = "Error fetching fun fact.";
            }
        });
    </script>
</body>
</html>
By setting up Azure CDN with a Storage Account, I can efficiently serve static content to users worldwide with low latency and high reliability. This guide provided the Terraform configuration for CDN and storage integration, CORS setup, and sample HTML content to demonstrate how these services work together.
Stay tuned for my next post, where I'll cover deploying the dynamic backend using Azure Function App and integrating it with the front-end content served by the CDN.

