feat: comprehensive project structure improvements and Cloud for Sovereignty landing zone
- Add Cloud for Sovereignty landing zone architecture and deployment - Implement complete legal document management system - Reorganize documentation with improved navigation - Add infrastructure improvements (Dockerfiles, K8s, monitoring) - Add operational improvements (graceful shutdown, rate limiting, caching) - Create comprehensive project structure documentation - Add Azure deployment automation scripts - Improve repository navigation and organization
This commit is contained in:
101
infra/terraform/aks.tf
Normal file
101
infra/terraform/aks.tf
Normal file
@@ -0,0 +1,101 @@
|
||||
# Azure Kubernetes Service (AKS) Configuration
|
||||
|
||||
variable "aks_cluster_name" {
|
||||
description = "Name of the AKS cluster"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "aks_node_count" {
|
||||
description = "Number of nodes in the AKS cluster"
|
||||
type = number
|
||||
default = 2
|
||||
}
|
||||
|
||||
variable "aks_vm_size" {
|
||||
description = "VM size for AKS nodes"
|
||||
type = string
|
||||
default = "Standard_B2s"
|
||||
}
|
||||
|
||||
resource "azurerm_kubernetes_cluster" "main" {
|
||||
name = var.aks_cluster_name != "" ? var.aks_cluster_name : "the-order-aks-${var.environment}"
|
||||
location = var.azure_region
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
dns_prefix = "the-order-${var.environment}"
|
||||
kubernetes_version = "1.28" # Update to latest stable
|
||||
|
||||
# Use subscription_id from variable if provided
|
||||
# This ensures proper Azure authentication
|
||||
|
||||
default_node_pool {
|
||||
name = "default"
|
||||
node_count = var.aks_node_count
|
||||
vm_size = var.aks_vm_size
|
||||
type = "VirtualMachineScaleSets"
|
||||
enable_auto_scaling = var.environment != "dev"
|
||||
min_count = var.environment != "dev" ? 2 : null
|
||||
max_count = var.environment != "dev" ? 10 : null
|
||||
os_disk_size_gb = 30
|
||||
}
|
||||
|
||||
identity {
|
||||
type = "SystemAssigned"
|
||||
}
|
||||
|
||||
# Enable Azure RBAC
|
||||
azure_active_directory_role_based_access_control {
|
||||
managed = true
|
||||
azure_rbac_enabled = true
|
||||
admin_group_object_ids = [] # Add admin group IDs
|
||||
}
|
||||
|
||||
# Network profile
|
||||
network_profile {
|
||||
network_plugin = "azure"
|
||||
network_policy = "azure"
|
||||
load_balancer_sku = "standard"
|
||||
}
|
||||
|
||||
# Enable monitoring
|
||||
oms_agent {
|
||||
log_analytics_workspace_id = azurerm_log_analytics_workspace.main[0].id
|
||||
}
|
||||
|
||||
tags = var.tags
|
||||
}
|
||||
|
||||
# Log Analytics Workspace for AKS monitoring
|
||||
resource "azurerm_log_analytics_workspace" "main" {
|
||||
count = var.create_aks_cluster ? 1 : 0
|
||||
name = "the-order-logs-${var.environment}"
|
||||
location = var.azure_region
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
sku = "PerGB2018"
|
||||
retention_in_days = var.environment == "prod" ? 90 : 30
|
||||
|
||||
tags = var.tags
|
||||
}
|
||||
|
||||
# Output AKS details
|
||||
output "aks_cluster_name" {
|
||||
value = var.create_aks_cluster ? azurerm_kubernetes_cluster.main[0].name : null
|
||||
description = "Name of the AKS cluster"
|
||||
}
|
||||
|
||||
output "aks_fqdn" {
|
||||
value = var.create_aks_cluster ? azurerm_kubernetes_cluster.main[0].fqdn : null
|
||||
description = "FQDN of the AKS cluster"
|
||||
}
|
||||
|
||||
output "aks_kube_config" {
|
||||
value = var.create_aks_cluster ? azurerm_kubernetes_cluster.main[0].kube_config_raw : null
|
||||
description = "Raw Kubernetes config"
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "log_analytics_workspace_id" {
|
||||
value = azurerm_log_analytics_workspace.main[0].workspace_id
|
||||
description = "Log Analytics Workspace ID"
|
||||
}
|
||||
|
||||
49
infra/terraform/azure-provider.tf
Normal file
49
infra/terraform/azure-provider.tf
Normal file
@@ -0,0 +1,49 @@
|
||||
# Azure Provider Configuration
|
||||
# Uses environment variables or terraform.tfvars for authentication
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.5.0"
|
||||
|
||||
required_providers {
|
||||
azurerm = {
|
||||
source = "hashicorp/azurerm"
|
||||
version = "~> 3.0"
|
||||
}
|
||||
}
|
||||
|
||||
# Backend configuration (uncomment and configure for remote state)
|
||||
# backend "azurerm" {
|
||||
# resource_group_name = "the-order-tfstate-rg"
|
||||
# storage_account_name = "theordertfstate"
|
||||
# container_name = "tfstate"
|
||||
# key = "terraform.tfstate"
|
||||
# }
|
||||
}
|
||||
|
||||
# Configure the Azure Provider
|
||||
provider "azurerm" {
|
||||
features {
|
||||
resource_group {
|
||||
prevent_deletion_if_contains_resources = false
|
||||
}
|
||||
key_vault {
|
||||
purge_soft_delete_on_destroy = true
|
||||
}
|
||||
storage {
|
||||
purge_soft_delete_on_destroy = true
|
||||
}
|
||||
}
|
||||
|
||||
# Use environment variables or terraform.tfvars
|
||||
# subscription_id = var.subscription_id
|
||||
# tenant_id = var.tenant_id
|
||||
# client_id = var.client_id
|
||||
# client_secret = var.client_secret
|
||||
}
|
||||
|
||||
# Data source for current subscription
|
||||
data "azurerm_subscription" "current" {}
|
||||
|
||||
# Data source for current client config
|
||||
data "azurerm_client_config" "current" {}
|
||||
|
||||
@@ -26,7 +26,7 @@ resource "azurerm_storage_account" "cdn_images" {
|
||||
}
|
||||
}
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
tags = merge(var.tags, {
|
||||
Purpose = "CDNImages"
|
||||
})
|
||||
}
|
||||
@@ -40,19 +40,19 @@ resource "azurerm_storage_container" "cdn_images" {
|
||||
|
||||
# CDN Profile
|
||||
resource "azurerm_cdn_profile" "cdn_images" {
|
||||
name = "${local.project_prefix}-cdn-profile"
|
||||
name = var.cdn_profile_name != "" ? var.cdn_profile_name : "theorder-cdn-${var.environment}"
|
||||
location = var.azure_region
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
sku = "Standard_Microsoft"
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
tags = merge(var.tags, {
|
||||
Purpose = "CDNProfile"
|
||||
})
|
||||
}
|
||||
|
||||
# CDN Endpoint
|
||||
resource "azurerm_cdn_endpoint" "cdn_images" {
|
||||
name = "${local.project_prefix}-cdn-endpoint"
|
||||
name = var.cdn_endpoint_name != "" ? var.cdn_endpoint_name : "theorder-cdn-endpoint-${var.environment}"
|
||||
profile_name = azurerm_cdn_profile.cdn_images.name
|
||||
location = var.azure_region
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
@@ -74,7 +74,7 @@ resource "azurerm_cdn_endpoint" "cdn_images" {
|
||||
}
|
||||
}
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
tags = merge(var.tags, {
|
||||
Purpose = "CDNEndpoint"
|
||||
})
|
||||
}
|
||||
|
||||
119
infra/terraform/database.tf
Normal file
119
infra/terraform/database.tf
Normal file
@@ -0,0 +1,119 @@
|
||||
# Azure Database for PostgreSQL
|
||||
# Flexible Server for production workloads
|
||||
|
||||
variable "database_name" {
|
||||
description = "PostgreSQL database name"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "database_admin_user" {
|
||||
description = "PostgreSQL admin username"
|
||||
type = string
|
||||
default = "theorder_admin"
|
||||
}
|
||||
|
||||
variable "database_sku_name" {
|
||||
description = "PostgreSQL SKU (e.g., Standard_B1ms, Standard_B2s)"
|
||||
type = string
|
||||
default = "Standard_B1ms"
|
||||
}
|
||||
|
||||
variable "database_storage_mb" {
|
||||
description = "PostgreSQL storage in MB"
|
||||
type = number
|
||||
default = 32768 # 32 GB
|
||||
}
|
||||
|
||||
resource "azurerm_postgresql_flexible_server" "main" {
|
||||
name = var.database_name != "" ? var.database_name : "the-order-db-${var.environment}"
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
location = var.azure_region
|
||||
version = "15"
|
||||
delegated_subnet_id = null # Set if using VNet integration
|
||||
private_dns_zone_id = null # Set if using private DNS
|
||||
administrator_login = var.database_admin_user
|
||||
administrator_password = null # Set via Key Vault secret
|
||||
zone = "1"
|
||||
|
||||
storage_mb = var.database_storage_mb
|
||||
sku_name = var.database_sku_name
|
||||
|
||||
backup {
|
||||
geo_redundant_backup_enabled = var.environment == "prod"
|
||||
backup_retention_days = var.environment == "prod" ? 35 : 7
|
||||
}
|
||||
|
||||
high_availability {
|
||||
mode = var.environment == "prod" ? "ZoneRedundant" : "Disabled"
|
||||
standby_availability_zone = var.environment == "prod" ? "2" : null
|
||||
}
|
||||
|
||||
maintenance_window {
|
||||
day_of_week = 0 # Sunday
|
||||
start_hour = 2
|
||||
start_minute = 0
|
||||
}
|
||||
|
||||
tags = merge(var.tags, {
|
||||
Purpose = "Database"
|
||||
})
|
||||
}
|
||||
|
||||
# Database
|
||||
resource "azurerm_postgresql_flexible_server_database" "main" {
|
||||
count = var.create_database ? 1 : 0
|
||||
name = "theorder_${var.environment}"
|
||||
server_id = azurerm_postgresql_flexible_server.main[0].id
|
||||
charset = "UTF8"
|
||||
collation = "en_US.utf8"
|
||||
}
|
||||
|
||||
# Firewall rules - allow Azure services
|
||||
resource "azurerm_postgresql_flexible_server_firewall_rule" "azure_services" {
|
||||
count = var.create_database ? 1 : 0
|
||||
name = "AllowAzureServices"
|
||||
server_id = azurerm_postgresql_flexible_server.main[0].id
|
||||
start_ip_address = "0.0.0.0"
|
||||
end_ip_address = "0.0.0.0"
|
||||
}
|
||||
|
||||
# Generate random password for database
|
||||
resource "random_password" "database_password" {
|
||||
count = var.create_database ? 1 : 0
|
||||
length = 32
|
||||
special = true
|
||||
}
|
||||
|
||||
# Store database connection string in Key Vault
|
||||
resource "azurerm_key_vault_secret" "database_url" {
|
||||
count = var.create_database ? 1 : 0
|
||||
name = "database-url"
|
||||
value = "postgresql://${var.database_admin_user}:${random_password.database_password[0].result}@${azurerm_postgresql_flexible_server.main[0].fqdn}:5432/${azurerm_postgresql_flexible_server_database.main[0].name}?sslmode=require"
|
||||
key_vault_id = azurerm_key_vault.main.id
|
||||
|
||||
tags = var.tags
|
||||
}
|
||||
|
||||
# Store password in Key Vault
|
||||
resource "azurerm_key_vault_secret" "database_password" {
|
||||
count = var.create_database ? 1 : 0
|
||||
name = "database-password"
|
||||
value = random_password.database_password[0].result
|
||||
key_vault_id = azurerm_key_vault.main.id
|
||||
|
||||
tags = var.tags
|
||||
}
|
||||
|
||||
# Outputs
|
||||
output "database_fqdn" {
|
||||
value = var.create_database ? azurerm_postgresql_flexible_server.main[0].fqdn : null
|
||||
description = "Fully qualified domain name of the database server"
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "database_name" {
|
||||
value = var.create_database ? azurerm_postgresql_flexible_server_database.main[0].name : null
|
||||
description = "Name of the database"
|
||||
}
|
||||
|
||||
57
infra/terraform/key-vault.tf
Normal file
57
infra/terraform/key-vault.tf
Normal file
@@ -0,0 +1,57 @@
|
||||
# Azure Key Vault for secrets management
|
||||
|
||||
resource "azurerm_key_vault" "main" {
|
||||
name = var.key_vault_name != "" ? var.key_vault_name : "the-order-kv-${var.environment}"
|
||||
location = var.azure_region
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
tenant_id = var.tenant_id != "" ? var.tenant_id : data.azurerm_client_config.current.tenant_id
|
||||
|
||||
sku_name = "standard"
|
||||
|
||||
# Network ACLs
|
||||
network_acls {
|
||||
default_action = "Deny"
|
||||
bypass = "AzureServices"
|
||||
ip_rules = [] # Add allowed IPs for access
|
||||
}
|
||||
|
||||
# Enable soft delete and purge protection
|
||||
soft_delete_retention_days = 7
|
||||
purge_protection_enabled = var.environment == "prod"
|
||||
|
||||
tags = merge(var.tags, {
|
||||
Purpose = "SecretsManagement"
|
||||
})
|
||||
}
|
||||
|
||||
# Grant current user/service principal access
|
||||
resource "azurerm_key_vault_access_policy" "current_user" {
|
||||
key_vault_id = azurerm_key_vault.main.id
|
||||
tenant_id = data.azurerm_client_config.current.tenant_id
|
||||
object_id = data.azurerm_client_config.current.object_id
|
||||
|
||||
key_permissions = [
|
||||
"Get", "List", "Create", "Delete", "Update", "Import", "Backup", "Restore"
|
||||
]
|
||||
|
||||
secret_permissions = [
|
||||
"Get", "List", "Set", "Delete", "Recover", "Backup", "Restore", "Purge"
|
||||
]
|
||||
|
||||
certificate_permissions = [
|
||||
"Get", "List", "Create", "Delete", "Update", "Import", "Backup", "Restore"
|
||||
]
|
||||
}
|
||||
|
||||
# Output Key Vault details
|
||||
output "key_vault_name" {
|
||||
value = azurerm_key_vault.main.name
|
||||
description = "Name of the Key Vault"
|
||||
}
|
||||
|
||||
output "key_vault_uri" {
|
||||
value = azurerm_key_vault.main.vault_uri
|
||||
description = "URI of the Key Vault"
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
137
infra/terraform/management-groups/main.tf
Normal file
137
infra/terraform/management-groups/main.tf
Normal file
@@ -0,0 +1,137 @@
|
||||
# Management Group Hierarchy for Cloud for Sovereignty
|
||||
# Root: SOVEREIGN-ORDER-OF-HOSPITALLERS
|
||||
|
||||
variable "management_group_id" {
|
||||
description = "Root management group ID"
|
||||
type = string
|
||||
default = "SOVEREIGN-ORDER-OF-HOSPITALLERS"
|
||||
}
|
||||
|
||||
# Configure Azure Provider
|
||||
provider "azurerm" {
|
||||
features {}
|
||||
}
|
||||
|
||||
# Data source for existing root management group
|
||||
data "azurerm_management_group" "root" {
|
||||
name = var.management_group_id
|
||||
}
|
||||
|
||||
# Landing Zones Management Group
|
||||
resource "azurerm_management_group" "landing_zones" {
|
||||
name = "LandingZones"
|
||||
display_name = "Landing Zones"
|
||||
parent_management_group_id = data.azurerm_management_group.root.id
|
||||
|
||||
subscription_ids = []
|
||||
}
|
||||
|
||||
# Platform Landing Zone
|
||||
resource "azurerm_management_group" "platform" {
|
||||
name = "Platform"
|
||||
display_name = "Platform Landing Zone"
|
||||
parent_management_group_id = azurerm_management_group.landing_zones.id
|
||||
|
||||
subscription_ids = []
|
||||
}
|
||||
|
||||
# Sandbox Landing Zone
|
||||
resource "azurerm_management_group" "sandbox" {
|
||||
name = "Sandbox"
|
||||
display_name = "Sandbox Landing Zone"
|
||||
parent_management_group_id = azurerm_management_group.landing_zones.id
|
||||
|
||||
subscription_ids = []
|
||||
}
|
||||
|
||||
# Workloads Landing Zone
|
||||
resource "azurerm_management_group" "workloads" {
|
||||
name = "Workloads"
|
||||
display_name = "Workload Workloads"
|
||||
parent_management_group_id = azurerm_management_group.landing_zones.id
|
||||
|
||||
subscription_ids = []
|
||||
}
|
||||
|
||||
# Management Management Group
|
||||
resource "azurerm_management_group" "management" {
|
||||
name = "Management"
|
||||
display_name = "Management"
|
||||
parent_management_group_id = data.azurerm_management_group.root.id
|
||||
|
||||
subscription_ids = []
|
||||
}
|
||||
|
||||
# Identity Management Group
|
||||
resource "azurerm_management_group" "identity" {
|
||||
name = "Identity"
|
||||
display_name = "Identity and Access Management"
|
||||
parent_management_group_id = azurerm_management_group.management.id
|
||||
|
||||
subscription_ids = []
|
||||
}
|
||||
|
||||
# Security Management Group
|
||||
resource "azurerm_management_group" "security" {
|
||||
name = "Security"
|
||||
display_name = "Security Operations"
|
||||
parent_management_group_id = azurerm_management_group.management.id
|
||||
|
||||
subscription_ids = []
|
||||
}
|
||||
|
||||
# Monitoring Management Group
|
||||
resource "azurerm_management_group" "monitoring" {
|
||||
name = "Monitoring"
|
||||
display_name = "Centralized Monitoring"
|
||||
parent_management_group_id = azurerm_management_group.management.id
|
||||
|
||||
subscription_ids = []
|
||||
}
|
||||
|
||||
# Connectivity Management Group
|
||||
resource "azurerm_management_group" "connectivity" {
|
||||
name = "Connectivity"
|
||||
display_name = "Connectivity"
|
||||
parent_management_group_id = data.azurerm_management_group.root.id
|
||||
|
||||
subscription_ids = []
|
||||
}
|
||||
|
||||
# Hub Networks Management Group
|
||||
resource "azurerm_management_group" "hub_networks" {
|
||||
name = "HubNetworks"
|
||||
display_name = "Hub Networks"
|
||||
parent_management_group_id = azurerm_management_group.connectivity.id
|
||||
|
||||
subscription_ids = []
|
||||
}
|
||||
|
||||
# Spoke Networks Management Group
|
||||
resource "azurerm_management_group" "spoke_networks" {
|
||||
name = "SpokeNetworks"
|
||||
display_name = "Spoke Networks"
|
||||
parent_management_group_id = azurerm_management_group.connectivity.id
|
||||
|
||||
subscription_ids = []
|
||||
}
|
||||
|
||||
# Outputs
|
||||
output "management_group_hierarchy" {
|
||||
description = "Management group hierarchy"
|
||||
value = {
|
||||
root = data.azurerm_management_group.root.id
|
||||
landing_zones = azurerm_management_group.landing_zones.id
|
||||
platform = azurerm_management_group.platform.id
|
||||
sandbox = azurerm_management_group.sandbox.id
|
||||
workloads = azurerm_management_group.workloads.id
|
||||
management = azurerm_management_group.management.id
|
||||
identity = azurerm_management_group.identity.id
|
||||
security = azurerm_management_group.security.id
|
||||
monitoring = azurerm_management_group.monitoring.id
|
||||
connectivity = azurerm_management_group.connectivity.id
|
||||
hub_networks = azurerm_management_group.hub_networks.id
|
||||
spoke_networks = azurerm_management_group.spoke_networks.id
|
||||
}
|
||||
}
|
||||
|
||||
8
infra/terraform/management-groups/variables.tf
Normal file
8
infra/terraform/management-groups/variables.tf
Normal file
@@ -0,0 +1,8 @@
|
||||
# Variables for Management Groups
|
||||
|
||||
variable "management_group_id" {
|
||||
description = "Root management group ID"
|
||||
type = string
|
||||
default = "SOVEREIGN-ORDER-OF-HOSPITALLERS"
|
||||
}
|
||||
|
||||
13
infra/terraform/management-groups/versions.tf
Normal file
13
infra/terraform/management-groups/versions.tf
Normal file
@@ -0,0 +1,13 @@
|
||||
# Terraform and Provider Version Constraints for Management Groups
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.5.0"
|
||||
|
||||
required_providers {
|
||||
azurerm = {
|
||||
source = "hashicorp/azurerm"
|
||||
version = "~> 3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
64
infra/terraform/modules/regional-landing-zone/README.md
Normal file
64
infra/terraform/modules/regional-landing-zone/README.md
Normal file
@@ -0,0 +1,64 @@
|
||||
# Regional Landing Zone Module
|
||||
|
||||
Reusable Terraform module for deploying a complete landing zone in a single Azure region, following Cloud for Sovereignty and Well-Architected Framework principles.
|
||||
|
||||
## Features
|
||||
|
||||
- **Hub-and-Spoke Network Architecture**
|
||||
- Hub VNet with gateway, firewall, and management subnets
|
||||
- Spoke VNet with application, database, and storage subnets
|
||||
- VNet peering between hub and spoke
|
||||
|
||||
- **Security**
|
||||
- Azure Firewall for centralized security
|
||||
- Private endpoints for Key Vault and Storage
|
||||
- Network security groups
|
||||
|
||||
- **Compliance**
|
||||
- Customer-managed encryption
|
||||
- Data residency tags
|
||||
- Private endpoints for data sovereignty
|
||||
|
||||
- **Monitoring**
|
||||
- Regional Log Analytics Workspace
|
||||
- Application Insights ready
|
||||
|
||||
## Usage
|
||||
|
||||
```hcl
|
||||
module "west_europe_landing_zone" {
|
||||
source = "../../modules/regional-landing-zone"
|
||||
|
||||
region = "westeurope"
|
||||
environment = "dev"
|
||||
management_group_id = "SOVEREIGN-ORDER-OF-HOSPITALLERS"
|
||||
hub_vnet_address_space = "10.0.0.0/16"
|
||||
spoke_vnet_address_space = "10.1.0.0/16"
|
||||
|
||||
tags = {
|
||||
Project = "the-order"
|
||||
CostCenter = "engineering"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Variables
|
||||
|
||||
- `region` (required): Azure region (must be non-US commercial)
|
||||
- `environment` (required): dev, stage, or prod
|
||||
- `management_group_id` (required): Management group ID
|
||||
- `hub_vnet_address_space` (optional): Hub VNet CIDR (default: 10.0.0.0/16)
|
||||
- `spoke_vnet_address_space` (optional): Spoke VNet CIDR (default: 10.1.0.0/16)
|
||||
- `tags` (optional): Additional tags
|
||||
|
||||
## Outputs
|
||||
|
||||
- `resource_group_name`: Resource group name
|
||||
- `hub_vnet_id`: Hub VNet ID
|
||||
- `spoke_vnet_id`: Spoke VNet ID
|
||||
- `firewall_id`: Azure Firewall ID
|
||||
- `key_vault_id`: Key Vault ID
|
||||
- `log_analytics_workspace_id`: Log Analytics Workspace ID
|
||||
- `storage_account_name`: Storage account name
|
||||
- `subnet_ids`: Map of subnet names to IDs
|
||||
|
||||
342
infra/terraform/modules/regional-landing-zone/main.tf
Normal file
342
infra/terraform/modules/regional-landing-zone/main.tf
Normal file
@@ -0,0 +1,342 @@
|
||||
# Regional Landing Zone Module
|
||||
# Deploys a complete landing zone for a single Azure region
|
||||
# Follows Cloud for Sovereignty and Well-Architected Framework principles
|
||||
|
||||
variable "region" {
|
||||
description = "Azure region (e.g., westeurope, northeurope)"
|
||||
type = string
|
||||
validation {
|
||||
condition = contains([
|
||||
"westeurope",
|
||||
"northeurope",
|
||||
"uksouth",
|
||||
"switzerlandnorth",
|
||||
"norwayeast",
|
||||
"francecentral",
|
||||
"germanywestcentral"
|
||||
], var.region)
|
||||
error_message = "Region must be a non-US commercial Azure region."
|
||||
}
|
||||
}
|
||||
|
||||
variable "environment" {
|
||||
description = "Environment name (dev, stage, prod)"
|
||||
type = string
|
||||
validation {
|
||||
condition = contains(["dev", "stage", "prod"], var.environment)
|
||||
error_message = "Environment must be dev, stage, or prod."
|
||||
}
|
||||
}
|
||||
|
||||
variable "management_group_id" {
|
||||
description = "Management group ID for this landing zone"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "hub_vnet_address_space" {
|
||||
description = "Address space for hub VNet"
|
||||
type = string
|
||||
default = "10.0.0.0/16"
|
||||
}
|
||||
|
||||
variable "spoke_vnet_address_space" {
|
||||
description = "Address space for spoke VNet"
|
||||
type = string
|
||||
default = "10.1.0.0/16"
|
||||
}
|
||||
|
||||
variable "tags" {
|
||||
description = "Tags to apply to all resources"
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
# Local values for naming
|
||||
locals {
|
||||
region_abbrev = {
|
||||
westeurope = "we"
|
||||
northeurope = "ne"
|
||||
uksouth = "uk"
|
||||
switzerlandnorth = "ch"
|
||||
norwayeast = "no"
|
||||
francecentral = "fr"
|
||||
germanywestcentral = "de"
|
||||
}
|
||||
|
||||
env_abbrev = {
|
||||
dev = "dev"
|
||||
stage = "stg"
|
||||
prod = "prd"
|
||||
}
|
||||
|
||||
region_short = lookup(local.region_abbrev, var.region, "we")
|
||||
env_short = lookup(local.env_abbrev, var.environment, "dev")
|
||||
name_prefix = "az-${local.region_short}"
|
||||
|
||||
common_tags = merge(var.tags, {
|
||||
Region = var.region
|
||||
Environment = var.environment
|
||||
DataResidency = var.region
|
||||
ManagedBy = "terraform"
|
||||
LandingZone = "regional"
|
||||
SovereigntyLevel = "high"
|
||||
})
|
||||
}
|
||||
|
||||
# Resource Group
|
||||
resource "azurerm_resource_group" "landing_zone" {
|
||||
name = "${local.name_prefix}-rg-${local.env_short}-lz"
|
||||
location = var.region
|
||||
|
||||
tags = local.common_tags
|
||||
}
|
||||
|
||||
# Hub Virtual Network
|
||||
resource "azurerm_virtual_network" "hub" {
|
||||
name = "${local.name_prefix}-vnet-${local.env_short}-hub"
|
||||
address_space = [var.hub_vnet_address_space]
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
Purpose = "HubNetwork"
|
||||
})
|
||||
}
|
||||
|
||||
# Hub Subnets
|
||||
resource "azurerm_subnet" "hub_gateway" {
|
||||
name = "GatewaySubnet"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.hub.name
|
||||
address_prefixes = [cidrsubnet(var.hub_vnet_address_space, 8, 0)]
|
||||
}
|
||||
|
||||
resource "azurerm_subnet" "hub_firewall" {
|
||||
name = "AzureFirewallSubnet"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.hub.name
|
||||
address_prefixes = [cidrsubnet(var.hub_vnet_address_space, 8, 1)]
|
||||
}
|
||||
|
||||
resource "azurerm_subnet" "hub_management" {
|
||||
name = "ManagementSubnet"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.hub.name
|
||||
address_prefixes = [cidrsubnet(var.hub_vnet_address_space, 8, 2)]
|
||||
}
|
||||
|
||||
# Azure Firewall
|
||||
resource "azurerm_public_ip" "firewall" {
|
||||
name = "${local.name_prefix}-pip-${local.env_short}-fw"
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
allocation_method = "Static"
|
||||
sku = "Standard"
|
||||
|
||||
tags = local.common_tags
|
||||
}
|
||||
|
||||
resource "azurerm_firewall" "hub" {
|
||||
name = "${local.name_prefix}-fw-${local.env_short}-hub"
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
sku_name = "AZFW_VNet"
|
||||
sku_tier = "Standard"
|
||||
|
||||
ip_configuration {
|
||||
name = "configuration"
|
||||
subnet_id = azurerm_subnet.hub_firewall.id
|
||||
public_ip_address_id = azurerm_public_ip.firewall.id
|
||||
}
|
||||
|
||||
tags = local.common_tags
|
||||
}
|
||||
|
||||
# Spoke Virtual Network
|
||||
resource "azurerm_virtual_network" "spoke" {
|
||||
name = "${local.name_prefix}-vnet-${local.env_short}-spoke"
|
||||
address_space = [var.spoke_vnet_address_space]
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
Purpose = "SpokeNetwork"
|
||||
})
|
||||
}
|
||||
|
||||
# Spoke Subnets
|
||||
resource "azurerm_subnet" "spoke_app" {
|
||||
name = "ApplicationSubnet"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.spoke.name
|
||||
address_prefixes = [cidrsubnet(var.spoke_vnet_address_space, 8, 0)]
|
||||
}
|
||||
|
||||
resource "azurerm_subnet" "spoke_db" {
|
||||
name = "DatabaseSubnet"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.spoke.name
|
||||
address_prefixes = [cidrsubnet(var.spoke_vnet_address_space, 8, 1)]
|
||||
|
||||
delegation {
|
||||
name = "postgresql-delegation"
|
||||
service_delegation {
|
||||
name = "Microsoft.DBforPostgreSQL/flexibleServers"
|
||||
actions = [
|
||||
"Microsoft.Network/virtualNetworks/subnets/join/action",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "azurerm_subnet" "spoke_storage" {
|
||||
name = "StorageSubnet"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.spoke.name
|
||||
address_prefixes = [cidrsubnet(var.spoke_vnet_address_space, 8, 2)]
|
||||
}
|
||||
|
||||
# VNet Peering: Hub to Spoke
|
||||
resource "azurerm_virtual_network_peering" "hub_to_spoke" {
|
||||
name = "hub-to-spoke"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.hub.name
|
||||
remote_virtual_network_id = azurerm_virtual_network.spoke.id
|
||||
allow_forwarded_traffic = true
|
||||
allow_gateway_transit = true
|
||||
}
|
||||
|
||||
resource "azurerm_virtual_network_peering" "spoke_to_hub" {
|
||||
name = "spoke-to-hub"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
virtual_network_name = azurerm_virtual_network.spoke.name
|
||||
remote_virtual_network_id = azurerm_virtual_network.hub.id
|
||||
allow_forwarded_traffic = true
|
||||
use_remote_gateways = false
|
||||
}
|
||||
|
||||
# Data source for current client config
|
||||
data "azurerm_client_config" "current" {}
|
||||
|
||||
# Key Vault (Regional)
|
||||
resource "azurerm_key_vault" "regional" {
|
||||
name = "${local.name_prefix}-kv-${local.env_short}-${local.region_short}"
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
tenant_id = data.azurerm_client_config.current.tenant_id
|
||||
sku_name = "premium"
|
||||
|
||||
# Network ACLs - Private endpoint only
|
||||
network_acls {
|
||||
default_action = "Deny"
|
||||
bypass = "AzureServices"
|
||||
}
|
||||
|
||||
# Enable soft delete and purge protection
|
||||
soft_delete_retention_days = 90
|
||||
purge_protection_enabled = var.environment == "prod"
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
Purpose = "RegionalSecrets"
|
||||
})
|
||||
}
|
||||
|
||||
# Private Endpoint for Key Vault
|
||||
resource "azurerm_private_endpoint" "key_vault" {
|
||||
name = "${local.name_prefix}-pe-${local.env_short}-kv"
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
subnet_id = azurerm_subnet.hub_management.id
|
||||
|
||||
private_service_connection {
|
||||
name = "kv-connection"
|
||||
private_connection_resource_id = azurerm_key_vault.regional.id
|
||||
subresource_names = ["vault"]
|
||||
is_manual_connection = false
|
||||
}
|
||||
|
||||
tags = local.common_tags
|
||||
}
|
||||
|
||||
# Log Analytics Workspace (Regional)
|
||||
resource "azurerm_log_analytics_workspace" "regional" {
|
||||
name = "${local.name_prefix}-law-${local.env_short}-${local.region_short}"
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
sku = "PerGB2018"
|
||||
retention_in_days = var.environment == "prod" ? 90 : 30
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
Purpose = "RegionalLogging"
|
||||
})
|
||||
}
|
||||
|
||||
# Storage Account (Regional)
|
||||
resource "azurerm_storage_account" "regional" {
|
||||
name = "${local.name_prefix}sa${local.env_short}${local.region_short}"
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
location = var.region
|
||||
account_tier = "Standard"
|
||||
account_replication_type = var.environment == "prod" ? "GRS" : "LRS"
|
||||
min_tls_version = "TLS1_2"
|
||||
allow_blob_public_access = false
|
||||
|
||||
# Customer-managed encryption
|
||||
identity {
|
||||
type = "SystemAssigned"
|
||||
}
|
||||
|
||||
blob_properties {
|
||||
versioning_enabled = true
|
||||
delete_retention_policy {
|
||||
days = var.environment == "prod" ? 90 : 30
|
||||
}
|
||||
}
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
Purpose = "RegionalStorage"
|
||||
})
|
||||
}
|
||||
|
||||
# Private Endpoint for Storage Account
|
||||
resource "azurerm_private_endpoint" "storage" {
|
||||
name = "${local.name_prefix}-pe-${local.env_short}-st"
|
||||
location = var.region
|
||||
resource_group_name = azurerm_resource_group.landing_zone.name
|
||||
subnet_id = azurerm_subnet.spoke_storage.id
|
||||
|
||||
private_service_connection {
|
||||
name = "storage-connection"
|
||||
private_connection_resource_id = azurerm_storage_account.regional.id
|
||||
subresource_names = ["blob"]
|
||||
is_manual_connection = false
|
||||
}
|
||||
|
||||
tags = local.common_tags
|
||||
}
|
||||
|
||||
# Outputs
|
||||
output "resource_group_name" {
|
||||
value = azurerm_resource_group.landing_zone.name
|
||||
}
|
||||
|
||||
output "hub_vnet_id" {
|
||||
value = azurerm_virtual_network.hub.id
|
||||
}
|
||||
|
||||
output "spoke_vnet_id" {
|
||||
value = azurerm_virtual_network.spoke.id
|
||||
}
|
||||
|
||||
output "key_vault_id" {
|
||||
value = azurerm_key_vault.regional.id
|
||||
}
|
||||
|
||||
output "log_analytics_workspace_id" {
|
||||
value = azurerm_log_analytics_workspace.regional.workspace_id
|
||||
}
|
||||
|
||||
output "storage_account_name" {
|
||||
value = azurerm_storage_account.regional.name
|
||||
}
|
||||
|
||||
94
infra/terraform/modules/regional-landing-zone/outputs.tf
Normal file
94
infra/terraform/modules/regional-landing-zone/outputs.tf
Normal file
@@ -0,0 +1,94 @@
|
||||
# Outputs for Regional Landing Zone Module
|
||||
|
||||
output "resource_group_name" {
|
||||
description = "Name of the resource group"
|
||||
value = azurerm_resource_group.landing_zone.name
|
||||
}
|
||||
|
||||
output "resource_group_id" {
|
||||
description = "ID of the resource group"
|
||||
value = azurerm_resource_group.landing_zone.id
|
||||
}
|
||||
|
||||
output "hub_vnet_id" {
|
||||
description = "ID of the hub virtual network"
|
||||
value = azurerm_virtual_network.hub.id
|
||||
}
|
||||
|
||||
output "hub_vnet_name" {
|
||||
description = "Name of the hub virtual network"
|
||||
value = azurerm_virtual_network.hub.name
|
||||
}
|
||||
|
||||
output "spoke_vnet_id" {
|
||||
description = "ID of the spoke virtual network"
|
||||
value = azurerm_virtual_network.spoke.id
|
||||
}
|
||||
|
||||
output "spoke_vnet_name" {
|
||||
description = "Name of the spoke virtual network"
|
||||
value = azurerm_virtual_network.spoke.name
|
||||
}
|
||||
|
||||
output "firewall_id" {
|
||||
description = "ID of the Azure Firewall"
|
||||
value = azurerm_firewall.hub.id
|
||||
}
|
||||
|
||||
output "firewall_private_ip" {
|
||||
description = "Private IP address of the Azure Firewall"
|
||||
value = azurerm_firewall.hub.ip_configuration[0].private_ip_address
|
||||
}
|
||||
|
||||
output "key_vault_id" {
|
||||
description = "ID of the Key Vault"
|
||||
value = azurerm_key_vault.regional.id
|
||||
}
|
||||
|
||||
output "key_vault_name" {
|
||||
description = "Name of the Key Vault"
|
||||
value = azurerm_key_vault.regional.name
|
||||
}
|
||||
|
||||
output "key_vault_uri" {
|
||||
description = "URI of the Key Vault"
|
||||
value = azurerm_key_vault.regional.vault_uri
|
||||
}
|
||||
|
||||
output "log_analytics_workspace_id" {
|
||||
description = "ID of the Log Analytics Workspace"
|
||||
value = azurerm_log_analytics_workspace.regional.workspace_id
|
||||
}
|
||||
|
||||
output "log_analytics_workspace_name" {
|
||||
description = "Name of the Log Analytics Workspace"
|
||||
value = azurerm_log_analytics_workspace.regional.name
|
||||
}
|
||||
|
||||
output "storage_account_name" {
|
||||
description = "Name of the storage account"
|
||||
value = azurerm_storage_account.regional.name
|
||||
}
|
||||
|
||||
output "storage_account_id" {
|
||||
description = "ID of the storage account"
|
||||
value = azurerm_storage_account.regional.id
|
||||
}
|
||||
|
||||
output "storage_account_primary_endpoint" {
|
||||
description = "Primary endpoint of the storage account"
|
||||
value = azurerm_storage_account.regional.primary_blob_endpoint
|
||||
}
|
||||
|
||||
output "subnet_ids" {
|
||||
description = "Map of subnet names to IDs"
|
||||
value = {
|
||||
hub_gateway = azurerm_subnet.hub_gateway.id
|
||||
hub_firewall = azurerm_subnet.hub_firewall.id
|
||||
hub_management = azurerm_subnet.hub_management.id
|
||||
spoke_app = azurerm_subnet.spoke_app.id
|
||||
spoke_db = azurerm_subnet.spoke_db.id
|
||||
spoke_storage = azurerm_subnet.spoke_storage.id
|
||||
}
|
||||
}
|
||||
|
||||
51
infra/terraform/modules/regional-landing-zone/variables.tf
Normal file
51
infra/terraform/modules/regional-landing-zone/variables.tf
Normal file
@@ -0,0 +1,51 @@
|
||||
# Variables for Regional Landing Zone Module
|
||||
|
||||
variable "region" {
|
||||
description = "Azure region (e.g., westeurope, northeurope)"
|
||||
type = string
|
||||
validation {
|
||||
condition = contains([
|
||||
"westeurope",
|
||||
"northeurope",
|
||||
"uksouth",
|
||||
"switzerlandnorth",
|
||||
"norwayeast",
|
||||
"francecentral",
|
||||
"germanywestcentral"
|
||||
], var.region)
|
||||
error_message = "Region must be a non-US commercial Azure region."
|
||||
}
|
||||
}
|
||||
|
||||
variable "environment" {
|
||||
description = "Environment name (dev, stage, prod)"
|
||||
type = string
|
||||
validation {
|
||||
condition = contains(["dev", "stage", "prod"], var.environment)
|
||||
error_message = "Environment must be dev, stage, or prod."
|
||||
}
|
||||
}
|
||||
|
||||
variable "management_group_id" {
|
||||
description = "Management group ID for this landing zone"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "hub_vnet_address_space" {
|
||||
description = "Address space for hub VNet"
|
||||
type = string
|
||||
default = "10.0.0.0/16"
|
||||
}
|
||||
|
||||
variable "spoke_vnet_address_space" {
|
||||
description = "Address space for spoke VNet"
|
||||
type = string
|
||||
default = "10.1.0.0/16"
|
||||
}
|
||||
|
||||
variable "tags" {
|
||||
description = "Tags to apply to all resources"
|
||||
type = map(string)
|
||||
default = {}
|
||||
}
|
||||
|
||||
16
infra/terraform/modules/regional-landing-zone/versions.tf
Normal file
16
infra/terraform/modules/regional-landing-zone/versions.tf
Normal file
@@ -0,0 +1,16 @@
|
||||
# Terraform and Provider Version Constraints for Regional Landing Zone Module
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.5.0"
|
||||
|
||||
required_providers {
|
||||
azurerm = {
|
||||
source = "hashicorp/azurerm"
|
||||
version = "~> 3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Configure Azure Provider (inherited from parent)
|
||||
# Provider configuration should be set in the calling module
|
||||
|
||||
60
infra/terraform/multi-region/README.md
Normal file
60
infra/terraform/multi-region/README.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# Multi-Region Landing Zone Deployment
|
||||
|
||||
Deploys Cloud for Sovereignty landing zones across all non-US commercial Azure regions.
|
||||
|
||||
## Supported Regions
|
||||
|
||||
1. **West Europe** (Netherlands) - Primary region
|
||||
2. **North Europe** (Ireland) - Secondary region
|
||||
3. **UK South** (London) - UK-specific workloads
|
||||
4. **Switzerland North** (Zurich) - Swiss-specific workloads
|
||||
5. **Norway East** (Oslo) - Nordic-specific workloads
|
||||
6. **France Central** (Paris) - French-specific workloads
|
||||
7. **Germany West Central** (Frankfurt) - German-specific workloads
|
||||
|
||||
## Architecture
|
||||
|
||||
Each region includes:
|
||||
- Hub Virtual Network (gateway, firewall, management)
|
||||
- Spoke Virtual Network (application, database, storage)
|
||||
- Azure Firewall (centralized security)
|
||||
- Key Vault (regional secrets with private endpoints)
|
||||
- Log Analytics Workspace (regional logging)
|
||||
- Storage Account (regional storage with private endpoints)
|
||||
|
||||
## Usage
|
||||
|
||||
### Deploy All Regions
|
||||
|
||||
```bash
|
||||
cd infra/terraform/multi-region
|
||||
terraform init
|
||||
terraform plan -var="environment=dev" -var="management_group_id=SOVEREIGN-ORDER-OF-HOSPITALLERS"
|
||||
terraform apply
|
||||
```
|
||||
|
||||
### Deploy Specific Regions
|
||||
|
||||
```bash
|
||||
terraform plan \
|
||||
-var="environment=dev" \
|
||||
-var="deploy_all_regions=false" \
|
||||
-var='regions_to_deploy=["westeurope", "northeurope"]'
|
||||
terraform apply
|
||||
```
|
||||
|
||||
## Variables
|
||||
|
||||
- `environment`: dev, stage, or prod
|
||||
- `management_group_id`: Root management group ID
|
||||
- `deploy_all_regions`: Deploy to all supported regions (default: true)
|
||||
- `regions_to_deploy`: Specific regions if deploy_all_regions is false
|
||||
|
||||
## Outputs
|
||||
|
||||
- `deployed_regions`: List of deployed regions
|
||||
- `regional_resource_groups`: Resource group names per region
|
||||
- `regional_key_vaults`: Key Vault names per region
|
||||
- `regional_storage_accounts`: Storage account names per region
|
||||
- `deployment_summary`: Complete deployment summary
|
||||
|
||||
120
infra/terraform/multi-region/main.tf
Normal file
120
infra/terraform/multi-region/main.tf
Normal file
@@ -0,0 +1,120 @@
|
||||
# Multi-Region Landing Zone Deployment
|
||||
# Deploys landing zones across all non-US commercial Azure regions
|
||||
# Uses the regional-landing-zone module
|
||||
|
||||
variable "environment" {
|
||||
description = "Environment name (dev, stage, prod)"
|
||||
type = string
|
||||
default = "dev"
|
||||
}
|
||||
|
||||
variable "management_group_id" {
|
||||
description = "Root management group ID"
|
||||
type = string
|
||||
default = "SOVEREIGN-ORDER-OF-HOSPITALLERS"
|
||||
}
|
||||
|
||||
variable "deploy_all_regions" {
|
||||
description = "Deploy to all supported regions"
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "regions_to_deploy" {
|
||||
description = "Specific regions to deploy (if deploy_all_regions is false)"
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
# Supported non-US commercial regions
|
||||
locals {
|
||||
supported_regions = [
|
||||
"westeurope", # Netherlands - Primary
|
||||
"northeurope", # Ireland - Secondary
|
||||
"uksouth", # London - UK workloads
|
||||
"switzerlandnorth", # Zurich - Swiss workloads
|
||||
"norwayeast", # Oslo - Nordic workloads
|
||||
"francecentral", # Paris - French workloads
|
||||
"germanywestcentral" # Frankfurt - German workloads
|
||||
]
|
||||
|
||||
regions = var.deploy_all_regions ? local.supported_regions : var.regions_to_deploy
|
||||
|
||||
# Hub VNet address spaces per region
|
||||
hub_address_spaces = {
|
||||
westeurope = "10.0.0.0/16"
|
||||
northeurope = "10.10.0.0/16"
|
||||
uksouth = "10.20.0.0/16"
|
||||
switzerlandnorth = "10.30.0.0/16"
|
||||
norwayeast = "10.40.0.0/16"
|
||||
francecentral = "10.50.0.0/16"
|
||||
germanywestcentral = "10.60.0.0/16"
|
||||
}
|
||||
|
||||
# Spoke VNet address spaces per region
|
||||
spoke_address_spaces = {
|
||||
westeurope = "10.1.0.0/16"
|
||||
northeurope = "10.11.0.0/16"
|
||||
uksouth = "10.21.0.0/16"
|
||||
switzerlandnorth = "10.31.0.0/16"
|
||||
norwayeast = "10.41.0.0/16"
|
||||
francecentral = "10.51.0.0/16"
|
||||
germanywestcentral = "10.61.0.0/16"
|
||||
}
|
||||
|
||||
common_tags = {
|
||||
Environment = var.environment
|
||||
Project = "the-order"
|
||||
ManagedBy = "terraform"
|
||||
SovereigntyLevel = "high"
|
||||
DataClassification = "confidential"
|
||||
Compliance = "gdpr,eidas"
|
||||
}
|
||||
}
|
||||
|
||||
# Deploy regional landing zones
|
||||
module "regional_landing_zones" {
|
||||
source = "../modules/regional-landing-zone"
|
||||
|
||||
for_each = toset(local.regions)
|
||||
|
||||
region = each.value
|
||||
environment = var.environment
|
||||
management_group_id = var.management_group_id
|
||||
hub_vnet_address_space = local.hub_address_spaces[each.value]
|
||||
spoke_vnet_address_space = local.spoke_address_spaces[each.value]
|
||||
tags = merge(local.common_tags, {
|
||||
Region = each.value
|
||||
})
|
||||
}
|
||||
|
||||
# Outputs
|
||||
output "deployed_regions" {
|
||||
description = "List of deployed regions"
|
||||
value = local.regions
|
||||
}
|
||||
|
||||
output "regional_resource_groups" {
|
||||
description = "Resource group names per region"
|
||||
value = {
|
||||
for region, module in module.regional_landing_zones :
|
||||
region => module.resource_group_name
|
||||
}
|
||||
}
|
||||
|
||||
output "regional_key_vaults" {
|
||||
description = "Key Vault IDs per region"
|
||||
value = {
|
||||
for region, module in module.regional_landing_zones :
|
||||
region => module.key_vault_id
|
||||
}
|
||||
}
|
||||
|
||||
output "regional_storage_accounts" {
|
||||
description = "Storage account names per region"
|
||||
value = {
|
||||
for region, module in module.regional_landing_zones :
|
||||
region => module.storage_account_name
|
||||
}
|
||||
}
|
||||
|
||||
65
infra/terraform/multi-region/outputs.tf
Normal file
65
infra/terraform/multi-region/outputs.tf
Normal file
@@ -0,0 +1,65 @@
|
||||
# Outputs for Multi-Region Landing Zone Deployment
|
||||
|
||||
output "deployed_regions" {
|
||||
description = "List of deployed regions"
|
||||
value = local.regions
|
||||
}
|
||||
|
||||
output "regional_resource_groups" {
|
||||
description = "Resource group names per region"
|
||||
value = {
|
||||
for region, module in module.regional_landing_zones :
|
||||
region => module.resource_group_name
|
||||
}
|
||||
}
|
||||
|
||||
output "regional_key_vaults" {
|
||||
description = "Key Vault names per region"
|
||||
value = {
|
||||
for region, module in module.regional_landing_zones :
|
||||
region => module.key_vault_name
|
||||
}
|
||||
}
|
||||
|
||||
output "regional_storage_accounts" {
|
||||
description = "Storage account names per region"
|
||||
value = {
|
||||
for region, module in module.regional_landing_zones :
|
||||
region => module.storage_account_name
|
||||
}
|
||||
}
|
||||
|
||||
output "regional_log_analytics_workspaces" {
|
||||
description = "Log Analytics Workspace names per region"
|
||||
value = {
|
||||
for region, module in module.regional_landing_zones :
|
||||
region => module.log_analytics_workspace_name
|
||||
}
|
||||
}
|
||||
|
||||
output "regional_hub_vnets" {
|
||||
description = "Hub VNet names per region"
|
||||
value = {
|
||||
for region, module in module.regional_landing_zones :
|
||||
region => module.hub_vnet_name
|
||||
}
|
||||
}
|
||||
|
||||
output "regional_spoke_vnets" {
|
||||
description = "Spoke VNet names per region"
|
||||
value = {
|
||||
for region, module in module.regional_landing_zones :
|
||||
region => module.spoke_vnet_name
|
||||
}
|
||||
}
|
||||
|
||||
output "deployment_summary" {
|
||||
description = "Summary of deployment"
|
||||
value = {
|
||||
total_regions = length(local.regions)
|
||||
regions = local.regions
|
||||
environment = var.environment
|
||||
management_group = var.management_group_id
|
||||
}
|
||||
}
|
||||
|
||||
44
infra/terraform/multi-region/variables.tf
Normal file
44
infra/terraform/multi-region/variables.tf
Normal file
@@ -0,0 +1,44 @@
|
||||
# Variables for Multi-Region Landing Zone Deployment
|
||||
|
||||
variable "environment" {
|
||||
description = "Environment name (dev, stage, prod)"
|
||||
type = string
|
||||
default = "dev"
|
||||
validation {
|
||||
condition = contains(["dev", "stage", "prod"], var.environment)
|
||||
error_message = "Environment must be dev, stage, or prod."
|
||||
}
|
||||
}
|
||||
|
||||
variable "management_group_id" {
|
||||
description = "Root management group ID"
|
||||
type = string
|
||||
default = "SOVEREIGN-ORDER-OF-HOSPITALLERS"
|
||||
}
|
||||
|
||||
variable "deploy_all_regions" {
|
||||
description = "Deploy to all supported regions"
|
||||
type = bool
|
||||
default = true
|
||||
}
|
||||
|
||||
variable "regions_to_deploy" {
|
||||
description = "Specific regions to deploy (if deploy_all_regions is false)"
|
||||
type = list(string)
|
||||
default = []
|
||||
validation {
|
||||
condition = alltrue([
|
||||
for region in var.regions_to_deploy : contains([
|
||||
"westeurope",
|
||||
"northeurope",
|
||||
"uksouth",
|
||||
"switzerlandnorth",
|
||||
"norwayeast",
|
||||
"francecentral",
|
||||
"germanywestcentral"
|
||||
], region)
|
||||
])
|
||||
error_message = "All regions must be non-US commercial Azure regions."
|
||||
}
|
||||
}
|
||||
|
||||
13
infra/terraform/multi-region/versions.tf
Normal file
13
infra/terraform/multi-region/versions.tf
Normal file
@@ -0,0 +1,13 @@
|
||||
# Terraform and Provider Version Constraints for Multi-Region Deployment
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.5.0"
|
||||
|
||||
required_providers {
|
||||
azurerm = {
|
||||
source = "hashicorp/azurerm"
|
||||
version = "~> 3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
102
infra/terraform/outputs-azure.tf
Normal file
102
infra/terraform/outputs-azure.tf
Normal file
@@ -0,0 +1,102 @@
|
||||
# Azure-specific outputs for integration with Kubernetes and services
|
||||
|
||||
output "subscription_id" {
|
||||
description = "Azure subscription ID"
|
||||
value = data.azurerm_subscription.current.subscription_id
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "tenant_id" {
|
||||
description = "Azure tenant ID"
|
||||
value = data.azurerm_client_config.current.tenant_id
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "resource_group_name" {
|
||||
description = "Main resource group name"
|
||||
value = azurerm_resource_group.main.name
|
||||
}
|
||||
|
||||
output "storage_account_name" {
|
||||
description = "Application data storage account name"
|
||||
value = azurerm_storage_account.app_data.name
|
||||
}
|
||||
|
||||
output "storage_account_primary_key" {
|
||||
description = "Storage account primary access key"
|
||||
value = azurerm_storage_account.app_data.primary_access_key
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "key_vault_name" {
|
||||
description = "Key Vault name"
|
||||
value = azurerm_key_vault.main.name
|
||||
}
|
||||
|
||||
output "key_vault_uri" {
|
||||
description = "Key Vault URI"
|
||||
value = azurerm_key_vault.main.vault_uri
|
||||
}
|
||||
|
||||
# CDN Outputs
|
||||
output "cdn_storage_account_name" {
|
||||
description = "CDN storage account name"
|
||||
value = azurerm_storage_account.cdn_images.name
|
||||
}
|
||||
|
||||
output "cdn_blob_url" {
|
||||
description = "CDN blob storage URL"
|
||||
value = "https://${azurerm_storage_account.cdn_images.name}.blob.core.windows.net/${azurerm_storage_container.cdn_images.name}/"
|
||||
}
|
||||
|
||||
output "cdn_endpoint_url" {
|
||||
description = "CDN endpoint URL"
|
||||
value = "https://${azurerm_cdn_endpoint.cdn_images.host_name}/${azurerm_storage_container.cdn_images.name}/"
|
||||
}
|
||||
|
||||
# Database Outputs
|
||||
output "database_fqdn" {
|
||||
description = "Database fully qualified domain name"
|
||||
value = try(azurerm_postgresql_flexible_server.main[0].fqdn, null)
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
output "database_name" {
|
||||
description = "Database name"
|
||||
value = try(azurerm_postgresql_flexible_server_database.main[0].name, null)
|
||||
}
|
||||
|
||||
# AKS Outputs (if created)
|
||||
output "aks_cluster_name" {
|
||||
description = "AKS cluster name"
|
||||
value = try(azurerm_kubernetes_cluster.main[0].name, null)
|
||||
}
|
||||
|
||||
output "aks_fqdn" {
|
||||
description = "AKS cluster FQDN"
|
||||
value = try(azurerm_kubernetes_cluster.main[0].fqdn, null)
|
||||
}
|
||||
|
||||
output "log_analytics_workspace_id" {
|
||||
description = "Log Analytics Workspace ID"
|
||||
value = try(azurerm_log_analytics_workspace.main[0].workspace_id, null)
|
||||
}
|
||||
|
||||
# Export all outputs to environment variables file
|
||||
output "env_file_snippet" {
|
||||
description = "Environment variables snippet for .env file"
|
||||
value = <<-EOT
|
||||
# Azure Infrastructure Outputs (auto-generated)
|
||||
AZURE_SUBSCRIPTION_ID="${data.azurerm_subscription.current.subscription_id}"
|
||||
AZURE_TENANT_ID="${data.azurerm_client_config.current.tenant_id}"
|
||||
AZURE_RESOURCE_GROUP="${azurerm_resource_group.main.name}"
|
||||
AZURE_STORAGE_ACCOUNT="${azurerm_storage_account.app_data.name}"
|
||||
AZURE_KEY_VAULT_NAME="${azurerm_key_vault.main.name}"
|
||||
AZURE_KEY_VAULT_URI="${azurerm_key_vault.main.vault_uri}"
|
||||
CDN_STORAGE_ACCOUNT="${azurerm_storage_account.cdn_images.name}"
|
||||
CDN_BASE_URL_BLOB="https://${azurerm_storage_account.cdn_images.name}.blob.core.windows.net/${azurerm_storage_container.cdn_images.name}/"
|
||||
CDN_BASE_URL_CDN="https://${azurerm_cdn_endpoint.cdn_images.host_name}/${azurerm_storage_container.cdn_images.name}/"
|
||||
EOT
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
254
infra/terraform/policies/main.tf
Normal file
254
infra/terraform/policies/main.tf
Normal file
@@ -0,0 +1,254 @@
|
||||
# Azure Policies for Cloud for Sovereignty
|
||||
# Ensures compliance with data residency and sovereignty requirements
|
||||
|
||||
variable "management_group_id" {
|
||||
description = "Root management group ID to assign policies"
|
||||
type = string
|
||||
default = "SOVEREIGN-ORDER-OF-HOSPITALLERS"
|
||||
}
|
||||
|
||||
# Configure Azure Provider
|
||||
provider "azurerm" {
|
||||
features {}
|
||||
}
|
||||
|
||||
# Policy: Allowed Locations (Non-US Commercial Regions Only)
|
||||
resource "azurerm_policy_definition" "allowed_locations" {
|
||||
name = "the-order-allowed-locations"
|
||||
policy_type = "Custom"
|
||||
mode = "All"
|
||||
display_name = "The Order - Allowed Locations (Non-US Commercial)"
|
||||
description = "Restricts resource deployment to non-US commercial Azure regions for data sovereignty"
|
||||
|
||||
metadata = jsonencode({
|
||||
category = "Location"
|
||||
})
|
||||
|
||||
policy_rule = jsonencode({
|
||||
if = {
|
||||
not = {
|
||||
field = "location"
|
||||
in = [
|
||||
"westeurope",
|
||||
"northeurope",
|
||||
"uksouth",
|
||||
"switzerlandnorth",
|
||||
"norwayeast",
|
||||
"francecentral",
|
||||
"germanywestcentral"
|
||||
]
|
||||
}
|
||||
}
|
||||
then = {
|
||||
effect = "Deny"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
# Policy: Deny US Regions
|
||||
resource "azurerm_policy_definition" "deny_us_regions" {
|
||||
name = "the-order-deny-us-regions"
|
||||
policy_type = "Custom"
|
||||
mode = "All"
|
||||
display_name = "The Order - Deny US Commercial and Government Regions"
|
||||
description = "Explicitly denies deployment to any US Commercial or Government regions"
|
||||
|
||||
metadata = jsonencode({
|
||||
category = "Location"
|
||||
})
|
||||
|
||||
policy_rule = jsonencode({
|
||||
if = {
|
||||
field = "location"
|
||||
like = "us*"
|
||||
}
|
||||
then = {
|
||||
effect = "Deny"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
# Policy: Require Data Residency Tags
|
||||
resource "azurerm_policy_definition" "require_data_residency_tag" {
|
||||
name = "the-order-require-data-residency-tag"
|
||||
policy_type = "Custom"
|
||||
mode = "Indexed"
|
||||
display_name = "The Order - Require Data Residency Tag"
|
||||
description = "Requires DataResidency tag on all resources for sovereignty tracking"
|
||||
|
||||
metadata = jsonencode({
|
||||
category = "Tags"
|
||||
})
|
||||
|
||||
policy_rule = jsonencode({
|
||||
if = {
|
||||
field = "[concat('tags[', parameters('tagName'), ']')]"
|
||||
exists = "false"
|
||||
}
|
||||
then = {
|
||||
effect = "Deny"
|
||||
}
|
||||
})
|
||||
|
||||
parameters = jsonencode({
|
||||
tagName = {
|
||||
type = "String"
|
||||
metadata = {
|
||||
displayName = "Tag Name"
|
||||
description = "Name of the tag, such as 'DataResidency'"
|
||||
}
|
||||
defaultValue = "DataResidency"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
# Policy: Require Encryption at Rest
|
||||
resource "azurerm_policy_definition" "require_encryption_at_rest" {
|
||||
name = "the-order-require-encryption-at-rest"
|
||||
policy_type = "Custom"
|
||||
mode = "All"
|
||||
display_name = "The Order - Require Encryption at Rest"
|
||||
description = "Ensures all storage accounts use encryption at rest with customer-managed keys"
|
||||
|
||||
metadata = jsonencode({
|
||||
category = "Security"
|
||||
})
|
||||
|
||||
policy_rule = jsonencode({
|
||||
if = {
|
||||
allOf = [
|
||||
{
|
||||
field = "type"
|
||||
equals = "Microsoft.Storage/storageAccounts"
|
||||
},
|
||||
{
|
||||
field = "Microsoft.Storage/storageAccounts/encryption.keySource"
|
||||
notEquals = "Microsoft.Keyvault"
|
||||
}
|
||||
]
|
||||
}
|
||||
then = {
|
||||
effect = "Deny"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
# Policy: Require Resource Tags
|
||||
resource "azurerm_policy_definition" "require_resource_tags" {
|
||||
name = "the-order-require-resource-tags"
|
||||
policy_type = "Custom"
|
||||
mode = "Indexed"
|
||||
display_name = "The Order - Require Resource Tags"
|
||||
description = "Requires specific tags on all resources for governance and cost management"
|
||||
|
||||
metadata = jsonencode({
|
||||
category = "Tags"
|
||||
})
|
||||
|
||||
policy_rule = jsonencode({
|
||||
if = {
|
||||
anyOf = [
|
||||
{
|
||||
field = "[concat('tags[', parameters('tagName1'), ']')]"
|
||||
exists = "false"
|
||||
},
|
||||
{
|
||||
field = "[concat('tags[', parameters('tagName2'), ']')]"
|
||||
exists = "false"
|
||||
},
|
||||
{
|
||||
field = "[concat('tags[', parameters('tagName3'), ']')]"
|
||||
exists = "false"
|
||||
}
|
||||
]
|
||||
}
|
||||
then = {
|
||||
effect = "Deny"
|
||||
}
|
||||
})
|
||||
|
||||
parameters = jsonencode({
|
||||
tagName1 = {
|
||||
type = "String"
|
||||
metadata = {
|
||||
displayName = "Tag Name 1"
|
||||
}
|
||||
defaultValue = "Environment"
|
||||
}
|
||||
tagName2 = {
|
||||
type = "String"
|
||||
metadata = {
|
||||
displayName = "Tag Name 2"
|
||||
}
|
||||
defaultValue = "Project"
|
||||
}
|
||||
tagName3 = {
|
||||
type = "String"
|
||||
metadata = {
|
||||
displayName = "Tag Name 3"
|
||||
}
|
||||
defaultValue = "DataClassification"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
# Policy Initiative: Cloud for Sovereignty Compliance
|
||||
resource "azurerm_policy_set_definition" "sovereignty_compliance" {
|
||||
name = "the-order-sovereignty-compliance"
|
||||
policy_type = "Custom"
|
||||
display_name = "The Order - Cloud for Sovereignty Compliance"
|
||||
description = "Policy initiative ensuring compliance with Cloud for Sovereignty requirements"
|
||||
|
||||
metadata = jsonencode({
|
||||
category = "Compliance"
|
||||
})
|
||||
|
||||
policy_definition_reference {
|
||||
policy_definition_id = azurerm_policy_definition.allowed_locations.id
|
||||
}
|
||||
|
||||
policy_definition_reference {
|
||||
policy_definition_id = azurerm_policy_definition.deny_us_regions.id
|
||||
}
|
||||
|
||||
policy_definition_reference {
|
||||
policy_definition_id = azurerm_policy_definition.require_data_residency_tag.id
|
||||
}
|
||||
|
||||
policy_definition_reference {
|
||||
policy_definition_id = azurerm_policy_definition.require_encryption_at_rest.id
|
||||
}
|
||||
|
||||
policy_definition_reference {
|
||||
policy_definition_id = azurerm_policy_definition.require_resource_tags.id
|
||||
}
|
||||
}
|
||||
|
||||
# Assign policy initiative to root management group
|
||||
resource "azurerm_management_group_policy_assignment" "sovereignty_compliance" {
|
||||
name = "sovereignty-compliance-assignment"
|
||||
management_group_id = var.management_group_id
|
||||
policy_definition_id = azurerm_policy_set_definition.sovereignty_compliance.id
|
||||
|
||||
identity {
|
||||
type = "SystemAssigned"
|
||||
}
|
||||
}
|
||||
|
||||
# Outputs
|
||||
output "policy_definitions" {
|
||||
description = "Created policy definitions"
|
||||
value = {
|
||||
allowed_locations = azurerm_policy_definition.allowed_locations.id
|
||||
deny_us_regions = azurerm_policy_definition.deny_us_regions.id
|
||||
require_data_residency_tag = azurerm_policy_definition.require_data_residency_tag.id
|
||||
require_encryption_at_rest = azurerm_policy_definition.require_encryption_at_rest.id
|
||||
require_resource_tags = azurerm_policy_definition.require_resource_tags.id
|
||||
}
|
||||
}
|
||||
|
||||
output "policy_initiative" {
|
||||
description = "Policy initiative ID"
|
||||
value = azurerm_policy_set_definition.sovereignty_compliance.id
|
||||
}
|
||||
|
||||
8
infra/terraform/policies/variables.tf
Normal file
8
infra/terraform/policies/variables.tf
Normal file
@@ -0,0 +1,8 @@
|
||||
# Variables for Sovereignty Policies
|
||||
|
||||
variable "management_group_id" {
|
||||
description = "Root management group ID to assign policies"
|
||||
type = string
|
||||
default = "SOVEREIGN-ORDER-OF-HOSPITALLERS"
|
||||
}
|
||||
|
||||
13
infra/terraform/policies/versions.tf
Normal file
13
infra/terraform/policies/versions.tf
Normal file
@@ -0,0 +1,13 @@
|
||||
# Terraform and Provider Version Constraints for Policies
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.5.0"
|
||||
|
||||
required_providers {
|
||||
azurerm = {
|
||||
source = "hashicorp/azurerm"
|
||||
version = "~> 3.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Resource Groups for The Order
|
||||
# Creates resource groups for each environment
|
||||
# Naming: az-we-rg-dev-main (provider-region-resource-env-purpose)
|
||||
# Naming: az-we-rg-dev-main (provider-region-resource-env-purpose) or custom from variable
|
||||
|
||||
resource "azurerm_resource_group" "main" {
|
||||
name = local.rg_name
|
||||
name = var.resource_group_name != "" ? var.resource_group_name : local.rg_name
|
||||
location = var.azure_region
|
||||
|
||||
tags = merge(local.common_tags, {
|
||||
tags = merge(var.tags, {
|
||||
Purpose = "Main"
|
||||
})
|
||||
}
|
||||
|
||||
@@ -32,9 +32,9 @@ resource "azurerm_storage_container" "terraform_state" {
|
||||
}
|
||||
|
||||
# Storage Account for application data (object storage)
|
||||
# Naming: azwesadevdata (provider+region+sa+env+purpose, alphanumeric only, max 24 chars)
|
||||
# Naming: azwesadevdata (provider+region+sa+env+purpose, alphanumeric only, max 24 chars) or custom from variable
|
||||
resource "azurerm_storage_account" "app_data" {
|
||||
name = local.sa_data_name
|
||||
name = var.storage_account_name != "" ? var.storage_account_name : local.sa_data_name
|
||||
resource_group_name = azurerm_resource_group.main.name
|
||||
location = var.azure_region
|
||||
account_tier = "Standard"
|
||||
|
||||
48
infra/terraform/terraform.tfvars.example
Normal file
48
infra/terraform/terraform.tfvars.example
Normal file
@@ -0,0 +1,48 @@
|
||||
# Terraform Variables Example
|
||||
# Copy this file to terraform.tfvars and fill in your values
|
||||
# DO NOT commit terraform.tfvars to version control
|
||||
|
||||
# Azure Configuration
|
||||
azure_region = "westeurope" # No US regions allowed
|
||||
environment = "dev" # dev, stage, or prod
|
||||
project_name = "the-order"
|
||||
|
||||
# Azure Subscription
|
||||
subscription_id = "" # Your Azure subscription ID
|
||||
tenant_id = "" # Your Azure tenant ID
|
||||
|
||||
# Resource Naming
|
||||
resource_group_name = "the-order-rg-${environment}"
|
||||
storage_account_name = "theorder${environment}" # Must be globally unique, lowercase, alphanumeric
|
||||
key_vault_name = "the-order-kv-${environment}" # Must be globally unique
|
||||
|
||||
# Networking
|
||||
domain_name = "" # Optional: your domain name
|
||||
vnet_name = "the-order-vnet-${environment}"
|
||||
subnet_name = "the-order-subnet-${environment}"
|
||||
|
||||
# Database
|
||||
database_name = "theorder_${environment}"
|
||||
database_admin_user = "theorder_admin"
|
||||
# database_admin_password set via environment variable or Azure Key Vault
|
||||
|
||||
# Storage
|
||||
storage_account_tier = "Standard"
|
||||
storage_account_replication = "LRS" # LRS for dev, GRS for prod
|
||||
|
||||
# CDN (if using)
|
||||
cdn_profile_name = "theorder-cdn-${environment}"
|
||||
cdn_endpoint_name = "theorder-cdn-endpoint-${environment}"
|
||||
|
||||
# Kubernetes (AKS)
|
||||
aks_cluster_name = "the-order-aks-${environment}"
|
||||
aks_node_count = 2
|
||||
aks_vm_size = "Standard_B2s"
|
||||
|
||||
# Tags
|
||||
tags = {
|
||||
Environment = "${environment}"
|
||||
Project = "the-order"
|
||||
ManagedBy = "terraform"
|
||||
}
|
||||
|
||||
@@ -40,6 +40,74 @@ variable "domain_name" {
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "subscription_id" {
|
||||
description = "Azure subscription ID"
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "tenant_id" {
|
||||
description = "Azure tenant ID"
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "client_id" {
|
||||
description = "Azure service principal client ID (optional, uses Azure CLI auth if not set)"
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "client_secret" {
|
||||
description = "Azure service principal client secret (optional, uses Azure CLI auth if not set)"
|
||||
type = string
|
||||
default = ""
|
||||
sensitive = true
|
||||
}
|
||||
|
||||
variable "resource_group_name" {
|
||||
description = "Azure resource group name"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "storage_account_name" {
|
||||
description = "Azure storage account name (must be globally unique)"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "key_vault_name" {
|
||||
description = "Azure Key Vault name (must be globally unique)"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "tags" {
|
||||
description = "Tags to apply to all resources"
|
||||
type = map(string)
|
||||
default = {
|
||||
Environment = "dev"
|
||||
Project = "the-order"
|
||||
ManagedBy = "terraform"
|
||||
}
|
||||
}
|
||||
|
||||
variable "cdn_profile_name" {
|
||||
description = "Azure CDN profile name"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "cdn_endpoint_name" {
|
||||
description = "Azure CDN endpoint name"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "enable_monitoring" {
|
||||
description = "Enable monitoring and observability"
|
||||
type = bool
|
||||
|
||||
@@ -9,6 +9,10 @@ terraform {
|
||||
source = "hashicorp/azurerm"
|
||||
version = "~> 3.0"
|
||||
}
|
||||
random = {
|
||||
source = "hashicorp/random"
|
||||
version = "~> 3.0"
|
||||
}
|
||||
}
|
||||
|
||||
# Configure backend for state management
|
||||
|
||||
Reference in New Issue
Block a user