-
Notifications
You must be signed in to change notification settings - Fork 0
Add Terraform Infrastructure as Code Setup #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
DJSaunders1997
wants to merge
1
commit into
main
Choose a base branch
from
terraform-iac
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+357
−0
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| # GPTeasers Terraform Infrastructure | ||
|
|
||
| This directory contains the Infrastructure as Code (IaC) configuration for deploying the GPTeasers quiz application to Azure Container Apps. | ||
|
|
||
| ## 📁 Directory Contents | ||
|
|
||
| - `main.tf` - Main infrastructure configuration (Resource Group, Container App Environment, Container App) | ||
| - `variables.tf` - Input variables for the infrastructure | ||
| - `outputs.tf` - Output values from the deployed infrastructure | ||
| - `providers.tf` - Terraform provider configurations | ||
| - `terraform.tfvars` - Actual values for sensitive variables (not committed to git) | ||
| - `terraform.tfvars.example` - Example file showing required variable structure | ||
| - `terraform.tfstate` - Current state of deployed infrastructure (not committed to git) | ||
| - `graph.dot` - Infrastructure dependency graph in DOT format | ||
| - `infrastructure.png` & `infrastructure.svg` - Visual representations of the infrastructure | ||
|
|
||
| ## 🚀 Deploying the Infrastructure | ||
|
|
||
| ### Prerequisites | ||
|
|
||
| 1. **Azure CLI installed and authenticated**: | ||
| ```bash | ||
| az login | ||
| ``` | ||
|
|
||
| 2. **Terraform installed** (v1.14.3+ recommended) | ||
|
|
||
| 3. **Configure your secrets**: | ||
| ```bash | ||
| cp terraform.tfvars.example terraform.tfvars | ||
| # Edit terraform.tfvars with your actual API keys and tokens | ||
| ``` | ||
|
|
||
| ### Deployment Commands | ||
|
|
||
| 1. **Initialize Terraform** (first time only): | ||
| ```bash | ||
| cd backend/terraform | ||
| terraform init | ||
| ``` | ||
|
|
||
| 2. **Review the planned changes**: | ||
| ```bash | ||
| cd backend/terraform | ||
| terraform plan | ||
| ``` | ||
|
|
||
| 3. **Deploy the infrastructure**: | ||
| ```bash | ||
| cd backend/terraform | ||
| terraform apply | ||
| ``` | ||
|
|
||
| 4. **Get the deployed app URL**: | ||
| ```bash | ||
| cd backend/terraform | ||
| terraform output container_app_url | ||
| ``` | ||
|
|
||
| ### Cleanup | ||
|
|
||
| To destroy all resources: | ||
| ```bash | ||
| cd backend/terraform | ||
| terraform destroy | ||
| ``` | ||
|
|
||
| ## ⚠️ Important Notes | ||
|
|
||
| ### Hardcoded Container Image Concern | ||
|
|
||
| **I'm a bit confused and concerned that the container image is hardcoded in `main.tf`**: | ||
|
|
||
| ```terraform | ||
| image = "ghcr.io/djsaunders1997/gpteasers:7aa9ecacb7f655e25c149d704108690263ec26b4" | ||
| ``` | ||
|
|
||
| This means: | ||
| - The infrastructure is tied to a specific image version/tag | ||
| - Updates require manual Terraform changes | ||
| - CI/CD pipelines can't automatically update the infrastructure | ||
| - Risk of the image being deleted or becoming unavailable | ||
|
|
||
| **Future Enhancement Idea**: This would be a cool project to parameterize the image reference: | ||
| - Add an `image_tag` variable to `variables.tf` | ||
| - Make the image reference dynamic: `"ghcr.io/djsaunders1997/gpteasers:${var.image_tag}"` | ||
| - Allow CI/CD to pass the latest image tag during deployment | ||
| - Enable blue/green deployments with different image versions | ||
|
|
||
| For now, the infrastructure works as-is, but image updates require manual intervention. This could be a fun Terraform parameterization project for the future! 🎯 | ||
|
|
||
| ## 🔧 Configuration Details | ||
|
|
||
| ### Resources Created | ||
|
|
||
| - **Resource Group**: `ContainerApps` (UK West region) | ||
| - **Container App Environment**: `container-app-environment` | ||
| - **Container App**: `gpteasers` with: | ||
| - 0.25 CPU cores, 0.5Gi memory | ||
| - External ingress on port 8000 | ||
| - GitHub Container Registry integration | ||
| - Environment variables for all AI provider API keys | ||
|
|
||
| ### Secrets Management | ||
|
|
||
| The following secrets are managed via Terraform: | ||
| - OpenAI API Key | ||
| - Azure AI API Key & Base URL | ||
| - Gemini API Key | ||
| - DeepSeek API Key | ||
| - GitHub Container Registry Token | ||
|
|
||
| **Security Note**: Secrets are defined in `terraform.tfvars` (gitignored) and managed through Terraform's lifecycle rules to prevent accidental overwrites during CI/CD deployments. | ||
|
|
||
| ### Network Configuration | ||
|
|
||
| - **Ingress**: External enabled, auto transport | ||
| - **Traffic**: 100% to latest revision | ||
| - **Scaling**: 0-1 replicas (manual scaling) | ||
|
|
||
| ## 📊 Infrastructure Visualization | ||
|
|
||
| View the infrastructure dependencies: | ||
| ```bash | ||
| # Generate DOT graph | ||
| cd backend/terraform | ||
| terraform graph > graph.dot | ||
|
|
||
| # Convert to PNG (requires GraphViz) | ||
| dot -Tpng graph.dot -o infrastructure.png | ||
| ``` | ||
|
|
||
| ## 🔍 Troubleshooting | ||
|
|
||
| - **State issues**: `terraform refresh` to sync with Azure | ||
| - **Import existing resources**: `terraform import <resource_type>.<name> <azure_resource_id>` | ||
| - **Debug mode**: `TF_LOG=DEBUG terraform apply` | ||
|
|
||
| ## 📝 Development Workflow | ||
|
|
||
| 1. Make changes to `.tf` files | ||
| 2. Run `terraform plan` to preview | ||
| 3. Run `terraform apply` to deploy | ||
| 4. Use `terraform output` to get URLs/endpoints | ||
| 5. Commit changes (excluding `.tfstate` and `.tfvars`) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| # Resource Group | ||
| resource "azurerm_resource_group" "gpteasers" { | ||
| name = var.resource_group_name | ||
| location = var.location | ||
| } | ||
|
|
||
| # Container App Environment | ||
| resource "azurerm_container_app_environment" "gpteasers" { | ||
| name = var.container_app_environment_name | ||
| location = azurerm_resource_group.gpteasers.location | ||
| resource_group_name = azurerm_resource_group.gpteasers.name | ||
| # Remove log_analytics_workspace_id - the existing environment doesn't have one | ||
| } | ||
|
|
||
| # Container App | ||
| resource "azurerm_container_app" "gpteasers" { | ||
| name = var.container_app_name | ||
| container_app_environment_id = azurerm_container_app_environment.gpteasers.id | ||
| resource_group_name = azurerm_resource_group.gpteasers.name | ||
| revision_mode = "Single" | ||
|
|
||
| template { | ||
| container { | ||
| name = "gpteasers" | ||
| image = "ghcr.io/djsaunders1997/gpteasers:7aa9ecacb7f655e25c149d704108690263ec26b4" | ||
| cpu = 0.25 | ||
| memory = "0.5Gi" | ||
|
|
||
| env { | ||
| name = "OPENAI_API_KEY" | ||
| secret_name = "openai-api-key-secret" | ||
| } | ||
| env { | ||
| name = "AZURE_AI_API_KEY" | ||
| secret_name = "azure-ai-api-key-secret" | ||
| } | ||
| env { | ||
| name = "GEMINI_API_KEY" | ||
| secret_name = "gemini-api-key-secret" | ||
| } | ||
| env { | ||
| name = "DEEPSEEK_API_KEY" | ||
| secret_name = "deepseek-api-key-secret" | ||
| } | ||
| env { | ||
| name = "AZURE_AI_API_BASE" | ||
| secret_name = "azure-ai-api-base-secret" | ||
| } | ||
| } | ||
|
|
||
| min_replicas = 0 | ||
| max_replicas = 1 | ||
| } | ||
|
|
||
| ingress { | ||
| external_enabled = true | ||
| target_port = 8000 | ||
| transport = "auto" # Changed from "Auto" to "auto" to match existing | ||
| traffic_weight { | ||
| latest_revision = true | ||
| percentage = 100 | ||
| } | ||
| } | ||
|
|
||
| registry { | ||
| server = "ghcr.io" | ||
| username = "DJSaunders1997" | ||
| password_secret_name = "ghcrio-djsaunders1997" | ||
| } | ||
|
|
||
| # Secrets are managed externally - these placeholders prevent Terraform from removing them | ||
| secret { | ||
| name = "openai-api-key-secret" | ||
| value = var.openai_api_key | ||
| } | ||
|
|
||
| secret { | ||
| name = "azure-ai-api-key-secret" | ||
| value = var.azure_ai_api_key | ||
| } | ||
|
|
||
| secret { | ||
| name = "gemini-api-key-secret" | ||
| value = var.gemini_api_key | ||
| } | ||
|
|
||
| secret { | ||
| name = "deepseek-api-key-secret" | ||
| value = var.deepseek_api_key | ||
| } | ||
|
|
||
| secret { | ||
| name = "azure-ai-api-base-secret" | ||
| value = var.azure_ai_api_base | ||
| } | ||
|
|
||
| secret { | ||
| name = "ghcrio-djsaunders1997" | ||
| value = var.ghcr_token | ||
| } | ||
|
|
||
| lifecycle { | ||
| ignore_changes = [ | ||
| secret, # Don't update secrets - they're managed by CI/CD or manually | ||
| template[0].container[0].image, # Allow image updates from CI/CD | ||
| ] | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| output "container_app_url" { | ||
| description = "URL of the deployed container app" | ||
| value = azurerm_container_app.gpteasers.latest_revision_fqdn | ||
| } | ||
|
|
||
| output "resource_group_name" { | ||
| description = "Name of the resource group" | ||
| value = azurerm_resource_group.gpteasers.name | ||
| } | ||
|
|
||
| output "container_app_environment_name" { | ||
| description = "Name of the container app environment" | ||
| value = azurerm_container_app_environment.gpteasers.name | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| terraform { | ||
| required_providers { | ||
| azurerm = { | ||
| source = "hashicorp/azurerm" | ||
| version = "~> 3.0" | ||
| } | ||
| } | ||
| } | ||
|
|
||
| provider "azurerm" { | ||
| features {} | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Copy this file to terraform.tfvars and fill in your actual values | ||
| # DO NOT commit terraform.tfvars to git - it contains sensitive information | ||
|
|
||
| # API Keys | ||
| openai_api_key = "your-openai-api-key-here" | ||
| gemini_api_key = "your-gemini-api-key-here" | ||
| azure_ai_api_key = "your-azure-ai-api-key-here" | ||
| azure_ai_api_base = "your-azure-ai-api-base-url-here" | ||
| deepseek_api_key = "your-deepseek-api-key-here" | ||
|
|
||
| # GitHub Container Registry | ||
| ghcr_token = "your-github-personal-access-token-here" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| variable "resource_group_name" { | ||
| description = "Name of the resource group" | ||
| type = string | ||
| default = "ContainerApps" | ||
| } | ||
|
|
||
| variable "location" { | ||
| description = "Azure region" | ||
| type = string | ||
| default = "UK West" | ||
| } | ||
|
|
||
| variable "container_app_name" { | ||
| description = "Name of the container app" | ||
| type = string | ||
| default = "gpteasers" | ||
| } | ||
|
|
||
| variable "container_app_environment_name" { | ||
| description = "Name of the container app environment" | ||
| type = string | ||
| default = "container-app-environment" | ||
| } | ||
|
|
||
| # Secret variables - values loaded from terraform.tfvars (not committed) | ||
| variable "openai_api_key" { | ||
| description = "OpenAI API key" | ||
| type = string | ||
| sensitive = true | ||
| } | ||
|
|
||
| variable "gemini_api_key" { | ||
| description = "Gemini API key" | ||
| type = string | ||
| sensitive = true | ||
| } | ||
|
|
||
| variable "azure_ai_api_key" { | ||
| description = "Azure AI API key" | ||
| type = string | ||
| sensitive = true | ||
| } | ||
|
|
||
| variable "azure_ai_api_base" { | ||
| description = "Azure AI API base URL" | ||
| type = string | ||
| sensitive = true | ||
| } | ||
|
|
||
| variable "deepseek_api_key" { | ||
| description = "DeepSeek API key" | ||
| type = string | ||
| sensitive = true | ||
| } | ||
|
|
||
| variable "ghcr_token" { | ||
| description = "GitHub Container Registry token" | ||
| type = string | ||
| sensitive = true | ||
| } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review:
Correctness:
terraform.tfvars,terraform.tfstate*,.terraform/).Improvements:
Risk Assessment:
.tfstateis managed securely as it can contain sensitive information.Overall:
The changes made appear to address the necessary additions for Terraform file management. Adding comments and sorting the entries would be helpful for code maintenance. Regularly reviewing these ignore lists is important to maintain security and prevent accidental exposure of sensitive information.