# Prerequisites: AWS resources before first deploy

This runbook describes all resources that must exist **before** you run any Terraform or GitHub Actions workflow for this project. Each section includes step-by-step instructions and copy-paste AWS CLI commands where applicable.

## Overview

This project uses a **two-account model** (which can be collapsed to one account):

- **Deployment account** - Where all Terraform layers run and where most resources are created (VPC, EKS, RDS, S3, etc.). GitHub Actions assumes a role in this account via OIDC.
- **Domain account** - The AWS account that owns the domain and the Route 53 hosted zone. It can be the same as the deployment account. Layer 01-dns-main creates an IAM role (dns_assume_role_arn) in this account so that layer 50 can create the Wiki.js DNS record there.

| Resource | Account | Used by |
| -------- | ------- | ------- |
| Domain | Any (Route 53 or external registrar) | Base for Wiki.js FQDN and hosted zone |
| Hosted zone | Domain account | Bootstrap `terraform.tfvars` (`hosted_zone_id`); DNS validation for ACM; layer 50 creates A record here |
| ACM certificate | Deployment account (same region as ALB) | Bootstrap `terraform.tfvars` (`acm_cert_arn`); TLS for Wiki.js (and optional ArgoCD UI) |
| GitHub OIDC role (deployment) | Deployment account | All workflows: **deployment_account_role_arn** (workflow input) |
| Assumable role in domain account | Domain account | 01-dns-main only: **domain_account_role_arn** (workflow input); used to create dns_assume_role_arn there |

## 1. Domain and DNS

### 1.1 Register a domain

- **Option A - Route 53:** Register the domain in Route 53 (same or any account). AWS will create a hosted zone automatically; note the hosted zone ID.
- **Option B - External registrar:** Register the domain elsewhere. You will create a hosted zone in the domain account and delegate the domain to it by creating NS records at the registrar.

### 1.2 Create a public hosted zone (if not already present)

If the domain is not in Route 53, or you want the hosted zone in a specific account (domain account), create a **public** Route 53 hosted zone in that account.

Replace `<domain>` with your base domain (e.g. `example.com`). For a subdomain-only zone use the full name (e.g. `wiki.example.com`); typically you use the base domain.

```bash
# Create the hosted zone (use a unique caller-reference, e.g. timestamp)
aws route53 create-hosted-zone \
  --name "<domain>" \
  --caller-reference "wikijs-$(date +%s)" \
  --hosted-zone-config Comment="Wiki.js deployment"
```

Example output includes `"Id": "/hostedzone/Z062636210C9MQM9Q5YST"`. The **Hosted Zone ID** is the suffix (e.g. `Z062636210C9MQM9Q5YST`).

### 1.3 Get the Hosted Zone ID

```bash
# List hosted zones and get the ID for your domain
aws route53 list-hosted-zones --query "HostedZones[?Name=='<domain>.'].Id" --output text
```

The API returns IDs with prefix `/hostedzone/`. Use only the part after `/hostedzone/` (e.g. `Z062636210C9MQM9Q5YST`) in bootstrap `terraform.tfvars` as `hosted_zone_id`.

### 1.4 Delegate the domain to Route 53 (if domain is registered elsewhere)

If the domain was registered outside Route 53, get the name servers for your hosted zone and create NS records at the registrar pointing the domain to those name servers.

```bash
# Get name servers for the hosted zone
aws route53 get-hosted-zone --id "<hosted-zone-id>" --query "DelegationSet.NameServers" --output table
```

At your domain registrar, create NS records for `<domain>` with the names returned (e.g. `ns-123.awsdns-12.com`, etc.).

## 2. ACM certificate (deployment account)

The certificate must be in the **deployment account** and in the **same region** as the deployment (e.g. if the ALB is in `us-east-1`, request the cert in `us-east-1`). Use the Wiki.js FQDN (e.g. `wiki.example.com`). If you will expose the ArgoCD UI on a subdomain, request a wildcard cert (e.g. `*.example.com`) or a second certificate.

### 2.1 Request the certificate

Replace `<wikijs_fqdn>` with your Wiki.js hostname (e.g. `wiki.example.com`) and `<region>` with the deployment region (e.g. `us-east-1`).

```bash
aws acm request-certificate \
  --domain-name "<wikijs_fqdn>" \
  --validation-method DNS \
  --region "<region>"
```

Note the returned `CertificateArn` (e.g. `arn:aws:acm:us-east-1:111122223333:certificate/3bff8a09-cd29-44cc-8a89-070ea23a2c66`).

### 2.2 Get the DNS validation CNAME

```bash
aws acm describe-certificate \
  --certificate-arn "<certificate-arn>" \
  --region "<region>" \
  --query "Certificate.DomainValidationOptions[0].ResourceRecord"
```

