chore(terraform): remove tf-poc rehearsal (SH-300) (#95)
Some checks are pending
Validate and deploy / Validate deployable source bundle (push) Waiting to run
Validate and deploy / Deploy shoc-backend-dev through Terraform (push) Blocked by required conditions
Validate and deploy / Deploy shoc-backend-staging to Elastic Beanstalk (push) Blocked by required conditions

* chore(terraform): remove tf-poc rehearsal

* chore(terraform): drop tf-poc from live module and CI

* chore: clean remaining tf-poc reference from `shared_certificate_arn`
This commit is contained in:
Adam Moussa 2026-09-03 10:40:32 -04:00 • committed by GitHub
parent 77c3016c9d
commit 69ba573315
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 42 additions and 379 deletions

View file

@ -45,7 +45,7 @@ jobs:
directories=()
case "${{ github.base_ref }}" in
dev)
directories+=(terraform/live/tf-poc terraform/live/dev)
directories+=(terraform/live/dev)
;;
staging)
directories+=(terraform/live/staging)

View file

@ -56,14 +56,14 @@ The script:
G10 permits only exact approved resource address/type pairs for the
environment-owned boundary: Elastic
Beanstalk environment, IAM role/inline policy/managed-policy attachment/
instance profile, Secrets Manager secret metadata, Route 53 zone/record, and
ACM certificate. Initial mode permits no update. Controlled mode requires one
instance profile, Secrets Manager secret metadata, and Route 53 record.
Initial mode permits no update. Controlled mode requires one
`--allow-update-address` argument per reviewed in-place update. Every invocation
also requires `--environment dev`, `--environment staging`, or
`--environment tf-poc`; an empty or incomplete environment plan fails.
also requires `--environment dev` or `--environment staging`; an empty or
incomplete environment plan fails.
G11 runs `terraform fmt -check -recursive`, `terraform init -backend=false`,
and `terraform validate`. PRs to `dev` validate `live/tf-poc` and `live/dev`.
and `terraform validate`. PRs to `dev` validate `live/dev`.
PRs to `staging` validate only `live/staging`. Org-baseline CloudFormation owns
the HCP role substrate, and Terraform owns the dev deploy role, so no backend
CDK or bootstrap root remains in the matrix.

View file

