Architecture¶
This module is a thin orchestration layer: it instantiates a few InfraHouse building-block modules and wires the surrounding AWS resources to them. Terraform loads every *.tf file regardless of name; the source is split by concern.
Component overview¶
flowchart TB
user([User]) -->|HTTPS| alb[Application Load Balancer]
subgraph pod["module.bookstack — website-pod"]
alb --> asg[Auto Scaling Group<br/>EC2 instances]
end
subgraph data["Data stores"]
rds[(RDS MySQL 8.4<br/>Multi-AZ, encrypted)]
efs[(EFS<br/>uploads & images)]
end
asg -->|MySQL 3306| rds
asg -->|NFS| efs
asg -->|SMTP 587| ses[SES]
asg -->|read| sm[Secrets Manager<br/>app key · DB creds · SES password]
route53[Route 53] -.->|A record| alb
cw[CloudWatch<br/>alarms · logs · dashboard] -.-> rds
cw -.-> ses
sns[SNS topic] -.->|email| ops([Operators]) Composed modules¶
| Module | Source | Role |
|---|---|---|
module.bookstack | infrahouse/website-pod/aws | ALB + Auto Scaling Group, target group, HTTPS/ACM, access logs, DNS A-records. |
module.rds | infrahouse/rds/aws | Multi-AZ encrypted MySQL 8.4 instance, security group, subnet group, parameter group, AWS-managed master secret, CloudWatch alarms + dashboard. |
module.bookstack-userdata | infrahouse/cloud-init/aws | Generates cloud-init userdata that runs Puppet to provision BookStack; injects application config as Puppet facts. |
module.bookstack_app_key, module.ses_smtp_password | infrahouse/secret/aws | Secrets Manager secrets (app encryption key, SES SMTP password). |
Source files¶
main.tf— the heart. Instantiatesmodule.bookstack-userdata,module.bookstack, and the app-key/SES secrets, and feeds application configuration into the instances via the cloud-init module'scustom_facts(Puppet Hiera facts): DB host/credentials, EFS mount, SES mail settings, Google OAuth secret, and the app key. It also sets apre_runcmdthat pre-stages the application tarball (see Application delivery below).db.tf— instantiatesmodule.rds(MySQL 8.4, Multi-AZ, gp3,binlog_formatnot overridden) and resolves the name of the AWS-managed master-password secret for the application.efs.tf— encrypted EFS for shared uploads/images, mount targets per backend subnet, and an NFS security group scoped to the VPC CIDR.smtp.tf— SES sending via a dedicated IAM user whose access key auto-rotates on atime_rotatingschedule (smtp_key_rotation_days); the IAM policy restricts theFromAddressto the Route 53 zone domain.secrets.tf— app key and SES SMTP password stored in Secrets Manager, each readable only by the EC2 instance role; a data source for the Google OAuth client secret.alarms.tf/sns.tf— SES bounce/complaint-rate CloudWatch alarms publishing to an SNS topic with email subscriptions. (RDS and ALB alarms are provided by their respective sub-modules.)datasources.tf— VPC/subnet/zone/AMI lookups and the EC2 instance-profile IAM policy that grants the role read access to exactly the secrets it needs.locals.tf— derived names, SES SMTP endpoints per region, and the EC2 role name/ARN.
Request & data flow¶
- A user hits
https://<service_name>.<zone-domain>; Route 53 resolves to the ALB. - The ALB terminates TLS (ACM certificate) and forwards to a healthy instance in the ASG.
- BookStack (running under nginx/PHP-FPM, provisioned by Puppet) connects to RDS for data and mounts EFS for uploads/images.
- Outbound email goes through SES (SMTP, port 587) using credentials from Secrets Manager.
- The instance reads the app key, DB master credentials, SES SMTP password, and Google OAuth client secret from Secrets Manager using its instance role.
Application delivery¶
Instances do not build PHP dependencies at boot. The module's pre_runcmd downloads a pre-built BookStack tarball — already containing the composer vendor/ directory — into /var/tmp/bookstack.tar.gz before Puppet runs. The Puppet profile's download_package and run_composer steps then no-op (their creates guards are already satisfied), so provisioning is deterministic and never depends on upstream package hosts at boot. The artifact URL is bookstack_prebuilt_package_url; its BookStack version must match the version configured in the Puppet profile.
Cross-cutting concerns¶
- Provider aliases. Callers must pass
aws.dns(a second AWS provider for Route 53) in addition to the defaultaws. - Userdata 16 KB limit. Cloud-init userdata must fit AWS's 16 KB cap; the
userdata_size_infooutput reports utilization andcompress_userdatacan gzip it. - One instance per backend subnet.
asg_min_size/asg_max_sizedefault to the number of backend subnets (and +1 for max), giving Multi-AZ spread.