Fintech Starter Kit: PCI DSS v4.0
For payment platforms, neobanks, buy-now-pay-later (BNPL) providers, and any fintech company approaching PCI DSS certification. The architecture decisions that are hardest to fix later (CDE network segmentation, RDS encryption at creation, tokenization scope) are built in from the start.
GitHub: compliancetf/starter-kit-fintech-pcidss
Registry: pcidss.compliance.tf
Resources deployed: approximately 130
RDS encryption must be enabled at database creation
PCI DSS requires encryption for cardholder data. storage_encrypted = true on RDS must be set when the database is created. It cannot be added to an existing unencrypted instance without destroying and recreating it. This kit enforces encryption by default. Do not disable it.
Architecture

The architecture has two distinct layers. The public edge (CloudFront + WAF + ALB) handles request routing and protocol enforcement. The CDE layer (EKS, Lambda, EC2, RDS Aurora, DynamoDB, ElastiCache) is fully private, with no public routes to any data store.
Quick Start
Prerequisites: Terraform >= 1.3, AWS credentials, a compliance.tf account, a Route 53 hosted zone, and an S3 replication IAM role + destination bucket (a kit prerequisite supporting audit-log durability; PCI DSS Req. 10.5.1 itself mandates 12 months of log retention with the most recent 3 months immediately available). See Create the replication prerequisites if you do not have those yet.
git clone https://github.com/compliancetf/starter-kit-fintech-pcidss.git
cd starter-kit-fintech-pcidss
cp terraform.tfvars.example terraform.tfvars
# Edit terraform.tfvars: set project_name, domain_name, route53_zone_id,
# availability_zones, s3_replication_role_arn, s3_replication_destination_bucket_arn
terraform login pcidss.compliance.tf
terraform init
terraform plan
terraform applyterraform plan fails if any PCI DSS control is violated. The error names the specific control and the module that triggered it. Fix the issue and re-run.
Module Inventory
| Module | Version | Purpose | Key PCI DSS Requirements |
|---|---|---|---|
vpc | ~> 6.0 | CDE network isolation, flow logs | Req. 1.3 (restrict CDE access), Req. 10.2 (audit logs) |
alb | ~> 10.0 | HTTPS load balancing, access logs | Req. 4.2.1 (TLS 1.2+), Req. 6.4.1 (WAF association) |
cloudfront | ~> 6.0 | CDN edge with TLS and WAF | Req. 4.2.1 (TLS at edge), Req. 6.4.1 (WAF) |
waf | ~> 1.0 | Regional WAF (ALB), OWASP Top 10 | Req. 6.4.1 (web application protection) |
waf_global | ~> 1.0 | Global WAF (CloudFront, us-east-1) | Req. 6.4.1 (web application protection at edge) |
acm | ~> 6.0 | Regional TLS certificate | Req. 4.2.1 (strong cryptography in transit) |
acm_us_east_1 | ~> 6.0 | Global TLS certificate for CloudFront | Req. 4.2.1 (strong cryptography in transit) |
kms | ~> 4.0 | Customer-managed key wired to EKS, Lambda, and OpenSearch | Req. 3.5.1 (PAN rendered unreadable) |
eks | ~> 21.0 | Kubernetes cluster, private endpoint, encrypted secrets | Req. 2.2 (secure configuration), Req. 10.2 (audit logs) |
lambda | ~> 8.0 | VPC-attached functions, KMS environment encryption | Req. 1.3 (restrict CDE access), Req. 3.5.1 (encryption) |
ec2_instance | ~> 6.0 | Private worker nodes, IMDSv2, IAM instance profile | Req. 2.2 (no public IP, IMDSv2 enforced) |
rds_aurora | ~> 10.0 | Aurora PostgreSQL, encrypted at creation, Multi-AZ, IAM auth | Req. 3.5.1 (encryption at rest), Req. 10.2 (audit logs) |
dynamodb | ~> 5.0 | Key-value store, KMS encryption, PITR | Req. 3.5.1 (encryption at rest) |
elasticache | ~> 1.0 | Redis, encrypted at rest and in transit | Req. 4.2.1 (TLS in transit) |
s3_bucket_data | ~> 5.0 | CDE data storage, KMS, versioning, cross-region replication | Req. 3.5.1 (encryption at rest), Req. 3.5 (no public access) |
s3_bucket_logs | ~> 5.0 | Audit log storage, versioning (MFA delete requested — see note), cross-region replication, 365-day lifecycle | Req. 10.3 (log protection), Req. 10.5.1 (log retention) |
sqs | ~> 5.0 | Message queue with dead-letter queue | Req. 3.5.1 (SSE encryption) |
opensearch | ~> 2.0 | VPC-only search, KMS, HTTPS, fine-grained access, audit logs | Req. 10.2 (audit logs), Req. 10.3 (log protection) |
cloudwatch | ~> 5.0 | Application log group, 365-day retention | Req. 10.2 (audit logs), Req. 10.5.1 (log retention) |
MFA Delete needs a manual root step
The kit requests MFA Delete on the data and logs buckets (and the s3_bucket_mfa_delete_enabled control enforces it), but two AWS constraints get in the way: MFA Delete can only be enabled with root-account credentials and an MFA code — a standard terraform apply can fail on the versioning resources — and AWS does not support S3 Lifecycle configurations on MFA-Delete-enabled buckets, while the logs bucket ships a 365-day lifecycle. Recommended path: disable the control for the rollout (?disable=s3_bucket_mfa_delete_enabled), keep the lifecycle, and if your assessor requires MFA Delete, enable it manually as root on a bucket without lifecycle rules. Do not run the whole apply as root — root should perform only the single MFA Delete operation.
Two ACM certificates
CloudFront only accepts certificates from us-east-1, regardless of where your other resources are. This kit provisions two certificates: one regional (for ALB) and one global (for CloudFront, using a provider alias). Both use the same Route 53 zone for DNS validation.
CDE Scoping
Reducing Cardholder Data Environment (CDE) scope reduces QSA audit hours and the total assessment cost.
In CDE scope: VPC private subnets, EKS, Lambda, EC2, RDS Aurora, DynamoDB, ElastiCache, S3 data bucket, OpenSearch
Typically out of CDE scope: SQS (application queuing for non-PAN data) and the CloudWatch log group (aggregated logs only). CloudFront is out of scope only when it serves static assets — a distribution that transmits cardholder data is in scope (covered under AWS's PCI attestation for the AWS-managed portion). Confirm scoping decisions with your QSA.
The modules in CDE scope have KMS encryption at rest enforced at terraform plan time (the kit's customer-managed key is wired to EKS, Lambda, and OpenSearch; other services use their respective AWS encryption defaults unless you pass key ARNs). The out-of-scope modules use standard server-side encryption.
PCI DSS v4.0 Control Coverage
| PCI DSS 4.0 Requirement | Description | Module(s) | What's Enforced |
|---|---|---|---|
| Req. 1.3 | Network access to and from the cardholder data environment is restricted | VPC, Lambda, RDS Aurora, ElastiCache | Private and database subnets, no public routes to any CDE data store, VPC-attached functions |
| Req. 2.2 | System components are configured and managed securely | EC2, EKS | IMDSv2 required, no public IPs, IAM instance profiles, private cluster endpoint |
| Req. 3.5 | Primary account number (PAN) is secured wherever it is stored | S3, RDS Aurora | Public access blocked, encryption enforced |
| Req. 3.5.1 | Primary account numbers (PAN) are secured with strong cryptography wherever stored | S3, RDS Aurora, DynamoDB, Lambda, KMS | KMS encryption at rest for all CDE data stores |
| Req. 4.2.1 | Strong cryptography is used to safeguard PAN during transmission | ALB, CloudFront, ElastiCache | TLS 1.2+ enforced on all connections |
| Req. 6.4.1 | Public-facing web applications are protected against attacks on an ongoing basis | ALB, CloudFront | WAF with OWASP Top 10, known bad inputs, IP reputation |
| Req. 10.2 | Audit logs are implemented to support detection of anomalies and suspicious activity and the forensic analysis of events | All modules | VPC flow logs, ALB access logs, EKS audit logs, CloudWatch log groups |
| Req. 10.3 | Audit logs are protected from destruction and unauthorized modifications | S3 (logs), OpenSearch | S3 versioning with MFA delete and cross-region replication, OpenSearch index retention |
| Req. 10.5.1 | Audit log history is retained for at least 12 months, with the most recent 3 months immediately available | S3 (logs), CloudWatch | 365-day retention on the log group and the logs bucket; the 90-day Glacier transition keeps the most recent three months in standard storage (immediately retrievable) while older logs stay retained through 365 days — confirm these thresholds with your QSA, since 365/90 days approximate the calendar months the requirement names |
CloudTrail is not in this kit
Req. 10.2 expects audit logs across all system components, and a QSA will ask for the AWS control plane trail alongside the resource-level logs this kit configures. CloudTrail is account-wide rather than module-level, so no module creates it. Enable it separately; the compliance.tf get started guide covers account-level prerequisites.
What compliance.tf Covers vs. What You Own
What this kit handles
- KMS encryption at rest for all CDE data stores (Req. 3.5.1)
- TLS 1.2+ at both ALB and CloudFront, enforced by the registry at plan time
- WAF with AWS managed rules: OWASP Top 10, known bad inputs, IP reputation (Req. 6.4.1)
- CDE network isolation; databases and caches have no public routes (Req. 1.3)
- IMDSv2 on all EC2 instances, IMDSv1 disabled (Req. 2.2)
- IAM authentication required for RDS, DynamoDB, and OpenSearch
- VPC flow logs, ALB access logs, EKS audit logs, CloudWatch log groups (Req. 10.2, 10.3)
- Deletion protection on RDS and ALB in
environment = "prod"
What you own
- Tokenization and PAN handling in application code (compliance.tf does not tokenize)
- Key rotation procedures and key administrator access policies
- User access reviews and role assignments
- Change management approvals and evidence collection
- Annual penetration testing (PCI DSS Req. 11.4 requires both internal and external testing at least once per 12 months)
- QSA engagement and Report on Compliance (ROC) preparation
- Cardholder data discovery and formal CDE scope documentation
Common Customizations
Disable a specific control
If a control does not apply to your use case, disable it with a query parameter on the module source. Query parameters require the HTTPS URL format (the Registry format does not support them):
module "s3_bucket_data" {
source = "https://pcidss.compliance.tf/terraform-aws-modules/s3-bucket/aws?version=5.0.0&disable=s3_bucket_event_notifications_enabled"
# ...
}That example turns off the S3 event notification control, which has nothing to configure unless something in your account consumes those events. Do not use this to switch off an encryption, public-access, or logging control on a module in CDE scope — your QSA will ask why the control is absent, and the answer has to be a scoping decision rather than a disabled check.
Control identifiers are listed in the controls catalog; see Customize modules for the full enable/disable interface.
QSA Pre-Verification Checklist
When your QSA asks about infrastructure controls, use this to locate the evidence:
| QSA Question | Where to Find Evidence |
|---|---|
| Is cardholder data encrypted at rest? | Terraform state: aws_kms_key, aws_rds_cluster, aws_s3_bucket_server_side_encryption_configuration |
| Is TLS 1.2+ enforced on all endpoints? | ALB listener policy: ELBSecurityPolicy-TLS13-1-2-2021-06; CloudFront security policy: TLSv1.2_2021 |
| Are databases in private subnets with no public endpoint? | VPC subnets: aws_subnet with map_public_ip_on_launch = false; RDS publicly_accessible = false |
| Is WAF protecting web-facing applications? | aws_wafv2_web_acl association with ALB and CloudFront distribution |
| Are audit logs retained and protected? | S3 logs bucket versioning and cross-region replication; OpenSearch index retention settings |
| Is IMDSv2 enforced on EC2 instances? | aws_instance with metadata_options.http_tokens = "required" |
Cost Estimate
| Component | Dev | Prod |
|---|---|---|
| VPC + NAT Gateway | ~$35 | ~$100 |
| ALB | ~$25 | ~$25+ |
| CloudFront | ~$1 | ~$5+ |
| EKS control plane | ~$75 | ~$75 |
| EKS nodes (t3.medium) | ~$60 | ~$120+ |
| Aurora PostgreSQL (db.r6g.large) | ~$200 | ~$400 |
| ElastiCache (cache.t4g.micro) | ~$10 | ~$20 |
| OpenSearch (t3.small.search) | ~$20 | ~$60 |
| S3 + Lambda + SQS + EC2 | ~$30 | ~$60+ |
| Total | ~$500-700 | ~$1,500-2,500 |
Next Steps
- PCI DSS v4.0 Framework Reference: full control list per requirement
- Audit Evidence Guide: how to prepare infrastructure evidence for QSA review
- Brownfield Migration Kit: if you have existing Terraform to migrate rather than starting fresh
- Version Compatibility Matrix: supported module versions