---
name: teammate-access
description: >
  Onboard or offboard a Pysae team member in the Terraform IaC — grant or revoke
  AWS IAM, EKS cluster (kubectl) and MongoDB Atlas access in a single pass.
  Inputs: the person's email, an access level (or à-la-carte roles) and the target
  environments (dev/prod). Use when the user says /teammate-access, 'onboard',
  'offboard', 'ajoute un collaborateur', 'nouveau collab', 'donne les accès à',
  'donne accès EKS/Mongo à', 'retire les accès de', 'révoque les accès',
  'départ de X', 'add a teammate to terraform', 'remove X from the IaC',
  or otherwise asks to give/remove someone's infrastructure access.
  Covers the whole flow: resync the infra repos, edit the three access layers,
  fmt/validate, then delegate push and CI follow-up to the other pysae skills.
argument-hint: "[onboard|offboard] [email] [level: admin|dev|ro | roles=...] [envs: dev,prod]"
allowed-tools:
  - Read
  - Edit
  - Bash
  - Glob
  - Grep
  - Skill
  - AskUserQuestion
---

Onboard or offboard a Pysae collaborator in the Terraform infrastructure-as-code. "Access" is not one thing: it spans three independent layers, in two repos, each with its own Terraform state. This skill gives or revokes all of them coherently and in the right order, then hands off the git/CI part to the existing pysae skills.

The three layers and the exact files, patterns and role mappings live in [references/access-layers.md](references/access-layers.md). Read it before editing — it is the source of truth for *where* and *how*, this file is the source of truth for *the flow*.

## Required tools

- `glab`
- `jq`

Run `pysae-ai-tools tools require glab jq` before starting; if it exits non-zero, abort and report the install command from stderr to the user. Terraform itself runs through `devbox shell` inside the infra repos (their own `CLAUDE.md` documents this) — do not try to install it.

## Mode detection

```bash
IS_CI=$(pysae-ai-tools internal detect-context --local | jq -r '.is_ci')
```

This skill is **local/interactive only**. Granting infra access is a human decision that needs inputs and judgement, so in CI mode (`IS_CI=true`) print a short message and exit 1 rather than guessing. Everything below assumes local mode.

## Step 0 : Resolve the request

Parse from `$ARGUMENTS` (and ask, one focused question at a time via `AskUserQuestion`, only for what is genuinely missing):

- **direction** — `onboard` (add access) or `offboard` (remove all access). If the user said "départ", "remove", "révoque" → offboard.
- **email** — the person's `prenom.nom@pysae.com`. The IAM username is derived from the email local-part; match the casing/separators of existing entries (see references). Do not invent a username scheme — reuse the dominant one in `users.tf`.
- **level / roles** — the access profile. Three presets cover the common cases (`admin`, `dev`, `ro`); à-la-carte roles (billing, route53, …) are IAM-only. Mapping table in references.
- **envs** — which clusters are concerned (`dev`, `prod`). Cluster-scoped layers (EKS, Mongo) apply per env; the AWS IAM layer is account-wide.

For **offboard**, level/envs are usually not needed: the goal is to remove the person from *every* layer they appear in. Discover their actual footprint instead of assuming (Step 2).

If the request touches `prod` or grants `admin`/`root`, restate the resulting access in one line and let the user confirm before editing — this is IAM on production, a wrong scope is either a security hole or a blocker.

## Step 1 : Resync the repos (do not skip)

The local checkouts are frequently stale or sitting on an unrelated feature branch. Editing on a stale base is the main failure mode here: you compute a diff against an old `users.tf`/`main.tf` and the push is rejected or, worse, you re-introduce removed users.

For **each** repo you will touch (`infra/infra-common` for the IAM layer, `infra/infra-cluster` for EKS + Mongo):

```bash
cd <repo>
git fetch origin
git rev-parse --abbrev-ref HEAD        # are we even on master/main ?
git rev-list --left-right --count origin/<default>...HEAD   # behind / ahead
```

Then bring it onto an up-to-date default branch:
- If on a feature branch with no related local edits: `git checkout <default>` then `git pull --ff-only origin <default>`.
- If you already started editing on a feature branch: discard those edits (`git restore <file>`), switch to the default branch, pull, and **redo the edits there** — the file layout on the default branch may differ a lot from the feature branch.

Confirm `git status` is clean (apart from intentional changes) before continuing. If unrelated files are already staged, never let them ride along into your commit (Step 4 handles this).

## Step 2 : Map the request to the layers

Read [references/access-layers.md](references/access-layers.md) and resolve, for this person, the exact edits:

- **AWS IAM** (`infra-common/users.tf`): one entry `"<username>" = [<roles>]` in `local.users`.
- **EKS** (`infra-cluster/clusters/<env>/main.tf`): one entry `"<username>" = "<tier>"` in `infra_users`, per env.
- **MongoDB Atlas** (`infra-cluster/clusters/<env>/main.tf`): the **email** added to `admin_usernames` or `read_usernames`, per env.

