# Test Tools Migration (Forge v2 to v3)

This guide walks through migrating a Forge 2.x test-tools repository to Forge 3.0. The migration covers Terraform restructuring, variable casing updates, and pipeline updates for test user management and non-prod role assignments.

> ℹ️ **What is a test-tools repository?** These repositories manage developer utilities for testing:
>
> - **Non-prod role assignments** - Assign yourself to business roles in non-production environments
> - **External test users** - Create and manage Okta test users for external authentication
> - **Token generation** - Generate OAuth 2.0 JWT tokens for API testing with tools like Postman or Swagger

[TOC]

---

## 📋 Prerequisites

- [ ] **No SDK installation required** — Test-tools repositories only use Terraform (no .NET or Node.js)

- [ ] **Access to Terraform Cloud** — Verify you have permissions to the workspace

- [ ] **Okta service account credentials** — Required for deployment (these are pipeline secrets)

---

## Overview of Changes

The migration updates test-tools repositories to align with Forge 3.0 conventions:

| Area               | Change                                                                 |
| ------------------ | ---------------------------------------------------------------------- |
| Folder structure   | Verify `-corp` suffix exists (rename if needed)                        |
| Module versions    | Update to `>= 3.0.0, < 4.0.0`                                          |
| YAML casing        | External test users: `UserMap` → `user_map` (non-prod roles unchanged) |
| Pipeline templates | Update to v3 templates with `refs/heads/releases/v3` reference         |

---

## 1️⃣ Verify Folder Structure

Check your current folder structure and add the bootstrap configuration.

### Expected Structure

```
infra/
├── np-role-assignment-corp/    # Non-prod role assignments
│   ├── my-roles.yml
│   └── okta-nprole-assignment.generated.tf
└── test-user-maint-external/   # External test users
    ├── bootstrap/              # Workspace bootstrap
    │   └── bootstrap.generated.tf
    └── okta/
        ├── external-test-users.generated.tf
        └── my-test-users.yml
```

### Checklist

Run these commands from your repository root:

- [ ] **If your folder is named `np-role-assignment/`**, rename it to `np-role-assignment-corp/`:

```powershell
# Only run if you have np-role-assignment/ instead of np-role-assignment-corp/
git mv ./infra/np-role-assignment/ ./infra/np-role-assignment-corp/
```

> ℹ️ **Note**: Most test-tools repositories already have the `-corp` suffix. Only older repos need this rename.

---

## 2️⃣ Module Version Updates

Update Terraform modules to version 3.0 and update external test user YAML structure.

### Non-Prod Role Assignment Updates

- [ ] Update `infra/np-role-assignment-corp/okta-nprole-assignment.generated.tf`

#### Before

```hcl
locals {
  yaml     = yamldecode(file("./my-roles.yml"))
  username = local.yaml.Username
  roles    = local.yaml.Roles
}

module "saif-nproles" {
  source   = "app.terraform.io/SAIFCorp/saif-nproles/okta"
  version  = ">=1.0.0, <2.0.0"
  Username = local.username
  Roles    = local.roles
}
```

#### After

**Full Updated File:**

```hcl
terraform {
  cloud {
    organization = "SAIFCorp"
    workspaces {
      name = "YourApplicationName-np-role-assignment"
    }
  }

  required_providers {
    okta = {
      source = "okta/okta"
    }
  }
}

provider "okta" {
  org_name       = var.okta_org_name
  base_url       = var.okta_base_url
  client_id      = var.okta_client_id
  private_key_id = var.okta_private_key_id
  private_key    = var.okta_pk
  scopes         = var.okta_scopes
}

variable "okta_pk" {
  description = "The value of the system environment variable okta_pk"
}

variable "okta_base_url" {
  description = "The base URL of the Okta organization"
}

variable "okta_client_id" {
  description = "The client ID of the Okta organization"
}

variable "okta_private_key_id" {
  description = "The private key ID of the Okta organization"
}

variable "okta_org_name" {
  description = "The name of the Okta organization"
}

variable "okta_scopes" {
  description = "The scopes of the Okta organization"
}

locals {
  yaml     = yamldecode(file("./my-roles.yml"))
  username = local.yaml.Username
  roles    = local.yaml.Roles
}

module "np-roles" {
  source   = "app.terraform.io/SAIFCorp/np-roles/okta"
  version  = ">= 3.0.0, < 4.0.0"
  Username = local.username
  Roles    = local.roles
}
```

