KeyEnvKeyEnv
SDKs & Integrations

Azure Functions

Load KeyEnv secrets in Azure Functions.

Azure Functions Integration

Load secrets from KeyEnv in your Azure Functions.

Prerequisites

  • Azure account with a Function App
  • Azure CLI installed
  • KeyEnv service token with read access

Quick Sync

Sync secrets to Function App settings:

export KEYENV_TOKEN=your-service-token

FUNCTION_APP="my-function-app"
RESOURCE_GROUP="my-resource-group"

# Export and sync
keyenv export -p YOUR_PROJECT_ID -e production --format json | \
  jq -r '.secrets[] | "\(.key)=\(.value)"' | \
  xargs -I {} az functionapp config appsettings set \
    --name "$FUNCTION_APP" \
    --resource-group "$RESOURCE_GROUP" \
    --settings "{}"

Sync Script

Create a reusable sync script:

#!/bin/bash
# sync-to-azure-functions.sh

set -euo pipefail

: "${KEYENV_TOKEN:?KEYENV_TOKEN is required}"

PROJECT_ID="${KEYENV_PROJECT:-YOUR_PROJECT_ID}"
ENVIRONMENT="${KEYENV_ENV:-production}"
FUNCTION_APP="${AZURE_FUNCTION_APP:-my-function-app}"
RESOURCE_GROUP="${AZURE_RESOURCE_GROUP:-my-resource-group}"

echo "Syncing KeyEnv ($ENVIRONMENT) to Azure Functions ($FUNCTION_APP)..."

# Export secrets and sync one by one
keyenv export -p "$PROJECT_ID" -e "$ENVIRONMENT" --format json | \
  jq -r '.secrets[] | @base64' | while read -r secret; do
    key=$(echo "$secret" | base64 -d | jq -r '.key')
    value=$(echo "$secret" | base64 -d | jq -r '.value')

    az functionapp config appsettings set \
      --name "$FUNCTION_APP" \
      --resource-group "$RESOURCE_GROUP" \
      --settings "$key=$value" \
      --output none

    echo "Set: $key"
done

echo "Done!"

Using SDK at Runtime

For dynamic secret loading, use the Node.js or Python SDK in your function.

Node.js Function

// src/functions/httpTrigger.ts
import { app, HttpRequest, HttpResponseInit, InvocationContext } from "@azure/functions";
import { KeyEnv } from '@keyenv/node';

// Initialize once, outside the handler
const keyenv = new KeyEnv({
  token: process.env.KEYENV_TOKEN!,
  cacheTtl: 300, // Cache for 5 minutes
});

let secretsLoaded = false;

export async function httpTrigger(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
  // Load secrets on first invocation
  if (!secretsLoaded) {
    await keyenv.loadEnv(process.env.KEYENV_PROJECT!, 'production');
    secretsLoaded = true;
  }

  // Use secrets
  const apiKey = process.env.API_KEY;

  return {
    body: JSON.stringify({ status: 'ok' }),
    headers: { 'Content-Type': 'application/json' },
  };
}

app.http('httpTrigger', {
  methods: ['GET', 'POST'],
  handler: httpTrigger,
});

Python Function

# function_app.py
import azure.functions as func
import os
from keyenv import KeyEnv

app = func.FunctionApp()

# Initialize client
keyenv = KeyEnv(
    token=os.environ["KEYENV_TOKEN"],
    cache_ttl=300,
)

secrets_loaded = False

@app.route(route="hello")
def hello(req: func.HttpRequest) -> func.HttpResponse:
    global secrets_loaded

    if not secrets_loaded:
        keyenv.load_env(os.environ["KEYENV_PROJECT"], "production")
        secrets_loaded = True

    api_key = os.environ.get("API_KEY")

    return func.HttpResponse(f"Hello!")

CI/CD Integration

GitHub Actions

name: Deploy Azure Functions

on:
  push:
    branches: [main]

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

      - name: Install KeyEnv CLI
        run: curl -fsSL https://keyenv.dev/install.sh | bash

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

      - name: Sync secrets
        run: |
          export PATH="$HOME/.keyenv/bin:$PATH"

          keyenv export -p ${{ vars.KEYENV_PROJECT }} -e production --format json | \
            jq -r '.secrets[] | "\(.key)=\(.value)"' | while IFS='=' read -r key value; do
              az functionapp config appsettings set \
                --name ${{ vars.AZURE_FUNCTION_APP }} \
                --resource-group ${{ vars.AZURE_RESOURCE_GROUP }} \
                --settings "$key=$value" \
                --output none
            done
        env:
          KEYENV_TOKEN: ${{ secrets.KEYENV_TOKEN }}

      - name: Deploy Function App
        uses: Azure/functions-action@v1
        with:
          app-name: ${{ vars.AZURE_FUNCTION_APP }}
          package: .

Azure DevOps Pipeline

trigger:
  - main

pool:
  vmImage: 'ubuntu-latest'

steps:
  - script: curl -fsSL https://keyenv.dev/install.sh | bash
    displayName: 'Install KeyEnv CLI'

  - task: AzureCLI@2
    displayName: 'Sync secrets'
    inputs:
      azureSubscription: 'your-subscription'
      scriptType: 'bash'
      scriptLocation: 'inlineScript'
      inlineScript: |
        export PATH="$HOME/.keyenv/bin:$PATH"

        keyenv export -p $(KEYENV_PROJECT) -e production --format json | \
          jq -r '.secrets[] | "\(.key)=\(.value)"' | while IFS='=' read -r key value; do
            az functionapp config appsettings set \
              --name $(AZURE_FUNCTION_APP) \
              --resource-group $(AZURE_RESOURCE_GROUP) \
              --settings "$key=$value" \
              --output none
          done
    env:
      KEYENV_TOKEN: $(KEYENV_TOKEN)

For production, store secrets in Azure Key Vault:

1. Sync to Key Vault

#!/bin/bash

VAULT_NAME="my-key-vault"

keyenv export -p YOUR_PROJECT_ID -e production --format json | \
  jq -r '.secrets[] | "\(.key)=\(.value)"' | while IFS='=' read -r key value; do
    # Key Vault doesn't allow underscores, replace with dashes
    vault_key=$(echo "$key" | tr '_' '-')

    az keyvault secret set \
      --vault-name "$VAULT_NAME" \
      --name "$vault_key" \
      --value "$value" \
      --output none

    echo "Set: $vault_key"
done

2. Reference in Function App

# Add Key Vault reference
az functionapp config appsettings set \
  --name my-function-app \
  --resource-group my-resource-group \
  --settings "[email protected](VaultName=my-key-vault;SecretName=DATABASE-URL)"

Deployment Slots

Sync different environments to different slots:

# Production slot
keyenv export -p YOUR_PROJECT_ID -e production --format json | \
  jq -r '.secrets[]' | sync_to_slot "production"

# Staging slot
keyenv export -p YOUR_PROJECT_ID -e staging --format json | \
  jq -r '.secrets[]' | sync_to_slot "staging"

# Helper function
sync_to_slot() {
  local slot=$1
  while IFS='=' read -r key value; do
    if [ "$slot" == "production" ]; then
      az functionapp config appsettings set \
        --name "$FUNCTION_APP" \
        --resource-group "$RESOURCE_GROUP" \
        --settings "$key=$value"
    else
      az functionapp config appsettings set \
        --name "$FUNCTION_APP" \
        --resource-group "$RESOURCE_GROUP" \
        --slot "$slot" \
        --settings "$key=$value"
    fi
  done
}

Best Practices

  1. Use Key Vault for production - Better security and secret rotation
  2. Enable managed identity - Let Functions access Key Vault without credentials
  3. Use deployment slots - Test with staging secrets before production
  4. Cache secrets - Use SDK caching to reduce API calls

Troubleshooting

Settings Not Updating

Restart the Function App:

az functionapp restart \
  --name my-function-app \
  --resource-group my-resource-group

Key Vault Access Denied

Grant the Function App access:

az keyvault set-policy \
  --name my-key-vault \
  --object-id $(az functionapp identity show --name my-function-app --resource-group my-resource-group --query principalId -o tsv) \
  --secret-permissions get list

Environment Variables Not Loading

Check current settings:

az functionapp config appsettings list \
  --name my-function-app \
  --resource-group my-resource-group

On this page