@ -23,12 +23,6 @@ REQUIRED_RESOURCES = {
"module.environment.aws_iam_role_policy.runtime_webhook[0]": "aws_iam_role_policy",
"module.environment.aws_route53_record.api_cname[0]": "aws_route53_record",
},
"tf-poc": {
**COMMON_RESOURCES,
"aws_acm_certificate.poc": "aws_acm_certificate",
"aws_route53_zone.poc": "aws_route53_zone",
"module.environment.aws_route53_record.api_cname[0]": "aws_route53_record",
},
}
DEV_IMPORT_IDS = {

View file

@ -186,15 +186,6 @@ def main() -> int:
),
0,
),
(
"tf-poc controlled update",
run_case(
"tf-poc",
actions_by_address={controlled_address: ["update"]},
allowed_updates=(controlled_address,),
),
0,
),
(
"wrong controlled address",
run_case(
@ -255,12 +246,12 @@ def main() -> int:
1,
),
(
"live webhook policy in tf-poc",
"staging resource in a dev plan",
run_case(
"tf-poc",
"dev",
extra_resource=(
"module.environment.aws_iam_role_policy.runtime_webhook[0]",
"aws_iam_role_policy",
"module.environment.aws_route53_record.api_cname[0]",
"aws_route53_record",
["no-op"],
),
),

View file

@ -7,8 +7,6 @@ keeping shared and Elastic Beanstalk-generated resources outside state.
- `live/dev/` imports the existing dev environment-owned resources.
- `live/staging/` imports the existing staging environment-owned resources.
- `live/tf-poc/` manages the retained import-rehearsal environment after its
completed transfer from CloudFormation.
Shared RDS, application, VPC, subnet, service-role, shared-certificate, and
Elastic Beanstalk-generated inventory remains data-only or provider-managed.
@ -42,8 +40,6 @@ terraform -chdir=terraform/live/dev init -backend=false
terraform -chdir=terraform/live/dev validate
terraform -chdir=terraform/live/staging init -backend=false
terraform -chdir=terraform/live/staging validate
terraform -chdir=terraform/live/tf-poc init -backend=false
terraform -chdir=terraform/live/tf-poc validate
python scripts/test-terraform-import-plan-check.py
python scripts/test-terraform-release-plan-check.py
```

View file

@ -8,10 +8,6 @@ shared or Elastic Beanstalk-generated infrastructure.
- `dev/` and `staging/` import the existing EB environment, runtime
role/profile/policies, deploy role/policy, app-config secret metadata, and API
record.
- `tf-poc/` manages the retained rehearsal environment after its completed
CloudFormation-to-Terraform transfer, excluding the live-only webhook and
Dynamo policies. It also owns the child zone and DNS-validated ACM
certificate.
- `modules/environment-inventory/` reads and pins only shared resources.
- Org-baseline CloudFormation owns the narrowly scoped HCP Terraform plan/apply
roles.
@ -19,8 +15,7 @@ shared or Elastic Beanstalk-generated infrastructure.
The shared `shoc-backend` Elastic Beanstalk application and
`shoc-sqlserver-shared` RDS instance, VPC, subnets, EB service role, shared
certificate, shared RDS security group, and EB-generated SG/ALB/ASG/CloudFormation
resources must never enter an environment state. The `shoc_tf_poc` SQL catalog
is out of band.
resources must never enter an environment state.
Secret values are not Terraform resources, variables, outputs, or managed EB
settings. Terraform manages the app-config secret shell and maps approved JSON
@ -78,9 +73,7 @@ Terraform does not perform this pre-import mutation.
## Two-phase adoption
Each dev/staging root pins `adoption_complete=false` in reviewed code until its
initial import is proven. It is not an HCP workspace variable. The retained
tf-poc rehearsal has completed both phases and therefore pins
`adoption_complete=true`.
initial import is proven. It is not an HCP workspace variable.
1. Create the HCP workspace and configure dynamic credentials.
2. Run the declarative imports.
@ -169,19 +162,6 @@ This change is the allowed exception that mixes deployable application CD with
the Terraform variable that application CD needs. Later PRs must not mix
deployable application changes with Terraform or CDK changes.
## POC retained identifiers
The tf-poc HCP workspace stores the exact retained environment ID, app-config
secret ARN, child-zone ID, and certificate ARN declared in
`tf-poc/variables.tf`. The declarative import blocks consumed those identifiers
during the completed transfer. Do not guess or replace them, and do not put
credentials or secret values in HCP variables.
ACM DNS validation remains part of the Terraform-owned certificate resource;
its generated validation record is not a separate ownership target. The public
delegation of `tf-poc.seahaven.com` from `seahaven.com` remains outside this
Terraform state.
## Pinned live identities
- Dev: workspace `shoc-backend-dev`; EB environment `shoc-backend-dev`
@ -198,5 +178,3 @@ identifiers make accidental cross-environment reuse fail review and planning.
- Auto-apply remains off.
- Org baseline owns final HCP plan/apply permissions and manager tags.
- Every imported Terraform resource has `prevent_destroy`.
- The tf-poc CloudFormation creator path was removed after its no-op import,
controlled update, and retained-resource ownership transfer completed.

View file

@ -196,7 +196,6 @@ resource "aws_iam_role" "github_deploy" {
data "aws_iam_policy_document" "deploy" {
statement {
sid = var.environment == "tf-poc" ? "DescribeDeploymentResources" : null
effect = "Allow"
actions = [
"autoscaling:Describe*",
@ -210,7 +209,6 @@ data "aws_iam_policy_document" "deploy" {
}
statement {
sid = var.environment == "tf-poc" ? "CreateApplicationVersion" : null
effect = "Allow"
actions = ["elasticbeanstalk:CreateApplicationVersion"]
resources = [
@ -220,45 +218,38 @@ data "aws_iam_policy_document" "deploy" {
}
statement {
sid = var.environment == "tf-poc" ? "UpdatePocEnvironment" : null
effect = "Allow"
actions = ["elasticbeanstalk:UpdateEnvironment"]
resources = [local.environment_arn]
}
dynamic "statement" {
for_each = var.environment != "tf-poc" ? [1] : []
content {
effect = "Allow"
actions = [
"cloudformation:CancelUpdateStack",
"cloudformation:DescribeStackEvents",
"cloudformation:DescribeStackResource",
"cloudformation:DescribeStackResources",
"cloudformation:DescribeStacks",
"cloudformation:GetTemplate",
"cloudformation:ListStackResources",
"cloudformation:UpdateStack",
]
resources = [
"arn:aws:cloudformation:${var.aws_region}:${var.aws_account_id}:stack/${local.environment_stack_name}/*",
]
}
statement {
effect = "Allow"
actions = [
"cloudformation:CancelUpdateStack",
"cloudformation:DescribeStackEvents",
"cloudformation:DescribeStackResource",
"cloudformation:DescribeStackResources",
"cloudformation:DescribeStacks",
"cloudformation:GetTemplate",
"cloudformation:ListStackResources",
"cloudformation:UpdateStack",
]
resources = [
"arn:aws:cloudformation:${var.aws_region}:${var.aws_account_id}:stack/${local.environment_stack_name}/*",
]
}
dynamic "statement" {
for_each = var.environment != "tf-poc" ? [1] : []
content {
effect = "Allow"
actions = [
"autoscaling:PutNotificationConfiguration",
"autoscaling:ResumeProcesses",
"autoscaling:SuspendProcesses",
]
resources = [
"arn:aws:autoscaling:${var.aws_region}:${var.aws_account_id}:autoScalingGroup:*:autoScalingGroupName/${local.environment_stack_name}-*",
]
}
statement {
effect = "Allow"
actions = [
"autoscaling:PutNotificationConfiguration",
"autoscaling:ResumeProcesses",
"autoscaling:SuspendProcesses",
]
resources = [
"arn:aws:autoscaling:${var.aws_region}:${var.aws_account_id}:autoScalingGroup:*:autoScalingGroupName/${local.environment_stack_name}-*",
]
}
dynamic "statement" {
@ -288,7 +279,6 @@ data "aws_iam_policy_document" "deploy" {
dynamic "statement" {
for_each = local.use_legacy_s3_policy ? [] : [1]
content {
sid = var.environment == "tf-poc" ? "UploadApplicationVersion" : null
effect = "Allow"
actions = ["s3:PutObject"]
resources = ["arn:aws:s3:::${local.eb_bucket_name}/${var.eb_application_name}/*"]
@ -298,25 +288,11 @@ data "aws_iam_policy_document" "deploy" {
dynamic "statement" {
for_each = local.use_legacy_s3_policy ? [] : [1]
content {
sid = var.environment == "tf-poc" ? "UseBeanstalkBucket" : null
effect = "Allow"
actions = ["s3:GetBucketLocation", "s3:ListBucket"]
resources = ["arn:aws:s3:::${local.eb_bucket_name}"]
}
}
dynamic "statement" {
for_each = var.environment == "tf-poc" ? [1] : []
content {
sid = "DenyLiveEnvironments"
effect = "Deny"
actions = ["elasticbeanstalk:*"]
resources = [
"arn:aws:elasticbeanstalk:${var.aws_region}:${var.aws_account_id}:environment/${var.eb_application_name}/shoc-backend-dev",
"arn:aws:elasticbeanstalk:${var.aws_region}:${var.aws_account_id}:environment/${var.eb_application_name}/shoc-backend-staging",
]
}
}
}
resource "aws_iam_role_policy" "github_deploy" {

View file

@ -10,8 +10,8 @@ variable "environment" {
type = string
validation {
condition = contains(["dev", "staging", "tf-poc"], var.environment)
error_message = "environment must be dev, staging, or tf-poc."
condition = contains(["dev", "staging"], var.environment)
error_message = "environment must be dev or staging."
}
}
@ -37,7 +37,7 @@ variable "eb_environment_name" {
variable "eb_environment_id" {
type = string
description = "Existing environment ID. Empty only before the CDK POC has been provisioned."
description = "Existing Elastic Beanstalk environment ID."
}
variable "platform_arn" {
@ -68,7 +68,7 @@ variable "eb_service_role_name" {
variable "shared_certificate_arn" {
type = string
description = "Existing shared certificate for dev/staging, or the POC certificate ARN."
description = "Existing shared certificate for dev/staging"
}
variable "runtime_role_name" {
@ -143,7 +143,7 @@ variable "webhook_decrypt_policy_sid" {
variable "dynamo_reader_role_arn" {
type = string
default = null
description = "Dev-only cross-account role. Null for staging and tf-poc."
description = "Dev-only cross-account role. Null for staging."
}
variable "dynamo_policy_sid" {

View file

@ -1,26 +0,0 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/aws" {
version = "6.62.0"
constraints = "~> 6.57"
hashes = [
"h1:OthB9UeoBgmy348EpDjs5GDGk6p6UxAMQD5cXn7u9Ho=",
"zh:35a9e4bc6fd622c5a99561b882025f2745f1256bbf1a8da8d6b39319b75ae0b5",
"zh:405927d470ff16201e40aa0fa2d0ab1de477360a0926d20719cd029179682ecd",
"zh:4ab7866593a90bcf18f066b0092a209b9f42852acd783b504031ae74cb6f7010",
"zh:5b477f313fc511648a4eed9f9085d0778414835896256ab14296d2345b7070e3",
"zh:87de70bc99751f94262cec2260d972555a98f588aa3a613e417438f88a1182df",
"zh:88f02a8ff07f00da4ffb3bee9e8ae25588e3a0a92633c625c1c2a63bac00a844",
"zh:8d8596257453357c9f3fccaa7d2f04299e8d35b16f364adb8a2c829143a9c090",
"zh:953c8e15fa9c12c081f17d66cf45246d032fa23bee33f264dc82242afdb98bc2",
"zh:9a7dd903e5e9b2b0cc1317ad2d2692e0ddf05ae2a5aaee20ec5dd1db456711b7",
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
"zh:a859154c75c1088d098a481f1ebb259720c5a2ad87781364abf556a741e5adb7",
"zh:b8d1e72ad39d5864118f64dd3273424ab637d34b3ff8dd3dfeb4aef9d458587f",
"zh:c3666fcfc7b131f5282d4e7249fa68c3a21665757888aa02bbaf6be1cd036bba",
"zh:c6b8ff94b3f49bf85fc087381cfe1b271c5b01cf74cf140d58aa500be7138913",
"zh:d8143d790e9dd77b8e2f9168e4a33ad6d064dc4b082a0196636b182105aaed14",
"zh:fa41eca042f377eb2741e95b36609c1de5b0cd675cd4e3e30c709497cb94db02",
]
}

View file

@ -1,54 +0,0 @@
import {
to = aws_route53_zone.poc
id = var.poc_hosted_zone_id
}
import {
to = aws_acm_certificate.poc
id = var.poc_certificate_arn
}
import {
to = module.environment.aws_elastic_beanstalk_environment.this
id = var.poc_environment_id
}
import {
to = module.environment.aws_iam_role.runtime
id = "shoc-backend-tf-poc"
}
import {
to = module.environment.aws_iam_instance_profile.runtime
id = "shoc-backend-tf-poc"
}
import {
to = module.environment.aws_iam_role_policy_attachment.web_tier
id = "shoc-backend-tf-poc/arn:aws:iam::aws:policy/AWSElasticBeanstalkWebTier"
}
import {
to = module.environment.aws_iam_role_policy.runtime_app_config
id = "shoc-backend-tf-poc:shoc-tf-poc-secrets-read"
}
import {
to = module.environment.aws_iam_role.github_deploy
id = "githubdeploy-shoc-backend-tf-poc"
}
import {
to = module.environment.aws_iam_role_policy.github_deploy
id = "githubdeploy-shoc-backend-tf-poc:githubdeploy-shoc-backend-tf-poc-eb"
}
import {
to = module.environment.aws_secretsmanager_secret.app_config
id = var.poc_app_config_secret_arn
}
import {
to = module.environment.aws_route53_record.api_cname[0]
id = "${var.poc_hosted_zone_id}_api.tf-poc.seahaven.com_CNAME"
}

View file

@ -1,115 +0,0 @@
data "aws_caller_identity" "current" {}
data "aws_vpc" "shared" {
id = "vpc-0d16336143f3da25e"
}
data "aws_db_instance" "shared" {
db_instance_identifier = "shoc-sqlserver-shared"
}
data "aws_iam_role" "eb_service" {
name = "shoc-eb-service-role"
}
check "account" {
assert {
condition = data.aws_caller_identity.current.account_id == "396287094661"
error_message = "Refusing to inspect or adopt the POC outside account 396287094661."
}
}
resource "aws_route53_zone" "poc" {
name = "tf-poc.seahaven.com"
comment = "Terraform import rehearsal child zone. Parent NS delegation is a separate approved operation."
force_destroy = false
tags = {
env = "tf-poc"
project = "shoc"
}
lifecycle {
prevent_destroy = true
}
}
resource "aws_acm_certificate" "poc" {
domain_name = "*.tf-poc.seahaven.com"
validation_method = "DNS"
tags = {
Name = "shoc-backend-terraform-import-poc/Certificate"
env = "tf-poc"
project = "shoc"
}
lifecycle {
prevent_destroy = true
}
}
module "environment" {
source = "../modules/environment-owned"
aws_account_id = "396287094661"
aws_region = "us-east-1"
environment = "tf-poc"
adoption_complete = true
eb_application_name = "shoc-backend"
eb_environment_name = "shoc-backend-tf-poc"
eb_environment_id = var.poc_environment_id
platform_arn = "arn:aws:elasticbeanstalk:us-east-1::platform/.NET 8 running on 64bit Amazon Linux 2023/3.11.3"
vpc_id = data.aws_vpc.shared.id
instance_subnet_ids = ["subnet-02946ccd6735742e9", "subnet-0bedaa6e0c750be4f", "subnet-09eaf2bfa468d206f"]
load_balancer_subnet_ids = ["subnet-09eaf2bfa468d206f", "subnet-02946ccd6735742e9", "subnet-0bedaa6e0c750be4f"]
instance_security_group_id = null
eb_service_role_name = data.aws_iam_role.eb_service.name
shared_certificate_arn = aws_acm_certificate.poc.arn
runtime_role_name = "shoc-backend-tf-poc"
runtime_app_config_policy_name = "shoc-tf-poc-secrets-read"
runtime_webhook_policy_name = null
permissions_boundary_arn = "arn:aws:iam::396287094661:policy/shoc-backend-tf-poc-runtime-boundary"
github_deploy_permissions_boundary_arn = "arn:aws:iam::396287094661:policy/shoc-backend-tf-poc-deploy-boundary"
app_config_secret_name = "shoc/tf-poc/app-config"
app_config_json_keys = [
"ConnectionStrings__DefaultConnection",
"JWT__Secret",
"JWT__ValidAudience",
"JWT__ValidIssuer",
"SendGrid__ApiKey",
]
webhook_secret_arn = null
work_order_webhook_enabled = false
github_repo = "Sea-Haven-Industries/shoc-backend"
github_environment = "tf-poc"
github_deploy_role_name = "githubdeploy-shoc-backend-tf-poc"
github_deploy_policy_name = "githubdeploy-shoc-backend-tf-poc-eb"
legacy_dev_s3_policy = false
hosted_zone_id = aws_route53_zone.poc.zone_id
api_domain = "api.tf-poc.seahaven.com"
api_record_type = "CNAME"
metadata_before_adoption = {
runtime_role_description = "SHOC backend tf-poc compute role (EB instance profile)"
runtime_role_tags = {
env = "tf-poc"
project = "shoc"
}
instance_profile_tags = {}
app_config_description = "SHOC tf-poc application config (conn string, JWT, SendGrid)"
app_config_tags = {
env = "tf-poc"
project = "shoc"
}
deploy_role_description = "GitHub OIDC deploy role for shoc-backend-tf-poc."
deploy_role_tags = {
HcpTerraformWorkspace = "shoc-backend-tf-poc"
env = "tf-poc"
project = "shoc"
}
environment_tags = {
env = "tf-poc"
project = "shoc"
}
}
}

View file

@ -1,25 +0,0 @@
output "environment_arn" {
value = module.environment.environment_arn
}
output "runtime_role_arn" {
value = module.environment.runtime_role_arn
}
output "github_deploy_role_arn" {
value = module.environment.github_deploy_role_arn
}
output "app_config_secret_arn" {
value = module.environment.app_config_secret_arn
}
output "child_zone_name_servers" {
description = "For a separate, explicitly approved parent-zone delegation operation."
value = aws_route53_zone.poc.name_servers
}
output "shared_rds_arn" {
description = "Data-only shared RDS instance. The shoc_tf_poc catalog remains out of band."
value = data.aws_db_instance.shared.db_instance_arn
}

View file

@ -1,3 +0,0 @@
provider "aws" {
region = "us-east-1"
}

View file

@ -1,30 +0,0 @@
variable "poc_environment_id" {
type = string
description = "Exact e-* ID of the retained POC environment."
validation {
condition = can(regex("^e-[a-z0-9]+$", var.poc_environment_id))
error_message = "poc_environment_id must be an Elastic Beanstalk e-* ID."
}
}
variable "poc_hosted_zone_id" {
type = string
description = "Exact Route 53 ID of the retained child zone."
validation {
condition = can(regex("^Z[A-Z0-9]+$", var.poc_hosted_zone_id))
error_message = "poc_hosted_zone_id must be a Route 53 hosted-zone ID."
}
}
variable "poc_certificate_arn" {
type = string
description = "Exact ARN of the retained ACM certificate."
}
variable "poc_app_config_secret_arn" {
type = string
description = "Exact ARN of the retained POC app-config secret."
}

View file

@ -1,19 +0,0 @@
terraform {
required_version = ">= 1.9.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.57"
}
}
cloud {
organization = "seahaven"
workspaces {
project = "seahaven-external-dev"
name = "shoc-backend-tf-poc"
}
}
}