From 1866887367d8b9ce525bf16f1bb19cd95b6070be Mon Sep 17 00:00:00 2001 From: Adedeji Mustapha Date: Sun, 29 Mar 2026 14:30:03 +0100 Subject: [PATCH] feat(devops): add terraform configurations for dev/staging/prod --- infra/README.md | 44 +++++++++++ infra/environments/dev.tfvars | 5 ++ infra/environments/prod.tfvars | 5 ++ infra/environments/staging.tfvars | 5 ++ infra/main.tf | 126 ++++++++++++++++++++++++++++++ infra/variables.tf | 34 ++++++++ 6 files changed, 219 insertions(+) create mode 100644 infra/README.md create mode 100644 infra/environments/dev.tfvars create mode 100644 infra/environments/prod.tfvars create mode 100644 infra/environments/staging.tfvars create mode 100644 infra/main.tf create mode 100644 infra/variables.tf diff --git a/infra/README.md b/infra/README.md new file mode 100644 index 0000000..3be0aa9 --- /dev/null +++ b/infra/README.md @@ -0,0 +1,44 @@ +# AgenticPay Infrastructure + +This directory contains the Infrastructure as Code (IaC) for AgenticPay using Terraform. It provisions AWS resources for the Next.js frontend (AWS Amplify), Express.js backend (AWS App Runner), and underlying networking (VPC). + +## Architecture +- **State Management**: Remote state stored securely in AWS S3 with DynamoDB state locking. +- **Frontend**: AWS Amplify (optimizes Next.js SSR and static asset delivery). +- **Backend**: AWS App Runner (serverless container compute pulling from ECR). +- **Networking**: Isolated VPC with public/private subnets and NAT Gateways. + +## Supported Environments +We use a workspace/tfvars approach to support multiple environments: +- `dev`: Active development and testing against Stellar Testnet. +- `staging`: Pre-production replica against Stellar Testnet. +- `prod`: Live production environment against Stellar Public network. + +## Usage Guide + +### Prerequisites +1. Install [Terraform](https://developer.hashicorp.com/terraform/downloads) (>= 1.5.0). +2. Configure your AWS CLI credentials (`aws configure`). + +### Deployment Steps + +1. **Initialize Terraform** + Downloads the required providers and initializes the S3 backend. + ```bash + terraform init + ``` +2. Select an Environment + Select the workspace corresponding to your environment (create it if it doesn't exist). + ``` + terraform workspace select dev || terraform workspace new dev + ``` +3. Plan the Deployment + Review the changes Terraform will make to your infrastructure. + ```bash + terraform plan -var-file="environments/dev.tfvars" + ``` +4. Apply the Changes + Provision the resources. + ```bash + terraform apply -var-file="environments/dev.tfvars" + ``` \ No newline at end of file diff --git a/infra/environments/dev.tfvars b/infra/environments/dev.tfvars new file mode 100644 index 0000000..949496c --- /dev/null +++ b/infra/environments/dev.tfvars @@ -0,0 +1,5 @@ +environment = "dev" +stellar_network = "testnet" +vpc_cidr = "10.0.0.0/16" +private_subnets = ["10.0.1.0/24", "10.0.2.0/24"] +public_subnets = ["10.0.101.0/24", "10.0.102.0/24"] diff --git a/infra/environments/prod.tfvars b/infra/environments/prod.tfvars new file mode 100644 index 0000000..f7970a4 --- /dev/null +++ b/infra/environments/prod.tfvars @@ -0,0 +1,5 @@ +environment = "prod" +stellar_network = "public" +vpc_cidr = "10.2.0.0/16" +private_subnets = ["10.2.1.0/24", "10.2.2.0/24"] +public_subnets = ["10.2.101.0/24", "10.2.102.0/24"] diff --git a/infra/environments/staging.tfvars b/infra/environments/staging.tfvars new file mode 100644 index 0000000..3cbef09 --- /dev/null +++ b/infra/environments/staging.tfvars @@ -0,0 +1,5 @@ +environment = "staging" +stellar_network = "testnet" +vpc_cidr = "10.1.0.0/16" +private_subnets = ["10.1.1.0/24", "10.1.2.0/24"] +public_subnets = ["10.1.101.0/24", "10.1.102.0/24"] diff --git a/infra/main.tf b/infra/main.tf new file mode 100644 index 0000000..4561517 --- /dev/null +++ b/infra/main.tf @@ -0,0 +1,126 @@ +terraform { + required_version = ">= 1.5.0" + + # Acceptance Criteria: State management + backend "s3" { + bucket = "agenticpay-terraform-state" + key = "infrastructure/terraform.tfstate" + region = "us-east-1" + dynamodb_table = "agenticpay-terraform-locks" + encrypt = true + } + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} + +provider "aws" { + region = var.aws_region + + default_tags { + tags = { + Project = "AgenticPay" + Environment = var.environment + ManagedBy = "Terraform" + } + } +} + +# ------------------------------------------------------------------------------ +# FOUNDATIONAL NETWORKING +# ------------------------------------------------------------------------------ +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "5.0.0" + + name = "agenticpay-${var.environment}-vpc" + cidr = var.vpc_cidr + + azs = ["${var.aws_region}a", "${var.aws_region}b"] + private_subnets = var.private_subnets + public_subnets = var.public_subnets + + enable_nat_gateway = true + single_nat_gateway = var.environment != "prod" # Cost optimization for non-prod +} + +# ------------------------------------------------------------------------------ +# BACKEND RESOURCES (Express.js API) +# ------------------------------------------------------------------------------ +resource "aws_ecr_repository" "backend" { + name = "agenticpay-backend-${var.environment}" + image_tag_mutability = "MUTABLE" + + image_scanning_configuration { + scan_on_push = true + } +} + +resource "aws_apprunner_service" "backend" { + service_name = "agenticpay-backend-${var.environment}" + + source_configuration { + image_repository { + image_configuration { + port = "3001" + runtime_environment_variables = { + NODE_ENV = var.environment + STELLAR_NETWORK = var.stellar_network + } + } + image_identifier = "${aws_ecr_repository.backend.repository_url}:latest" + image_repository_type = "ECR" + } + auto_deployments_enabled = true + } + + network_configuration { + egress_configuration { + egress_type = "VPC" + vpc_connector_arn = aws_apprunner_vpc_connector.connector.arn + } + } +} + +resource "aws_apprunner_vpc_connector" "connector" { + vpc_connector_name = "agenticpay-vpc-connector-${var.environment}" + subnets = module.vpc.private_subnets + security_groups = [module.vpc.default_security_group_id] +} + +# ------------------------------------------------------------------------------ +# FRONTEND RESOURCES (Next.js) +# ------------------------------------------------------------------------------ +resource "aws_amplify_app" "frontend" { + name = "agenticpay-frontend-${var.environment}" + repository = "https://github.com/Smartdevs17/agenticpay" + + build_spec = <<-EOT + version: 1 + frontend: + phases: + preBuild: + commands: + - cd frontend + - npm install + build: + commands: + - npm run build + artifacts: + baseDirectory: frontend/.next + files: + - '**/*' + cache: + paths: + - frontend/node_modules/**/* + EOT + + environment_variables = { + NEXT_PUBLIC_API_URL = "https://${aws_apprunner_service.backend.service_url}/api/v1" + NODE_ENV = var.environment + } +} \ No newline at end of file diff --git a/infra/variables.tf b/infra/variables.tf new file mode 100644 index 0000000..6de8e68 --- /dev/null +++ b/infra/variables.tf @@ -0,0 +1,34 @@ +variable "aws_region" { + description = "The AWS region to deploy into" + type = string + default = "us-east-1" +} + +variable "environment" { + description = "The deployment environment (dev, staging, prod)" + type = string + validation { + condition = contains(["dev", "staging", "prod"], var.environment) + error_message = "Environment must be dev, staging, or prod." + } +} + +variable "stellar_network" { + description = "Stellar network to connect to (testnet or public)" + type = string +} + +variable "vpc_cidr" { + description = "CIDR block for the VPC" + type = string +} + +variable "private_subnets" { + description = "List of private subnet CIDRs" + type = list(string) +} + +variable "public_subnets" { + description = "List of public subnet CIDRs" + type = list(string) +} \ No newline at end of file