Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ Last updated: 2025-08-27

<!-- START BADGE -->
<div align="center">
<img src="https://img.shields.io/badge/Total%20views-1403-limegreen" alt="Total views">
<p>Refresh Date: 2025-09-05</p>
<img src="https://img.shields.io/badge/Total%20views-1334-limegreen" alt="Total views">
<p>Refresh Date: 2025-09-30</p>
</div>
<!-- END BADGE -->
4 changes: 2 additions & 2 deletions scenario1-high-decay/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ Last updated: 2025-08-27

<!-- START BADGE -->
<div align="center">
<img src="https://img.shields.io/badge/Total%20views-1403-limegreen" alt="Total views">
<p>Refresh Date: 2025-09-05</p>
<img src="https://img.shields.io/badge/Total%20views-1334-limegreen" alt="Total views">
<p>Refresh Date: 2025-09-30</p>
</div>
<!-- END BADGE -->
4 changes: 2 additions & 2 deletions scenario1-high-decay/terraform-infrastructure/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ graph TD;

<!-- START BADGE -->
<div align="center">
<img src="https://img.shields.io/badge/Total%20views-1403-limegreen" alt="Total views">
<p>Refresh Date: 2025-09-05</p>
<img src="https://img.shields.io/badge/Total%20views-1334-limegreen" alt="Total views">
<p>Refresh Date: 2025-09-30</p>
</div>
<!-- END BADGE -->
4 changes: 2 additions & 2 deletions scenario2-optimized/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ Last updated: 2025-09-05

<!-- START BADGE -->
<div align="center">
<img src="https://img.shields.io/badge/Total%20views-865-limegreen" alt="Total views">
<p>Refresh Date: 2025-09-05</p>
<img src="https://img.shields.io/badge/Total%20views-1334-limegreen" alt="Total views">
<p>Refresh Date: 2025-09-30</p>
</div>
<!-- END BADGE -->
293 changes: 293 additions & 0 deletions scenario2-optimized/az-functionapps-cicd-pipeline-template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
# Azure DevOps CI/CD Pipeline Template for Azure Functions
# This template provides a complete CI/CD workflow for building and deploying Azure Function Apps
# It includes both build and deployment stages with support for deployment slots and multi-environment deployments

parameters:
# Build Configuration (Optional)
# Specifies whether to build in Debug or Release mode
buildConfiguration: 'Release'

# Branch Name (Required)
# The Git branch to build from (e.g., 'main', 'develop', 'feature/xyz')
branchName: ''

# Artifact Name (Optional)
# Name for the resulting build artifact
artifactName: 'Setup'

# NuGet Feed (Required)
# Your Azure DevOps NuGet feed name (e.g., 'MyOrganization/MyFeed')
nugetFeed: ''

# .NET SDK Version (Optional)
# The version of .NET SDK to use for building
dotnetVersion: '6.0.x'

# Azure Subscription (Required for deployment)
# The name of your Azure DevOps service connection to Azure
azureSubscription: ''

# Function App Name (Required for deployment)
# The name of your Azure Function App resource
functionAppName: ''

# Resource Group Name (Required for deployment)
# The name of the Azure resource group containing your Function App
resourceGroupName: ''

# Deploy to Slot (Optional)
# Whether to use deployment slots for zero-downtime deployments
deployToSlot: false

# Slot Name (Optional, required if deployToSlot is true)
# Name of the deployment slot if using slots
slotName: 'staging'

# Environment (Optional)
# Target environment name (dev/test/prod)
environment: 'dev'

# Swap After Deployment (Optional)
# Whether to swap slots after successful deployment
swapAfterDeployment: true

# App Settings (Optional)
# Array of application settings to configure (key-value pairs)
appSettings: []

# Deployment Method (Optional)
# The method used to deploy the Function App
# Valid options:
# - 'auto': Lets Azure decide the best deployment method based on the app type and configuration.
# Use this if you're unsure which method is best for your scenario.
#
# - 'zipDeploy': Basic ZIP deployment that extracts files to the wwwroot folder.
# USE CASE: Simple deployments where you need to update only the application files.
# LIMITATIONS: Doesn't set any app settings, may cause cold start delays as files are extracted.
#
# - 'zipDeployWithRestartAppSetting': RECOMMENDED FOR PRODUCTION.
# Sets WEBSITE_RUN_FROM_PACKAGE=1 and deploys as a ZIP package.
# USE CASE: Production deployments where you need read-only operation and optimal performance.
# BENEFITS:
# - Makes wwwroot folder read-only (prevents in-portal editing)
# - Runs directly from the package (no file extraction, reducing temp storage usage)
# - Improves cold start performance
# - Simplifies rollback (just switch app setting back to previous package)
#
# - 'runFromPackage': Sets WEBSITE_RUN_FROM_PACKAGE to a URL of your package.
# USE CASE: When your package is stored in Azure Blob Storage or other external location.
# BENEFITS: Separates code deployment from activation, enabling blue/green deployments.
#
# - 'webDeploy': Uses MSDeploy (Web Deploy) protocol for deployment.
# USE CASE: Complex deployments with parameters, specific configurations, or transforms.
# LIMITATIONS: Slower than ZIP deployments and more complex to configure.
deploymentMethod: 'zipDeployWithRestartAppSetting'

