-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathaz-functionapps-cicd-pipeline-template-optimized.yml
More file actions
294 lines (264 loc) · 15.3 KB
/
az-functionapps-cicd-pipeline-template-optimized.yml
File metadata and controls
294 lines (264 loc) · 15.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# Example
# 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