This Terraform project creates a simple Azure infrastructure with:
- Resource Group
- Virtual Network (VNet) with subnets
- Azure Container App with configurable Docker images
- Key Vault for secure credential storage
- PostgreSQL Flexible Server database
- Storage Account with blob container
- Azure OpenAI Service with private networking
- HTTPS access from the internet
AZURE SIMPLE TERRAFORM INFRASTRUCTURE
======================================
┌─────────────────────────────────────────────────────────────────────────────┐
│ AZURE RESOURCE GROUP │
│ (rg-simple-terraform) │
│ East US Region │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ VIRTUAL NETWORK │
│ (simple-terraform-vnet) │
│ 10.0.0.0/16 CIDR │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ CONTAINER APP ENVIRONMENT │
│ (simple-terraform-env) │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ CONTAINER APP │ │
│ │ (simple-terraform-app) │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ CONTAINER │ │ │
│ │ │ (nginx:latest) │ │ │
│ │ │ │ │ │
│ │ │ • CPU: 1 cores │ │ │
│ │ │ • Memory: 2Gi │ │ │
│ │ │ • Port: 8000 │ │ │
│ │ │ • Min Replicas: 1 │ │ │
│ │ │ • Max Replicas: 3 │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────┐ │ │
│ │ │ INGRESS CONFIGURATION │ │ │
│ │ │ │ │ │
│ │ │ • External Access: Enabled │ │ │
│ │ │ • HTTPS Only: Yes │ │ │
│ │ │ • Target Port: 8000 │ │ │
│ │ │ • URL: https://[app].azurecontainerapps.io │ │ │
│ │ └─────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────────────────┐
│ LOG ANALYTICS WORKSPACE │
│ (simple-terraform-logs) │
│ │
│ • SKU: PerGB2018 │
│ • Retention: 30 days │
│ • Container App Logs │
└────────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────────┐
│ POSTGRESQL FLEXIBLE SERVER │
│ (simple-terraform-postgres) │
│ │
│ • Version: PostgreSQL 16 │
│ • SKU: "GP_Standard_D2s_v3" #2 vCores, 8GB RAM │
│ • Storage: 32GB (auto-grow enabled) │
│ • Zone: 2 │
│ • Backup: 7 days retention │
│ • Public Access: Disabled (private subnets plus firewall rules) │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ DATABASE │ │
│ │ (appdb) │ │
│ │ │ │
│ │ • Collation: en_US.utf8 │ │
│ │ • Charset: utf8 │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ FIREWALL RULES │ │
│ │ │ │
│ │ • Allow Azure Services (0.0.0.0 - 0.0.0.0) │ │
│ │ • Allow Container App (10.0.0.0/16) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ USERS │ │
│ │ │ │
│ │ • Admin: postgresadmin (random password) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ STORAGE ACCOUNT │
│ (simpleterraformstor) │
│ │
│ • Tier: Standard │
│ • Replication: LRS │
│ • Kind: StorageV2 │
│ • Public Access: Disabled │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ BLOB CONTAINER │ │
│ │ (data) │ │
│ │ │ │
│ │ • Access Type: Private │ │
│ │ • CORS: Configured for * │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ KEY VAULT │
│ (kv-[random-suffix]) │
│ │
│ • SKU: Standard │
│ • Soft Delete: 7 days │
│ • Purge Protection: Disabled │
│ • Network Access: Allow Azure Services │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ SECRETS │ │
│ │ │ │
│ │ • docker-registry-username (if provided) │ │
│ │ • docker-registry-password (if provided) │ │
│ │ • postgresql-password (if provided) │ │
│ │ • openai-api-secret (if provided) │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ ACCESS POLICIES │ │
│ │ │ │
│ │ • Current User: Full access │ │
│ │ • Container App Identity: Get/List secrets │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────-─────┐
│ MANAGED IDENTITY │
│ (simple-terraform-container-identity) │
│ │
│ • Type: System Assigned │
│ • Assigned to: Container App │
│ • Permissions: Storage Blob Data Contributor, KeyVault Get/List, OpenAI User│
└────────────────────────────────────────────────────────────────────────────-─┘
┌─────────────────────────────────────────────────────────────────────────────┐
│ AZURE OPENAI SERVICE │
│ (simple-terraform-openai) │
│ │
│ • Kind: OpenAI │
│ • SKU: S0 │
│ • Public Access: Disabled (private endpoint only) │
│ • Custom Subdomain: [project-name]-openai │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ MODEL DEPLOYMENT │ │
│ │ (GPT-4.1-[stage]) │ │
│ │ │ │
│ │ • Model: GPT-4.1 (configurable) │ │
│ │ • Version: Latest (configurable) │ │
│ │ • SKU: GlobalStandard │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────┐ │
│ │ PRIVATE ENDPOINT │ │
│ │ (10.0.3.0/24 subnet) │ │
│ │ │ │
│ │ • Private DNS Zone: privatelink.openai.azure.com │ │
│ │ • Network Access: VNet only │ │
│ └─────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
CONNECTION FLOWS
================
Container App ──────────────┐
│
▼
┌───────────────┐
│ Database │
│ (PostgreSQL) │
└───────────────┘
Container App ──────────────┐
│
▼
┌───────────────┐
│ Storage │
│ (Blob Store) │
└───────────────┘
Container App ──────────────┐
│
▼
┌───────────────┐
│ Key Vault │
│ (Secrets) │
└───────────────┘
Container App ──────────────┐
│
▼
┌───────────────┐
│ Azure OpenAI │
│ (Private) │
└───────────────┘
▲
│
Container App ──────────────┘
=====================================
┌───────────────┐
│ Container App │
│ (HTTPS) │
└───────────────┘
▲
│
External Users ────────────┘
- Azure CLI installed and configured
- Terraform installed (>= 1.0)
- Azure subscription with appropriate permissions
-
Login to Azure:
az login
-
Set your subscription:
az account set --subscription "your-subscription-id"
-
Copy and customize variables:
cp terraform.tfvars.example terraform.tfvars # Edit terraform.tfvars with your preferred values -
Initialize Terraform:
terraform init -var-file terraform.tfvars -backend-config=backend.hcl
-
Plan the deployment:
terraform plan
-
Apply the configuration:
terraform apply
-
Access your Planar App: After deployment, the output will show the HTTPS URL where your nginx container is accessible.
- Resource Group: Contains all resources
- Virtual Network: 10.0.0.0/16 with three subnets (app, database, OpenAI)
- Log Analytics Workspace: For container app monitoring
- Container App Environment: Managed environment for container apps
- Container App: Configurable container with HTTPS ingress and database environment variables
- Key Vault: Secure storage for Docker registry credentials and other secrets
- PostgreSQL Flexible Server: Managed PostgreSQL database (B_Standard_B1ms SKU)
- PostgreSQL Database: Application database with dedicated user
- Storage Account: Blob storage with private container
- Azure OpenAI Service: Private OpenAI service with GPT model deployment
- Private Endpoint: Secure private access to Azure OpenAI service
- Private DNS Zone: DNS resolution for OpenAI private endpoint
- Managed Identity: For secure access to Azure resources
Key variables you can customize in terraform.tfvars:
resource_group_name: Name of the Azure resource grouplocation: Azure region (default: "East US")project_name: Prefix for all resource namestags: Tags applied to all resourcescontainer_image: Docker image to deploy (default: "nginx:latest")container_name: Name of the container within the appcontainer_port: Port the container listens on (default: 80)docker_registry_server: Private registry server URL (optional)docker_registry_username: Registry username (optional, stored in Key Vault)docker_registry_password: Registry password (optional, stored in Key Vault)azure_openai_model: Azure OpenAI model name (default: "gpt-4.1")azure_openai_version: Azure OpenAI model version (default: "2025-04-14")- PostgreSQL passwords are automatically generated for security
To destroy all resources:
terraform destroyThe container app has the following environment variables for database connectivity:
DATABASE_URL: Complete PostgreSQL connection stringDB_HOST: Database server hostnameDB_PORT: Database port (5432)DB_NAME: Database nameDB_USER: Application database userDB_PASSWORD: Application database password
The Azure OpenAI service is configured with:
- Private Networking: Accessible only from within the VNet via private endpoint
- Model Deployment: GPT-4.1 model deployed with GlobalStandard SKU
- Authentication: Container app has "Cognitive Services OpenAI User" role
- Endpoint: Available at
https://[project-name]-openai.openai.azure.com/
The container app can access Azure OpenAI using the managed identity for authentication, eliminating the need to manage API keys in your application code.
To deploy containers from private Docker registries (like Azure Container Registry):
-
Configure registry credentials in
terraform.tfvars:docker_registry_server = "myregistry.azurecr.io" docker_registry_username = "myregistry" docker_registry_password = "your-access-key"
-
Update the container image:
container_image = "myregistry.azurecr.io/myapp:v1.0"
-
Deploy with Terraform:
terraform apply
Note: you might neet to
export ARM_SUBSCRIPTION_ID=with your Azure Subscription id.
The credentials are securely stored in Azure Key Vault and automatically used by the container app for registry authentication.
- The container app is configured with HTTPS only (no insecure connections)
- The container runs on the specified port internally and is exposed via HTTPS
- Auto-scaling is configured (1-3 replicas based on load)
- PostgreSQL server uses minimal standard SKU
- Passwords are randomly generated and stored securely in Key Vault
- Docker registry credentials are stored securely in Key Vault when provided
- All resources are tagged for easy identification and cost tracking