Modern cloud deployments are not just about provisioning infrastructure—they’re about securing how that infrastructure is deployed. When using GitHub Actions with Microsoft Azure and Terraform, the most critical yet often overlooked decision is how your pipeline authenticates to Azure. Traditionally, Service Principals (SPNs) with client secrets were used, but this introduces security risks and operational overhead. Today, OpenID Connect (OIDC) offers a more secure, secretless approach based on identity and trust.
From Secrets to Trust: Modernizing CI/CD Authentication
When building infrastructure pipelines on Microsoft Azure using GitHub Actions and Terraform, one design choice quietly determines your entire security posture:
How does your pipeline authenticate to Azure?
For years, the answer was simple:
- Use a Service Principal (SPN)
- Store a client secret in GitHub
- Authenticate using credentials
It works—but it doesn’t scale securely.
This article walks through a real, production-ready implementation comparing:
Backed by a working repo: WorkFlowBasedDeployment
Architecture Overview
This repository implements a workflow-driven Terraform deployment model with modular Azure infrastructure.
Repository Structure
.github/workflows/
deploy-infrastructure.yml # OIDC deployment
deploy-infrastructure-spn.yml # SPN deployment
destroy-infrastructure.yml # OIDC destroy
destroy-infrastructure-spn.yml # SPN destroy
Deployment/
main.tf
providers.tf
variables.tf
terraform.tfvars
modules/
Azure Resources Provisioned
| Resource | Module | Resource Group |
|---|---|---|
| Virtual Network + NSGs | vnet | rg-network |
| Storage Account | sa | rg-data |
| Container Apps | containerapps | rg-compute |
| AI Foundry | aifoundry | rg-data |
| AI Search | aisearch | rg-data |
| Azure Container Registry | acr | rg-compute |
| Key Vault | azkeyvault | rg-data |
| Monitoring | azmonitor | rg-compute |
| Private Endpoints | private_endpoints | rg-network |
Authentication Models
-
Service Principal (SPN) – The Traditional Way
How it works- Create App Registration
- Generate client secret
- Store it in GitHubTerraform authenticates using environment variables
env: ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} ARM_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
-
The problem
-
Risk Impact Long-lived secrets Can be leaked Manual rotation Operational burden Repo compromise Full environment exposure
-
This model is still supported—but increasingly considered legacy for secure pipelines.
-
OIDC (OpenID Connect) – The Modern Approach
How it works
- GitHub Actions generates a short-lived identity token
-
Microsoft Entra ID validates it
-
Azure issues a temporary access token
-
Terraform executes using that token
No secrets. No storage. No rotation.
Authentication Models Compared
OIDC Flow (Mental Model)
Think of OIDC like this:
- GitHub → Identity Provider
- Azure → Trust Authority
- Workflow → Temporary Identity
OIDC Flow
OIDC Implementation (From the Repo)
Workflow Configuration
permissions:
id-token: write
contents: read
env:
ARM_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
ARM_SUBSCRIPTION_ID: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
ARM_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
ARM_USE_OIDC: true
Azure Login
- name: Azure Login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
Backend (Terraform State with OIDC)
terraform init \
-backend-config="use_oidc=true"
Even your state storage is secretless
Azure Setup for OIDC
- Create App Registration
- No client secret required
- Configure Federated Credential
- Example:
Issuer: https://token.actions.githubusercontent.com Subject: repo:<org>/<repo>:ref:refs/heads/master
- Example:
You can restrict by:
-
- Branch
- Environment
- Repository
- Assign RBAC: Grant roles like:
-
- Contributor
- Or scoped resource-level access
CI/CD Workflow Design
Both SPN and OIDC pipelines follow a 2-stage pattern:
- Plan Stage
- terraform fmt
- terraform validate
- terraform plan
- Upload plan artifact
- Apply Stage
- Triggered only on main
- Downloads plan
- Runs apply -auto-approve
- Protected via environment approvals
This ensures safe, auditable deployments
Deployment Flow
OIDC vs SPN — Real Comparison
| Feature | SPN | OIDC |
|---|---|---|
| Secrets | Stored in GitHub | None |
| Token lifetime | Long-lived | Short-lived |
| Rotation | Manual | Not required |
| Security | Medium | High |
| Setup | Simple | Slightly complex |
| Recommended | No | Yes |
Common Pitfalls (Real-World Lessons)
- Missing id-token permission
- Without this, OIDC fails silently.
- Federated credential mismatch
- Wrong branch
- Incorrect repo name
- Case sensitivity issues
Azure rejects the token completely.
- RBAC delay
- Role assignments can take time → causes confusing failures.
- Backend misconfiguration
- Forgetting use_oidc=true breaks Terraform state auth.
Debugging Tips
- Enable debug logs in GitHub Actions
- Check Sign-in logs in Microsoft Entra ID
- Validate federated credential subject format
Always isolate:
- Identity issue vs
- Permission issue
Migration Strategy (SPN → OIDC)
A safe transition looks like this:
- Keep SPN as fallback
- Add OIDC alongside
- Test in DEV environment
- Remove client secret
- Revoke old credentials
No downtime, no risk.
Where This Fits in Modern Azure Architecture
This pattern integrates naturally with:
- Azure Container Apps
- AI/ML workloads (AI Foundry, Search)
- Multi-environment deployments
- Zero-trust enterprise architectures
Authentication becomes identity-driven, not secret-driven
When NOT to Use OIDC
- Legacy CI/CD systems without OIDC support
- Organisations with strict identity federation constraints
- Cross-tenant scenarios with limited trust setup
Note: These cases are becoming increasingly rare in modern cloud setups.
Security Perspective
| Threat | SPN Risk | OIDC Risk |
|---|---|---|
| Secret leak | High | None |
| Credential reuse | High | Low |
| Token replay | Possible | Limited |
| Repo compromise | Full access | Scoped |
Final Takeaway
This repository demonstrates a key shift in modern DevOps:
- Secrets were a workaround for identity.
- OIDC replaces that workaround with trust.
By combining:
- GitHub Actions
- OIDC federation
- Azure RBAC
You get:
- Secure pipelines
- Scalable deployments
- Zero secret management
In enterprise environments, moving to OIDC can eliminate secret rotation pipelines entirely, reducing operational overhead and significantly lowering breach risk.
Reference Implementation
GitHub Repository: WorkFlowBasedDeployment
Closing Thought
OIDC doesn’t just improve authentication, it fundamentally changes how trust is established in cloud systems. In a world moving toward zero-trust architectures, identity is the new perimeter and OIDC is how you enforce it.