stages:
#################################################
# BUILD STAGE
# Compiles the code and creates deployment packages
#################################################
- stage: Build
displayName: Build ${{parameters.artifactName}} for ${{parameters.branchName}}
jobs:
- job: Build
displayName: Build Function App
steps:
# Step 1: Checkout the correct branch
# This step fetches the source code from the repository
- checkout: self
clean: true # Remove any previous files
persistCredentials: true # Keep git credentials for potential later git operations
fetchDepth: 0 # Fetch full history
displayName: Checkout ${{ parameters.branchName }}

# Step 2: Ensure branch synchronization
# This ensures we're working with the latest code from the specified branch
# It fetches all remote branches and resets the local branch to match the remote
- script: |
git fetch --all
git clean -fd
git reset --hard origin/${{ parameters.branchName }}
displayName: Synchronize with ${{ parameters.branchName }}

# Step 3: Install .NET Core SDK
# Ensures the correct .NET SDK version is available for building
# This allows you to standardize the SDK version across different build agents
- task: UseDotNet@2
inputs:
packageType: 'sdk'
version: '${{ parameters.dotnetVersion }}'
displayName: Install .NET SDK Version ${{ parameters.dotnetVersion }}

# Step 4: Restore NuGet Packages
# Downloads and installs all NuGet dependencies required by the projects
# This helps ensure all required libraries are available for the build
- task: NuGetCommand@2
inputs:
command: 'restore'
restoreSolution: '**/*.sln'
feedsToUse: 'select'
vstsFeed: '${{ parameters.nugetFeed }}'
displayName: Restore NuGet Packages

# Step 5: Build the Solution
# Compiles and publishes the projects to a directory
# We use zipAfterPublish: false to maintain control over the archive process
# This step creates the compiled application that will be deployed to Azure
- task: DotNetCoreCLI@2
displayName: Publish Function App
inputs:
command: 'publish'
projects: '**/*.csproj'
arguments: '--configuration ${{ parameters.buildConfiguration }} --output $(Build.ArtifactStagingDirectory)/publish'
publishWebProjects: false # Ensures we publish all projects, not just web projects
zipAfterPublish: false # Don't zip during publish, we'll do that in next step for more control

# Step 6: Archive Published Artifacts
# Creates a single ZIP file from all published files
# This is important for Azure Functions as they require a specific deployment package format
# The resulting ZIP file includes all files needed to run the Function App in Azure
- task: ArchiveFiles@2
inputs:
rootFolderOrFile: '$(Build.ArtifactStagingDirectory)/publish'
includeRootFolder: false # Don't include the publish folder itself in the archive
archiveType: 'zip' # Create a ZIP archive
archiveFile: '$(Build.ArtifactStagingDirectory)/archives/${{ parameters.artifactName }}-${{ parameters.environment }}-${{ parameters.branchName }}.zip'
replaceExistingArchive: true # Overwrite any existing archive with the same name
displayName: Archive Function App Package

# Step 7: Publish Archived Build Artifacts
# Makes the ZIP archive available for download or use in subsequent pipeline stages
# This allows the deployment stage to access the build artifacts
- task: PublishBuildArtifacts@1
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)/archives'
ArtifactName: 'archives' # Name of the artifact feed in Azure DevOps
publishLocation: 'Container' # Publish to the pipeline's artifact storage
displayName: Publish Function App Artifacts

#################################################
# DEPLOYMENT STAGE
# Deploys the built artifacts to Azure Function App
# This stage will only run if the required deployment parameters are provided
#################################################
- stage: Deploy
displayName: Deploy to ${{ parameters.environment }} Environment
dependsOn: Build # Only run after Build stage completes successfully
condition: and(succeeded(), ne('${{ parameters.azureSubscription }}', ''), ne('${{ parameters.functionAppName }}', ''), ne('${{ parameters.resourceGroupName }}', ''))
jobs:
- deployment: DeployFunctionApp
displayName: Deploy Function App
# Create an environment in Azure DevOps for tracking deployments
environment: ${{ parameters.environment }}-${{ parameters.functionAppName }}
strategy:
# Define a standard deployment strategy
runOnce:
deploy:
steps:
# Step 1: Download the artifacts from the build stage
# This retrieves the ZIP package created in the Build stage
# The deployment job automatically downloads artifacts to $(Pipeline.Workspace)
- download: current
artifact: archives
displayName: Download Build Artifacts

