MSDN Documentation

Microsoft Developer Network

Implementing CI/CD for Azure Functions

Continuous Integration and Continuous Deployment (CI/CD) is a crucial practice for modern software development, enabling faster iterations, improved code quality, and reliable deployments. This article guides you through setting up a robust CI/CD pipeline for your Azure Functions.

Why CI/CD for Azure Functions?

Azure Functions, being serverless, allow you to focus on code. However, managing deployments, testing, and rollbacks can become complex as your project grows. A CI/CD pipeline automates these processes, offering:

Choosing Your CI/CD Tool

Several tools can be used to build CI/CD pipelines for Azure Functions. Popular choices include:

This guide will primarily focus on Azure Pipelines and GitHub Actions due to their tight integration with Azure services.

Setting Up CI/CD with Azure Pipelines

Azure Pipelines provides a powerful and flexible way to automate your build and release processes.

Prerequisites:

1. Creating a Build Pipeline (YAML)

Navigate to your Azure DevOps project, go to Pipelines, and click "New pipeline". Choose your repository location and then select "Starter pipeline".

Here's an example azure-pipelines.yml for a C# Azure Functions project:

trigger:
- main

pool:
  vmImage: 'windows-latest' # Or 'ubuntu-latest' if using .NET Core/5+

variables:
  buildConfiguration: 'Release'
  projectPath: 'src/MyAzureFunctionApp/MyAzureFunctionApp.csproj' # Adjust to your project path

steps:
- task: UseDotNet@2
  displayName: 'Use .NET SDK'
  inputs:
    version: '6.x' # Specify your .NET version

- task: DotNetCoreCLI@2
  displayName: 'Restore NuGet Packages'
  inputs:
    command: 'restore'
    projects: '$(projectPath)'

- task: DotNetCoreCLI@2
  displayName: 'Build Project'
  inputs:
    command: 'build'
    projects: '$(projectPath)'
    arguments: '--configuration $(buildConfiguration)'

- task: DotNetCoreCLI@2
  displayName: 'Run Unit Tests'
  inputs:
    command: 'test'
    projects: '$(projectPath)'
    arguments: '--configuration $(buildConfiguration)'

- task: PublishBuildArtifacts@1
  displayName: 'Publish Artifact: drop'
  inputs:
    PathtoPublish: '$(Build.ArtifactStagingDirectory)/$(buildConfiguration)'
    ArtifactName: 'drop'
    publishLocation: 'Container'

2. Creating a Release Pipeline

After your build pipeline successfully runs and produces an artifact, you'll need a release pipeline to deploy it.

  1. In Azure DevOps, go to Pipelines > Releases and click "New pipeline".
  2. Select a template, typically "Azure Functions deployment".
  3. Add an artifact, linking it to your build pipeline.
  4. Configure a stage (e.g., "Dev", "Staging", "Production").
  5. In the stage, select the Azure subscription and the Azure Functions App you want to deploy to.
  6. Choose the deployment method (e.g., Zip Deploy).
  7. Add pre-deployment or post-deployment approvals if needed.
  8. Save and create the release.

Setting Up CI/CD with GitHub Actions

GitHub Actions provides a powerful, event-driven automation platform directly within GitHub.

Prerequisites:

1. Creating a Workflow File

In your GitHub repository, create a directory named .github/workflows and add a YAML file, for example, azure-functions-ci.yml.

Here's an example workflow for a C# Azure Functions project:

name: Azure Functions CI/CD

on:
  push:
    branches: [ main ]

env:
  AZURE_WEBAPP_NAME: 'your-function-app-name' # Replace with your function app name
  BUILD_CONFIGURATION: 'Release'
  PROJECT_PATH: 'src/MyAzureFunctionApp/MyAzureFunctionApp.csproj' # Adjust to your project path

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3

    - name: Setup .NET
      uses: actions/setup-dotnet@v3
      with:
        dotnet-version: '6.x' # Specify your .NET version

    - name: Restore dependencies
      run: dotnet restore $(PROJECT_PATH)

    - name: Build
      run: dotnet build $(PROJECT_PATH) --configuration $(BUILD_CONFIGURATION) --no-restore

    - name: Test
      run: dotnet test $(PROJECT_PATH) --configuration $(BUILD_CONFIGURATION) --no-build

    - name: Publish
      run: dotnet publish $(PROJECT_PATH) --configuration $(BUILD_CONFIGURATION) --no-build --output ${{env.AZURE_WEBAPP_PACKAGE_PATH}}

    - name: Upload artifact
      uses: actions/upload-artifact@v3
      with:
        name: azure-functions-package
        path: ${{env.AZURE_WEBAPP_PACKAGE_PATH}}

  deploy:
    runs-on: ubuntu-latest
    needs: build
    steps:
    - name: Download artifact
      uses: actions/download-artifact@v3
      with:
        name: azure-functions-package
        path: ${{env.AZURE_WEBAPP_PACKAGE_PATH}}

    - name: Azure Login
      uses: azure/login@v1
      with:
        creds: ${{ secrets.AZURE_CREDENTIALS }}

    - name: Deploy to Azure Functions
      uses: azure/functions-deploy@v2
      with:
        app-name: ${{env.AZURE_WEBAPP_NAME}}
        package: ${{env.AZURE_WEBAPP_PACKAGE_PATH}}

    - name: Azure Logout
      run: |
        az logout
        az cache purge
        az account clear

Note: You need to set up the AZURE_CREDENTIALS secret in your GitHub repository's settings. This should contain your Azure Service Principal's JSON credentials.

Configuring Service Principal for Azure Access

To allow your CI/CD pipeline to deploy to Azure, you'll need to create an Azure Service Principal. This acts as an identity for your application or service.

  1. In the Azure portal, search for "App registrations" and click "New registration".
  2. Give it a name (e.g., azure-functions-ci-cd-sp).
  3. Under "Supported account types", select the appropriate option.
  4. Click "Register".
  5. Once registered, navigate to "Certificates & secrets" and create a new client secret. Copy the secret value immediately, as it won't be shown again.
  6. Go to "API permissions" and click "Add a permission". Select "Azure Service Management" > "Application permissions". Grant the "Contributor" role (or a more granular role if required).
  7. Finally, you'll need to assign this Service Principal to your Azure Functions App. Go to your Functions App, then "Access control (IAM)", click "Add role assignment", select the "Contributor" role (or appropriate), and search for your Service Principal's name.

Best Practices

Conclusion

Implementing CI/CD for Azure Functions significantly enhances development velocity and reliability. By leveraging tools like Azure Pipelines or GitHub Actions, you can automate your build, test, and deployment processes, allowing you to deliver value to your users faster and with greater confidence. Remember to tailor the pipeline configurations to your specific project needs and follow best practices for security and maintainability.