You will get a `Name` and `Value` for a CNAME record.

### 2.3 Create the CNAME record in the hosted zone

Create a JSON file (e.g. `acm-validation.json`) for the change batch. Replace `<validation-name>` and `<validation-value>` with the `Name` and `Value` from the previous step. If `Name` ends with a dot, include it; otherwise add a trailing dot for the FQDN. `Name` is often a subdomain of your zone (e.g. `_abc123.wiki.example.com.`).

```json
{
  "Changes": [
    {
      "Action": "UPSERT",
      "ResourceRecordSet": {
        "Name": "<validation-name>",
        "Type": "CNAME",
        "TTL": 300,
        "ResourceRecords": [
          {
            "Value": "<validation-value>"
          }
        ]
      }
    }
  ]
}
```

Apply the change (replace `<hosted-zone-id>` with your hosted zone ID):

```bash
aws route53 change-resource-record-sets \
  --hosted-zone-id "<hosted-zone-id>" \
  --change-batch file://acm-validation.json
```

### 2.4 Wait for validation

```bash
aws acm wait certificate-validated \
  --certificate-arn "<certificate-arn>" \
  --region "<region>"
```

Use the **ACM certificate ARN** in bootstrap `terraform.tfvars` as `acm_cert_arn`. Bootstrap writes it to Parameter Store for downstream layers.

## 3. Deployment account: GitHub OIDC role

GitHub Actions authenticates to AWS using OIDC. You need an IAM OIDC identity provider for GitHub and an IAM role that the workflows assume. This role is the **deployment_account_role_arn** you will pass as a workflow input.

### 3.1 Create the OIDC identity provider (if not already present)

Use the GitHub OIDC provider URL in **lowercase**. The thumbprint for `token.actions.githubusercontent.com` is:

`6938fd4d98bab03faadb97b34396831e3780aea1`

Replace `<account-id>` with your deployment account ID.

```bash
aws iam create-open-id-connect-provider \
  --url "https://token.actions.githubusercontent.com" \
  --client-id-list "sts.amazonaws.com" \
  --thumbprint-list "6938fd4d98bab03faadb97b34396831e3780aea1"
```

If the provider already exists, you will get an error; that is fine.

### 3.2 Create the IAM role (e.g. github-role)

**Trust policy** - Save as `github-trust-policy.json`. Replace `<deployment-account-id>` with your deployment account ID and `<org>/<repo>` with your GitHub org and repo (e.g. `myorg/my-repo`). To restrict to a branch, use e.g. `repo:myorg/my-repo:ref:refs/heads/main`.

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<deployment-account-id>:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:<org>/<repo>:*"
        }
      }
    }
  ]
}
```

Create the role:

```bash
aws iam create-role \
  --role-name "github-role" \
  --assume-role-policy-document file://github-trust-policy.json \
  --description "Role for GitHub Actions (Wiki.js deployment)"
```

### 3.3 Attach permissions to the role

The role needs permissions for all Terraform layers: S3 (state bucket and lockfile; bucket is created by bootstrap), KMS, SSM Parameter Store, EC2/VPC, EKS, RDS, IAM (for IRSA and for 01-dns-main), Secrets Manager, and the ability to **assume the domain account role** (see next section). You can attach a combination of AWS managed policies and a custom policy for Parameter Store and state bucket.

**Minimal custom policy for state and Parameter Store** - Save as `github-deployment-policy.json`. Replace `<prefix>` with your prefix (e.g. `wikijs`) and `<region>` and `<env>` with your default region and env. Broaden the resource conditions if you use multiple regions/envs.

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ParameterStore",
      "Effect": "Allow",
      "Action": [
        "ssm:GetParameter",
        "ssm:PutParameter",
        "ssm:DeleteParameter",
        "ssm:GetParametersByPath"
      ],
      "Resource": "arn:aws:ssm:<region>:*:parameter/<prefix>/*"
    },
    {
      "Sid": "StateBucket",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:DeleteObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::<prefix>-<region>-tfstate-*",
        "arn:aws:s3:::<prefix>-<region>-tfstate-*/*"
      ]
    },
    {
      "Sid": "AssumeDomainRole",
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Resource": "arn:aws:iam::<domain-account-id>:role/github-role"
    }
  ]
}
```

Replace `<domain-account-id>` with the domain account ID (can be the same as deployment account if using one account). Then:

```bash
aws iam put-role-policy \
  --role-name "github-role" \
  --policy-name "WikiJsDeployment" \
  --policy-document file://github-deployment-policy.json
```

In addition, attach AWS managed policies (or equivalent custom policies) so the role can create and manage VPC, EKS, RDS, S3, KMS, IAM roles (for IRSA), Secrets Manager, and related resources. For example:

