This repo provides a Terraform Module for deploying the base layer infrastructure for the Coprocessor of the Zama Protocol.
Infrastructure deployed by the examples/testnet-complete example.
| 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 |
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:
- Copy the relevant example directory into your own infrastructure repository.
- Update the
sourceinmain.tfto reference the remote versioned release:git::https://github.com/zama-ai/terraform-coprocessor-modules.git?ref=<version> - Replace every value marked
# CHANGE MEinterraform.tfvars— primarilypartner_name,aws_region, and any account-specific IDs. - Update the backend bucket/key/region in
versions.tf. - All other values are pre-configured with sensible defaults and require no changes for a standard testnet deployment.
Kubernetes provider authentication:
When eks.enabled = true (testnet-complete), the Kubernetes and Helm providers are configured automatically using the EKS cluster outputs — no manual credential configuration is needed.
When eks.enabled = false (testnet-existing-infra), you must supply kubernetes_provider in terraform.tfvars:
kubernetes_provider = {
host = "https://<cluster-endpoint>"
cluster_ca_certificate = "<base64-encoded-ca-cert>"
cluster_name = "<cluster-name>"
oidc_provider_arn = "arn:aws:iam::<account>:oidc-provider/<oidc-id>"
}Retrieve these values from your existing cluster:
aws eks describe-cluster --name <cluster-name> --query 'cluster.{endpoint:endpoint,ca:certificateAuthority.data}'
aws eks describe-cluster --name <cluster-name> --query 'cluster.identity.oidc.issuer'terraform init
terraform plan
terraform applyUses 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 submoduleTests live in tests/unit.tftest.hcl within each module directory.
This repo uses pre-commit to enforce consistency on every commit.
Dependencies — must be on your PATH:
brew install pre-commit terraform-docs tflintHooks 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
| Name | Version |
|---|---|
| terraform | >= 1.11 |
| aws | ~> 6.0 |
| helm | ~> 3.0 |
| kubernetes | ~> 2.0 |
| random | ~> 3.0 |
| time | ~> 0.13 |
| Name | Version |
|---|---|
| aws | ~> 6.0 |
| time | ~> 0.13 |
| Name | Source | Version |
|---|---|---|
| eks | ./modules/eks | n/a |
| k8s_coprocessor_deps | ./modules/k8s-coprocessor-deps | n/a |
| k8s_system_charts | ./modules/k8s-system-charts | n/a |
| kms | ./modules/kms | n/a |
| networking | ./modules/networking | n/a |
| rds | ./modules/rds | n/a |
| s3 | ./modules/s3 | n/a |
| Name | Type |
|---|---|
| aws_vpc_security_group_ingress_rule.cluster_from_rds_client | resource |
| aws_vpc_security_group_ingress_rule.node_from_rds_client | resource |
| time_sleep.wait_for_tx_sender_iam_propagation | resource |
| aws_caller_identity.current | data source |
| aws_partition.current | data source |
| 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({ |
"SPOT" min_size = optional(number, 1) max_size = optional(number, 3) desired_size = optional(number, 1) # Instance instance_types = optional(list(string), ["m6i.large"]) 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 |
| k8s_coprocessor_deps | Kubernetes coprocessor resource configuration (namespaces, service accounts, storage classes, ExternalName services). | object({ |
{ |
no |
| k8s_system_charts | Kubernetes system-level applications to deploy via Helm. | object({ |
{ |
no |
| kms | Coprocessor KMS keypair configuration. Creates an asymmetric AWS KMS key (ECC_SECG_P256K1, SIGN_VERIFY) with EXTERNAL origin so an Ethereum secp256k1 private key can be imported, plus an alias alias/<partner_name>-<environment>-coprocessor-keypair.Cross-account deployments are handled out-of-band: invoke the kms submodule directly with an AWS provider configured for the target account. The root module always creates the key in the same account as the rest of the infrastructure. |
object({ |
{ |
no |
| 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({ |
{} |
no |
| networking | VPC and subnet configuration. Set enabled = false to skip all networking resources. | object({ |
"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 = optional(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({ |
{ |
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({ |
{ |
no |
| 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. |
| kms_alias_arn | KMS alias ARN. Null when kms.enabled = false. |
| kms_alias_name | KMS alias name. Null when kms.enabled = false. |
| kms_key_arn | KMS key ARN of the coprocessor keypair. Null when kms.enabled = false. |
| kms_key_id | KMS key ID of the coprocessor keypair. Null when kms.enabled = false. |
| private_subnet_ids | List of private subnet IDs. |
| rds_client_security_group_id | ID of the rds-client SG attached to pods (via SecurityGroupPolicy) that need DB access. |
| 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_master_secret_arn | ARN of the Secrets Manager secret containing the RDS master user password. Null when manage_master_user_password = false or rds.enabled = false. |
| rds_server_security_group_id | ID of the rds-server SG attached to the RDS instance. |
| s3_bucket_arns | Map of logical bucket key to bucket ARN. |
| s3_bucket_names | Map of logical bucket key to bucket name. |
| s3_cloudfront_distribution_ids | Map of logical bucket key to CloudFront distribution ID. |
| s3_cloudfront_domain_names | Map of logical bucket key to CloudFront distribution domain name. |
| vpc_id | The ID of the VPC. |
