# drop-api-secrets-migration-evidence

# drop-api Secrets Migration Evidence
**Date:** 2026-04-29T14:18:08Z
**Operator:** Parisa Tabriz (Securion)
**MC:** #10150
**Outcome:** BLOCKED — IAM permission gap prevents completion. Remediation documented below.

---

## Pre-Migration State (drop-api)

**Service ARN:** arn:aws:apprunner:eu-west-1:324480209768:service/drop-api/bdebb303a47c409393691ef8f5530144
**Status:** RUNNING
**RuntimeEnvironmentVariables (plain-text — NAMES ONLY, no values):**
- DATABASE_URL (CRITICAL — plain text, 1 of 2 secrets)
- JWT_SECRET (CRITICAL — plain text, 2 of 2 secrets)
- DROP_MODE (non-sensitive)
- PORT (non-sensitive)
Total: 4 vars, 2 plain-text secrets

**RuntimeEnvironmentSecrets:** EMPTY (none configured)
**InstanceRoleArn:** NULL (no instance role attached)
**AccessRoleArn:** arn:aws:iam::324480209768:role/AppRunnerECRAccessRole (ECR pull only)

---

## AWS Secrets Manager Verification (Step 3)

Both target secrets confirmed PRESENT and VALUES MATCH current plain-text:

| Secret Name | ARN | Value Match |
|---|---|---|
| drop/production/jwt_secret | arn:aws:secretsmanager:eu-west-1:324480209768:secret:drop/production/jwt_secret-QEsMUJ | MATCH |
| drop/production/database_url | arn:aws:secretsmanager:eu-west-1:324480209768:secret:drop/production/database_url-QEsMUJ | MATCH |

No new secrets needed. Existing SM entries are current and correct.

---

## IAM Analysis (Step 4 — BLOCKER FOUND)

### Finding 1: drop-api has NO instance role
drop-web (working SM integration) uses:
- **InstanceRoleArn:** arn:aws:iam::324480209768:role/drop-production-apprunner-instance
- This role has secretsmanager:GetSecretValue on drop/production/* secrets

drop-api has:
- **InstanceRoleArn:** NULL

Without an instance role, App Runner instances cannot call Secrets Manager. Attaching `RuntimeEnvironmentSecrets` without an instance role would cause service startup failure.

### Finding 2: alai-cli-deployer lacks iam:PassRole
`aws apprunner update-service` requires `iam:PassRole` on both:
1. The ECR access role (AppRunnerECRAccessRole) — needed even for config-only updates
2. The instance role (drop-production-apprunner-instance) — needed to attach it

**Exact errors:**
```
AccessDeniedException: User alai-cli-deployer is not authorized to perform: iam:PassRole 
  on resource: arn:aws:iam::324480209768:role/drop-production-apprunner-instance
  
AccessDeniedException: User alai-cli-deployer is not authorized to perform: iam:PassRole 
  on resource: arn:aws:iam::324480209768:role/AppRunnerECRAccessRole
```

---

## Target State (READY TO APPLY — pending IAM fix)

New source configuration built and saved to /tmp/drop-api-NEW-source-config.json (chmod 600):

**RuntimeEnvironmentVariables (post-migration):**
- DROP_MODE (non-sensitive, remains plain)
- PORT (non-sensitive, remains plain)

**RuntimeEnvironmentSecrets (post-migration):**
- DATABASE_URL → arn:aws:secretsmanager:eu-west-1:324480209768:secret:drop/production/database_url-QEsMUJ
- JWT_SECRET → arn:aws:secretsmanager:eu-west-1:324480209768:secret:drop/production/jwt_secret-QEsMUJ

**InstanceRoleArn to attach:** arn:aws:iam::324480209768:role/drop-production-apprunner-instance

---

## Required IAM Grants (what must be added before migration can complete)

An IAM administrator (or role with iam:PutUserPolicy / iam:AttachUserPolicy) must grant alai-cli-deployer:

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "iam:PassRole",
      "Resource": [
        "arn:aws:iam::324480209768:role/AppRunnerECRAccessRole",
        "arn:aws:iam::324480209768:role/drop-production-apprunner-instance"
      ],
      "Condition": {
        "StringEquals": {
          "iam:PassedToService": "build.apprunner.amazonaws.com"
        }
      }
    }
  ]
}
```

---

## Execute Command (ready — run after IAM grant)

```bash
aws apprunner update-service \
  --service-arn arn:aws:apprunner:eu-west-1:324480209768:service/drop-api/bdebb303a47c409393691ef8f5530144 \
  --source-configuration file:///tmp/drop-api-NEW-source-config.json \
  --instance-configuration InstanceRoleArn=arn:aws:iam::324480209768:role/drop-production-apprunner-instance \
  --profile alai-cli-deployer \
  --region eu-west-1
```

Then verify:
```bash
aws apprunner describe-service \
  --service-arn arn:aws:apprunner:eu-west-1:324480209768:service/drop-api/bdebb303a47c409393691ef8f5530144 \
  --profile alai-cli-deployer --region eu-west-1 | jq '.Service.Status'

curl -sI https://app.getdrop.no/api/health | head -5
```

Expected: Status = RUNNING, HTTP 200.

---

## Rollback Path

Rollback file: /tmp/drop-api-rollback-vars.json (chmod 600, contains plain-text values — /tmp only)
Pre-migration full config: /tmp/drop-api-PRE-migration.json (chmod 600)

Rollback command (restores plain-text state, removes secrets indirection):
```bash
aws apprunner update-service \
  --service-arn arn:aws:apprunner:eu-west-1:324480209768:service/drop-api/bdebb303a47c409393691ef8f5530144 \
  --source-configuration "$(jq '.Service.SourceConfiguration' /tmp/drop-api-PRE-migration.json)" \
  --instance-configuration Cpu=1024,Memory=2048 \
  --profile alai-cli-deployer --region eu-west-1
```
Note: Rollback also requires iam:PassRole — same IAM grant needed.

---

## Smoke Test (pending migration)

- Health endpoint: https://app.getdrop.no/api/health (per DEPLOY-MAP)
- Expected: HTTP 200, status: "ok"
- NOT YET EXECUTED — service not updated

---

## Files (sensitive — /tmp only, never committed)

- /tmp/drop-api-PRE-migration.json (chmod 600) — full pre-state including plain values
- /tmp/drop-api-rollback-vars.json (chmod 600) — extracted plain env vars for rollback
- /tmp/drop-api-NEW-source-config.json — new source config ready to apply