AnsibleUpdated July 3, 2026
Terraform Automation
terraformiacautomationazure-providerstate-managementmodulescicdepicazureinfrastructure
Terraform Automation
Overview
Terraform serves as our primary Infrastructure as Code (IaC) tool for provisioning and managing Azure resources in the Epic on Azure environment. This document covers Terraform automation strategies, best practices, and integration patterns specific to our Epic workloads.
Terraform in Epic on Azure
Why Terraform?
- Multi-Cloud Support: Future-proofs our infrastructure strategy
- State Management: Reliable tracking of resource lifecycles
- Plan/Apply Workflow: Safe, predictable infrastructure changes
- Rich Azure Provider: Comprehensive Azure resource coverage
- Module Ecosystem: Reusable infrastructure components
Epic-Specific Benefits
- Large Scale Management: Handles thousands of VMs and complex networking
- Environment Consistency: Identical infrastructure across dev/test/prod
- Compliance: Declarative policies and audit trails
- Integration: Seamless workflow with Ansible and CI/CD
Architecture
graph TB
A[Terraform Enterprise] --> B[Workspaces]
B --> C[Epic Production]
B --> D[Epic Non-Production]
B --> E[Epic Shared Services]
C --> F[Private Module Registry]
D --> F
E --> F
F --> G[VM Modules]
F --> H[Network Modules]
F --> I[Storage Modules]
F --> J[Security Modules]
C --> K[Azure Resources]
D --> K
E --> K
K --> L[Post-Provisioning]
L --> M[Ansible Configuration]
Terraform Enterprise Workspaces
Our Terraform infrastructure is organized into logical workspaces:
Production Workspaces
- epic-prod-east: East region production environment
- epic-prod-west: West region production environment
- epic-prod-shared: Shared services (networking, DNS, monitoring)
Non-Production Workspaces
- epic-dev-east: Development environment
- epic-test-east: Testing environment
- epic-staging-east: Staging environment
- epic-training-east: Training environment
Infrastructure Workspaces
- epic-networking: Core networking components
- epic-security: Security services and policies
- epic-monitoring: Monitoring and logging infrastructure
Key Patterns
1. Modular Infrastructure
module "epic_vm" {
source = "app.terraform.io/optum-epic/vm/azurerm"
version = "~> 2.0"
# Epic-specific configurations
epic_application = "hyperspace"
epic_environment = var.environment
epic_region = var.azure_region
# VM specifications
vm_count = var.hyperspace_vm_count
vm_size = var.hyperspace_vm_size
vm_image = var.epic_golden_image
# Networking
subnet_id = module.networking.epic_app_subnet_id
# Tags
tags = local.epic_tags
}
2. Environment-Specific Variables
# variables.tf
variable "environment" {
description = "Environment name (prod, test, dev)"
type = string
validation {
condition = contains(["prod", "test", "dev", "staging"], var.environment)
error_message = "Environment must be prod, test, dev, or staging."
}
}
variable "epic_vm_sizes" {
description = "VM sizes by environment"
type = map(object({
hyperspace = string
chronicles = string
clarity = string
}))
default = {
prod = {
hyperspace = "Standard_D8s_v3"
chronicles = "Standard_D16s_v3"
clarity = "Standard_D4s_v3"
}
test = {
hyperspace = "Standard_D4s_v3"
chronicles = "Standard_D8s_v3"
clarity = "Standard_D2s_v3"
}
}
}
3. Epic-Specific Resource Tagging
locals {
epic_tags = {
# Epic-specific tags
"epic-application" = var.epic_application
"epic-environment" = var.environment
"epic-stamp" = var.epic_stamp
"epic-tier" = var.epic_tier
# Compliance tags
"data-classification" = "PHI"
"backup-required" = "true"
"monitoring-level" = var.epic_monitoring_level
# Operational tags
"maintenance-window" = var.maintenance_window
"patch-schedule" = var.patch_schedule
"dr-tier" = var.dr_tier
# Azure governance tags
"cost-center" = var.cost_center
"technical-owner" = var.technical_owner
"business-owner" = var.business_owner
}
}
4. Networking Configuration
# Epic-specific networking patterns
resource "azurerm_virtual_network" "epic" {
name = "vnet-epic-${var.environment}-${var.region}"
address_space = [var.epic_vnet_cidr]
location = var.azure_region
resource_group_name = azurerm_resource_group.epic.name
tags = local.epic_tags
}
resource "azurerm_subnet" "epic_app" {
name = "subnet-epic-app-${var.environment}"
resource_group_name = azurerm_resource_group.epic.name
virtual_network_name = azurerm_virtual_network.epic.name
address_prefixes = [var.epic_app_subnet_cidr]
# Epic security requirements
enforce_private_link_endpoint_network_policies = true
enforce_private_link_service_network_policies = true
}
resource "azurerm_subnet" "epic_db" {
name = "subnet-epic-db-${var.environment}"
resource_group_name = azurerm_resource_group.epic.name
virtual_network_name = azurerm_virtual_network.epic.name
address_prefixes = [var.epic_db_subnet_cidr]
# Database subnet delegation
delegation {
name = "epic-db-delegation"
service_delegation {
name = "Microsoft.DBforMySQL/flexibleServers"
actions = ["Microsoft.Network/virtualNetworks/subnets/join/action"]
}
}
}
Private Module Registry
Our Epic-specific modules are stored in the Terraform Enterprise private registry:
Available Modules
vm-windows: Epic Windows VM with standard configurationvm-linux: Epic Linux VM with standard configurationnetworking: Epic networking patterns (VNets, subnets, NSGs)storage: Epic storage accounts and file sharesdatabase: Epic database configurations (SQL Server, MySQL)monitoring: Epic monitoring and alerting setupsecurity: Epic security configurations (Key Vault, policies)
Module Usage Example
module "epic_hyperspace_vms" {
source = "app.terraform.io/optum-epic/vm-windows/azurerm"
version = "~> 3.1"
# Epic application configuration
epic_application = "hyperspace"
epic_component = "app-server"
epic_environment = var.environment
# VM configuration
vm_count = var.hyperspace_vm_count
vm_size = local.epic_vm_sizes[var.environment].hyperspace
vm_admin_username = var.epic_admin_username
# Networking
subnet_id = data.azurerm_subnet.epic_app.id
# Storage
os_disk_size_gb = 128
data_disk_size_gb = 512
# Epic-specific settings
install_epic_agents = true
join_domain = true
domain_name = var.epic_domain_name
tags = local.epic_tags
}
Automation Workflows
1. CI/CD Integration
# .github/workflows/terraform.yml
name: Terraform Epic Infrastructure
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
terraform:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.5.0
cli_config_credentials_token: ${{ secrets.TF_API_TOKEN }}
- name: Terraform Format Check
run: terraform fmt -check -recursive
- name: Terraform Init
run: terraform init
- name: Terraform Validate
run: terraform validate
- name: Terraform Plan
run: terraform plan -out=tfplan
env:
TF_VAR_epic_environment: ${{ github.ref_name }}
- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: terraform apply tfplan
2. Epic-Specific Validation
# validation.tf
# Epic VM naming validation
resource "terraform_data" "epic_vm_naming_validation" {
for_each = var.epic_vms
lifecycle {
precondition {
condition = can(regex("^epic-[a-z]+-[a-z]+-[0-9]+$", each.value.name))
error_message = "Epic VM names must follow pattern: epic-{app}-{env}-{number}"
}
precondition {
condition = contains(["prod", "test", "dev", "staging"], each.value.environment)
error_message = "Epic VMs must have valid environment designation."
}
precondition {
condition = each.value.data_classification == "PHI"
error_message = "Epic VMs must be classified as PHI data."
}
}
}
# Epic compliance validation
check "epic_compliance" {
assert {
condition = alltrue([
for vm in var.epic_vms :
vm.backup_enabled == true
])
error_message = "All Epic VMs must have backup enabled."
}
assert {
condition = alltrue([
for vm in var.epic_vms :
vm.monitoring_enabled == true
])
error_message = "All Epic VMs must have monitoring enabled."
}
}
3. Post-Deployment Automation
# outputs.tf
output "epic_vm_details" {
description = "Epic VM information for Ansible inventory"
value = {
for vm in azurerm_windows_virtual_machine.epic :
vm.name => {
private_ip = vm.private_ip_address
public_ip = vm.public_ip_address
epic_app = vm.tags["epic-application"]
epic_env = vm.tags["epic-environment"]
epic_tier = vm.tags["epic-tier"]
ansible_groups = [
"epic_${vm.tags["epic-application"]}",
"epic_${vm.tags["epic-environment"]}",
"epic_windows"
]
}
}
sensitive = false
}
# Integration with Ansible
resource "local_file" "ansible_inventory" {
content = templatefile("${path.module}/templates/inventory.tpl", {
epic_vms = azurerm_windows_virtual_machine.epic
})
filename = "${path.module}/generated/epic-inventory.yml"
provisioner "local-exec" {
command = "ansible-playbook -i ${self.filename} epic-post-deploy.yml"
}
}
Best Practices
Security
- State Encryption: Terraform Enterprise handles state encryption
- Secret Management: Use Azure Key Vault data sources, never hardcode secrets
- RBAC: Implement workspace-level access controls
- Audit Logging: Enable comprehensive audit trails
Reliability
- State Locking: Terraform Enterprise provides automatic state locking
- Backup Strategy: Regular state backups and disaster recovery procedures
- Rollback Plans: Prepare rollback procedures for failed deployments
- Testing: Validate modules in non-production environments first
Performance
- Parallel Execution: Use
-parallelismflag for large deployments - State Optimization: Keep state files reasonably sized
- Resource Targeting: Use
-targetfor selective updates - Module Versioning: Pin module versions for consistency
Integration Points
With Ansible
# Trigger Ansible after VM creation
resource "null_resource" "epic_vm_configuration" {
depends_on = [azurerm_windows_virtual_machine.epic]
triggers = {
vm_ids = join(",", azurerm_windows_virtual_machine.epic[*].id)
}
provisioner "local-exec" {
command = <<EOF
ansible-playbook \
-i ${local_file.ansible_inventory.filename} \
--extra-vars "target_vms=epic_${var.epic_application}" \
epic-vm-setup.yml
EOF
}
}
With Azure DevOps
# Azure DevOps pipeline trigger
resource "azuredevops_build_definition" "epic_deployment" {
project_id = var.azdo_project_id
name = "Epic-${var.epic_application}-Deploy"
repository {
repo_type = "TfsGit"
repo_id = var.azdo_repo_id
branch_name = "main"
}
ci_trigger {
use_yaml = true
}
variable {
name = "TF_WORKSPACE"
value = terraform.workspace
}
}
With Monitoring
# Automatic monitoring setup
resource "azurerm_monitor_diagnostic_setting" "epic_vm" {
for_each = azurerm_windows_virtual_machine.epic
name = "epic-vm-diagnostics"
target_resource_id = each.value.id
log_analytics_workspace_id = data.azurerm_log_analytics_workspace.epic.id
enabled_log {
category = "Administrative"
}
enabled_log {
category = "Security"
}
metric {
category = "AllMetrics"
}
}
Troubleshooting
Common Issues
- State Lock Conflicts: Use
terraform force-unlockcarefully - Provider Version Conflicts: Pin provider versions
- Resource Dependencies: Use explicit
depends_onwhen needed - Azure Quota Limits: Monitor subscription quotas
Debugging Tips
- Verbose Logging: Set
TF_LOG=DEBUGfor detailed output - Plan Analysis: Always review plans before applying
- State Inspection: Use
terraform showandterraform state list - Import Existing Resources: Use
terraform importfor brownfield scenarios
Related Documentation
Support
- Terraform Enterprise: Contact platform team for workspace access
- Module Development: Contribute to private registry
- Training: Regular Terraform workshops and certification programs
- Community: Internal Terraform user groups and best practice sharing