> ℹ️ **Note**: Replace `YourApplicationName` with your actual application name. The workspace name should match what's configured in Terraform Cloud.

**Key Changes:**

- Module version `>=1.0.0, <2.0.0` → `>= 3.0.0, < 4.0.0`
- YAML keys remain PascalCase: `local.yaml.Username`, `local.yaml.Roles`
- Module parameters remain PascalCase: `Username`, `Roles`
- Local variable names use snake_case: `local.username`, `local.roles`

- [ ] Update `infra/np-role-assignment-corp/my-roles.yml`

> ℹ️ **No changes required** - The YAML file structure remains the same. Only the module version needs updating in the `.tf` file.

```yaml
Username: your.name@saif.com
Roles:
  - role_name_1
  - role_name_2
```

### External Test Users Updates

- [ ] Update `infra/test-user-maint-external/okta/external-test-users.generated.tf`

#### Key Changes

| Before                                                  | After                                            |
| ----------------------------------------- | ------------------------------------------------ |
| `local.user_map_yaml = yamldecode(...).UserMap`         | `local.user_map_yaml = yamldecode(...).user_map` |
| `module "saif-externaltestusers"` version `>= 1.0.0, < 2.0.0` | `module "external-test-users"` version `>= 3.0.0, < 4.0.0`                  |
| Module parameter `UserMap`                              | `user_map`                                       |
| Module parameter `PasswordMap`                          | `password_map`                                   |

**Full Updated File:**

```hcl
terraform {
  cloud {
    organization = "SAIFCorp"
    workspaces {
      project = "YourApplicationName-ext-test-users"
    }
  }

  required_providers {
    okta = {
      source = "okta/okta"
    }
  }
}

provider "okta" {
  org_name       = var.okta_org_name
  base_url       = var.okta_base_url
  client_id      = var.okta_client_id
  private_key_id = var.okta_private_key_id
  private_key    = var.okta_pk
  scopes         = var.okta_scopes
}

variable "okta_pk" {
  description = "The value of the system environment variable okta_pk"
}

variable "okta_base_url" {
  description = "The base URL of the Okta organization"
}

variable "okta_client_id" {
  description = "The client ID of the Okta organization"
}

variable "okta_private_key_id" {
  description = "The private key ID of the Okta organization"
}

variable "okta_org_name" {
  description = "The name of the Okta organization"
}

variable "okta_scopes" {
  description = "The scopes of the Okta organization"
}

variable "password_map" {
  description = "The map of user passwords"
  type        = map(string)
  sensitive   = true
}

locals {
  user_map_yaml = yamldecode(file("./my-test-users.yml")).user_map
  user_map      = { for value in local.user_map_yaml : value.login => value }
}

module "external-test-users" {
  source       = "app.terraform.io/SAIFCorp/external-test-users/okta"
  version      = ">= 3.0.0, < 4.0.0"
  user_map     = local.user_map
  password_map = var.password_map
}
```

- [ ] Update `infra/test-user-maint-external/okta/my-test-users.yml`

#### Before

```yaml
UserMap:
  - login: testuser1@example.com
    first_name: Test
    last_name: User1
    email: testuser1@example.com
```

#### After

```yaml
user_map:
  - login: testuser1@example.com
    first_name: Test
    last_name: User1
    email: testuser1@example.com
```

**Key Changes:**

- `UserMap` → `user_map`

---

## 3️⃣ Bootstrap Configuration

If your repository has external test user management, verify the bootstrap configuration.