For **offboard**, find every occurrence first — do not trust memory:

```bash
grep -rn "<username>\|<email>" \
  infra/infra-common/users.tf \
  infra/infra-cluster/clusters/*/main.tf
```

Verify a role's wiring before relying on it. A role listed as "available" is not necessarily connected to a group: confirm with `grep -n "<role>_users" infra-common/*.tf` that the derived `local.<role>_users` is actually consumed by a group membership. The references file flags the known no-op case and what to use instead.

## Step 3 : Apply the edits

- **onboard**: add the entries. Place each near its peers (people with the same kind of access), not at a random spot. Keep alignment loose — `terraform fmt` fixes it.
- **offboard**: remove every entry found in Step 2. Removing a user from `local.users` is enough to destroy the IAM user, its access key and group memberships (`force_destroy` is already set); the EKS access entry and Mongo team membership disappear with their list/map entries.

Do not touch anything unrelated to this person.

## Step 4 : Format, validate, commit

```bash
# in each repo touched
terraform fmt -recursive
```

`terraform validate` / `plan` need `terraform init` first (providers + submodules), which means `devbox shell` and backend access. If that is available locally, run `cd <root> && terraform validate` per root; if not, rely on the CI `plan` job — the edits here are list/map additions, low syntactic risk, so a failed local `validate` for "module not installed" is expected, not a blocker.

Commit **only** the files you changed, by pathspec, so any pre-existing staged files do not sneak in:

```bash
git commit users.tf -m "feat(iam): onboard <username> (<short access summary>)"
# or, in infra-cluster:
git commit clusters/dev/main.tf clusters/prod/main.tf -m "feat(iam): grant <username> EKS rw + Mongo read on dev/prod"
```

Conventional Commits, scope `iam`. For offboard use `chore(iam): offboard <username> (revoke all access)`.

## Step 5 : Push and open the change

IAM on prod deserves a review trail, so the default is a branch + MR per repo, not a direct push. Reuse the existing skills instead of re-implementing git/MR logic:

- `/glab-mr` — create the branch (e.g. `feat/onboard-<username>`) and the merge request for each touched repo.
- Only if the user explicitly asks for a direct push to the default branch, do `git push origin <default>` (and never `--force`).

State the apply ordering in the MR description: **`infra-common` must be applied before `infra-cluster`** — the EKS access entries reference the IAM user's ARN, so the user must exist first.

## Step 6 : CI follow-up

Delegate, don't poll by hand:

- `/ci-run` — watch the pipeline (`validate` → `plan` → `trivy` → `apply`) for each repo.
- `/ci-deploy-status` — confirm the apply outcome if needed.

For **onboard**, once `infra-common` is applied, tell the user the new access key lives in AWS Secrets Manager at `iam/<username>/access-key` (they retrieve it via `/aws-secrets`). For **offboard**, confirm the IAM user, its key and all memberships are gone after apply.

## Critical rules

- **Resync before editing.** A stale base is the #1 cause of rejected pushes and accidental user re-introduction.
- **Right order, always.** `infra-common` (creates the IAM user) before `infra-cluster` (references its ARN). Offboard is the reverse only if a hard dependency complains; usually order is irrelevant for removal.
- **Commit by pathspec.** Never `git add -A` / `commit -a` here — unrelated staged files (specs, WIP) must not ride along.
- **Verify role wiring, don't assume.** A role name existing in the list ≠ a role granting anything. Confirm the `local.<role>_users` is consumed by a group before promising AWS access.
- **EKS access only needs the IAM user to exist** — the tier is granted by the cluster access entry, not by an IAM group. For someone who only needs cluster + DB access, an empty IAM role list is the honest choice (see references).
- **Mongo uses the email**, IAM/EKS use the **username**. Don't mix them up.
- **No real teammate data in this skill or its references** — this package is published publicly. Examples use placeholder names.
- **Language**: explanations to the user in French; code, commits and identifiers in English.

## Self-check before finishing

- [ ] Repos resynced on an up-to-date default branch (not a stale feature branch)
- [ ] Direction (onboard/offboard) and access scope confirmed with the user for prod/admin
- [ ] All three layers handled coherently for the chosen envs (or all occurrences removed, for offboard)
- [ ] Role wiring verified (no silent no-op grant)
- [ ] `terraform fmt` run; commit done by pathspec with a Conventional Commit message
- [ ] Push/MR created via `/glab-mr`; apply ordering (infra-common first) stated
- [ ] CI follow-up handed to `/ci-run`; access-key location (onboard) or full removal (offboard) reported

$ARGUMENTS
