file-share/README.md
Adam Moussa 7cb3f6510f
feat(infra): add HCP Terraform for the prod file share (PLAT-77)
The prod host will live on a subnet in the syslog VPC. The data volume stays unmanaged and is attached only after a snapshot copy.
2026-09-28 16:38:03 -04:00

6 KiB

file-share

CI TypeScript AWS CDK

Personal file share server on AWS — Samba for macOS Finder integration and FileBrowser for web-based file management. Accessible exclusively over the site-to-site VPN.

Infrastructure

The live share is still the management-account CDK stack until cutover proof. The prod replacement is HCP Terraform in terraform/, workspace file-share-prod, trigger terraform/**. CDK deploy on push to main is frozen.

Path Role
terraform/ Prod EC2, subnet in the syslog VPC, DLM, and HCP roles
lib/file-share-stack.ts Management-account CDK stack, still the live path until decommission
bin/app.ts                 # CDK app entry point — instantiates the stack
lib/file-share-stack.ts    # FileShareStack — all resource definitions
cdk.json                   # App command + CDK feature flags / context
cdk.context.json           # Cached VPC/subnet lookups and the resolved AL2023 AMI

cdk.json is the CDK app manifest. Its app command (npx tsx bin/app.ts) runs the TypeScript entry point directly via tsx — no separate compile step is needed for synth or deploy. bin/app.ts instantiates FileShareStack with an explicit stackName: "file-share" (kebab-case, per convention) and the target account/region.

aws-cdk-lib is pinned to an exact version (2.261.0); the aws-cdk CLI is available as a dev dependency and via npx cdk.

Common commands (also exposed as npm scripts):

Command Purpose
npx cdk synth (npm run synth) Synthesize the CloudFormation template to cdk.out/
npx cdk diff (npm run diff) Diff the synthesized stack against what is deployed
npx cdk deploy (npm run deploy) Deploy the stack
npm run build Type-check via tsc

Architecture

  • EC2 — t4g.small (ARM64, Amazon Linux 2023) in the private subnet. The AMI is cached in the committed cdk.context.json (cachedInContext: true), so deploys never pick up a new AL2023 release implicitly — an AMI change forces instance replacement and must be deliberate (cdk context --reset <ami key> && cdk synth).
  • Samba — SMB file share at /data/share, optimized for macOS (vfs_fruit)
  • FileBrowser — Web UI on port 8080, backed by the same /data/share directory
  • SFTP — password auth for user adam (ForceCommand internal-sftp, same password as SMB); used by Hazel for automated uploads
  • EBS data volume — vol-04d951cccacc435b5, 500 GiB gp3 encrypted, mounted at /data. Unmanaged import: the stack references it by ID (Volume.fromVolumeAttributes + CfnVolumeAttachment), so CloudFormation can attach it but can never create, replace, or delete it — the data survives instance replacement and even stack deletion. UserData waits for the attachment, then mounts the existing filesystem; a blkid guard ensures a disk that already has a filesystem is never formatted.
  • DLM — Daily EBS snapshots at 06:00 UTC, 30-day retention (targets the file-share-backup=true tag, set directly on the volume)
  • SSM — Session Manager for instance access (no SSH key)

History: the data volume was originally an inline blockDevice, which destroyed data on instance replacement (2026-05-27 incident), then a stack-managed standalone volume, which was orphaned when an uncommitted deploy got reverted by CD (2026-06-05 incident). The unmanaged-import design ends that failure class.

Documentation

The canonical map of Sea Haven's AWS infrastructure lives in Confluence. This project's file-share stack is represented there as a Mermaid subgraph.

Access

Requires VPN connection to the office network (10.10.0.0/16).

Finder (SMB)

  1. Finder > Go > Connect to Server
  2. Enter smb://<private-ip>/files
  3. Authenticate with adam and the password from file-share/smb-password in Secrets Manager

FileBrowser (Web)

Open http://<private-ip>:8080 in a browser.

Secrets

Both stored in AWS Secrets Manager:

Secret Purpose
file-share/smb-password Samba user password
file-share/filebrowser-password FileBrowser admin password

Create these secrets before deploying the stack:

aws secretsmanager create-secret --name file-share/smb-password --secret-string '<password>'
aws secretsmanager create-secret --name file-share/filebrowser-password --secret-string '<password>'

Deploy

Prod changes go through HCP Terraform workspace file-share-prod (manual apply until the move is sealed). The bootstrap apply creates the HCP roles and the instance boundary. The following apply, using hcptf-file-share, creates the subnet, security group, and DLM policy. data_volume_id stays empty until the snapshot copy exists, so those applies do not boot an instance.

The management-account CDK workflow no longer runs on push. workflow_dispatch remains for an explicit rollback of that stack.

Clients use the private IP. Office routing must include 10.40.20.0/24 on the existing syslog IPsec before SMB from the office will work. A check from 10.10.70.0/24 on 2026-09-28 reached the gateway for 10.40.10.254 and got no hop-1 reply for 10.40.20.1.

Expanding Storage

The data volume is not managed by CloudFormation (imported by ID), so changing a size in lib/file-share-stack.ts has no effect. Expand it directly — no downtime:

  1. aws ec2 modify-volume --volume-id vol-04d951cccacc435b5 --size <new-GiB>
  2. Wait for the modification to leave modifying: aws ec2 describe-volumes-modifications --volume-ids vol-04d951cccacc435b5
  3. Resize the filesystem via an SSM session:
    sudo resize2fs /dev/nvme1n1   # xvdf surfaces as nvme1n1 on Nitro instances