Azure CI/CD Breach Simulation
Attacker + Defender, written side-by-side in Terraform and GitHub Actions
Two live Azure stacks - one deliberately vulnerable, one hardened. A malicious app exfiltrates CLIENT_SECRET through the insecure pipeline. Forensic evidence and an incident response playbook sit next to the fix.
Year
2025
Role
DevSecOps + Red Team
Stack
Azure, Terraform, GitHub Actions
Angle
Attacker + Defender
OIDC FederationTerraformGitHub ActionsBanditForensicsIncident Response
github.com/gocko1004/azure-ci-pipeline-breach-sim ->High-level design
The infrastructure, the data flow,
and where each security control lives.
and where each security control lives.
Numbered steps trace the hardened secure-deploy.yml flow, 1 through 7. R1 and R2 are the insecure-deploy.yml shortcuts. Full step names in the legend below.
Actor
Developer
commits to main
Attacker
reads pipeline logs
External- github.com
GitHub repo
source of truth
Actions runner
insecure + secure deploy.yml
continue-on-errorPR approval
Azure Subscription- sub-prod / Switzerland North
Entra ID
identity + federation
OIDC
Managed Identity
short-lived tokens
RBAC
Resource Group rg-prod- VNet 10.0.0.0/16 / Subnet [NSG]
Key Vault
secrets store
RBACSoft-delete
App Service
webapp runtime
TLS 1.3NSG
Log Analytics
platform logs
SP secret visible
Storage Account
blob + table
Private EP
1git push
2trigger
3OIDC
4federated token
5secret-get (RBAC)
6inject secret
7HTTPS
R1static SP secret (insecure)
R2stdout leak to logs
Red path - insecure-deploy.yml
Runner uses long-lived CLIENT_SECRET → app.py prints it to stdout → lands in Log Analytics → any reader with Log Analytics access reads the secret.
Green path - secure-deploy.yml
Runner requests OIDC federated token → Entra ID exchanges for Managed Identity → pulls secret from Key Vault over RBAC → App Service runs under Managed Identity. No static secret exists.
Same Azure subscription, same services. Two credential paths, one with a standing secret and one without.
Why it matters
Pipeline breaches are not exotic. Long-lived secrets in CI, silenced scanners, and unrestricted deploy paths are the attack surface most teams actually carry.
The point is not to prove Azure can be hardened. The point is the two stacks next to each other, so the cost of the shortcut is obvious.
What this shows about me
I read attack paths, not compliance checklists.
The insecure stack works. It is not a strawman.
I write Terraform and GitHub Actions to a review bar.
Input validation, OIDC, environment gates, compliance constraints.
I capture evidence.
Forensics folder + IR playbook alongside the fix, not a commit message.
I can walk a CISO or a CTO through this in ten minutes.
Both sides on one page, no jargon, with a demo to run.
Outcome
A working demonstration of a CI/CD breach and the exact Terraform + workflow changes that stop it. Evidence lives in the repo. The incident response doc reads like a real writeup, not a checklist.
Vulnerabilities demonstrated20+
Stacks deployed2 (insecure + secure)
Primary fixOIDC federation
Forensic artifactsBandit JSON + screenshots
Docs shippedCOMPARISON.md + IR
VerifiableLive Azure, not localhost