🚀 “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:
- Create a folder called modules
- 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
✅ 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
Add a variable:
var appServicePlanName = 'toy-product-launch-plan'
🧩 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
  }
}
🌐 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
✅ 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
  }
}
💡 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
  
  
  ✅ 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
🚀 Deploy It!
Make sure you're ready:
az bicep install && az bicep upgrade
az login
az group create --name BicepRG --location westus3
Then deploy:
az deployment group create \
  --resource-group BicepRG \
  --name main \
  --template-file main.bicep
🔎 Check the Deployment
In the Azure Portal:
- Go to Resource Groups > BicepRG
- Go to Deployments > main
- Expand it to see both modules: toy-launch-appand (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!
 
 
    
Top comments (0)