Skip to content

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 to np-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 -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

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 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.

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:

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.tf exists 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 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

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/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

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/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

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


🤖 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.