NTC Organizations Migration (v1 → v2)
This guide covers the migration of terraform-aws-ntc-organizations from v1.x to v2.x.
Overview of Changes
v2.0.0 introduces significant architectural improvements to simplify multi-region configurations:
Key Changes
| Feature | v1.x | v2.x |
|---|---|---|
| Provider Architecture | AWS Provider v5 | AWS Provider v6 with region attribute |
| Admin Delegations | Separate module calls per region | Integrated into root module |
| Service Quota Templates | Separate module | Integrated into root module |
| Multi-Region Support | Multiple provider aliases + module calls | Single module with regions configuration |
| Module Complexity | 3+ module calls needed | Single module call |
Before & After Comparison
- v1.x Structure
- v2.x Structure
Previous approach required 3 separate module configurations:
# 1. Main organizations module
module "ntc_organizations" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-organizations?ref=1.6.0"
# Configuration...
}
# 2. Admin delegations for eu-central-1
module "ntc_delegated_admins_euc1" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-organizations//modules/regional-admin-delegations?ref=1.6.0"
delegated_administrators = [...]
providers = { aws = aws.euc1 }
}
# 3. Admin delegations for eu-central-2
module "ntc_delegated_admins_euc2" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-organizations//modules/regional-admin-delegations?ref=1.6.0"
delegated_administrators = [...]
providers = { aws = aws.euc2 }
}
# 4. Admin delegations for us-east-1
module "ntc_delegated_admins_use1" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-organizations//modules/regional-admin-delegations?ref=1.6.0"
delegated_administrators = [...]
providers = { aws = aws.use1 }
}
New consolidated approach with a single module:
module "ntc_organizations" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-organizations?ref=2.0.0"
region = "eu-central-1" # Primary region (optional)
# All delegations configured in one place
delegated_administrators = [
{
service_principal = "securityhub.amazonaws.com"
admin_account_id = "123456789012"
regions = ["eu-central-1", "eu-central-2", "us-east-1"]
},
{
service_principal = "guardduty.amazonaws.com"
admin_account_id = "123456789012"
regions = ["eu-central-1", "eu-central-2", "us-east-1"]
},
{
service_principal = "inspector2.amazonaws.com"
admin_account_id = "123456789012"
regions = ["eu-central-1", "eu-central-2", "us-east-1"]
},
{
service_principal = "config.amazonaws.com"
admin_account_id = "123456789012"
regions = ["eu-central-1"] # Global service - delegate in primary region only
},
{
service_principal = "backup.amazonaws.com"
admin_account_id = "234567890123"
regions = ["eu-central-1"] # Global service - delegate in primary region only
},
]
# Service quotas also integrated
service_quota_templates = [...]
}
Migration Steps
Step 1: Upgrade AWS Provider to v6 (Keep NTC at v1.x)
Upgrade the AWS provider to v6 before upgrading NTC modules. This writes the new region attribute into the Terraform state for all existing resources. Without this intermediate step, resources managed through non-default (aliased) providers may be flagged for recreation during the NTC v2 upgrade.
Update only the provider version constraint. Keep all existing provider aliases and NTC module versions unchanged.
terraform {
required_version = ">= 1.3.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0" # ← Updated from ~> 5.0
}
}
}
# Keep all existing provider aliases unchanged
provider "aws" {
region = "eu-central-1"
}
provider "aws" {
alias = "euc1"
region = "eu-central-1"
}
provider "aws" {
alias = "euc2"
region = "eu-central-2"
}
provider "aws" {
alias = "use1"
region = "us-east-1"
}
Initialize and apply:
terraform init -upgrade
terraform plan
# Expected: in-place updates adding the region attribute — NO recreations
terraform apply
Step 2: Simplify Provider Configuration
After the provider upgrade is applied, simplify the provider configuration for NTC v2:
terraform {
required_version = ">= 1.5.7"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
}
provider "aws" {
region = "eu-central-1"
}
# Regional providers still needed for some exceptions (where region attribute isn't supported)
provider "aws" {
alias = "use1"
region = "us-east-1"
}
Step 3: Consolidate Module Configuration
2a. Extract Admin Delegations Configuration
If you have separate admin delegation modules, consolidate them:
- Old Configuration
- New Configuration
# In ntc_organizations_admin_delegations.tf
locals {
global_delegated_administrators = [
{
service_principal = "config.amazonaws.com"
admin_account_id = local.security_account_id
},
{
service_principal = "access-analyzer.amazonaws.com"
admin_account_id = local.security_account_id
},
{
service_principal = "backup.amazonaws.com"
admin_account_id = local.backup_account_id
},
]
regional_delegated_administrators = [
{
service_principal = "securityhub.amazonaws.com"
admin_account_id = local.security_account_id
},
{
service_principal = "guardduty.amazonaws.com"
admin_account_id = local.security_account_id
},
{
service_principal = "inspector2.amazonaws.com"
admin_account_id = local.security_account_id
}
]
}
module "ntc_delegated_admins_euc1" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-organizations//modules/regional-admin-delegations?ref=1.5.0"
delegated_administrators = concat(local.global_delegated_administrators, local.regional_delegated_administrators)
providers = { aws = aws.euc1 }
}
module "ntc_delegated_admins_euc2" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-organizations//modules/regional-admin-delegations?ref=1.5.0"
delegated_administrators = local.regional_delegated_administrators
providers = { aws = aws.euc2 }
}
module "ntc_delegated_admins_use1" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-organizations//modules/regional-admin-delegations?ref=1.5.0"
delegated_administrators = local.regional_delegated_administrators
providers = { aws = aws.use1 }
}
# In ntc_organizations.tf - add to main module block
module "ntc_organizations" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-organizations?ref=2.0.0"
region = "eu-central-1" # Optional: falls back to provider default
# ... existing configuration ...
# NEW: Consolidated delegated administrators
delegated_administrators = [
{
service_principal = "securityhub.amazonaws.com"
admin_account_id = local.security_account_id
regions = [
"eu-central-1",
"eu-central-2",
"us-east-1",
]
},
{
service_principal = "guardduty.amazonaws.com"
admin_account_id = local.security_account_id
regions = [
"eu-central-1",
"eu-central-2",
"us-east-1",
]
},
{
service_principal = "inspector2.amazonaws.com"
admin_account_id = local.security_account_id
regions = [
"eu-central-1",
"eu-central-2",
"us-east-1",
]
},
{
service_principal = "config.amazonaws.com"
admin_account_id = local.security_account_id
regions = [
"eu-central-1", # Global service - delegate in primary region only
]
},
{
service_principal = "access-analyzer.amazonaws.com"
admin_account_id = local.security_account_id
regions = [
"eu-central-1", # Global service - delegate in primary region only
]
},
{
service_principal = "backup.amazonaws.com"
admin_account_id = local.backup_account_id
regions = [
"eu-central-1", # Global service - delegate in primary region only
]
},
]
}
2b. Update Service Quota Templates (if used)
If you were using service quota templates, they're now part of the main module:
module "ntc_organizations" {
source = "github.com/nuvibit-terraform-collection/terraform-aws-ntc-organizations?ref=2.0.0"
region = "eu-central-1"
# ... other configuration ...
# Service quota templates integrated into main module
service_quota_templates = [
{
regions = ["us-east-1"]
quota_name = "Managed policies per role"
service_code = "iam"
new_value = 20 # Default: 10, Increased for complex permission models
}
]
}
Step 4: Add State Migration Blocks
Create a state_migrations.tf file to migrate resources without recreating them:
# ---------------------------------------------------------------------------------------------------------------------
# ¦ STATE MIGRATIONS - v1.x to v2.x
# ---------------------------------------------------------------------------------------------------------------------
# These moved blocks ensure Terraform state is updated without recreating resources
# Run: terraform plan - should show only "moved" operations (no creates/destroys)
# After successful migration, this file can be deleted
# -------------------------------------------------------------------------------------------------------------------
# Global Admin Delegations
# -------------------------------------------------------------------------------------------------------------------
moved {
from = module.ntc_delegated_admins_euc1.aws_organizations_delegated_administrator.ntc_delegated_admin["access-analyzer.amazonaws.com"]
to = module.ntc_organizations.module.admin_delegations.aws_organizations_delegated_administrator.ntc_delegated_admin["access-analyzer.amazonaws.com"]
}
moved {
from = module.ntc_delegated_admins_euc1.aws_organizations_delegated_administrator.ntc_delegated_admin["backup.amazonaws.com"]
to = module.ntc_organizations.module.admin_delegations.aws_organizations_delegated_administrator.ntc_delegated_admin["backup.amazonaws.com"]
}
moved {
from = module.ntc_delegated_admins_euc1.aws_organizations_delegated_administrator.ntc_delegated_admin["config.amazonaws.com"]
to = module.ntc_organizations.module.admin_delegations.aws_organizations_delegated_administrator.ntc_delegated_admin["config.amazonaws.com"]
}
# -------------------------------------------------------------------------------------------------------------------
# Regional Admin Delegations - eu-central-1
# -------------------------------------------------------------------------------------------------------------------
moved {
from = module.ntc_delegated_admins_euc1.aws_guardduty_detector.ntc_guardduty[0]
to = module.ntc_organizations.module.admin_delegations.aws_guardduty_detector.ntc_guardduty["eu-central-1"]
}
moved {
from = module.ntc_delegated_admins_euc1.aws_guardduty_organization_admin_account.ntc_guardduty[0]
to = module.ntc_organizations.module.admin_delegations.aws_guardduty_organization_admin_account.ntc_guardduty["eu-central-1"]
}
moved {
from = module.ntc_delegated_admins_euc1.aws_inspector2_delegated_admin_account.ntc_inspector[0]
to = module.ntc_organizations.module.admin_delegations.aws_inspector2_delegated_admin_account.ntc_inspector["eu-central-1"]
}
moved {
from = module.ntc_delegated_admins_euc1.aws_securityhub_account.ntc_security_hub[0]
to = module.ntc_organizations.module.admin_delegations.aws_securityhub_account.ntc_security_hub["eu-central-1"]
}
moved {
from = module.ntc_delegated_admins_euc1.aws_securityhub_organization_admin_account.ntc_security_hub[0]
to = module.ntc_organizations.module.admin_delegations.aws_securityhub_organization_admin_account.ntc_security_hub["eu-central-1"]
}
# -------------------------------------------------------------------------------------------------------------------
# Regional Admin Delegations - eu-central-2
# -------------------------------------------------------------------------------------------------------------------
moved {
from = module.ntc_delegated_admins_euc2.aws_guardduty_detector.ntc_guardduty[0]
to = module.ntc_organizations.module.admin_delegations.aws_guardduty_detector.ntc_guardduty["eu-central-2"]
}
moved {
from = module.ntc_delegated_admins_euc2.aws_guardduty_organization_admin_account.ntc_guardduty[0]
to = module.ntc_organizations.module.admin_delegations.aws_guardduty_organization_admin_account.ntc_guardduty["eu-central-2"]
}
moved {
from = module.ntc_delegated_admins_euc2.aws_inspector2_delegated_admin_account.ntc_inspector[0]
to = module.ntc_organizations.module.admin_delegations.aws_inspector2_delegated_admin_account.ntc_inspector["eu-central-2"]
}
moved {
from = module.ntc_delegated_admins_euc2.aws_securityhub_account.ntc_security_hub[0]
to = module.ntc_organizations.module.admin_delegations.aws_securityhub_account.ntc_security_hub["eu-central-2"]
}
moved {
from = module.ntc_delegated_admins_euc2.aws_securityhub_organization_admin_account.ntc_security_hub[0]
to = module.ntc_organizations.module.admin_delegations.aws_securityhub_organization_admin_account.ntc_security_hub["eu-central-2"]
}
# -------------------------------------------------------------------------------------------------------------------
# Regional Admin Delegations - us-east-1
# -------------------------------------------------------------------------------------------------------------------
moved {
from = module.ntc_delegated_admins_use1.aws_guardduty_detector.ntc_guardduty[0]
to = module.ntc_organizations.module.admin_delegations.aws_guardduty_detector.ntc_guardduty["us-east-1"]
}
moved {
from = module.ntc_delegated_admins_use1.aws_guardduty_organization_admin_account.ntc_guardduty[0]
to = module.ntc_organizations.module.admin_delegations.aws_guardduty_organization_admin_account.ntc_guardduty["us-east-1"]
}
moved {
from = module.ntc_delegated_admins_use1.aws_inspector2_delegated_admin_account.ntc_inspector[0]
to = module.ntc_organizations.module.admin_delegations.aws_inspector2_delegated_admin_account.ntc_inspector["us-east-1"]
}
moved {
from = module.ntc_delegated_admins_use1.aws_securityhub_account.ntc_security_hub[0]
to = module.ntc_organizations.module.admin_delegations.aws_securityhub_account.ntc_security_hub["us-east-1"]
}
moved {
from = module.ntc_delegated_admins_use1.aws_securityhub_organization_admin_account.ntc_security_hub[0]
to = module.ntc_organizations.module.admin_delegations.aws_securityhub_organization_admin_account.ntc_security_hub["us-east-1"]
}
- These
movedblocks tell Terraform to update its state without recreating resources - After successful migration (successful apply), you can delete
state_migrations.tf - If you have different regions, adjust the region names accordingly
Step 5: Remove Old Definitions
After consolidating the configuration, remove the Admin Delegation and Service Quota Template module calls.
Step 6: Initialize and Plan
# Upgrade to AWS Provider v6
terraform init -upgrade
# Review the plan
terraform plan
Expected plan output:
- ✅ Many
movedoperations (state migrations) - ✅ Some in-place updates (configuration changes)
- ❌ NO resource deletions or recreations
If you see deletions or recreations, do NOT apply! Review your configuration and state migration blocks carefully.
Step 7: Apply Migration
terraform apply
Monitor the output. The migration should complete without errors.
Step 8: Clean Up
After successful migration:
# Remove the state migration file (optional, but recommended)
rm state_migrations.tf
# Commit your changes
git add .
git commit -m "Migrate ntc-organizations from v1.x to v2.x"
Complete Example
See a complete before/after example in the GitHub repository:
Troubleshooting
Issue: Resources Being Recreated
Symptom: terraform plan shows deletions and recreations
Solution:
- Check your
movedblocks match your existing state exactly - Verify resource names haven't changed
- Use
terraform state listto see current resource addresses
Issue: Provider Configuration Errors
Symptom: "provider configuration not present" errors
Solution:
- Ensure AWS Provider v6 is installed (
terraform init -upgrade) - Verify your
required_providersblock hasversion = "~> 6.0"
Issue: Region Not Supported
Symptom: "region not available" errors in ESC
Solution:
- For ESC, ensure you're using supported regions
- Check region codes match your partition (e.g.,
eu-central-1vseusc-de-east-1)
Need Help?
- 📚 Review the main migration guide
- 🐛 Check common issues
- 💬 Contact NTC Support