freundcloud

Kubernetes Provider for Bicep (2025)

The Kubernetes provider in Bicep lets you define and deploy Kubernetes resources directly alongside your Azure infrastructure. This enables unified, end-to-end IaC workflows for DevOps and SRE teams managing both cloud and Kubernetes environments.


Why Use the Kubernetes Provider?

  • Unified IaC: Manage Azure and Kubernetes resources in a single Bicep template
  • Automation: Integrate with CI/CD (GitHub Actions, Azure Pipelines) for full-stack deployments
  • Security: Use @secure() for sensitive kubeConfig values and integrate with Azure Key Vault
  • Modularity: Separate infra and app layers with Bicep modules

Enable the Extensibility Preview Feature

The Kubernetes provider is in preview. Enable it by adding a bicepconfig.json to your project root:

{
  "experimentalFeaturesEnabled": {
    "extensibility": true
  }
}

Basic Usage

Import the Kubernetes Provider

@secure()
param kubeConfig string

import 'kubernetes@1.0.0' with {
  namespace: 'default'
  kubeConfig: kubeConfig
} as k8s

Creating Kubernetes Resources

// Create a ConfigMap
resource myConfigMap 'core/v1' = {
  kind: 'ConfigMap'
  metadata: {
    name: 'my-config'
  }
  data: {
    'key1': 'value1'
    'key2': 'value2'
  }
}

// Create a Deployment
resource myDeployment 'apps/v1' = {
  kind: 'Deployment'
  metadata: {
    name: 'my-app'
  }
  spec: {
    replicas: 3
    selector: {
      matchLabels: {
        app: 'my-app'
      }
    }
    template: {
      metadata: {
        labels: {
          app: 'my-app'
        }
      }
      spec: {
        containers: [
          {
            name: 'my-container'
            image: 'nginx:latest'
            ports: [
              {
                containerPort: 80
              }
            ]
          }
        ]
      }
    }
  }
}

Real-Life DevOps & SRE Examples

1. Deploy a Namespace, ConfigMap, and Deployment

resource myNamespace 'core/v1' = {
  kind: 'Namespace'
  metadata: {
    name: 'devops-apps'
  }
}

import 'kubernetes@1.0.0' with {
  namespace: myNamespace.metadata.name
  kubeConfig: kubeConfig
} as k8s

resource myConfigMap 'core/v1' = {
  kind: 'ConfigMap'
  metadata: {
    name: 'app-config'
  }
  data: {
    'ENV': 'production'
    'LOG_LEVEL': 'info'
  }
}

resource myDeployment 'apps/v1' = {
  kind: 'Deployment'
  metadata: {
    name: 'nginx-app'
  }
  spec: {
    replicas: 2
    selector: {
      matchLabels: {
        app: 'nginx-app'
      }
    }
    template: {
      metadata: {
        labels: {
          app: 'nginx-app'
        }
      }
      spec: {
        containers: [
          {
            name: 'nginx'
            image: 'nginx:1.25.0'
            envFrom: [
              {
                configMapRef: {
                  name: 'app-config'
                }
              }
            ]
            ports: [
              {
                containerPort: 80
              }
            ]
          }
        ]
      }
    }
  }
}

2. Load YAML Manifests from File

resource fromYaml 'core/v1' = {
  kind: 'List'
  apiVersion: 'v1'
  items: loadYamlContent('k8s-manifests.yaml')
}

End-to-End Example: AKS Cluster with App Deployment

1. Create the AKS Cluster (main.bicep)

@description('The name of the Managed Cluster resource.')
param clusterName string = 'aks101cluster'

@description('The location of the Managed Cluster resource.')
param location string = resourceGroup().location

@description('Optional DNS prefix to use with hosted Kubernetes API server FQDN.')
param dnsPrefix string

@description('Disk size (in GB) to provision for each of the agent pool nodes. This value ranges from 0 to 1023. Specifying 0 will apply the default disk size for that agentVMSize.')
@minValue(0)
@maxValue(1023)
param osDiskSizeGB int = 0

@description('The number of nodes for the cluster.')
@minValue(1)
@maxValue(50)
param agentCount int = 3

@description('The size of the Virtual Machine.')
param agentVMSize string = 'standard_d2s_v3'

@description('User name for the Linux Virtual Machines.')
param linuxAdminUsername string

@description('Configure all linux machines with the SSH RSA public key string. Your key should include three parts, for example \'ssh-rsa AAAAB...snip...UcyupgH azureuser@linuxvm\'')
param sshRSAPublicKey string

resource aks 'Microsoft.ContainerService/managedClusters@2022-05-02-preview' = {
  name: clusterName
  location: location
  identity: {
    type: 'SystemAssigned'
  }
  properties: {
    dnsPrefix: dnsPrefix
    agentPoolProfiles: [
      {
        name: 'agentpool'
        osDiskSizeGB: osDiskSizeGB
        count: agentCount
        vmSize: agentVMSize
        osType: 'Linux'
        mode: 'System'
      }
    ]
    linuxProfile: {
      adminUsername: linuxAdminUsername
      ssh: {
        publicKeys: [
          {
            keyData: sshRSAPublicKey
          }
        ]
      }
    }
  }
}

// Reference the Kubernetes application deployment module
module kubernetes './azure-vote.bicep' = {
  name: 'kubernetes-app-deploy'
  params: {
    kubeConfig: aks.listClusterAdminCredential().kubeconfigs[0].value
  }
}

