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
đ 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 tonp-role-assignment-corp/:
# 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
-corpsuffix. 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¶
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:
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
YourApplicationNamewith 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
.tffile.
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:
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¶
UserMap:
- login: testuser1@example.com
first_name: Test
last_name: User1
email: testuser1@example.com
After¶
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.tfexists and has the correct module version
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
YourApplicationNamewith your actual application name. TheProjectIdvariable uses PascalCase to match what the pipeline passes. The module parameterproject_iduses 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¶
resources:
repositories:
- repository: templates
type: git
name: SAIF/pipeline-templates
extends:
template: okta-terraform-np-roles-user.yml@templates
parameters:
ProjectId: $(ProjectId)
After¶
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/v3to 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¶
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¶
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/v3to 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¶
resources:
repositories:
- repository: templates
type: git
name: SAIF/pipeline-templates
extends:
template: okta-terraform-gen-token.yml@templates
After¶
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
# 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
# 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
# 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
- Managing Non-Production Role Assignments
- Managing External Test Users
- Generating OAuth 2.0 JWT Tokens
đ¤ Using an AI Agent for Migration¶
Use GitHub Copilot to automate this migration. Copy and paste the following prompt:
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.