DEV Community

1suleyman
1suleyman

Posted on

Exercise 09 – Add a CDN with Bicep Modules (Optional Style!)

🚀 “The toy wombat website needs a CDN!”

“Other teams don’t need it.”

Me: “No problem – I’ll make it optional.”


🎯 The Mission

We're launching a website for the new toy wombat — and to handle traffic spikes, we’ll add a Content Delivery Network (CDN).

But... only if needed. Other teams can skip it.

So let’s build this smart and reusable:

  • 📦 Use modules for the App Service and CDN
  • ✅ Make the CDN optional
  • 🌍 Output the right hostname depending on what’s deployed

🧱 Step 1: Create the App Module

In VS Code:

  1. Create a folder called modules
  2. Inside it, create a file: app.bicep

Paste in this logic:

@description('The Azure region into which the resources should be deployed.')
param location string

@description('The name of the App Service app.')
param appServiceAppName string

@description('The name of the App Service plan.')
param appServicePlanName string

@description('The name of the App Service plan SKU.')
param appServicePlanSkuName string

resource appServicePlan 'Microsoft.Web/serverfarms@2024-04-01' = {
  name: appServicePlanName
  location: location
  sku: {
    name: appServicePlanSkuName
  }
}

resource appServiceApp 'Microsoft.Web/sites@2024-04-01' = {
  name: appServiceAppName
  location: location
  properties: {
    serverFarmId: appServicePlan.id
    httpsOnly: true
  }
}

@description('The default host name of the App Service app.')
output appServiceAppHostName string = appServiceApp.properties.defaultHostName
Enter fullscreen mode Exit fullscreen mode

What this does:

It spins up a basic App Service Plan + App. Outputs the app’s host name.


💻 Step 2: Set Up Your Main Template

Create a file called main.bicep.

Add these parameters:

param location string = 'westus3'
param appServiceAppName string = 'toy-${uniqueString(resourceGroup().id)}'
param appServicePlanSkuName string = 'F1'
param deployCdn bool = true
Enter fullscreen mode Exit fullscreen mode

Add a variable:

var appServicePlanName = 'toy-product-launch-plan'
Enter fullscreen mode Exit fullscreen mode

🧩 Step 3: Use the App Module

Still in main.bicep, insert this module:

module app 'modules/app.bicep' = {
  name: 'toy-launch-app'
  params: {
    appServiceAppName: appServiceAppName
    appServicePlanName: appServicePlanName
    appServicePlanSkuName: appServicePlanSkuName
    location: location
  }
}
Enter fullscreen mode Exit fullscreen mode

🌐 Step 4: Create the CDN Module

Create a new file inside modules called cdn.bicep.

Paste this code:

param originHostName string
param profileName string = 'cdn-${uniqueString(resourceGroup().id)}'
param endpointName string = 'endpoint-${uniqueString(resourceGroup().id)}'
param httpsOnly bool

var originName = 'my-origin'

resource cdnProfile 'Microsoft.Cdn/profiles@2024-09-01' = {
  name: profileName
  location: 'global'
  sku: {
    name: 'Standard_Microsoft'
  }
}

resource endpoint 'Microsoft.Cdn/profiles/endpoints@2024-09-01' = {
  parent: cdnProfile
  name: endpointName
  location: 'global'
  properties: {
    originHostHeader: originHostName
    isHttpAllowed: !httpsOnly
    isHttpsAllowed: true
    queryStringCachingBehavior: 'IgnoreQueryString'
    contentTypesToCompress: [
      'text/plain'
      'text/html'
      'text/css'
      'application/x-javascript'
      'text/javascript'
    ]
    isCompressionEnabled: true
    origins: [
      {
        name: originName
        properties: {
          hostName: originHostName
        }
      }
    ]
  }
}

output endpointHostName string = endpoint.properties.hostName
Enter fullscreen mode Exit fullscreen mode

What this does:

It creates a CDN profile + endpoint, pointing to your app. Outputs the CDN host name.


🧩 Step 5: Use the CDN Module (With a Condition)

Back in main.bicep, add the CDN module:

module cdn 'modules/cdn.bicep' = if (deployCdn) {
  name: 'toy-launch-cdn'
  params: {
    httpsOnly: true
    originHostName: app.outputs.appServiceAppHostName
  }
}
Enter fullscreen mode Exit fullscreen mode

💡 This only runs if deployCdn is true.


📦 Step 6: Output the Right Hostname

Now, let’s give the user the correct URL based on whether CDN is on:

output websiteHostName string = deployCdn ? cdn.outputs.endpointHostName : app.outputs.appServiceAppHostName
Enter fullscreen mode Exit fullscreen mode

✅ Final main.bicep Snapshot

param location string = 'westus3'
param appServiceAppName string = 'toy-${uniqueString(resourceGroup().id)}'
param appServicePlanSkuName string = 'F1'
param deployCdn bool = true

var appServicePlanName = 'toy-product-launch-plan'

module app 'modules/app.bicep' = {
  name: 'toy-launch-app'
  params: {
    appServiceAppName: appServiceAppName
    appServicePlanName: appServicePlanName
    appServicePlanSkuName: appServicePlanSkuName
    location: location
  }
}

module cdn 'modules/cdn.bicep' = if (deployCdn) {
  name: 'toy-launch-cdn'
  params: {
    httpsOnly: true
    originHostName: app.outputs.appServiceAppHostName
  }
}

output websiteHostName string = deployCdn ? cdn.outputs.endpointHostName : app.outputs.appServiceAppHostName
Enter fullscreen mode Exit fullscreen mode

🚀 Deploy It!

Make sure you're ready:

az bicep install && az bicep upgrade
az login
az group create --name BicepRG --location westus3
Enter fullscreen mode Exit fullscreen mode

Then deploy:

az deployment group create \
  --resource-group BicepRG \
  --name main \
  --template-file main.bicep
Enter fullscreen mode Exit fullscreen mode

🔎 Check the Deployment

In the Azure Portal:

  • Go to Resource Groups > BicepRG
  • Go to Deployments > main
  • Expand it to see both modules: toy-launch-app and (if enabled) toy-launch-cdn
  • Check Outputs:
    • If CDN is enabled, you’ll get the CDN host name
    • Otherwise, you get the App Service host name

Try both in the browser (add https:// in front)!

📌 CDN might take a minute to activate — if it doesn't load instantly, just wait and retry.


🧠 In Short

Feature What We Did
App Module Reusable App Service plan + app deployment
CDN Module Created a globally distributed cache layer
Conditional Module Made CDN deploy only when needed
Smart Output Automatically return the correct public URL

Wanna follow my Azure learning journey?

Stick around — I’m sharing it all, wins and stumbles included 😄

You can find me on LinkedIn — drop me a message and just say hi 👋

Would love to hear what you're working on or learning!

DevCycle image

Fast, Flexible Releases with OpenFeature Built-in

Ship faster on the first feature management platform with OpenFeature built-in to all of our open source SDKs.

Start shipping

Top comments (0)

Dev Diairies image

User Feedback & The Pivot That Saved The Project ↪️

We’re following the journey of a dev team building on the Stellar Network as they go from hackathon idea to funded startup, testing their product in the real world and adapting as they go.

Watch full video 🎥

👋 Kindness is contagious

Dive into this thoughtful piece, beloved in the supportive DEV Community. Coders of every background are invited to share and elevate our collective know-how.

A sincere "thank you" can brighten someone's day—leave your appreciation below!

On DEV, sharing knowledge smooths our journey and tightens our community bonds. Enjoyed this? A quick thank you to the author is hugely appreciated.

Okay