# Full Terraform Config for Azure Demo Infra - Fixed for Free Tier terraform { required_version = ">= 1.3" required_providers { azurerm = { source = "hashicorp/azurerm", version = ">= 3.50" } random = { source = "hashicorp/random" } azuread = { source = "hashicorp/azuread" } time = { source = "hashicorp/time" } } } provider "azurerm" { features {} subscription_id = "1d363cb6-5669-42c2-98d3-5b9a1604b797" } provider "random" {} provider "azuread" {} data "azurerm_client_config" "current" {} locals { default_tags = { environment = "Demo" owner = "Linux Lenape" purpose = "Technical-Demo" } } # Resource Group resource "azurerm_resource_group" "core" { name = "Prod-Native-American-Empires" location = var.location tags = local.default_tags } # Key Vault resource "azurerm_key_vault" "vault" { name = var.key_vault_name location = var.location resource_group_name = azurerm_resource_group.core.name tenant_id = data.azurerm_client_config.current.tenant_id sku_name = "standard" purge_protection_enabled = false public_network_access_enabled = true soft_delete_retention_days = 7 tags = local.default_tags access_policy { tenant_id = data.azurerm_client_config.current.tenant_id object_id = data.azurerm_client_config.current.object_id key_permissions = [ "Get", ] secret_permissions = [ "Get", "Set", "Delete", "List" ] storage_permissions = [ "Get", ] } } # Generate VM admin password resource "random_password" "vm_admin" { length = 16 special = true } # Store VM admin password in Key Vault resource "azurerm_key_vault_secret" "vm_admin_password" { name = "vm-admin-password" value = random_password.vm_admin.result key_vault_id = azurerm_key_vault.vault.id depends_on = [azurerm_key_vault.vault] } # Networking resource "azurerm_virtual_network" "vnet" { name = "vnet-prod" location = var.location resource_group_name = azurerm_resource_group.core.name address_space = ["10.0.0.0/16"] tags = local.default_tags } resource "azurerm_subnet" "subnet" { name = "subnet-prod" resource_group_name = azurerm_resource_group.core.name virtual_network_name = azurerm_virtual_network.vnet.name address_prefixes = ["10.0.1.0/24"] } resource "azurerm_network_security_group" "nsg" { name = "nsg-prod" location = var.location resource_group_name = azurerm_resource_group.core.name tags = local.default_tags security_rule { name = "RDP" priority = 1001 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "3389" source_address_prefix = "*" destination_address_prefix = "*" } security_rule { name = "SSH" priority = 1002 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "22" source_address_prefix = "*" destination_address_prefix = "*" } } resource "azurerm_subnet_network_security_group_association" "nsg_assoc" { subnet_id = azurerm_subnet.subnet.id network_security_group_id = azurerm_network_security_group.nsg.id } # Public IPs resource "azurerm_public_ip" "win_pip1" { name = "pip-okeus" resource_group_name = azurerm_resource_group.core.name location = var.location allocation_method = "Static" sku = "Standard" tags = local.default_tags } resource "azurerm_public_ip" "linux_pip" { name = "pip-kokopelli" resource_group_name = azurerm_resource_group.core.name location = var.location allocation_method = "Static" sku = "Standard" tags = local.default_tags } # Network Interfaces resource "azurerm_network_interface" "win_nic1" { name = "nic-okeus" location = var.location resource_group_name = azurerm_resource_group.core.name ip_configuration { name = "ipconfig1" subnet_id = azurerm_subnet.subnet.id public_ip_address_id = azurerm_public_ip.win_pip1.id private_ip_address_allocation = "Dynamic" } tags = local.default_tags } resource "azurerm_network_interface" "linux_nic" { name = "nic-kokopelli" location = var.location resource_group_name = azurerm_resource_group.core.name ip_configuration { name = "ipconfig1" subnet_id = azurerm_subnet.subnet.id public_ip_address_id = azurerm_public_ip.linux_pip.id private_ip_address_allocation = "Dynamic" } tags = local.default_tags } # Virtual Machines - Reduced to 2 VMs for free tier resource "azurerm_windows_virtual_machine" "okeus" { name = "okeus" resource_group_name = azurerm_resource_group.core.name location = var.location network_interface_ids = [azurerm_network_interface.win_nic1.id] size = "Standard_B1s" # Free tier eligible admin_username = var.vm_admin_username admin_password = random_password.vm_admin.result os_disk { name = "okeus-osdisk" caching = "ReadWrite" storage_account_type = "Standard_LRS" } source_image_reference { publisher = "MicrosoftWindowsServer" offer = "WindowsServer" sku = "2022-Datacenter" version = "latest" } tags = local.default_tags } resource "azurerm_linux_virtual_machine" "kokopelli" { name = "kokopelli" resource_group_name = azurerm_resource_group.core.name location = var.location network_interface_ids = [azurerm_network_interface.linux_nic.id] size = "Standard_B1s" # Free tier eligible admin_username = var.vm_admin_username admin_password = random_password.vm_admin.result disable_password_authentication = false os_disk { name = "kokopelli-osdisk" caching = "ReadWrite" storage_account_type = "Standard_LRS" } source_image_reference { publisher = "Canonical" offer = "0001-com-ubuntu-server-focal" sku = "20_04-lts-gen2" version = "latest" } tags = local.default_tags } # Azure SQL - Fixed resource names resource "random_integer" "sqlsuffix" { min = 1000 max = 9999 } resource "random_password" "sql_admin" { length = 16 special = true } resource "azurerm_mssql_server" "sqlsvr" { name = "sqlsrv${random_integer.sqlsuffix.result}" resource_group_name = azurerm_resource_group.core.name location = var.location version = "12.0" administrator_login = "sqladmin" administrator_login_password = random_password.sql_admin.result tags = local.default_tags } resource "azurerm_mssql_database" "hoporenkv" { name = "Hoporenkv" server_id = azurerm_mssql_server.sqlsvr.id sku_name = "Basic" # Free tier has 32MB limit, Basic is cheapest paid option tags = local.default_tags } resource "azurerm_key_vault_secret" "sql_admin_secret" { name = "sql-admin-password" value = random_password.sql_admin.result key_vault_id = azurerm_key_vault.vault.id depends_on = [azurerm_key_vault.vault] } # AAD Users - 10 users as requested resource "random_password" "demo_user_passwords" { count = 10 length = 16 special = true } resource "azuread_user" "demo_users" { count = 10 user_principal_name = "demo-user-${count.index + 1}@${var.tenant_domain}" display_name = "Demo User ${count.index + 1}" mail_nickname = "demo-user-${count.index + 1}" password = random_password.demo_user_passwords[count.index].result force_password_change = false } resource "azurerm_key_vault_secret" "demo_user_secrets" { count = 10 name = "demo-user-${count.index + 1}-password" value = random_password.demo_user_passwords[count.index].result key_vault_id = azurerm_key_vault.vault.id depends_on = [azurerm_key_vault.vault] } resource "azurerm_role_assignment" "demo_user_roles" { count = 10 principal_id = azuread_user.demo_users[count.index].object_id role_definition_name = "Reader" scope = azurerm_resource_group.core.id } # Monitoring - Fixed diagnostic settings resource "azurerm_log_analytics_workspace" "law_vm" { name = "demo-law-vm" location = var.location resource_group_name = azurerm_resource_group.core.name sku = "PerGB2018" retention_in_days = 30 tags = local.default_tags } # Budget resource "azurerm_consumption_budget_subscription" "demo_budget" { name = "demo-budget" amount = 200 # Increased to match your budget time_grain = "Monthly" subscription_id = "/subscriptions/${data.azurerm_client_config.current.subscription_id}" time_period { start_date = formatdate("YYYY-MM-01'T'00:00:00Z", timestamp()) } notification { enabled = true operator = "GreaterThan" threshold = 80 contact_emails = [var.admin_email] } notification { enabled = true operator = "GreaterThan" threshold = 100 contact_emails = [var.admin_email] } } # ACR & Container App - Simplified for free tier resource "random_integer" "rand" { min = 1000 max = 9999 } resource "azurerm_container_registry" "acr" { name = "demoacr${random_integer.rand.result}" resource_group_name = azurerm_resource_group.core.name location = var.location sku = "Basic" admin_enabled = true tags = local.default_tags } resource "azurerm_log_analytics_workspace" "law_app" { name = "demo-law-app" location = var.location resource_group_name = azurerm_resource_group.core.name sku = "PerGB2018" retention_in_days = 30 tags = local.default_tags } resource "azurerm_container_app_environment" "env" { name = "demo-env" location = var.location resource_group_name = azurerm_resource_group.core.name log_analytics_workspace_id = azurerm_log_analytics_workspace.law_app.id tags = local.default_tags } # Add wait time to ensure container environment is fully provisioned resource "time_sleep" "wait_for_container_env" { depends_on = [azurerm_container_app_environment.env] create_duration = "60s" } # FIXED: Container App with proper dependencies and public image resource "azurerm_container_app" "skennen" { name = "skennen" container_app_environment_id = azurerm_container_app_environment.env.id resource_group_name = azurerm_resource_group.core.name revision_mode = "Single" # More explicit dependency management depends_on = [ time_sleep.wait_for_container_env, azurerm_container_app_environment.env, azurerm_log_analytics_workspace.law_app, azurerm_container_registry.acr ] secret { name = "acr-password" value = azurerm_container_registry.acr.admin_password } registry { server = azurerm_container_registry.acr.login_server username = azurerm_container_registry.acr.admin_username password_secret_name = "acr-password" } template { container { name = "skennen" image = "mcr.microsoft.com/azuredocs/containerapps-helloworld:latest" # Use a public image initially cpu = 0.25 memory = "0.5Gi" } revision_suffix = "initial" } ingress { external_enabled = true target_port = 80 traffic_weight { latest_revision = true percentage = 100 } } tags = local.default_tags } # Static Web App - Fixed deprecated resource resource "azurerm_static_web_app" "landing" { name = "plan-jacquesingram" resource_group_name = azurerm_resource_group.core.name location = var.location sku_tier = "Free" sku_size = "Free" tags = local.default_tags } # Outputs output "windows_vm_ip" { value = azurerm_public_ip.win_pip1.ip_address } output "linux_vm_ip" { value = azurerm_public_ip.linux_pip.ip_address } output "container_registry_url" { value = azurerm_container_registry.acr.login_server } output "key_vault_uri" { value = azurerm_key_vault.vault.vault_uri } output "static_site_url" { value = azurerm_static_web_app.landing.default_host_name } output "sql_server_fqdn" { value = azurerm_mssql_server.sqlsvr.fully_qualified_domain_name } output "vm_admin_password" { value = random_password.vm_admin.result sensitive = true } output "container_app_url" { value = azurerm_container_app.skennen.latest_revision_fqdn description = "The URL of the container app" }