- [ ] Verify `infra/test-user-maint-external/bootstrap/bootstrap.generated.tf` exists and has the correct module version

```hcl
terraform {
  cloud {
    organization = "SAIFCorp"
    workspaces {
      name = "bootstrap-YourApplicationName-ext-test-users"
    }
  }
}

variable "ProjectId" {
  type        = string
  description = "The id of the project to create the resources in."
}

module "bootstrapper" {
  source     = "app.terraform.io/SAIFCorp/bootstrap-exttestusers/saif"
  version    = ">= 3.0.0, < 4.0.0"
  project_id = var.ProjectId
}
```

> ℹ️ **Note**: Replace `YourApplicationName` with your actual application name. The `ProjectId` variable uses PascalCase to match what the pipeline passes. The module parameter `project_id` uses snake_case to match the module's expected input.

---

## 4️⃣ Pipeline Updates

Update Azure DevOps pipelines to reference Forge 3.0 templates.

### Non-Prod Roles Pipeline

- [ ] Update `.azdo/np-roles-pipeline.yml`

#### Before

```yaml
resources:
  repositories:
    - repository: templates
      type: git
      name: SAIF/pipeline-templates

extends:
  template: okta-terraform-np-roles-user.yml@templates
  parameters:
    ProjectId: $(ProjectId)
```

#### After

```yaml
resources:
  repositories:
    - repository: templates
      type: git
      name: SAIF/pipeline-templates
      ref: refs/heads/releases/v3

extends:
  template: okta-terraform-np-roles-user.yml@templates
  parameters:
    ProjectId: $(ProjectId)
```

**Key Changes:**

- Add `ref: refs/heads/releases/v3` to the template repository reference
- Template name remains the same (no v3 suffix for test-tools pipelines)

### External Test Users Pipeline

- [ ] Update `.azdo/external-test-users-pipeline.yml`

#### Before

```yaml
resources:
  repositories:
    - repository: templates
      type: git
      name: SAIF/pipeline-templates

extends:
  template: okta-terraform-external-test-users.yml@templates
  parameters:
    CreateUser: ${{variables.Username}}
    DeployType: 'apply'
```

#### After

```yaml
resources:
  repositories:
    - repository: templates
      type: git
      name: SAIF/pipeline-templates
      ref: refs/heads/releases/v3

extends:
  template: okta-terraform-external-test-users.yml@templates
  parameters:
    CreateUser: ${{variables.Username}}
    DeployType: 'apply'
```

**Key Changes:**

- Add `ref: refs/heads/releases/v3` to the template repository reference
- Template name remains the same

### Token Generation Pipeline (Optional)

If your test-tools repository includes a token generation pipeline:

- [ ] Update `.azdo/gen-token-pipeline.yml`

#### Before

```yaml
resources:
  repositories:
    - repository: templates
      type: git
      name: SAIF/pipeline-templates

extends:
  template: okta-terraform-gen-token.yml@templates
```

#### After

```yaml
resources:
  repositories:
    - repository: templates
      type: git
      name: SAIF/pipeline-templates
      ref: refs/heads/releases/v3

extends:
  template: okta-terraform-gen-token.yml@templates
```

---

## 5️⃣ Validation

After completing all changes, validate your configuration:

- [ ] Check Terraform formatting

```powershell
# Format all Terraform files
terraform -chdir=./infra/np-role-assignment-corp fmt
terraform -chdir=./infra/test-user-maint-external/okta fmt
terraform -chdir=./infra/test-user-maint-external/bootstrap fmt
```

- [ ] Validate YAML syntax

```powershell
# Check YAML files are valid
Get-Content ./infra/np-role-assignment-corp/my-roles.yml | ConvertFrom-Yaml
Get-Content ./infra/test-user-maint-external/okta/my-test-users.yml | ConvertFrom-Yaml
```

- [ ] Review pipeline changes

```powershell
# Search for any remaining references to old template refs
Get-ChildItem -Path .azdo -Filter *.yml | Select-String "repository: templates" -Context 0,3
```

