Repository navigation
Getting Started
The shortest path from an empty AWS account to a running ReportMate. Read Prerequisites first.
The root module's S3 backend needs a bucket and a lock table that Terraform
cannot create for itself. The bootstrap/ module creates both, using local
state, and is meant to be run once.
cd bootstrap
terraform init
terraform apply
Note the state_bucket and lock_table outputs.
Copy the example and fill in the values from the bootstrap outputs.
cd ..
cp backend.hcl.example backend.hcl
backend.hcl is gitignored. The keys the backend needs are bucket, key,
region, dynamodb_table, and encrypt.
cp terraform.tfvars.example terraform.tfvars
Fill in db_password, api_internal_secret, and client_passphrase — the
example file ships them as CHANGE_ME. The example file does not include
public_api_url, which is also required; add it. For the very first apply you
can point it at a placeholder such as http://localhost:3000 and correct it in
step 6.
For anything beyond a throwaway environment also set acm_certificate_arn,
alert_email, and db_multi_az. Every variable is listed in Inputs.
terraform init -backend-config=backend.hcl
terraform plan
terraform apply
The apply takes a while; RDS is the long pole. Expect the ECS services to report zero healthy targets at this point — the ECR repositories are empty.
Log in to ECR for your account and region.
aws ecr get-login-password --region <region> | docker login --username AWS --password-stdin <account-id>.dkr.ecr.<region>.amazonaws.com
The API image is built by the reportmate-api repo's CI and published to GHCR,
so deploying it is a mirror rather than a build.
docker pull --platform linux/amd64 ghcr.io/reportmate/reportmate-api:latest
docker tag ghcr.io/reportmate/reportmate-api:latest $(terraform output -raw ecr_api_repository_url):latest
docker push $(terraform output -raw ecr_api_repository_url):latest
For the frontend, build an image from
reportmate-app-web, tag it
against terraform output -raw ecr_frontend_repository_url, and push. The
dashboard is also published as ghcr.io/reportmate/web (tagged latest and
with each release tag) and can be mirrored the same way as the API. That image
is built with the Dockerfile defaults, so NEXT_PUBLIC_DEMO_MODE is compiled in
as false and NEXT_PUBLIC_API_BASE_URL as empty; build your own when you need
other build-time values.
Then force a rollout on each service. The cluster is
<project_name>-<environment>-cluster and the services are
<project_name>-<environment>-api and <project_name>-<environment>-frontend.
aws ecs update-service --cluster <project>-<env>-cluster --service <project>-<env>-api --force-new-deployment --region <region>
aws ecs update-service --cluster <project>-<env>-cluster --service <project>-<env>-frontend --force-new-deployment --region <region>
Read the ALB hostname out of the outputs.
terraform output frontend_url
Set public_api_url in terraform.tfvars to the hostname users will actually
type — the ALB DNS name if you have no custom domain, or your own hostname if
you put a CDN or a Route 53 record in front. Re-apply. This rewrites the
frontend task definition and triggers a new deployment.
Getting this wrong is the most common first-deploy failure: the server-rendered HTML will contain API URLs pointing somewhere the browser cannot reach, and NextAuth will reject callbacks. See Troubleshooting.
Request an ACM certificate in the same region, set acm_certificate_arn, and
apply. Until you do, the port 443 listener speaks plain HTTP while the port 80
listener unconditionally redirects to https:// — so a browser hitting port 80
ends up talking TLS to a non-TLS listener.
The module creates a Cognito user pool with two empty groups, admin and
user, and stores the app client id and secret in Secrets Manager. It does not
connect the dashboard to it. The dashboard's NextAuth configuration
(lib/auth.ts in reportmate-app-web) offers Entra ID (azure-ad, the default
when AUTH_PROVIDERS is unset), Google, and a credentials provider that
rejects every login; it has no Cognito provider. The frontend task definition
sets none of AUTH_PROVIDERS, AZURE_AD_CLIENT_ID, AZURE_AD_CLIENT_SECRET
or AZURE_AD_TENANT_ID, so sign-in does not work until you add the settings
for a provider the dashboard supports to the frontend task definition in
modules/containers.
If you add a Cognito-backed provider yourself, set auth_callback_urls and
auth_logout_urls to your real hostname — the defaults point at
localhost:3000.
scripts/ contains deploy-api.sh, deploy-containers.sh,
deploy-demo-loop.sh, and status.sh. They automate the steps above but were
extracted from a monorepo and still carry hardcoded names and paths that will
not match a fresh deployment. Read them before running them; see
Troubleshooting.
What each one does:
-
deploy-api.sh [tag]pullsghcr.io/reportmate/reportmate-api:<tag>(defaultlatest), pushes it to the API ECR repository as both<tag>andlatest, and forces a new deployment of the API service. -
deploy-containers.sh [api|frontend|both](defaultboth) mirrors the API image the same way, reading the GHCR tag fromAPI_GHCR_TAG(defaultlatest) and pushing it under a timestamp tag pluslatest. For the frontend it builds the dashboard withNEXT_PUBLIC_DEMO_MODE=trueand pushes it the same way. -
deploy-containers.shbuilds the frontend fromapps/wwwtwo directories above this repo (a monorepo path). -
deploy-demo-loop.shbuilds the demo-loop image fromscripts/Dockerfile.demo-looptwo directories above this repo (a monorepo path), pushes it, and rolls the demo-loop service. -
status.shprints the Terraform outputs, the ECS services with running and desired counts, and the RDS instances and ECR repositories whose names start withreportmate.
Each script pushes latest, which is what the task definitions run while
api_image_tag and frontend_image_tag are left at their defaults.
MIT licensed. — reportmate.app