mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-03 19:03:22 +00:00
feat(terraform): complete dev environment adoption
This commit is contained in:
parent
e1e547f7d0
commit
e25a936da4
4 changed files with 88 additions and 11 deletions
|
|
@ -187,6 +187,32 @@ npm run deploy # deploy the stack (requires AWS)
|
||||||
|
|
||||||
All commands run from `infra/cdk/`.
|
All commands run from `infra/cdk/`.
|
||||||
|
|
||||||
|
## Terraform ownership transfer
|
||||||
|
|
||||||
|
`ManageGithubDeployRole` deliberately has no default. Every CDK deployment must
|
||||||
|
state the intended ownership phase:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Before the controlled Terraform apply: install Retain on the role and policy.
|
||||||
|
npx cdk deploy shoc-backend-deploy-dev \
|
||||||
|
--parameters shoc-backend-deploy-dev:ManageGithubDeployRole=true
|
||||||
|
|
||||||
|
# After Terraform succeeds and live verification passes: relinquish ownership.
|
||||||
|
npx cdk deploy shoc-backend-deploy-dev \
|
||||||
|
--parameters shoc-backend-deploy-dev:ManageGithubDeployRole=false
|
||||||
|
```
|
||||||
|
|
||||||
|
Both deployments must use the same reviewed SHA. The first keeps the role and
|
||||||
|
generated inline policy under CloudFormation while adding retention metadata.
|
||||||
|
The second removes both resources from CloudFormation ownership while retaining
|
||||||
|
them live for Terraform. After the second deployment succeeds,
|
||||||
|
`ManageGithubDeployRole=true` must never be used again.
|
||||||
|
|
||||||
|
Omitting the parameter fails closed before deployment. If the `true` deployment
|
||||||
|
rolls back, inspect the stack resources and live role/policy before retrying;
|
||||||
|
retained resources can outlive a failed update and must not be cleaned up
|
||||||
|
automatically.
|
||||||
|
|
||||||
## CI integration
|
## CI integration
|
||||||
|
|
||||||
`npm run synth` is the deterministic local/CI validation. After synth, inspect
|
`npm run synth` is the deterministic local/CI validation. After synth, inspect
|
||||||
|
|
@ -194,6 +220,9 @@ All commands run from `infra/cdk/`.
|
||||||
`AWS::IAM::Role`:
|
`AWS::IAM::Role`:
|
||||||
|
|
||||||
- Trust policy `StringEquals` matches the exact audience and subject above.
|
- Trust policy `StringEquals` matches the exact audience and subject above.
|
||||||
|
- The role, generated `AWS::IAM::Policy`, and role ARN output share the
|
||||||
|
`ManageGithubDeployRoleCondition`; both resources use `DeletionPolicy` and
|
||||||
|
`UpdateReplacePolicy` set to `Retain`.
|
||||||
- The inline policy contains no `Resource: "*"` mutation action and no service
|
- The inline policy contains no `Resource: "*"` mutation action and no service
|
||||||
outside `elasticbeanstalk` / `s3` / `cloudformation` / `ec2` /
|
outside `elasticbeanstalk` / `s3` / `cloudformation` / `ec2` /
|
||||||
`elasticloadbalancing` / `autoscaling`. CloudFormation discovery and
|
`elasticloadbalancing` / `autoscaling`. CloudFormation discovery and
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,27 @@ export class DeployDevStack extends cdk.Stack {
|
||||||
constructor(scope: Construct, id: string, props: cdk.StackProps = {}) {
|
constructor(scope: Construct, id: string, props: cdk.StackProps = {}) {
|
||||||
super(scope, id, props);
|
super(scope, id, props);
|
||||||
|
|
||||||
|
const manageGithubDeployRole = new cdk.CfnParameter(
|
||||||
|
this,
|
||||||
|
'ManageGithubDeployRole',
|
||||||
|
{
|
||||||
|
type: 'String',
|
||||||
|
allowedValues: ['true', 'false'],
|
||||||
|
description:
|
||||||
|
'Set true only before Terraform adoption. After ownership transfer, always reuse false.',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const manageGithubDeployRoleCondition = new cdk.CfnCondition(
|
||||||
|
this,
|
||||||
|
'ManageGithubDeployRoleCondition',
|
||||||
|
{
|
||||||
|
expression: cdk.Fn.conditionEquals(
|
||||||
|
manageGithubDeployRole.valueAsString,
|
||||||
|
'true',
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const applicationArn = `arn:aws:elasticbeanstalk:${REGION}:${ACCOUNT_ID}:application/${APPLICATION_NAME}`;
|
const applicationArn = `arn:aws:elasticbeanstalk:${REGION}:${ACCOUNT_ID}:application/${APPLICATION_NAME}`;
|
||||||
const environmentArn = `arn:aws:elasticbeanstalk:${REGION}:${ACCOUNT_ID}:environment/${APPLICATION_NAME}/${ENVIRONMENT_NAME}`;
|
const environmentArn = `arn:aws:elasticbeanstalk:${REGION}:${ACCOUNT_ID}:environment/${APPLICATION_NAME}/${ENVIRONMENT_NAME}`;
|
||||||
const oidcProviderArn = `arn:aws:iam::${ACCOUNT_ID}:oidc-provider/token.actions.githubusercontent.com`;
|
const oidcProviderArn = `arn:aws:iam::${ACCOUNT_ID}:oidc-provider/token.actions.githubusercontent.com`;
|
||||||
|
|
@ -38,6 +59,7 @@ export class DeployDevStack extends cdk.Stack {
|
||||||
const cfnRole = deployRole.node.defaultChild as iam.CfnRole;
|
const cfnRole = deployRole.node.defaultChild as iam.CfnRole;
|
||||||
cfnRole.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
|
cfnRole.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
|
||||||
cfnRole.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN;
|
cfnRole.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN;
|
||||||
|
cfnRole.cfnOptions.condition = manageGithubDeployRoleCondition;
|
||||||
|
|
||||||
deployRole.addToPolicy(
|
deployRole.addToPolicy(
|
||||||
new iam.PolicyStatement({
|
new iam.PolicyStatement({
|
||||||
|
|
@ -134,10 +156,25 @@ export class DeployDevStack extends cdk.Stack {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
new cdk.CfnOutput(this, 'GithubDeployRoleArn', {
|
const defaultPolicy = deployRole.node.findChild(
|
||||||
value: deployRole.roleArn,
|
'DefaultPolicy',
|
||||||
description: 'ARN of the GitHub OIDC deploy role for shoc-backend dev.',
|
) as iam.Policy;
|
||||||
exportName: 'shoc-backend-deploy-dev-role-arn',
|
defaultPolicy.applyRemovalPolicy(cdk.RemovalPolicy.RETAIN);
|
||||||
});
|
const cfnDefaultPolicy = defaultPolicy.node.defaultChild as iam.CfnPolicy;
|
||||||
|
cfnDefaultPolicy.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
|
||||||
|
cfnDefaultPolicy.cfnOptions.updateReplacePolicy =
|
||||||
|
cdk.CfnDeletionPolicy.RETAIN;
|
||||||
|
cfnDefaultPolicy.cfnOptions.condition = manageGithubDeployRoleCondition;
|
||||||
|
|
||||||
|
const githubDeployRoleArn = new cdk.CfnOutput(
|
||||||
|
this,
|
||||||
|
'GithubDeployRoleArn',
|
||||||
|
{
|
||||||
|
value: deployRole.roleArn,
|
||||||
|
description: 'ARN of the GitHub OIDC deploy role for shoc-backend dev.',
|
||||||
|
exportName: 'shoc-backend-deploy-dev-role-arn',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
githubDeployRoleArn.condition = manageGithubDeployRoleCondition;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,8 +94,9 @@ tf-poc rehearsal has completed both phases and therefore pins
|
||||||
creates, deletes, replacements, and managed resource types outside the
|
creates, deletes, replacements, and managed resource types outside the
|
||||||
approved environment-owned boundary.
|
approved environment-owned boundary.
|
||||||
4. Apply the no-op import only after review.
|
4. Apply the no-op import only after review.
|
||||||
5. Change the environment root to `adoption_complete=true` in a reviewed code
|
5. Change the environment root to `adoption_complete=true` and
|
||||||
change, then review the controlled in-place role and policy update:
|
`manage_eb_settings=true` in a reviewed code change, then review the
|
||||||
|
controlled in-place role, policy, secret, and Elastic Beanstalk update:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Dev example. Omit any address that is not updating.
|
# Dev example. Omit any address that is not updating.
|
||||||
|
|
@ -104,11 +105,21 @@ tf-poc rehearsal has completed both phases and therefore pins
|
||||||
--allow-update-address module.environment.aws_iam_role.runtime \
|
--allow-update-address module.environment.aws_iam_role.runtime \
|
||||||
--allow-update-address module.environment.aws_iam_role.github_deploy \
|
--allow-update-address module.environment.aws_iam_role.github_deploy \
|
||||||
--allow-update-address module.environment.aws_iam_role_policy.github_deploy \
|
--allow-update-address module.environment.aws_iam_role_policy.github_deploy \
|
||||||
--allow-update-address module.environment.aws_secretsmanager_secret.app_config
|
--allow-update-address module.environment.aws_secretsmanager_secret.app_config \
|
||||||
|
--allow-update-address module.environment.aws_elastic_beanstalk_environment.this
|
||||||
```
|
```
|
||||||
|
|
||||||
6. Apply only when every update address is named on the command line and the
|
6. Apply only when every update address is named on the command line and the
|
||||||
plan contains no create, delete, or replacement action.
|
plan contains no create, delete, or replacement action. The dev direct ALB
|
||||||
|
alias remains pinned during this phase and must not update.
|
||||||
|
|
||||||
|
The same reviewed change prepares the legacy dev CDK stack for ownership
|
||||||
|
transfer. Before the Terraform apply, deploy `shoc-backend-deploy-dev` with
|
||||||
|
`ManageGithubDeployRole=true` so both the role and generated inline-policy
|
||||||
|
resource carry `Retain`. After Terraform succeeds and live verification passes,
|
||||||
|
deploy the same reviewed SHA with `ManageGithubDeployRole=false`. This removes
|
||||||
|
both resources from CloudFormation ownership without deleting them. Never use
|
||||||
|
`ManageGithubDeployRole=true` again after that transfer.
|
||||||
|
|
||||||
The reviewed `adoption_complete=true` change updates ownership tags on IAM
|
The reviewed `adoption_complete=true` change updates ownership tags on IAM
|
||||||
roles, instance profiles, and app-config secrets, and narrows the dev role to
|
roles, instance profiles, and app-config secrets, and narrows the dev role to
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ module "environment" {
|
||||||
aws_account_id = local.aws_account_id
|
aws_account_id = local.aws_account_id
|
||||||
aws_region = local.aws_region
|
aws_region = local.aws_region
|
||||||
environment = "dev"
|
environment = "dev"
|
||||||
adoption_complete = false
|
adoption_complete = true
|
||||||
manage_eb_settings = false
|
manage_eb_settings = true
|
||||||
eb_application_name = local.eb_application_name
|
eb_application_name = local.eb_application_name
|
||||||
eb_environment_name = local.eb_environment_name
|
eb_environment_name = local.eb_environment_name
|
||||||
eb_environment_id = local.eb_environment_id
|
eb_environment_id = local.eb_environment_id
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue