main.tf•15.7 kB
################################################################################
# Terraform Configuration for KYC MCP Server on AWS EC2
# This configuration provisions all necessary AWS resources
################################################################################
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
# Uncomment to use S3 backend for state management
# backend "s3" {
# bucket = "your-terraform-state-bucket"
# key = "kyc-mcp-server/terraform.tfstate"
# region = "us-east-1"
# encrypt = true
# dynamodb_table = "terraform-state-lock"
# }
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Project = "KYC-MCP-Server"
Environment = var.environment
ManagedBy = "Terraform"
}
}
}
################################################################################
# Data Sources
################################################################################
# Get latest Amazon Linux 2 AMI
data "aws_ami" "amazon_linux_2" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
# Get latest Ubuntu 22.04 AMI
data "aws_ami" "ubuntu_22_04" {
most_recent = true
owners = ["099720109477"] # Canonical
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
# Get current AWS account ID
data "aws_caller_identity" "current" {}
# Get current AWS region
data "aws_region" "current" {}
################################################################################
# VPC and Networking
################################################################################
resource "aws_vpc" "main" {
count = var.create_vpc ? 1 : 0
cidr_block = var.vpc_cidr
enable_dns_hostnames = true
enable_dns_support = true
tags = {
Name = "${var.project_name}-vpc"
}
}
resource "aws_internet_gateway" "main" {
count = var.create_vpc ? 1 : 0
vpc_id = aws_vpc.main[0].id
tags = {
Name = "${var.project_name}-igw"
}
}
resource "aws_subnet" "public" {
count = var.create_vpc ? length(var.availability_zones) : 0
vpc_id = aws_vpc.main[0].id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-public-subnet-${count.index + 1}"
}
}
resource "aws_route_table" "public" {
count = var.create_vpc ? 1 : 0
vpc_id = aws_vpc.main[0].id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main[0].id
}
tags = {
Name = "${var.project_name}-public-rt"
}
}
resource "aws_route_table_association" "public" {
count = var.create_vpc ? length(var.availability_zones) : 0
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public[0].id
}
################################################################################
# Security Groups
################################################################################
resource "aws_security_group" "kyc_mcp_server" {
name_prefix = "${var.project_name}-sg-"
description = "Security group for KYC MCP Server"
vpc_id = var.create_vpc ? aws_vpc.main[0].id : var.existing_vpc_id
# SSH access
ingress {
description = "SSH from allowed IPs"
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = var.allowed_ssh_cidrs
}
# HTTP access
ingress {
description = "HTTP"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# HTTPS access
ingress {
description = "HTTPS"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# Metrics port (restrict to monitoring IPs in production)
ingress {
description = "Prometheus metrics"
from_port = 9090
to_port = 9091
protocol = "tcp"
cidr_blocks = var.allowed_monitoring_cidrs
}
# Node exporter
ingress {
description = "Node exporter"
from_port = 9100
to_port = 9100
protocol = "tcp"
cidr_blocks = var.allowed_monitoring_cidrs
}
# Outbound traffic
egress {
description = "All outbound traffic"
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "${var.project_name}-sg"
}
lifecycle {
create_before_destroy = true
}
}
################################################################################
# IAM Role and Instance Profile
################################################################################
resource "aws_iam_role" "kyc_mcp_server" {
name_prefix = "${var.project_name}-role-"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})
tags = {
Name = "${var.project_name}-role"
}
}
# CloudWatch Logs policy
resource "aws_iam_role_policy" "cloudwatch_logs" {
name_prefix = "cloudwatch-logs-"
role = aws_iam_role.kyc_mcp_server.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
]
Resource = "arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:log-group:/aws/ec2/${var.project_name}*"
}
]
})
}
# CloudWatch Metrics policy
resource "aws_iam_role_policy" "cloudwatch_metrics" {
name_prefix = "cloudwatch-metrics-"
role = aws_iam_role.kyc_mcp_server.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"cloudwatch:PutMetricData"
]
Resource = "*"
}
]
})
}
# S3 backup policy
resource "aws_iam_role_policy" "s3_backup" {
count = var.enable_s3_backup ? 1 : 0
name_prefix = "s3-backup-"
role = aws_iam_role.kyc_mcp_server.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"s3:PutObject",
"s3:GetObject",
"s3:ListBucket",
"s3:DeleteObject"
]
Resource = [
"arn:aws:s3:::${var.backup_bucket_name}",
"arn:aws:s3:::${var.backup_bucket_name}/*"
]
}
]
})
}
# SSM policy for parameter store
resource "aws_iam_role_policy" "ssm_parameters" {
name_prefix = "ssm-parameters-"
role = aws_iam_role.kyc_mcp_server.id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:GetParametersByPath"
]
Resource = "arn:aws:ssm:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:parameter/${var.project_name}/*"
}
]
})
}
# Attach CloudWatch agent policy
resource "aws_iam_role_policy_attachment" "cloudwatch_agent" {
role = aws_iam_role.kyc_mcp_server.name
policy_arn = "arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy"
}
# Attach SSM managed instance policy
resource "aws_iam_role_policy_attachment" "ssm_managed_instance" {
role = aws_iam_role.kyc_mcp_server.name
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
resource "aws_iam_instance_profile" "kyc_mcp_server" {
name_prefix = "${var.project_name}-profile-"
role = aws_iam_role.kyc_mcp_server.name
tags = {
Name = "${var.project_name}-instance-profile"
}
}
################################################################################
# Key Pair
################################################################################
resource "aws_key_pair" "deployer" {
count = var.create_key_pair ? 1 : 0
key_name = "${var.project_name}-key"
public_key = var.ssh_public_key
tags = {
Name = "${var.project_name}-key"
}
}
################################################################################
# Elastic IP
################################################################################
resource "aws_eip" "kyc_mcp_server" {
count = var.use_elastic_ip ? 1 : 0
domain = "vpc"
tags = {
Name = "${var.project_name}-eip"
}
}
resource "aws_eip_association" "kyc_mcp_server" {
count = var.use_elastic_ip ? 1 : 0
instance_id = aws_instance.kyc_mcp_server.id
allocation_id = aws_eip.kyc_mcp_server[0].id
}
################################################################################
# EC2 Instance
################################################################################
resource "aws_instance" "kyc_mcp_server" {
ami = var.ami_id != "" ? var.ami_id : (var.os_type == "ubuntu" ? data.aws_ami.ubuntu_22_04.id : data.aws_ami.amazon_linux_2.id)
instance_type = var.instance_type
subnet_id = var.create_vpc ? aws_subnet.public[0].id : var.existing_subnet_id
vpc_security_group_ids = [aws_security_group.kyc_mcp_server.id]
iam_instance_profile = aws_iam_instance_profile.kyc_mcp_server.name
key_name = var.create_key_pair ? aws_key_pair.deployer[0].key_name : var.existing_key_name
associate_public_ip_address = !var.use_elastic_ip
root_block_device {
volume_type = var.root_volume_type
volume_size = var.root_volume_size
delete_on_termination = true
encrypted = true
}
user_data = templatefile("${path.module}/user-data.sh", {
project_name = var.project_name
environment = var.environment
})
metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
}
monitoring = var.enable_detailed_monitoring
tags = {
Name = "${var.project_name}-instance"
}
lifecycle {
ignore_changes = [ami]
}
}
################################################################################
# CloudWatch Log Group
################################################################################
resource "aws_cloudwatch_log_group" "kyc_mcp_server" {
name = "/aws/ec2/${var.project_name}"
retention_in_days = var.log_retention_days
tags = {
Name = "${var.project_name}-logs"
}
}
################################################################################
# CloudWatch Alarms
################################################################################
# CPU Utilization Alarm
resource "aws_cloudwatch_metric_alarm" "cpu_utilization" {
alarm_name = "${var.project_name}-high-cpu"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "2"
metric_name = "CPUUtilization"
namespace = "AWS/EC2"
period = "300"
statistic = "Average"
threshold = var.cpu_alarm_threshold
alarm_description = "This metric monitors EC2 CPU utilization"
alarm_actions = var.alarm_sns_topic_arn != "" ? [var.alarm_sns_topic_arn] : []
dimensions = {
InstanceId = aws_instance.kyc_mcp_server.id
}
tags = {
Name = "${var.project_name}-cpu-alarm"
}
}
# Memory Utilization Alarm (requires CloudWatch agent)
resource "aws_cloudwatch_metric_alarm" "memory_utilization" {
alarm_name = "${var.project_name}-high-memory"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "2"
metric_name = "mem_used_percent"
namespace = "CWAgent"
period = "300"
statistic = "Average"
threshold = var.memory_alarm_threshold
alarm_description = "This metric monitors EC2 memory utilization"
alarm_actions = var.alarm_sns_topic_arn != "" ? [var.alarm_sns_topic_arn] : []
dimensions = {
InstanceId = aws_instance.kyc_mcp_server.id
}
tags = {
Name = "${var.project_name}-memory-alarm"
}
}
# Disk Utilization Alarm
resource "aws_cloudwatch_metric_alarm" "disk_utilization" {
alarm_name = "${var.project_name}-high-disk"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "2"
metric_name = "disk_used_percent"
namespace = "CWAgent"
period = "300"
statistic = "Average"
threshold = var.disk_alarm_threshold
alarm_description = "This metric monitors EC2 disk utilization"
alarm_actions = var.alarm_sns_topic_arn != "" ? [var.alarm_sns_topic_arn] : []
dimensions = {
InstanceId = aws_instance.kyc_mcp_server.id
path = "/"
fstype = "ext4"
}
tags = {
Name = "${var.project_name}-disk-alarm"
}
}
# Status Check Failed Alarm
resource "aws_cloudwatch_metric_alarm" "status_check_failed" {
alarm_name = "${var.project_name}-status-check-failed"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "2"
metric_name = "StatusCheckFailed"
namespace = "AWS/EC2"
period = "60"
statistic = "Maximum"
threshold = "0"
alarm_description = "This metric monitors EC2 status checks"
alarm_actions = var.alarm_sns_topic_arn != "" ? [var.alarm_sns_topic_arn] : []
dimensions = {
InstanceId = aws_instance.kyc_mcp_server.id
}
tags = {
Name = "${var.project_name}-status-alarm"
}
}
################################################################################
# S3 Bucket for Backups (Optional)
################################################################################
resource "aws_s3_bucket" "backups" {
count = var.enable_s3_backup && var.create_backup_bucket ? 1 : 0
bucket_prefix = "${var.project_name}-backups-"
tags = {
Name = "${var.project_name}-backups"
}
}
resource "aws_s3_bucket_versioning" "backups" {
count = var.enable_s3_backup && var.create_backup_bucket ? 1 : 0
bucket = aws_s3_bucket.backups[0].id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "backups" {
count = var.enable_s3_backup && var.create_backup_bucket ? 1 : 0
bucket = aws_s3_bucket.backups[0].id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket_lifecycle_configuration" "backups" {
count = var.enable_s3_backup && var.create_backup_bucket ? 1 : 0
bucket = aws_s3_bucket.backups[0].id
rule {
id = "delete-old-backups"
status = "Enabled"
expiration {
days = var.backup_retention_days
}
noncurrent_version_expiration {
noncurrent_days = 30
}
}
}
resource "aws_s3_bucket_public_access_block" "backups" {
count = var.enable_s3_backup && var.create_backup_bucket ? 1 : 0
bucket = aws_s3_bucket.backups[0].id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}