Am ajuns la ultima piesă din serie – și e una la care am tot făcut trimitere. În toate exemplele de până acum, pasul azure/login s-a autentificat la Azure fără nicio parolă, doar cu câteva ID-uri. E timpul să vedem cum și, mai ales, de ce ăsta e modul corect.
Sunt de fapt două întrebări diferite despre secrete:
-
Cum se autentifică GitHub Actions la Azure ca să facă deploy – fără un client secret stocat în repo?
-
Cum accesează aplicația ta resurse Azure (Key Vault, SQL, Storage) – fără connection strings cu parolă?
Răspunsul la prima e OIDC. Răspunsul la a doua e Managed Identity. Împreună, fac să dispară parolele „la vedere”.
🔓 Problema parolelor clasice
Varianta veche arăta așa: generai un client secret pentru un service principal, îl puneai în GitHub Secrets, iar connection string-urile cu parole stăteau în app settings. Funcționează, dar:
-
Secretele expiră – și într-o zi deploy-ul pică pentru că parola a expirat.
-
Trebuie rotite manual – o corvoadă pe care toată lumea o amână.
-
Pot fi scurse – un log, un screenshot, un fork greșit configurat.
-
Oricine are acces la repo sau la configurație se apropie periculos de ele.
Cel mai sigur secret e cel care nu există. Dacă nu stochezi nicio parolă pe termen lung, nu ai ce să scurgi sau să rotești.
🎫 Partea 1: GitHub → Azure prin OIDC
OIDC (OpenID Connect) înlocuiește parola cu încredere. La fiecare rulare, GitHub emite un token scurt (un JWT) care spune, verificabil, „sunt rularea X din repo-ul Y, pe environment-ul production”. Azure a fost configurat în prealabil să aibă încredere în astfel de token-uri – o relație numită federated credential.
Rezultatul: GitHub primește, doar pe durata rulării, un token de acces la Azure. Niciun client secret nu e stocat nicăieri.
Pasul 1 – creezi identitatea și încrederea federată
Într-o aplicație Entra ID, legi un federated credential de repo-ul și environment-ul tău:
az ad app create --display-name "github-myapp-deploy"
az ad app federated-credential create \
--id <APP_ID> \
--parameters '{
"name": "github-prod",
"issuer": "https://token.actions.githubusercontent.com",
"subject": "repo:owner/myapp:environment:production",
"audiences": ["api://AzureADTokenExchange"]
}'
subject e cheia: leagă încrederea exact de environment-ul production din repo-ul tău. Un alt repo, alt branch sau alt environment nu se potrivește – deci nu primește token. Asta se leagă direct de approval gate-urile din partea 2.
Pasul 2 – azure/login fără client secret
În workflow, dai job-ului permisiunea de a cere un token OIDC și te loghezi doar cu ID-uri:
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: production
steps:
- uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
permissions: id-token: write e obligatoriu – fără el, runner-ul nu poate cere token-ul OIDC. Iar AZURE_CLIENT_ID, AZURE_TENANT_ID și AZURE_SUBSCRIPTION_ID nu sunt parole: sunt simpli identificatori. Chiar dacă s-ar vedea, nu deblochează nimic fără încrederea federată configurată în Azure.
🪪 Partea 2: aplicația → Azure prin Managed Identity
Am rezolvat deploy-ul. Dar aplicația în sine? Ea trebuie să citească un secret din Key Vault, să se conecteze la SQL, să scrie în Storage. Varianta clasică: connection strings cu parole în app settings. Varianta corectă: Managed Identity.
O Managed Identity e o identitate gestionată de Azure, atașată App Service-ului tău. Azure se ocupă complet de ciclul ei de viață – nu există nicio parolă pe care s-o vezi sau s-o administrezi.
Activezi identitatea în Bicep
Pe web app, adaugi un bloc identity (continuând Bicep-ul din partea 3):
resource webApp 'Microsoft.Web/sites@2023-12-01' = {
name: webAppName
location: location
identity: {
type: 'SystemAssigned'
}
properties: {
serverFarmId: plan.id
httpsOnly: true
}
}
Apoi îi dai acces cu RBAC – de exemplu rolul Key Vault Secrets User pe vault – tot din Bicep. Identitatea primește exact permisiunile de care are nevoie, nimic în plus.
În cod: DefaultAzureCredential
În aplicația .NET, folosești pachetul Azure.Identity. DefaultAzureCredential detectează automat Managed Identity în Azure (și credențialele tale locale când dezvolți), fără niciun secret în cod:
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
var client = new SecretClient(
new Uri("https://my-vault.vault.azure.net/"),
new DefaultAzureCredential());
KeyVaultSecret secret = await client.GetSecretAsync("DbPassword");
Pentru SQL, nici măcar nu mai ai nevoie de parolă în connection string – te conectezi cu identitatea:
Server=tcp:my-sql.database.windows.net,1433;
Database=mydb;
Authentication=Active Directory Default;
Authentication=Active Directory Default spune clientului SQL să folosească aceeași Managed Identity. Zero parole, nici în cod, nici în app settings.
🧹 De ce dispar secretele „la vedere”
Pune cap la cap cele două bucăți și rezultatul e curat:
-
În GitHub nu stochezi niciun client secret – doar ID-uri nesensibile, iar autentificarea se face cu token-uri de scurtă durată prin OIDC.
-
În app settings nu ai connection strings cu parole – aplicația se autentifică prin Managed Identity.
-
Nimic de rotit, nimic de expirat, nimic de scurs. Secretele care nu există nu pot fi furate.
✅ Câteva reguli sănătoase
-
Leagă federated credential-ul de environment (production), nu doar de repo – least privilege.
-
Folosește RBAC minim pentru Managed Identity – doar rolurile strict necesare.
-
O user-assigned managed identity poate fi reutilizată de mai multe resurse, dacă ai nevoie.
-
Păstrează în GitHub Secrets doar identificatori, niciodată parole pe termen lung.
Concluzie: unde am ajuns
Cu asta, seria e completă. Am pornit de la „am un build automat” și am ajuns la un pipeline cu adevărat profesionist pentru .NET + Azure:
-
Partea 1 – fundația: build o singură dată, teste care blochează, un artifact promovabil.
-
Partea 2 – environments cu approval gates: același artifact prin dev → staging → producție, cu aprobare umană.
-
Partea 3 – Infrastructure as Code cu Bicep: infrastructura definită în cod, versionată și reproductibilă.
-
Partea 4 – deployment slots: release-uri cu zero downtime și rollback în câteva secunde.
-
Partea 5 – secrete fără parole: OIDC pentru deploy, Managed Identity pentru aplicație.
Un pipeline profesionist nu înseamnă mai mult YAML, ci mai multe garanții: construiești o dată, promovezi controlat, definești totul în cod, livrezi fără downtime și nu mai ții nicio parolă la vedere. Acum ai harta completă – pas cu pas, de la zero la producție.