diff --git a/README.md b/README.md index 46dd45e..f7ded1c 100644 --- a/README.md +++ b/README.md @@ -108,7 +108,7 @@ Last updated: 2025-08-27
- Total views -

Refresh Date: 2025-09-05

+ Total views +

Refresh Date: 2025-09-30

diff --git a/scenario1-high-decay/README.md b/scenario1-high-decay/README.md index bb45b3f..49f9805 100644 --- a/scenario1-high-decay/README.md +++ b/scenario1-high-decay/README.md @@ -147,7 +147,7 @@ Last updated: 2025-08-27
- Total views -

Refresh Date: 2025-09-05

+ Total views +

Refresh Date: 2025-09-30

diff --git a/scenario1-high-decay/terraform-infrastructure/README.md b/scenario1-high-decay/terraform-infrastructure/README.md index 1d037bc..059f2ce 100644 --- a/scenario1-high-decay/terraform-infrastructure/README.md +++ b/scenario1-high-decay/terraform-infrastructure/README.md @@ -126,7 +126,7 @@ graph TD;
- Total views -

Refresh Date: 2025-09-05

+ Total views +

Refresh Date: 2025-09-30

diff --git a/scenario2-optimized/README.md b/scenario2-optimized/README.md index 18272a2..bf46a44 100644 --- a/scenario2-optimized/README.md +++ b/scenario2-optimized/README.md @@ -147,7 +147,7 @@ Last updated: 2025-09-05
- Total views -

Refresh Date: 2025-09-05

+ Total views +

Refresh Date: 2025-09-30

diff --git a/scenario2-optimized/az-functionapps-cicd-pipeline-template.yml b/scenario2-optimized/az-functionapps-cicd-pipeline-template.yml new file mode 100644 index 0000000..c5ff478 --- /dev/null +++ b/scenario2-optimized/az-functionapps-cicd-pipeline-template.yml @@ -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 diff --git a/scenario2-optimized/terraform-infrastructure/README.md b/scenario2-optimized/terraform-infrastructure/README.md index 2d11ce4..1531259 100644 --- a/scenario2-optimized/terraform-infrastructure/README.md +++ b/scenario2-optimized/terraform-infrastructure/README.md @@ -126,7 +126,7 @@ graph TD;
- Total views -

Refresh Date: 2025-09-05

+ Total views +

Refresh Date: 2025-09-30