Skip to content

06 - CI/CD (Premium)

Automate build, test, and deployment using GitHub Actions so every change ships through the same pipeline.

Prerequisites

Tool Version Purpose
.NET SDK 8.0 (LTS) Build and run isolated worker functions
Azure Functions Core Tools v4 Start local host and publish artifacts
Azure CLI 2.61+ Provision Azure resources and inspect app state

Premium plan basics

Premium (EP1) keeps at least one warm instance, supports VNet integration, private endpoints, and deployment slots. No cold-start penalty, with up to 100 instances and no execution timeout.

What You'll Build

You will configure a GitHub Actions pipeline that builds and deploys a .NET isolated worker Function App to a Premium plan, then verify the release with a smoke test and workflow run evidence.

flowchart LR
    A[Push to main] --> B[GitHub Actions]
    B --> C[dotnet build + publish]
    C --> D[azure/functions-action]
    D --> E[Smoke test]

Steps

Step 1 - Get the publish profile

Download the publish profile for use in GitHub Actions:

az functionapp deployment list-publishing-profiles \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --xml

Step 2 - Store deployment secrets in GitHub

Add repository secrets:

  • AZURE_FUNCTIONAPP_PUBLISH_PROFILE — paste the XML from Step 1
  • AZURE_FUNCTIONAPP_NAME — your function app name (e.g., func-dnetprem-04100301)

Step 3 - Create workflow file

name: deploy-dotnet-function

on:
  push:
    branches: [ main ]
    paths:
      - 'apps/dotnet/**'

env:
  DOTNET_VERSION: '8.0.x'
  AZURE_FUNCTIONAPP_NAME: ${{ secrets.AZURE_FUNCTIONAPP_NAME }}
  DOTNET_PROJECT_DIRECTORY: 'apps/dotnet'

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: ${{ env.DOTNET_VERSION }}

      - name: Build
        run: dotnet build --configuration Release
        working-directory: ${{ env.DOTNET_PROJECT_DIRECTORY }}

      - name: Publish
        run: dotnet publish --configuration Release --output ./publish
        working-directory: ${{ env.DOTNET_PROJECT_DIRECTORY }}

      - name: Deploy to Azure Functions
        uses: Azure/functions-action@v1
        with:
          app-name: ${{ env.AZURE_FUNCTIONAPP_NAME }}
          package: '${{ env.DOTNET_PROJECT_DIRECTORY }}/publish'
          publish-profile: ${{ secrets.AZURE_FUNCTIONAPP_PUBLISH_PROFILE }}

Deploy from publish output directory

The package path must point to the dotnet publish output directory (./publish). The .NET isolated worker publishes all required DLLs and runtime dependencies into this folder. The azure/functions-action handles the --dotnet-isolated flag automatically.

Premium deployment slots

Premium plans support deployment slots. Consider deploying to a staging slot first, validating, then swapping to production. Add slot-name: staging to the azure/functions-action configuration to use slots.

Step 4 - Add post-deployment smoke test

Add a smoke test step after deployment:

      - name: Smoke test
        run: |
          sleep 30
          HTTP_STATUS=$(curl --silent --output /dev/null --write-out "%{http_code}" \
            "https://${{ env.AZURE_FUNCTIONAPP_NAME }}.azurewebsites.net/api/health")
          if [ "$HTTP_STATUS" -ne 200 ]; then
            echo "Smoke test failed with status $HTTP_STATUS"
            exit 1
          fi
          echo "Smoke test passed with status $HTTP_STATUS"

Step 5 - Validate the release

# Check function app last modified time
az functionapp show \
  --name "$APP_NAME" \
  --resource-group "$RG" \
  --query "lastModifiedTimeUtc" \
  --output tsv

# Test health endpoint
curl --request GET "https://$APP_NAME.azurewebsites.net/api/health"

# Test hello endpoint
curl --request GET "https://$APP_NAME.azurewebsites.net/api/hello/CICD"

Use GitHub Actions run history as the deployment timeline of record (Actions tab → workflow runs → latest commit SHA), and compare it with lastModifiedTimeUtc to confirm release timing.

Verification

Build and publish output:

  Determining projects to restore...
  All projects are up-to-date for restore.
  AzureFunctionsGuide -> /apps/dotnet/bin/Release/net8.0/AzureFunctionsGuide.dll
  AzureFunctionsGuide -> /apps/dotnet/publish/

Health endpoint response after deployment:

{"status":"healthy","timestamp":"2026-04-09T18:25:15.779Z","version":"1.0.0"}

Hello endpoint response:

{"message":"Hello, CICD"}

Next Steps

Next: 07 - Extending with Triggers

See Also

Sources