This repository contains an enterprise-grade modular Infrastructure-as-Code (IaC) project built using Azure Bicep. It provisions a complete environment including:
- Virtual Network & Subnet
- Network Security Group (NSG)
- Public IP Address
- Network Interface (NIC)
- Storage Account
- Azure Key Vault + Secret injection
- Windows Server 2022 Virtual Machine
- Custom Script Extension (
init.ps1) for post-provisioning configuration - Managed Identity for the VM
- Boot Diagnostics
This project demonstrates best practices in ARM/Bicep modularization, secure secret handling, dependency chaining, reusable IaC patterns, and enterprise CI/CD workflows.
Resource Group
│
├── Virtual Network (VNet)
│ ├── Subnet
│ └── NSG association
│
├── Public IP
│
├── Network Security Group (NSG)
│
├── NIC
│ ├── Subnet association
│ └── Public IP association
│
├── Storage Account (Boot Diagnostics)
│
├── Key Vault
│ └── Secret: vmAdminPassword
│
├── Virtual Machine (Windows Server 2022)
│ ├── Managed Identity
│ ├── Boot Diagnostics
│ └── Custom Script Extension
│ └── init.ps1 (downloaded from GitHub)This modular structure ensures loose coupling, clarity, and long-term maintainability, following cloud-native IaC standards.
This repository implements a clear separation between Continuous Integration (CI) and Continuous Deployment (CD) using GitHub Actions and protected environments.
┌────────────┐
│ Push / │
│ Pull Req │
└─────┬──────┘
│
▼
┌────────────┐
│ Validate │
│ (Bicep) │
│ │
│ - build │
│ - format │
│ - lint │
└─────┬──────┘
│
▼
┌────────────┐
│ What-If │
│ (Azure) │
│ │
│ - preview │
│ - non-blk │
└─────┬──────┘
│
│ (Manual trigger only)
▼
┌────────────┐
│ Deploy Dev │◄─────────────── Run workflow (deploy=true)
│ │
│ Env: dev │
│ Approval │
└────────────┘
-
CI runs automatically on
pushandpull_request -
CD is manual only via
workflow_dispatch -
Deployment requires:
- Explicit input:
deploy=true - Manual approval via GitHub Environment (
dev)
- Explicit input:
-
Azure What-If is informational and non-blocking, allowing flexibility in constrained subscriptions
This design mirrors real enterprise delivery pipelines, where CI is automated and CD is gated by human approval.
azure-iac/
│
├── main.bicep # Root orchestrator
├── main.parameters.json # Deployment parameters
├── bootstrap.bicep # Bootstrap resources (Key Vault, initial secrets)
├── bootstrap.parameters.json
│
├── modules/
│ ├── network.bicep # VNet + Subnet
│ ├── nsg.bicep # NSG
│ ├── publicip.bicep # Public IP
│ ├── nic.bicep # NIC
│ ├── storage.bicep # Storage Account
│ ├── keyvault.bicep # Key Vault
│ └── vm.bicep # Windows Server 2022 VM
│
├── scripts/
│ └── init.ps1 # Custom Script Extension
│
└── .github/
└── workflows/
└── ci.yml # Enterprise CI/CD pipelineInstead of passing the VM admin password directly to the VM module, this architecture follows Azure security best practices:
- Password is provided only once via parameters
- It is securely stored in Azure Key Vault
- The VM retrieves the password at deployment time using a Key Vault reference
- The password is never stored in templates, logs, or pipeline output
This pattern prevents credential leakage and aligns with enterprise security policies.
The VM includes a Custom Script Extension that downloads and runs a PowerShell script hosted in this repository:
fileUris: [
'https://raw.githubusercontent.com/brucie83/azure-iac/main/scripts/init.ps1'
]
commandToExecute: 'powershell.exe -ExecutionPolicy Unrestricted -File init.ps1'This script can be extended to:
- Install IIS or other roles
- Configure services
- Apply security baselines
- Register monitoring agents
- Perform post-provisioning automation
Before deploying, ensure you are logged into your Azure subscription:
az login
az account set --subscription "<SUBSCRIPTION-ID>"az group create \
--name rg-devops-lab \
--location eastusaz bicep generate-params -f main.bicep --out-file main.parameters.jsonaz deployment group create \
--resource-group rg-devops-lab \
--template-file main.bicep \
--parameters @main.parameters.jsonValidate and compile the Bicep templates:
az bicep build -f main.bicepPreview infrastructure changes without creating resources:
az deployment group what-if \
--resource-group rg-devops-lab \
--template-file main.bicep \
--parameters @main.parameters.jsonThe repository includes a multistage GitHub Actions pipeline designed to follow enterprise CI/CD practices.
Stage 1 – Validate (CI)
- Bicep formatting
- Bicep linting
- Template compilation
- No Azure authentication required
Stage 2 – Azure What-If (CI)
- Runs after validation
- Authenticates using a Service Principal
- Executes Azure What-If
- Does not create resources
- Non-blocking for environment or subscription constraints
Stage 3 – Deploy (CD)
- Triggered manually only
- Requires explicit input (
deploy=true) - Protected by GitHub Environment approval (
dev)
This project currently authenticates to Azure using a Service Principal stored as a GitHub Secret. This approach was chosen due to limited access to the Azure tenant (borrowed subscription), where identity configuration (App Registrations, Federated Credentials) is not available.
In a full enterprise Azure environment, this authentication model would be replaced with GitHub Actions OIDC (Workload Identity Federation) to eliminate long-lived credentials and enforce the principle of least privilege.
Planned enterprise improvements include:
- Replace static Service Principal secrets with OIDC-based authentication
- Scope permissions at Resource Group level instead of subscription-wide access
- Remove long-lived credentials from GitHub Secrets
- Use per-environment identities (dev / prod) for improved auditability
- Preserve the existing CI/CD flow while strengthening security posture
The current pipeline structure is intentionally designed to support this transition without requiring changes to the overall CI/CD workflow.
✔ Modular Bicep architecture ✔ Secure secret handling with Key Vault ✔ Separation of bootstrap and workload deployments ✔ Dependency chaining via module outputs ✔ Managed Identity usage ✔ Boot Diagnostics enabled ✔ Custom Script Extension automation ✔ Enterprise CI/CD with CI vs CD separation ✔ Manual deployment approval gates ✔ Environment-agnostic pipeline design
This project was validated using Azure What-If and ARM template validation.
When tested in personal or low-usage Azure subscriptions, VM deployment may fail due to SKU capacity restrictions in certain regions.
Key points:
- Infrastructure components deploy and validate correctly
- Templates compile successfully
- What-If completes without dependency errors
- VM creation may fail only due to unavailable compute SKUs
The pipeline and templates are designed to operate correctly in standard enterprise Azure environments where capacity is available.
Bruno Mijail Díaz Barba Cloud & DevOps Engineer — Azure | Bicep | Terraform | CI/CD GitHub: [https://github.com/brucie83]
⭐ Give this repo a star if it helped you