Skip to content

zama-ai/terraform-coprocessor-modules

Repository files navigation

Terraform-Coprocessor-Modules

This repo provides a Terraform Module for deploying the base layer infrastructure for the Coprocessor of the Zama Protocol.


Architecture

Infrastructure deployed by the examples/testnet-complete example.

Coprocessor Infrastructure


Requirements

Requirement Notes
AWS account IAM permissions to create VPC, EKS, RDS, S3, IAM, SQS, and EventBridge resources
Terraform ≥ 1.11 Required for write-only variables (password_wo) and S3 native state locking
AWS CLI Configured with credentials for the target account (aws configure or env vars)
kubectl For post-deployment cluster access (aws eks update-kubeconfig ...)
S3 bucket for remote state Pre-created; referenced in the example versions.tf backend block

Usage

Two ready-to-deploy examples are provided. Each is a fully-formed, immediately deployable configuration — not a showcase of every available parameter, but a production-ready starting point that covers the standard Coprocessor deployment.

Example Use case
examples/testnet-complete Greenfield — creates VPC, EKS, RDS, and S3 from scratch
examples/testnet-existing-infra Bring-your-own VPC and EKS — only deploys RDS and S3

For the full set of available inputs and their defaults, see the Inputs table below or terraform.tfvars.example at the repo root.

Steps:

  1. Copy the relevant example directory into your own infrastructure repository.
  2. Update the source in main.tf to reference the remote versioned release: git::https://github.com/zama-ai/terraform-coprocessor-modules.git?ref=<version>
  3. Replace every value marked # CHANGE ME in terraform.tfvars — primarily partner_name, aws_region, and any account-specific IDs.
  4. Update the backend bucket/key/region in versions.tf.
  5. All other values are pre-configured with sensible defaults and require no changes for a standard testnet deployment.
terraform init
terraform plan
terraform apply

Tests

Uses the native Terraform test framework (requires Terraform ≥ 1.10). All tests use mock providers and command = plan — no real AWS credentials needed.

Run all tests:

terraform test                        # root module
cd modules/<name> && terraform test   # individual submodule

Tests live in tests/unit.tftest.hcl within each module directory.


Pre-commit

This repo uses pre-commit to enforce consistency on every commit.

Dependencies — must be on your PATH:

brew install pre-commit terraform-docs tflint

Hooks that run automatically:

Hook What it does
terraform_fmt Formats all .tf files
terraform_validate Validates module configuration
terraform_tflint Lints for common mistakes and best practices
terraform_docs Regenerates the BEGIN_TF_DOCS sections in all README.md files
check-merge-conflict Blocks commits containing unresolved merge conflict markers
end-of-file-fixer Ensures files end with a newline
trailing-whitespace Removes trailing whitespace

To run all hooks manually: pre-commit run --all-files


Requirements

Name Version
terraform >= 1.11
aws ~> 6.0
kubernetes ~> 2.0
random ~> 3.0

Providers

No providers.

Modules

Name Source Version
eks ./modules/eks n/a
networking ./modules/networking n/a
rds ./modules/rds n/a
s3 ./modules/s3 n/a

Resources

No resources.

Inputs