output controlPlaneFQDN string = aks.properties.fqdn
output applicationIp string = kubernetes.outputs.frontendIp

2. Define the Application Resources (azure-vote.bicep)

@secure()
param kubeConfig string

// Import the Kubernetes provider
import 'kubernetes@1.0.0' with {
  namespace: 'default'
  kubeConfig: kubeConfig
} as k8s

// Define Redis backend deployment
resource redisDeployment 'apps/v1' = {
  kind: 'Deployment'
  metadata: {
    name: 'azure-vote-back'
  }
  spec: {
    replicas: 1
    selector: {
      matchLabels: {
        app: 'azure-vote-back'
      }
    }
    template: {
      metadata: {
        labels: {
          app: 'azure-vote-back'
        }
      }
      spec: {
        nodeSelector: {
          'kubernetes.io/os': 'linux'
        }
        containers: [
          {
            name: 'azure-vote-back'
            image: 'mcr.microsoft.com/oss/bitnami/redis:6.0.8'
            env: [
              {
                name: 'ALLOW_EMPTY_PASSWORD'
                value: 'yes'
              }
            ]
            resources: {
              requests: {
                cpu: '100m'
                memory: '128Mi'
              }
              limits: {
                cpu: '250m'
                memory: '256Mi'
              }
            }
            ports: [
              {
                containerPort: 6379
                name: 'redis'
              }
            ]
          }
        ]
      }
    }
  }
}

// Redis service
resource redisService 'core/v1' = {
  kind: 'Service'
  metadata: {
    name: 'azure-vote-back'
  }
  spec: {
    ports: [
      {
        port: 6379
      }
    ]
    selector: {
      app: 'azure-vote-back'
    }
  }
}

// Frontend deployment
resource frontendDeployment 'apps/v1' = {
  kind: 'Deployment'
  metadata: {
    name: 'azure-vote-front'
  }
  spec: {
    replicas: 1
    selector: {
      matchLabels: {
        app: 'azure-vote-front'
      }
    }
    template: {
      metadata: {
        labels: {
          app: 'azure-vote-front'
        }
      }
      spec: {
        nodeSelector: {
          'kubernetes.io/os': 'linux'
        }
        containers: [
          {
            name: 'azure-vote-front'
            image: 'mcr.microsoft.com/azuredocs/azure-vote-front:v1'
            resources: {
              requests: {
                cpu: '100m'
                memory: '128Mi'
              }
              limits: {
                cpu: '250m'
                memory: '256Mi'
              }
            }
            ports: [
              {
                containerPort: 80
              }
            ]
            env: [
              {
                name: 'REDIS'
                value: 'azure-vote-back'
              }
            ]
          }
        ]
      }
    }
  }
}

// Frontend service with LoadBalancer to expose the application
resource frontendService 'core/v1' = {
  kind: 'Service'
  metadata: {
    name: 'azure-vote-front'
  }
  spec: {
    type: 'LoadBalancer'
    ports: [
      {
        port: 80
      }
    ]
    selector: {
      app: 'azure-vote-front'
    }
  }
}

// Output the frontend service IP address
output frontendIp string = frontendService.status.loadBalancer.ingress[0].ip

Deployment Instructions

Using Azure CLI

# Create a resource group
az group create --name myResourceGroup --location eastus

# Deploy the Bicep template
az deployment group create \
  --resource-group myResourceGroup \
  --template-file main.bicep \
  --parameters \
      clusterName=myAksCluster \
      dnsPrefix=myakscluster \
      linuxAdminUsername=azureuser \
      sshRSAPublicKey="$(cat ~/.ssh/id_rsa.pub)"

Using PowerShell

# Create a resource group
New-AzResourceGroup -Name myResourceGroup -Location eastus

# Deploy the Bicep template
New-AzResourceGroupDeployment `
  -ResourceGroupName myResourceGroup `
  -TemplateFile ./main.bicep `
  -clusterName myAksCluster `
  -dnsPrefix myakscluster `
  -linuxAdminUsername azureuser `
  -sshRSAPublicKey (Get-Content ~/.ssh/id_rsa.pub -Raw)

Best Practices (2025)

  • Use @secure() for kubeConfig and secrets
  • Separate infra and app layers with Bicep modules
  • Use parameter files for environment-specific values
  • Validate with az bicep build and test with az deployment group what-if
  • Use logical namespaces for multi-tenant clusters
  • Store kubeConfig in Azure Key Vault for production

Common Pitfalls

  • Using admin kubeConfig in production (prefer service accounts)
  • Not validating resource dependencies (use dependsOn)
  • Overloading a single template with too many resources
  • Not using YAML for complex or custom CRDs

CI/CD Integration Example (GitHub Actions)

- name: Deploy Infra and K8s Resources
  run: |
    az deployment group create \
      --resource-group myResourceGroup \
      --template-file main.bicep \
      --parameters kubeConfig="${{ secrets.KUBECONFIG }}"

Azure & Bicep Jokes

Bicep Joke: Why did the SRE use Bicep for Kubernetes? To flex on YAML!

Azure Joke: Why did the pod love Azure? It always had a resource group to hang out in!


References


Search Tip: Use keywords like bicep kubernetes, aks, namespace, configmap, or ci/cd to quickly find relevant examples and best practices.