Introduction to Infrastructure as Code

Infrastructure as Code (IaC) is the practice of managing and provisioning computing infrastructure through machine-readable definition files rather than physical hardware configuration or interactive configuration tools. In this post, we will combine Terraform for infrastructure provisioning and Ansible for post-deployment configuration.

This article explores the repository structure, architectural design, and how to leverage it for building enterprise-grade Azure environments with full automation.

Repository Overview

The iachubv2preprod repository (https://github.com/MourIdri/iachubv2preprod) provides a complete automation framework for deploying a Hub and Spoke network topology on Azure with integrated security, monitoring, and management capabilities.

Repository Composition:

  • terraform-iac: Infrastructure provisioning using Terraform
  • ansible-postconf: Post-deployment configuration using Ansible
  • hub_v1.jpg: Network topology visualization

Key Features

✓ Complete Hub and Spoke architecture automation ✓ Enterprise-grade security with NSGs and firewalls ✓ Native Azure VPN site-to-site connectivity ✓ Linux-based NVA for advanced routing (iptables) ✓ Nginx WAF for web protection ✓ Comprehensive logging and monitoring ✓ Azure DevOps pipeline integration ready ✓ Modular Terraform design for reusability

Architecture Overview

Hub and Spoke Topology

The topology implements a centralized hub infrastructure with spoke environments, providing:

  • Hub Infrastructure: Central management, routing, and security point
  • Security Layer: Network Virtual Appliance (NVA) with PFSense/Linux
  • Management Area: Windows Domain Controller + Admin workstations
  • Web Tier: WAF/Nginx for public-facing applications
  • VPN Gateway: Native Azure VPN for site-to-site connectivity
  • Logging & Monitoring: Centralized storage accounts and analytics

Part 1: Infrastructure as Code with Terraform

Terraform Project Structure

terraform-iac/
├── main.tf                    # Main module orchestration
├── variables.tf               # Variable definitions
├── variables.tfvars           # Variable values
├── provider.tf                # Azure provider config
├── output.tf                  # Output values
├── tags.json                  # Resource tagging
└── modules/
    ├── rg/                    # Resource Group
    ├── network/               # VNet and VNet config
    ├── logging/               # Storage and Analytics
    ├── generic-subnet-nsg/    # Subnet + NSG templates
    ├── vm-win-pub/            # Windows public VM
    ├── vm-win-pri/            # Windows private VM
    ├── vm-lnx-pri/            # Linux private VM
    ├── security-appliance-lnx/# NVA Linux appliance
    ├── waf-lnx/               # WAF/Nginx Linux
    └── vpn-onprem-cloud/      # VPN connectivity

Pre-Configuration: Terraform Modules

1. Resource Group Module

module "rg" {
  source = "./modules/rg"
  current-name-convention-core-module = var.current-name-convention-core-main
  preferred-location-module = var.preferred-location-main
  tags-rg-module = {
    environment = "production"
    scope_1 = "shared_infrastructure"
    scope_2 = "core_infrastructure"
    type_1 = "network_security"
    type_2 = "network"
  }
}

2. Network Module

Core virtual network with subnet configuration:

module "network" {
  source = "./modules/network"
  current-name-convention-core-public-module = var.current-name-convention-core-public-main
  current-name-convention-core-module = var.current-name-convention-core-main
  preferred-location-module = var.preferred-location-main
  ip-range-module = var.current-vnet-space  # e.g., 10.255.254.0/23
}

3. Logging & Monitoring Module

Centralized logging infrastructure:

module "logging" {
  source = "./modules/logging"
  current-name-convention-core-public-module = var.current-name-convention-core-public-main
  current-name-convention-core-module = var.current-name-convention-core-main
  preferred-location-module = var.preferred-location-main
  # Outputs: Storage account, Log Analytics workspace, Event Hub
}

Security Layer: Network Virtual Appliance

The NVA is a dual-NIC Linux VM acting as router and firewall:

module "security-appliance-dmz" {
  source = "./modules/security-appliance-lnx"
  subnet_in_id_module = module.subnet-nsg-privatedmzin.subnet-iac-id
  subnet_out_id_module = module.subnet-nsg-privatedmzoutlan.subnet-iac-id
  ip-in-nva-module = var.security-appliance-dmz-private-ip-address-in   # 10.255.255.36
  ip-out-nva-module = var.security-appliance-dmz-private-ip-address-out  # 10.255.255.52
  nva-size = var.vmsize_small_1_2  # Standard_F1s
  tags-security-appliance-dmz-module = {
    type_1 = "network_security"
    type_2 = "router"
  }
}

NVA Capabilities:

  • IP forwarding enabled on both NICs
  • iptables for stateful firewall rules
  • Route table manipulation for dynamic routing
  • NAT/Masquerading for outbound traffic
  • Connection logging to central storage

Management Tier

Windows Domain Controller for identity and DNS:

module "mt-area-mgmt" {
  source = "./modules/vm-win-pri"
  subnet_in_id_module = module.subnet-nsg-privatedmzoutlan.subnet-iac-id
  ip-in-dcaddns-module = var.mt-area-dc-dns-private-ip-address  # 10.255.255.53
  dcaddns-size = var.vmsize_small_1_2
  dcaddns-login = var.current-vm-default-username-main
  dcaddns-passwd = var.current-vm-default-pass-main
  tags = {
    type_1 = "domain_identity"
    type_2 = "ad_dc_dns"
  }
}

Plus admin management Linux VM for operations:

module "mtl-area-1" {
  source = "./modules/vm-lnx-pri"
  subnet_in_id_module = module.subnet-nsg-mt.subnet-iac-id
  ip-in-mtl-module = var.mtl-vm-private-ip-address  # 10.255.255.21
  tags = {
    type_2 = "adminlnx"
  }
}

Web Tier: WAF with Nginx

Web Application Firewall using Nginx:

module "waf-public-dmz" {
  source = "./modules/waf-lnx"
  subnet_in_id_module = module.subnet-nsg-publicdmzin.subnet-iac-id
  ip-in-waf-module = var.waf-vm-private-public-ip-address  # 10.255.254.68
  waf-size = var.vmsize_small_1_2
  tags = {
    type_2 = "waf"
  }
}

VPN Connectivity

Site-to-site VPN to on-premises:

module "vpn-standard-connect" {
  source = "./modules/vpn-onprem-cloud"
  iprange-onprem-module = [var.iprange-onprem-vpn]  # e.g., ["192.168.0.0/16"]
  ipaddress-routeur-onprem-1-module = var.ipaddress-routeur-onprem-1-azuredevops
  subnet-vpn-target-id-module = module.subnet-nsg-vpn.subnet-iac-id
  corph-s2s-connection-pass = var.current-vm-default-pass-main
}

Variables Configuration

Key variables from variables.tfvars:

# Network Configuration
current-vnet-space = "10.255.254.0/23"
preferred-location-main = "westeurope"
second-location-main = "northeurope"

# Naming Convention
current-name-convention-core-main = "crpc-prod-shar-hub"
current-name-convention-core-public-main = "crcprdshrhubstolog"

# Subnet Definitions
subnet-privatedmzin = "10.255.254.32/28"          # Private DMZ Inbound
subnet-privatedmzoutlan = "10.255.254.48/28"      # Private DMZ Outbound
subnet-mt = "10.255.255.16/28"                    # Management
subnet-publicdmzin = "10.255.255.64/28"           # Public DMZ
subnet-GatewaySubnet = "10.255.254.0/28"          # VPN Gateway

# VM Credentials
current-vm-default-username-main = "demouser"
current-vm-default-pass-main = "M@nP@ssw@rd!"

# VPN Configuration
iprange-onprem-vpn = "192.168.0.0/16"
ipaddress-routeur-onprem-1-azuredevops = "40.89.184.82"

# VM Sizes
vmsize_small_1_2 = "Standard_F1s"
vmsize_medium_2_4 = "Standard_F2s_v2"
vmsize_high_4_8 = "Standard_F4s_v2"

Part 2: Post-Configuration with Ansible

After Terraform provisions the infrastructure, Ansible handles configuration management and software deployment:

Ansible Post-Configuration Folders

ansible-postconf/
├── ansible/          # Core Ansible playbooks and roles
├── nginx/            # Nginx/WAF configuration playbooks
└── letsencrypt/      # SSL/TLS certificate automation

Post-Configuration Tasks

1. Ansible Core Configuration

Ansible playbooks handle:

  • OS hardening and patching
  • Domain join and Active Directory integration
  • SSH key distribution
  • Firewall rules configuration
  • Service initialization

2. Nginx WAF Configuration

Nginx post-configuration includes:

  • SSL/TLS termination
  • WAF rules and ModSecurity integration
  • Reverse proxy configuration
  • Load balancing across backend servers
  • DDoS protection rules
  • Access logging to central storage

3. Let's Encrypt Integration

Automated certificate management:

  • Certificate provisioning and renewal
  • DNS-01 challenge automation
  • Certificate rotation without downtime
  • Integration with Nginx for SSL/TLS

Part 3: Implementation Workflow

Deployment Process

# 1. Clone the repository
git clone https://github.com/MourIdri/iachubv2preprod.git
cd iachubv2preprod/terraform-iac

# 2. Initialize Terraform
terraform init -backend-config="key=hub.tfstate"

# 3. Plan infrastructure deployment
terraform plan -var-file="variables.tfvars" -out=plan.tfplan

# 4. Apply Terraform configuration
terraform apply plan.tfplan

# 5. Wait for VMs to be ready (~10-15 minutes)

# 6. Configure Ansible inventory with new VM IPs
# 7. Execute Ansible playbooks for post-configuration
cd ../ansible-postconf
ansible-playbook -i inventory.ini site.yml

# 8. Deploy Nginx configuration
ansible-playbook -i inventory.ini nginx/main.yml

# 9. Deploy SSL certificates
ansible-playbook -i inventory.ini letsencrypt/main.yml

Azure DevOps Pipeline Integration

This repository is designed to integrate with Azure DevOps:

trigger:
  - master

pool:
  vmImage: 'ubuntu-latest'

steps:
- task: TerraformInstaller@0
  inputs:
    terraformVersion: '0.13.0'

- task: TerraformTaskV1@0
  displayName: 'Terraform Init'
  inputs:
    provider: 'azurerm'
    command: 'init'

- task: TerraformTaskV1@0
  displayName: 'Terraform Plan'
  inputs:
    provider: 'azurerm'
    command: 'plan'

- task: TerraformTaskV1@0
  displayName: 'Terraform Apply'
  inputs:
    provider: 'azurerm'
    command: 'apply'

- task: AnsiblePlaybook@0
  displayName: 'Run Ansible Post-Configuration'
  inputs:
    ansibleVersion: 'latest'
    playbookPathOnAgentMachine: 'ansible-postconf/site.yml'

Key Design Decisions

1. Modular Architecture

Each component is isolated in its own Terraform module, enabling:

  • Code reusability across projects
  • Easy customization and extension
  • Clear separation of concerns
  • Simplified testing and debugging

2. Linux-Based NVA vs. Azure PaaS

The solution uses a Linux VM with iptables for routing instead of Azure's managed solutions:

Advantages:

  • Lower cost (Standard_F1s vs. managed appliances)
  • Full control over routing logic
  • Advanced firewall capabilities
  • Easier debugging with standard Linux tools

Trade-offs:

  • Requires operational expertise
  • Manual HA/DR configuration needed
  • Performance scaling requires VM sizing changes

3. Centralized Logging

All components stream logs to:

  • Storage Account: Long-term retention and archival
  • Log Analytics: Real-time analysis and alerting
  • Event Hub: Stream processing for security events

4. VPN Connectivity

Native Azure VPN Gateway provides:

  • Automated failover and redundancy
  • IPSec/IKEv2 encryption
  • Support for multiple on-premises sites
  • Integration with Azure Security Center

Deployment Considerations

Prerequisites

✓ Azure subscription with appropriate permissions ✓ Terraform installed (v0.12+) ✓ Azure CLI configured with credentials ✓ Ansible installed for post-configuration ✓ SSH keys configured for Linux VM access

Parameter Customization

Before deployment, customize:

  • Network address space and subnet ranges
  • VM sizes based on workload requirements
  • Naming conventions for your organization
  • VPN peer IP and address ranges
  • Administrator credentials (store securely in Azure KeyVault)

Security Best Practices

  • Store passwords in Azure KeyVault, not in files
  • Use managed identities for Azure resource access
  • Enable NSG flow logs for network monitoring
  • Implement Azure Policy for compliance
  • Use private endpoints for sensitive services
  • Enable Azure Defender for cloud security

Extending the Infrastructure

Adding Spoke Networks

To add additional spokes:

module "spoke-prod-vnet" {
  source = "./modules/network"
  ip-range-module = "10.1.0.0/16"
  # Additional configuration...
}

module "spoke-prod-peering" {
  source = "./modules/vnet-peering"
  hub_vnet_id = module.network.vnet_id
  spoke_vnet_id = module.spoke-prod-vnet.vnet_id
}

Adding Custom Applications

Post-deployment with Ansible:

- name: Deploy custom application
  hosts: app_servers
  roles:
    - common
    - monitoring
    - your-app-name

Conclusion

The iachubv2preprod repository demonstrates enterprise-grade Infrastructure as Code practices combining Terraform's declarative infrastructure provisioning with Ansible's configuration management capabilities. This complete automation framework enables rapid, repeatable, and reliable Azure deployments while maintaining security, monitoring, and operational best practices.

By leveraging this repository as a foundation, organizations can:

  • Reduce manual deployment errors
  • Accelerate infrastructure provisioning
  • Maintain consistent environments across regions
  • Enable disaster recovery and business continuity
  • Simplify infrastructure documentation and auditability
  • Integrate with CI/CD pipelines for automated deployments

Repository: https://github.com/MourIdri/iachubv2preprod License: MIT (or as specified by repository owner)


This Infrastructure as Code approach represents the future of cloud operations, where infrastructure is version-controlled, tested, and deployed like application code.