# Step 2: Deploy to Azure Function App
# This deploys the ZIP package to the Azure Function App using the specified deployment method
# If using slots, it deploys to the staging slot first
# The deployment is done using the Azure Functions deployment API
- task: AzureFunctionApp@1
inputs:
azureSubscription: '${{ parameters.azureSubscription }}' # Azure service connection
appType: 'functionApp' # Specifies this is a Function App
appName: '${{ parameters.functionAppName }}' # Name of the Function App in Azure
# Configure slot deployment if enabled
${{ if eq(parameters.deployToSlot, true) }}:
deployToSlotOrASE: true # Deploy to a slot
resourceGroupName: '${{ parameters.resourceGroupName }}' # Resource group containing the Function App
slotName: '${{ parameters.slotName }}' # Name of the slot to deploy to
${{ if ne(parameters.deployToSlot, true) }}:
deployToSlotOrASE: false # Deploy directly to production
package: '$(Pipeline.Workspace)/archives/${{ parameters.artifactName }}-${{ parameters.environment }}-${{ parameters.branchName }}.zip' # Path to the ZIP package
deploymentMethod: '${{ parameters.deploymentMethod }}' # Use the specified deployment method
displayName: Deploy to Azure Function App ${{ parameters.functionAppName }} using ${{ parameters.deploymentMethod }}

# Step 3: Configure Application Settings
# This sets up environment variables and app settings for the Function App
# Only runs if appSettings parameter is provided
- task: AzureAppServiceSettings@1
condition: and(succeeded(), gt(length('${{ parameters.appSettings }}'), 0))
inputs:
azureSubscription: '${{ parameters.azureSubscription }}'
appName: '${{ parameters.functionAppName }}'
resourceGroupName: '${{ parameters.resourceGroupName }}'
${{ if eq(parameters.deployToSlot, true) }}:
slotName: '${{ parameters.slotName }}'
appSettings: '${{ parameters.appSettings }}'
displayName: Configure Application Settings

# Step 4: Swap Slots (if enabled)
# This swaps the staging slot with production for zero-downtime deployments
# Only runs if deployToSlot and swapAfterDeployment are both true
- task: AzureAppServiceManage@0
condition: and(succeeded(), eq(parameters.deployToSlot, true), eq(parameters.swapAfterDeployment, true))
inputs:
azureSubscription: '${{ parameters.azureSubscription }}'
Action: 'Swap Slots'
WebAppName: '${{ parameters.functionAppName }}'
ResourceGroupName: '${{ parameters.resourceGroupName }}'
SourceSlot: '${{ parameters.slotName }}'
displayName: Swap Deployment Slots

# Step 5: Verify Deployment
# This checks if the deployment was successful
# It's a simple echo command, but could be extended to perform actual health checks
- script: |
echo "Deployment to ${{ parameters.functionAppName }} completed successfully"
echo "Environment: ${{ parameters.environment }}"
echo "Deployment Method: ${{ parameters.deploymentMethod }}"
echo "Function App URL: https://${{ parameters.functionAppName }}${{ if eq(parameters.deployToSlot, true) }}${{ if ne(parameters.swapAfterDeployment, true) }}-${{ parameters.slotName }}${{ end }}${{ end }}.azurewebsites.net"
displayName: Verify Deployment

#################################################
# OPTIONAL: SMOKE TEST STAGE
# Runs basic tests against the deployed Function App
# This stage will only run if the deployment stage succeeds
#################################################
- stage: SmokeTest
displayName: Run Smoke Tests
dependsOn: Deploy
condition: and(succeeded(), ne('${{ parameters.azureSubscription }}', ''), ne('${{ parameters.functionAppName }}', ''))
jobs:
- job: TestFunctionApp
displayName: Test Function App Endpoints
steps:
# Run basic health check tests against the deployed Function App
# This ensures the deployment is functioning correctly
- task: PowerShell@2
inputs:
targetType: 'inline'
script: |
# Define the base URL for the Function App
$baseUrl = "https://${{ parameters.functionAppName }}.azurewebsites.net"

Write-Host "Running health check against $baseUrl"

try {
# You can replace this with actual endpoint tests specific to your Function App
$response = Invoke-WebRequest -Uri "$baseUrl" -Method Get -TimeoutSec 30 -ErrorAction Stop
Write-Host "Health check status code: $($response.StatusCode)"

if ($response.StatusCode -ge 200 -and $response.StatusCode -lt 400) {
Write-Host "##vso[task.complete result=Succeeded;]Health check passed"
} else {
Write-Warning "Health check returned unexpected status code: $($response.StatusCode)"
}
} catch {
Write-Warning "Health check failed: $_"
}
failOnStderr: false
pwsh: true
displayName: Run Function App Health Check
continueOnError: true # Continue even if health check fails
4 changes: 2 additions & 2 deletions scenario2-optimized/terraform-infrastructure/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ graph TD;

<!-- START BADGE -->
<div align="center">
<img src="https://img.shields.io/badge/Total%20views-1403-limegreen" alt="Total views">
<p>Refresh Date: 2025-09-05</p>
<img src="https://img.shields.io/badge/Total%20views-1334-limegreen" alt="Total views">
<p>Refresh Date: 2025-09-30</p>
</div>
<!-- END BADGE -->