---

## 🔍 Common Casing Patterns

### YAML File Keys

**Non-Prod Role Assignment** (no changes):

| Key        | Casing     |
| ---------- | ---------- |
| `Username` | PascalCase |
| `Roles`    | PascalCase |

**External Test Users** (snake_case):

| Before (PascalCase) | After (snake_case) |
| ------------------- | ------------------ |
| `UserMap`           | `user_map`         |

### Module Input Parameters

**Okta modules** (PascalCase - no changes):

| Module Parameter | Casing     |
| ---------------- | ---------- |
| `Username`       | PascalCase |
| `Roles`          | PascalCase |
| `user_map`       | snake_case |
| `password_map`   | snake_case |

### Module Versions

All Terraform modules must be updated to version 3.x:

| Before | After |
| ------ | ----- |
| `saif-nproles/okta` `>=1.0.0, <2.0.0` | `np-roles/okta` `>= 3.0.0, < 4.0.0` |
| `saif-externaltestusers/okta` `>=1.0.0, <2.0.0` | `external-test-users/okta` `>= 3.0.0, < 4.0.0` |
| `bootstrap-exttestusers/saif` `>= 1.0.0, < 2.0.0` | `bootstrap-exttestusers/saif` `>= 3.0.0, < 4.0.0` |

---

## 📚 Resources

- [Terraform Module Registry](https://app.terraform.io/app/SAIFCorp/registry/modules)
- [Managing Non-Production Role Assignments](../security/testing/non-production-role-assignment.md)
- [Managing External Test Users](../security/testing/manage-external-test-users.md)
- [Generating OAuth 2.0 JWT Tokens](../security/testing/create-jwt-for-testing-apis.md)

---

## 🤖 Using an AI Agent for Migration

Use GitHub Copilot to automate this migration. Copy and paste the following prompt:

```text
Complete the Forge v2 to v3 migration for this test-tools repository following the migration guide.

First, use `get_doc` to retrieve the full migration guide: `guides/migration/test-tools-v2-to-v3`
Then copy it to `docs/migration/test-tools-v2-to-v3.md` in the repo so you can track progress.

Use the `#todo` tool to create a todo list with these tasks:

**Migration Sections (from the guide):**
1. Verify Folder Structure - Confirm np-role-assignment-corp/ exists (rename if needed)
2. Module Version Updates - Update module versions to 3.0, update external test users YAML to snake_case
3. Bootstrap Configuration - Update bootstrap.generated.tf if it exists
4. Pipeline Updates - Update to v3 pipeline templates with refs/heads/releases/v3 reference
5. Validation - Format Terraform files, validate YAML syntax, review pipeline changes

After completing each section:
1. Validate the Terraform syntax using `terraform fmt`
2. Update the local migration progress file at `docs/migration/test-tools-v2-to-v3.md` with completed checkboxes
3. Mark the todo as complete using `#todo` before moving to the next section

**Delegation Strategy:**
Use the `#runSubagent` tool to efficiently discover files before making changes. Launch subagents in parallel for discovery tasks:

- **For Folder Structure (Task 1):** Verify np-role-assignment-corp/ exists (rename if needed)
- **For Module Updates (Task 2):** Find module version constraints in .tf files and UserMap in external test users YAML
- **For Pipeline Updates (Task 4):** Find all .yml files in .azdo/ that reference pipeline templates

Each subagent should return the complete list of file paths that need updating for its assigned task.
```

> 💡 **Tip**: The agent will create a todo list to track progress, run terminal commands, delegate complex searches to sub-agents, and can even submit improvements to this guide based on issues encountered during your migration.

---

- 💬 [Teams Support Channel](https://teams.microsoft.com/l/channel/19%3Acb611810fb0b42b080cfff5590bdd51c%40thread.tacv2/Support?groupId=514d2dac-2d62-48ce-bf99-0fa0ce39469c&tenantId=a86cb8ed-369b-4df5-ace5-43811f6e08cf)