Name Description Type Default Required
aws_region AWS region where resources will be deployed. string n/a yes
default_tags Tags to apply to all resources. map(string) {} no
eks EKS cluster configuration. Set enabled = false to skip all EKS resources.
object({
enabled = optional(bool, false)

cluster = optional(object({
# Naming
version = optional(string, "1.35")
name_override = optional(string, null) # overrides computed "-" cluster name

# Endpoint access
endpoint_public_access = optional(bool, false)
endpoint_private_access = optional(bool, true)
endpoint_public_access_cidrs = optional(list(string), [])

# Auth
enable_irsa = optional(bool, true)
enable_creator_admin_permissions = optional(bool, true) # grants the Terraform caller admin access
admin_role_arns = optional(list(string), [])
}), {})

addons = optional(object({
# Standard managed addons; each value is passed verbatim to the upstream eks module
defaults = optional(map(any), {
aws-ebs-csi-driver = { most_recent = true }
coredns = { most_recent = true }
vpc-cni = { most_recent = true, before_compute = true }
kube-proxy = { most_recent = true }
eks-pod-identity-agent = { most_recent = true, before_compute = true }
})

# Additional addons merged on top of defaults
extra = optional(map(any), {})

# VPC CNI environment tuning
vpc_cni_config = optional(object({
init = optional(object({
env = optional(object({
DISABLE_TCP_EARLY_DEMUX = optional(string, "true")
}), {})
}), {})
env = optional(object({
ENABLE_POD_ENI = optional(string, "true")
POD_SECURITY_GROUP_ENFORCING_MODE = optional(string, "standard")
ENABLE_PREFIX_DELEGATION = optional(string, "true")
WARM_PREFIX_TARGET = optional(string, "1")
}), {})
}), {})
}), {})

node_groups = optional(object({
# Defaults merged into every node group (same schema as groups entries)
defaults = optional(map(any), {})

# IAM policies attached to every node group's IAM role
default_iam_policies = optional(map(string), {
AmazonEBSCSIDriverPolicy = "arn:aws:iam::aws:policy/service-role/AmazonEBSCSIDriverPolicy"
AmazonEC2ContainerRegistryReadOnly = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
AmazonEKSWorkerNodePolicy = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
AmazonEKS_CNI_Policy = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
})

groups = optional(map(object({
# Capacity
capacity_type = optional(string, "ON_DEMAND") # "ON_DEMAND"
"SPOT"
min_size = optional(number, 1)
max_size = optional(number, 3)
desired_size = optional(number, 1)

# Instance
instance_types = optional(list(string), ["t3.medium"])
ami_type = optional(string, "AL2023_x86_64_STANDARD")
use_custom_launch_template = optional(bool, true)

# Storage
disk_size = optional(number, 30)
disk_type = optional(string, "gp3")

# Scheduling
labels = optional(map(string), {})
tags = optional(map(string), {})
use_additional_subnets = optional(bool, false) # place group in additional_subnet_ids instead of private
taints = optional(map(object({
key = string
value = optional(string)
effect = string # "NO_SCHEDULE"
"NO_EXECUTE"
environment Deployment environment (e.g. testnet, mainnet). string n/a yes
kubernetes_provider Kubernetes provider configuration. When eks.enabled = true these are resolved automatically from the EKS module. Set explicitly when bringing your own cluster.
object({
host = optional(string, null)
cluster_ca_certificate = optional(string, null)
cluster_name = optional(string, null)
})
{} no
networking VPC and subnet configuration. Set enabled = false to skip all networking resources.
object({
enabled = optional(bool, false)

vpc = optional(object({
# Base
cidr = string
availability_zones = optional(list(string), []) # leave empty to auto-discover AZs
single_nat_gateway = optional(bool, false) # true = one NAT GW shared across AZs (cheaper, less resilient)

# Subnet CIDR calculation
private_subnet_cidr_mask = optional(number, 20)
public_subnet_cidr_mask = optional(number, 20)

# Flow logs
flow_log_enabled = optional(bool, false)
flow_log_destination_arn = optional(string, null)
}), null)

additional_subnets = optional(object({
enabled = optional(bool, false)
cidr_mask = optional(number, 20)

# EKS integration
expose_for_eks = optional(bool, false) # add karpenter.sh/discovery tag
elb_role = optional(string, null) # "internal"
"public" null
tags = optional(map(string), {})
node_groups = optional(list(string), [])
}), { enabled = false })

# For usage of an existing VPC (bypasses networking module for RDS)
existing_vpc = optional(object({
vpc_id = string
private_subnet_ids = list(string)
private_subnet_cidr_blocks = list(string)
}))
})
partner_name Partner identifier — used as a name prefix across all resources. string n/a yes
rds RDS instance configuration. Set enabled = false to skip.
object({
enabled = optional(bool, false)

# Naming
db_name = optional(string, null)
identifier_override = optional(string, null)

# Engine
engine = optional(string, "postgres")
engine_version = optional(string, "17")

# Instance
instance_class = optional(string, "db.m5.4xlarge")
allocated_storage = optional(number, 400)
max_allocated_storage = optional(number, 1000)
multi_az = optional(bool, false)
port = optional(number, 5432)

# Credentials
username = optional(string, "postgres")
manage_master_user_password = optional(bool, true) # true = Secrets Manager managed (recommended)
password_wo = optional(string, null) # write-only; only used when manage_master_user_password = false
password_wo_version = optional(number, 1) # increment to rotate a non-managed password
enable_master_password_rotation = optional(bool, true)
master_password_rotation_days = optional(number, 7)
iam_database_authentication_enabled = optional(bool, true)

# Maintenance & backups
maintenance_window = optional(string, "Mon:00:00-Mon:03:00")
backup_retention_period = optional(number, 7)
deletion_protection = optional(bool, true)

# Monitoring
monitoring_interval = optional(number, 60)
create_monitoring_role = optional(bool, true)
monitoring_role_name = optional(string, null)
existing_monitoring_role_arn = optional(string, null)

# Parameters
parameters = optional(list(object({
name = string
value = string
})), [])

# Security group
additional_allowed_cidr_blocks = optional(list(string), [])
})
{
"enabled": true
}
no
s3 S3 configuration.

- buckets: Map of S3 buckets to create.
The map key is a short logical name (e.g. "coprocessor", "raw-data").
Each entry defines configuration and behavior for that bucket.
object({
buckets = map(object({
# Human-readable description (used for tagging)
purpose = string

# Override the computed bucket name (use when importing a pre-existing bucket)
name_override = optional(string, null)

# Allow deletion even if objects exist
force_destroy = optional(bool, false)

# Enable object versioning
versioning = optional(bool, true)

# Public access configuration
public_access = optional(object({
enabled = bool
}), {
enabled = false
})

# CORS configuration
cors = optional(object({
enabled = bool
allowed_origins = list(string)
allowed_methods = list(string)
allowed_headers = list(string)
expose_headers = list(string)
}), {
enabled = false
allowed_origins = []
allowed_methods = []
allowed_headers = []
expose_headers = []
})

# Bucket policies
policy_statements = optional(list(object({
sid = string
effect = string
principals = map(list(string))
actions = list(string)
resources = list(string)
conditions = optional(list(object({
test = string
variable = string
values = list(string)
})), [])
})), [])
}))
})
{
"buckets": {}
}
no

Outputs

Name Description
additional_subnet_ids List of additional subnet IDs.
eks_cluster_certificate_authority_data Base64-encoded certificate authority data for the EKS cluster.
eks_cluster_endpoint The EKS cluster API endpoint.
eks_cluster_name The EKS cluster name.
eks_karpenter_iam_role_arn IAM role ARN for the Karpenter controller.
eks_karpenter_node_iam_role_arn IAM role ARN for Karpenter-managed nodes.
eks_karpenter_queue_name SQS queue name for Karpenter interruption handling.
eks_oidc_provider_arn The ARN of the OIDC provider for IRSA.
private_subnet_ids List of private subnet IDs.
rds_db_instance_address The RDS instance hostname (without port).
rds_db_instance_arn The ARN of the RDS instance.
rds_db_instance_endpoint The RDS instance connection endpoint (host:port).
rds_db_instance_identifier The identifier of the RDS instance.
rds_db_instance_port The port the RDS instance is listening on.
rds_security_group_id The ID of the RDS security group.
s3_bucket_arns Map of logical bucket key to bucket ARN.
s3_bucket_names Map of logical bucket key to bucket name.
vpc_id The ID of the VPC.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages