Bicep CI/CD: GitHub Actions pipeline pro Azure
V českých enterprise firmách pozoruji jeden opakující se vzorec: tým zvládne napsat Bicep šablony, ale nasazování běží ručně. Jeden člověk má na starosti deploymenty, zná správné parametry zpaměti a když je nemocný, nikdo jiný neví, jak nasadit do produkce. Tohle není DevOps -- tohle je single point of failure zabalený do Azure CLI.
Effort: 1-2 dny na počáteční pipeline, půl dne na každé další prostředí Cost: $0 za GitHub Actions (2 000 minut/měsíc zdarma na veřejných repozitářích), ~$0.008/min na Ubuntu runnerech pro privátní repozitáře Prerequisites: Azure subscription, GitHub repozitář s Bicep soubory, Azure AD app registration s federovanými přihlašovacími údaji
Co se změnilo v roce 2025
GitHub Actions pro Azure prošly v roce 2025 několika zásadními vylepšeními:
- OIDC federace je nový standard. Akce
azure/login@v2už nevyžaduje JSON blob s klientským secretem. Nakonfigurujete federovanou identitu na Azure AD app registraci a runner se autentizuje krátkodobým tokenem. - Reusable workflows jsou v GA. Jeden workflow soubor sdílíte napříč repozitáři. Pro firmy s desítkami Bicep modulů to znamená konec kopírování YAML souborů.
az deployment group what-ifvýstup se dá automaticky vložit jako komentář do pull requestu. Reviewer vidí přesný diff infrastruktury ještě před schválením.- Environment protection rules nyní podporují povinné reviewery, časové prodlevy a omezení na konkrétní větve -- vše nativně v GitHubu.
Proč je to důležité
Ruční nasazování infrastruktury má jeden zásadní problém: škáluje lineárně s počtem prostředí. Jeden developer, jedno prostředí -- žádný problém. Tři prostředí a tým čtyř lidí? Začnou se dít věci.
Typický scénář z praxe: páteční odpoledne, developer spustí az deployment group create na staging, ale zapomene přepnout parametrový soubor. Staging dostane produkční CIDR rozsahy. V pondělí se staging VNet překrývá s produkcí a peering padá s chybou AddressOverlap. Debugging trvá půl dne, protože nikdo neví, kdo co nasadil a kdy.
CI/CD pipeline řeší tři věci najednou:
- Review před nasazením -- každá změna prochází PR s what-if náhledem
- Konzistence mezi prostředími -- stejná šablona, jiné parametry, žádná ruční improvizace
- Auditovatelnost -- Git historie je záznam kdo, co a kdy změnil
Implementace: GitHub Actions pipeline krok za krokem
OIDC federace -- autentizace bez secretů
Prvním krokem je nastavení OIDC federace. Vytvořte app registraci v Azure AD a přidejte federovaný přihlašovací údaj:
# Vytvoření app registrace
az ad app create --display-name "gh-actions-bicep-deployer"
# Zaznamenejte appId z výstupu, pak vytvořte service principal
az ad sp create --id <appId>
# Přidání federovaného přihlašovacího údaje pro main větev
az ad app federated-credential create --id <appId> --parameters '{
"name": "github-main-deploy",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:contoso/infra-bicep:ref:refs/heads/main",
"audiences": ["api://AzureADTokenExchange"]
}'
# Přiřazení role Contributor na cílovou resource group
az role assignment create \
--assignee <appId> \
--role "Contributor" \
--scope "/subscriptions/a1b2c3d4-5678-90ab-cdef-1234567890ab/resourceGroups/rg-platform-prod-westeurope"Do GitHub repository settings přidejte tři secrets: AZURE_CLIENT_ID, AZURE_TENANT_ID a AZURE_SUBSCRIPTION_ID. Žádný client secret -- to je celý smysl OIDC.
Workflow soubor
Celý pipeline žije v .github/workflows/deploy-infra.yml. Na pull requestech spouští what-if náhled, na merge do main provádí deployment:
# .github/workflows/deploy-infra.yml
name: Deploy Bicep Infrastructure
on:
pull_request:
paths:
- 'infra/**'
push:
branches:
- main
paths:
- 'infra/**'
permissions:
id-token: write # Nutné pro OIDC
contents: read
pull-requests: write # Komentář s what-if výstupem
env:
RESOURCE_GROUP: rg-platform-prod-westeurope
LOCATION: westeurope
TEMPLATE: infra/main.bicep
PARAMETERS: infra/parameters/prod.bicepparam
jobs:
what-if:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
environment: preview
steps:
- uses: actions/checkout@v4
- name: Přihlášení do Azure (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Spuštění what-if
id: whatif
run: |
result=$(az deployment group what-if \
--resource-group ${{ env.RESOURCE_GROUP }} \
--template-file ${{ env.TEMPLATE }} \
--parameters ${{ env.PARAMETERS }} \
--no-pretty-print 2>&1)
echo "output<<EOF" >> $GITHUB_OUTPUT
echo "$result" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Vložení what-if do PR komentáře
uses: actions/github-script@v7
with:
script: |
const output = `### Bicep What-If náhled
\`\`\`
${{ steps.whatif.outputs.output }}
\`\`\`
*Spuštěno uživatelem @${{ github.actor }}*`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
});
deploy-staging:
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- name: Přihlášení do Azure (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Nasazení na staging
run: |
az deployment group create \
--resource-group rg-platform-staging-westeurope \
--template-file ${{ env.TEMPLATE }} \
--parameters infra/parameters/staging.bicepparam \
--name "staging-$(date +%Y%m%d-%H%M%S)"
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment:
name: production
url: https://portal.azure.com
steps:
- uses: actions/checkout@v4
- name: Přihlášení do Azure (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Nasazení na produkci
run: |
az deployment group create \
--resource-group ${{ env.RESOURCE_GROUP }} \
--template-file ${{ env.TEMPLATE }} \
--parameters ${{ env.PARAMETERS }} \
--name "prod-$(date +%Y%m%d-%H%M%S)"Ochrana prostředí
V nastavení GitHub repozitáře pod Environments nastavte production:
- Required reviewers: Minimálně jeden senior z týmu. Žádný deployment do produkce bez druhého páru očí.
- Wait timer: 5 minut po schválení. Dostatečná doba na "počkej, vlastně ne."
- Branch restriction: Pouze
main. Feature větve nemůžou obejít staging.
Na co vás dokumentace nepřipraví
Pole subject ve federovaném přihlašovacím údaji je case-sensitive. Pokud váš repozitář je Contoso/Infra-Bicep ale vy nakonfigurujete contoso/infra-bicep, výměna tokenů tiše selže s generickou chybou AADSTS700024. Strávili jsme na tom dvě hodiny, než jsme si všimli rozdílu ve velikosti písmen v logu.
Další častý problém: pokud máte více subscription a OIDC identita má Contributor pouze na jedné resource group, ale šablona odkazuje Key Vault v jiné resource group, dostanete kryptickou chybu Forbidden bez jakéhokoliv náznaku, že problém je v chybějícím role assignment.
Výsledky z praxe
Po nasazení pipeline jsme sledovali metriky za prvních 30 běhů:
| Fáze | Průměrná doba |
|---|---|
| What-if náhled (PR) | 1m 42s |
| Deployment staging | 3m 15s |
| Deployment produkce (po schválení) | 3m 22s |
| Celkem (staging + produkce) | ~7 min |
Pro srovnání: ruční proces zahrnoval přihlášení přes VPN, az login s osobními credentials, navigaci do správného adresáře, vzpomínání na správný parametrový soubor a ruční spuštění. Minimálně 20 minut na prostředí, plus riziko lidské chyby.
Za první měsíc provozu pipeline zachytil:
- 3 konflikty CIDR rozsahů ve what-if náhledech (dříve by se projevily až v produkci)
- 1 chybějící role assignment pro OIDC identitu na cross-resource-group Key Vault
- 2 pokusy o deployment z feature větve přímo do produkce (zablokováno branch restriction)
Klíčové poznatky
- OIDC federace místo client secrets. Eliminuje rotaci secrets a zmenšuje attack surface. Dávejte pozor na case-sensitivity pole
subject. - What-if na každém PR. Infrastrukturní diff by měl být stejně viditelný jako diff v kódu. Automatický PR komentář zajistí, že ho reviewer nemůže přehlédnout.
- Environment gates nejsou luxus. Pětiminutová prodleva před produkčním deploymentem nás za první měsíc zachránila dvakrát.
- Pojmenované deploymenty. Vzor
--name "prod-$(date +%Y%m%d-%H%M%S)"umožňuje jednoduché korelace mezi GitHub Actions a Azure portálem.
Pokud pracujete s Terraformem a zajímá vás srovnání přístupů, podívejte se na náš přehled Terraform best practices pro Azure. Volba mezi Bicep a Terraform často závisí na tom, zda je váš tým čistě na Azure, nebo pracuje multi-cloud.
Potřebujete pomoct s nastavením CI/CD pipeline pro vaši Azure infrastrukturu? Podívejte se na naše konzultační služby v oblasti infrastruktury a DevOps -- tyto pipeline jsme stavěli pro týmy od tříčlenných startupů po enterprise platformní týmy.
O autorovi

Martin Rylko
Senior Cloud Architect & DevOps Engineer
Více než 14 let v IT – od on-premises datacenter a Hyper-V clusteringu po cloudovou infrastrukturu v Microsoft Azure. Specializuji se na Landing Zones, IaC automatizaci, Kubernetes a bezpečnostní compliance.
Nejcastejsi dotazy
Mám použít OIDC federaci nebo service principal secret pro GitHub Actions do Azure?▾
Jaký deployment scope mám použít pro Bicep v CI/CD -- subskripci nebo resource group?▾
Jak řešit rollback, když deployment Bicepu selže v pipeline?▾
Lze spustit Bicep what-if jako check v pull requestu před mergem?▾
Mohlo by vás zajímat
Terraform Azure moduly: Privátní registr a testování
Znovupoužitelné Terraform moduly pro Azure s publikací do privátního registru, automatizované testování Terratest a verzovaná spotřeba modulů v produkci.
ČístTerraform Azure: Best practices pro produkci
Terraform Azure best practices pro produkční projekty. Remote state locking, modulární struktura, drift detection, naming konvence a testování.
ČístAzure Landing Zone v Bicepu: Enterprise nasazení
Nasaďte enterprise-ready Azure Landing Zone pomocí Bicep modulů. Hub-spoke síťová architektura, governance politiky a integrace do CI/CD pipeline.
Číst