```bash
# Example: broad power for Terraform (restrict in production)
aws iam attach-role-policy --role-name "github-role" --policy-arn "arn:aws:iam::aws:policy/AdministratorAccess"
```

For production, use least-privilege policies scoped to the resources and prefixes used by this project.

The output you need is the **deployment_account_role_arn** (e.g. `arn:aws:iam::111122223333:role/github-role`). Use it as the **deployment_account_role_arn** input when running any provision or destroy workflow.

## 4. Domain account: assumable role for 01-dns-main

Layer 01-dns-main runs in the deployment account but assumes a role in the **domain account** to create the IAM DNS role (dns_assume_role_arn) there. Create this role in the domain account.

### 4.1 Trust policy

The role must trust the **deployment account's GitHub role**. Save as `domain-trust-policy.json`. Replace `<deployment-account-id>` with the deployment account ID and `github-role` with the role name you used there.

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<deployment-account-id>:role/github-role"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

### 4.2 Create the role in the domain account

Run these commands in the **domain account** (replace `<deployment-account-id>` in the trust policy first):

```bash
aws iam create-role \
  --role-name "github-role" \
  --assume-role-policy-document file://domain-trust-policy.json \
  --description "Role for GitHub Actions to create DNS role in domain account"
```

### 4.3 Permissions for creating IAM roles

01-dns-main will create an IAM role (dns_assume_role_arn) and attach an inline policy. The domain account role needs permission to create and manage that role. Save as `domain-dns-setup-policy.json`. Adjust the resource condition if you use a different naming pattern (this example uses `wikijs-*-dns-*`).

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CreateAndManageDnsRole",
      "Effect": "Allow",
      "Action": [
        "iam:CreateRole",
        "iam:PutRolePolicy",
        "iam:AttachRolePolicy",
        "iam:GetRole",
        "iam:DeleteRole",
        "iam:DeleteRolePolicy",
        "iam:DetachRolePolicy",
        "iam:PassRole"
      ],
      "Resource": "arn:aws:iam::*:role/wikijs-*-dns-*"
    }
  ]
}
```

Attach it:

```bash
aws iam put-role-policy \
  --role-name "github-role" \
  --policy-name "CreateDnsRole" \
  --policy-document file://domain-dns-setup-policy.json
```

The output you need is the **domain_account_role_arn** (e.g. `arn:aws:iam::444455556666:role/github-role`). Use it only when running **01-dns-main** (provision or destroy) or **tf-all-provision** / **tf-all-destroy**. Using the same role name (`github-role`) in both accounts makes the roles easy to find; the ARN (account ID) distinguishes deployment from domain.

## 5. Summary table

| Resource | Account | Where to use it |
| -------- | ------- | ---------------- |
| Domain | Registrar / Route 53 | Base for `domain_name` and `wikijs_fqdn` in bootstrap tfvars |
| Hosted zone ID | Domain account | Bootstrap `terraform.tfvars`: `hosted_zone_id` |
| ACM certificate ARN | Deployment (same region as ALB) | Bootstrap `terraform.tfvars`: `acm_cert_arn` |
| Deployment GitHub role ARN | Deployment account | Workflow input **deployment_account_role_arn** (all workflows) |
| Domain account role ARN | Domain account | Workflow input **domain_account_role_arn** (01-dns-main and tf-all-*) |

## 6. Terraform variables to set before first run

Before running the **00-bootstrap** layer, set the following in **bootstrap** `terraform.tfvars` (see [terraform/00-bootstrap/terraform.tfvars](../../terraform/00-bootstrap/terraform.tfvars) and [NAMING_CONVENTION.md](../plan-and-requirements/NAMING_CONVENTION.md)):

| Variable | Description | Source |
| -------- | ----------- | ------ |
| `env` | Environment (e.g. `dev`, `prod`) | Your choice |
| `region` | AWS region (e.g. `us-east-1`, `eu-west-1`) | Your choice |
| `prefix` | Prefix for resource names and Parameter Store paths (e.g. `wikijs`) | Your choice |
| `domain_name` | Base domain (e.g. `example.com`) | Prerequisites: domain |
| `wikijs_fqdn` | Wiki.js FQDN (e.g. `wiki.example.com`) | Prerequisites: domain |
| `hosted_zone_id` | Route 53 hosted zone ID (e.g. `Z062636210C9MQM9Q5YST`) | Prerequisites: hosted zone |
| `acm_cert_arn` | ACM certificate ARN in deployment account | Prerequisites: ACM |

**Do not** put `domain_account_role_arn` or `deployment_account_role_arn` in `terraform.tfvars`. They are supplied only as **workflow inputs** when you run **01-dns-main** (provision or destroy) or **tf-all-provision** / **tf-all-destroy**.

After completing the prerequisites and setting bootstrap tfvars, proceed to [Setup](setup.md) to run the layers.
