seahaven-org-baseline/lib/seahaven-hcptf-stack.ts
Adam Moussa 031e1d1a3b
Some checks are pending
Deploy / deploy-management (push) Waiting to run
Deploy / deploy-external-dev (push) Waiting to run
Deploy / deploy-security (push) Waiting to run
Deploy / deploy-dev (push) Waiting to run
Deploy / deploy-prod (push) Waiting to run
refactor(iam): fold seahaven-site roles into seahaven-hcptf (#175)
Prod HCP exec roles for seahaven-site now live in the same stack as payments-dashboard so role ownership is one stack.
2026-10-02 17:30:52 +00:00

1251 lines
34 KiB
TypeScript

import * as cdk from "aws-cdk-lib";
import * as iam from "aws-cdk-lib/aws-iam";
import { Construct } from "constructs";
import { HcptfPolicyAspect } from "./hcptf-policy-aspect";
import { SeahavenSiteRoles } from "./seahaven-site-hcptf-stack";
/**
* HCP exec roles. One stack per account. Prod is 011934824531 and also
* hosts the seahaven-site apply and plan roles. Dev is 710827005802 and
* stays payments-dashboard only.
*
* payments-dashboard: workspace payments-dashboard-prod, project
* seahaven-prod, or workspace payments-dashboard-dev, project seahaven-dev.
*
* hcptf-payments-dashboard, hcptf-payments-dashboard-plan, and
* payments-dashboard-lambda-boundary already existed in both accounts and
* were imported. A create fails with a name conflict. The stacks are on
* the dev and prod deploy jobs. That deploy creates the three managed
* policies and removes the inline policies.
*
* `cdk import -c hcptfPaymentsImport=true` synthesizes only those three
* resources, with the roles' current inline policy names and no reference
* to the policies that do not exist yet. The default template is the
* managed-policy state.
*/
export interface SeahavenHcptfStackProps extends cdk.StackProps {
/** HCP project name: seahaven-prod or seahaven-dev. */
hcpProject: string;
/** HCP workspace name: payments-dashboard-prod or payments-dashboard-dev. */
hcpWorkspace: string;
/** DynamoDB CMK in this account. */
dynamodbCmkArn: string;
/**
* Secret ARNs in this order: slack-bot-token, slack-signing-secret,
* boa-check-mgmt, boa-reporting, expense-slack-token,
* expense-slack-signing-secret.
*/
secretArns: readonly string[];
/**
* Synthesize the import template. Set from `-c hcptfPaymentsImport=true`.
* Default is the managed-policy template.
*/
importExisting?: boolean;
/** Prod only. Fold seahaven-site exec roles into this stack. */
includeSeahavenSite?: boolean;
/**
* Synthesize the seahaven-site import template. Set from
* `-c hcptfSiteImport=true`. Omits site role tags and site outputs.
*/
siteImportExisting?: boolean;
}
export class SeahavenHcptfStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: SeahavenHcptfStackProps) {
super(scope, id, props);
if (props.importExisting && props.siteImportExisting) {
throw new Error("hcptfPaymentsImport and hcptfSiteImport cannot both be set");
}
paymentsDashboard(this, props);
if (props.includeSeahavenSite) {
new SeahavenSiteRoles(this, "SeahavenSite", {
importExisting: props.siteImportExisting === true,
});
}
cdk.Aspects.of(this).add(new HcptfPolicyAspect(props.importExisting === true));
}
}
const VIEW_ONLY = "arn:aws:iam::aws:policy/job-function/ViewOnlyAccess";
/** Account and CMK id baked into the policy literals. Retarget rewrites them. */
const TEMPLATE_ACCOUNT = "011934824531";
const TEMPLATE_CMK_ID = "be5fa4cb-c546-40fe-a13d-c7bec79f5d12";
const TEMPLATE_SECRET_ARNS = [
"arn:aws:secretsmanager:us-east-1:011934824531:secret:payments-dashboard/slack-bot-token-0pAM3S",
"arn:aws:secretsmanager:us-east-1:011934824531:secret:payments-dashboard/slack-signing-secret-u0T6h8",
"arn:aws:secretsmanager:us-east-1:011934824531:secret:payments-dashboard/boa-check-mgmt-LEbC65",
"arn:aws:secretsmanager:us-east-1:011934824531:secret:payments-dashboard/boa-reporting-JoR9lq",
"arn:aws:secretsmanager:us-east-1:011934824531:secret:payments-dashboard/expense-slack-token-SeMg3s",
"arn:aws:secretsmanager:us-east-1:011934824531:secret:payments-dashboard/expense-slack-signing-secret-lbb78J",
] as const;
function retarget(
document: object,
account: string,
target: SeahavenHcptfStackProps,
): object {
if (target.secretArns.length !== TEMPLATE_SECRET_ARNS.length) {
throw new Error("payments-dashboard secretArns must list six secrets");
}
const cmkId = target.dynamodbCmkArn.split("/").pop();
if (!cmkId || !target.dynamodbCmkArn.includes(":key/")) {
throw new Error("dynamodbCmkArn must be a KMS key ARN");
}
let json = JSON.stringify(document);
const containedCmk = json.includes(TEMPLATE_CMK_ID);
for (let i = 0; i < TEMPLATE_SECRET_ARNS.length; i++) {
json = json.replaceAll(TEMPLATE_SECRET_ARNS[i], target.secretArns[i]);
}
json = json.replaceAll(TEMPLATE_CMK_ID, cmkId);
json = json.replaceAll(TEMPLATE_ACCOUNT, account);
if (containedCmk && !json.includes(target.dynamodbCmkArn)) {
throw new Error("CMK retarget did not produce dynamodbCmkArn");
}
return JSON.parse(json);
}
function paymentsDashboard(
stack: cdk.Stack,
target: SeahavenHcptfStackProps,
): void {
const account = stack.account;
const providerArn = `arn:aws:iam::${account}:oidc-provider/app.terraform.io`;
const workspace = `organization:seahaven:project:${target.hcpProject}:workspace:${target.hcpWorkspace}`;
// CloudFormation rejects Tags and any RoleArn output on an IAM role import.
// The deploy after import adds both.
if (!target.importExisting) {
cdk.Tags.of(stack).add("Project", "payments-dashboard");
cdk.Tags.of(stack).add("Owner", "adam@seahavenind.com");
cdk.Tags.of(stack).add("ManagedBy", "cdk");
}
const boundary = managedPolicy(
stack,
"PaymentsDashboardLambdaBoundary",
"payments-dashboard-lambda-boundary",
boundaryDocument(account, target),
"Per-workload Lambda permissions boundary for payments-dashboard (PLAT-79).",
);
const applyTrust = trust(
providerArn,
`${workspace}:run_phase:apply`,
"HcpApply",
);
const planTrust = trust(providerArn, `${workspace}:run_phase:plan`, "HcpPlan");
// Import template: the two roles and the boundary only. Inline policy names
// match the live roles so the later deploy can delete them. No Ref to the
// three managed policies that do not exist yet.
const apply = new iam.CfnRole(stack, "PaymentsDashboardApplyRole", {
roleName: "hcptf-payments-dashboard",
maxSessionDuration: 3600,
assumeRolePolicyDocument: applyTrust,
...(target.importExisting
? {
policies: [
{
policyName: "payments-dashboard-services",
policyDocument: servicesDocument(account, target),
},
{
policyName: "scoped-iam-management",
policyDocument: scopedIamDocument(account, target),
},
],
}
: {
managedPolicyArns: [
managedPolicy(
stack,
"PaymentsDashboardIam",
"payments-dashboard-hcptf-iam",
scopedIamDocument(account, target),
).ref,
managedPolicy(
stack,
"PaymentsDashboardServices",
"payments-dashboard-hcptf-services",
servicesDocument(account, target),
).ref,
],
}),
...(target.importExisting ? {} : { tags: roleTags() }),
});
retain(apply);
const plan = new iam.CfnRole(stack, "PaymentsDashboardPlanRole", {
roleName: "hcptf-payments-dashboard-plan",
maxSessionDuration: 3600,
assumeRolePolicyDocument: planTrust,
...(target.importExisting
? {
managedPolicyArns: [VIEW_ONLY],
policies: [
{
policyName: "payments-dashboard-plan-refresh",
policyDocument: planDocument(account, target),
},
],
}
: {
managedPolicyArns: [
VIEW_ONLY,
managedPolicy(
stack,
"PaymentsDashboardPlan",
"payments-dashboard-hcptf-plan",
planDocument(account, target),
).ref,
],
}),
...(target.importExisting ? {} : { tags: roleTags() }),
});
retain(plan);
if (!target.importExisting) {
new cdk.CfnOutput(stack, "ApplyRoleArn", { value: apply.attrArn });
new cdk.CfnOutput(stack, "PlanRoleArn", { value: plan.attrArn });
new cdk.CfnOutput(stack, "LambdaBoundaryArn", { value: boundary.ref });
}
}
function managedPolicy(
scope: Construct,
id: string,
name: string,
policyDocument: object,
description?: string,
): iam.CfnManagedPolicy {
const policy = new iam.CfnManagedPolicy(scope, id, {
managedPolicyName: name,
path: "/tf-managed/",
policyDocument,
...(description === undefined ? {} : { description }),
});
retain(policy);
return policy;
}
function retain(resource: cdk.CfnResource): void {
resource.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN;
resource.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN;
}
function roleTags(): cdk.CfnTag[] {
return [
{ key: "Project", value: "payments-dashboard" },
{ key: "Owner", value: "adam@seahavenind.com" },
{ key: "ManagedBy", value: "cdk" },
];
}
function trust(providerArn: string, sub: string, sid: string): object {
return {
Version: "2012-10-17",
Statement: [
{
Sid: sid,
Effect: "Allow",
Action: "sts:AssumeRoleWithWebIdentity",
Principal: { Federated: providerArn },
Condition: {
StringEquals: {
"app.terraform.io:aud": "aws.workload.identity",
"app.terraform.io:sub": sub,
},
},
},
],
};
}
function scopedIamDocument(
account: string,
target: SeahavenHcptfStackProps,
): object {
return retarget({
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyCreatePolicy",
"Effect": "Deny",
"Action": [
"iam:CreatePolicy",
"iam:CreatePolicyVersion",
"iam:DeletePolicy",
"iam:DeletePolicyVersion",
"iam:SetDefaultPolicyVersion"
],
"Resource": [
"*"
]
},
{
"Sid": "CreateExecRoleWithBoundary",
"Effect": "Allow",
"Action": [
"iam:CreateRole"
],
"Resource": [
"arn:aws:iam::011934824531:role/tf-managed/payments-dashboard-*"
],
"Condition": {
"StringLike": {
"iam:PermissionsBoundary": [
"arn:aws:iam::011934824531:policy/tf-managed/payments-dashboard-*",
"arn:aws:iam::011934824531:policy/seahaven-lambda-execution-boundary",
"arn:aws:iam::011934824531:policy/seahaven-lambda-execution-boundary-payments-dashboard"
]
}
}
},
{
"Sid": "MutateExecRoleWithBoundary",
"Effect": "Allow",
"Action": [
"iam:AttachRolePolicy",
"iam:PutRolePolicy",
"iam:PutRolePermissionsBoundary"
],
"Resource": [
"arn:aws:iam::011934824531:role/tf-managed/payments-dashboard-*"
],
"Condition": {
"StringLike": {
"iam:PermissionsBoundary": [
"arn:aws:iam::011934824531:policy/tf-managed/payments-dashboard-*",
"arn:aws:iam::011934824531:policy/seahaven-lambda-execution-boundary",
"arn:aws:iam::011934824531:policy/seahaven-lambda-execution-boundary-payments-dashboard"
]
}
}
},
{
"Sid": "WriteExecRoles",
"Effect": "Allow",
"Action": [
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:DetachRolePolicy",
"iam:TagRole",
"iam:UntagRole",
"iam:UpdateAssumeRolePolicy",
"iam:UpdateRole",
"iam:UpdateRoleDescription"
],
"Resource": [
"arn:aws:iam::011934824531:role/tf-managed/payments-dashboard-*"
]
},
{
"Sid": "PassExecRolesToLambda",
"Effect": "Allow",
"Action": [
"iam:PassRole"
],
"Resource": [
"arn:aws:iam::011934824531:role/tf-managed/payments-dashboard-*"
],
"Condition": {
"StringEquals": {
"iam:PassedToService": [
"lambda.amazonaws.com"
]
}
}
},
{
"Sid": "CreateDeployRole",
"Effect": "Allow",
"Action": [
"iam:CreateRole"
],
"Resource": [
"arn:aws:iam::011934824531:role/tf-managed/githubdeploy-payments-dashboard"
],
"Condition": {
"Null": {
"iam:PermissionsBoundary": [
"true"
]
}
}
},
{
"Sid": "WriteDeployRoles",
"Effect": "Allow",
"Action": [
"iam:AttachRolePolicy",
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:DetachRolePolicy",
"iam:PutRolePolicy",
"iam:TagRole",
"iam:UntagRole",
"iam:UpdateAssumeRolePolicy",
"iam:UpdateRole",
"iam:UpdateRoleDescription"
],
"Resource": [
"arn:aws:iam::011934824531:role/tf-managed/githubdeploy-payments-dashboard"
]
},
{
"Sid": "IamReadOnly",
"Effect": "Allow",
"Action": [
"iam:GetPolicy",
"iam:GetPolicyVersion",
"iam:GetRole",
"iam:GetRolePolicy",
"iam:ListAttachedRolePolicies",
"iam:ListInstanceProfilesForRole",
"iam:ListPolicies",
"iam:ListPolicyVersions",
"iam:ListRolePolicies",
"iam:ListRoleTags",
"iam:ListRoles"
],
"Resource": [
"*"
]
},
{
"Sid": "DenySelfMutation",
"Effect": "Deny",
"Action": [
"iam:AttachRolePolicy",
"iam:DeleteRole",
"iam:DeleteRolePolicy",
"iam:DeleteRolePermissionsBoundary",
"iam:DetachRolePolicy",
"iam:PutRolePolicy",
"iam:PutRolePermissionsBoundary",
"iam:UpdateAssumeRolePolicy",
"iam:UpdateRole",
"iam:UpdateRoleDescription"
],
"Resource": [
"arn:aws:iam::011934824531:role/hcptf-*",
"arn:aws:iam::011934824531:role/github-cfn-execution-role",
"arn:aws:iam::011934824531:role/githubdeploy-*",
"arn:aws:iam::011934824531:role/cdk-hnb659fds-*",
"arn:aws:iam::011934824531:role/OrganizationAccountAccessRole",
"arn:aws:iam::011934824531:role/seahaven-*"
]
},
{
"Sid": "DenyBoundaryTampering",
"Effect": "Deny",
"Action": [
"iam:DeleteRolePermissionsBoundary",
"iam:DeleteUserPermissionsBoundary"
],
"Resource": [
"arn:aws:iam::011934824531:role/*",
"arn:aws:iam::011934824531:user/*"
]
},
{
"Sid": "DenyBoundaryPolicyEdit",
"Effect": "Deny",
"Action": [
"iam:CreatePolicyVersion",
"iam:DeletePolicy",
"iam:DeletePolicyVersion",
"iam:SetDefaultPolicyVersion"
],
"Resource": [
"arn:aws:iam::011934824531:policy/seahaven-*"
]
}
]
}, account, target);
}
function servicesDocument(
account: string,
target: SeahavenHcptfStackProps,
): object {
return retarget({
"Version": "2012-10-17",
"Statement": [
{
"Sid": "LambdaAll",
"Effect": "Allow",
"Action": [
"lambda:*"
],
"Resource": [
"arn:aws:lambda:us-east-1:011934824531:function:payments-*"
]
},
{
"Sid": "LambdaList",
"Effect": "Allow",
"Action": [
"lambda:ListFunctions",
"lambda:ListLayers",
"lambda:GetAccountSettings"
],
"Resource": [
"*"
]
},
{
"Sid": "EventBridgeRules",
"Effect": "Allow",
"Action": [
"events:*"
],
"Resource": [
"arn:aws:events:us-east-1:011934824531:rule/payments-dashboard-*"
]
},
{
"Sid": "EventBridgeList",
"Effect": "Allow",
"Action": [
"events:ListRules",
"events:ListRuleNamesByTarget"
],
"Resource": [
"*"
]
},
{
"Sid": "CloudWatchLogs",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:DeleteLogGroup",
"logs:PutRetentionPolicy",
"logs:DeleteRetentionPolicy",
"logs:TagResource",
"logs:UntagResource",
"logs:ListTagsForResource",
"logs:PutMetricFilter",
"logs:DeleteMetricFilter",
"logs:DescribeMetricFilters"
],
"Resource": [
"arn:aws:logs:us-east-1:011934824531:log-group:/aws/lambda/payments-*",
"arn:aws:logs:us-east-1:011934824531:log-group:/aws/apigateway/payments-dashboard",
"arn:aws:logs:us-east-1:011934824531:log-group:/aws/apigateway/payments-dashboard:*"
]
},
{
"Sid": "CloudWatchLogsDescribe",
"Effect": "Allow",
"Action": [
"logs:DescribeLogGroups"
],
"Resource": [
"*"
]
},
{
"Sid": "ApiGwAccessLogDelivery",
"Effect": "Allow",
"Action": [
"logs:CreateLogDelivery",
"logs:GetLogDelivery",
"logs:UpdateLogDelivery",
"logs:DeleteLogDelivery",
"logs:ListLogDeliveries",
"logs:PutResourcePolicy",
"logs:DescribeResourcePolicies"
],
"Resource": [
"*"
]
},
{
"Sid": "StackBuckets",
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::payments-dashboard-artifacts-011934824531",
"arn:aws:s3:::payments-dashboard-artifacts-011934824531/*",
"arn:aws:s3:::seahaven-payments-csv-011934824531",
"arn:aws:s3:::seahaven-payments-csv-011934824531/*",
"arn:aws:s3:::seahaven-payments-boa-raw-011934824531",
"arn:aws:s3:::seahaven-payments-boa-raw-011934824531/*"
]
},
{
"Sid": "DynamoDBTable",
"Effect": "Allow",
"Action": [
"dynamodb:*"
],
"Resource": [
"arn:aws:dynamodb:us-east-1:011934824531:table/PaymentsDashboard",
"arn:aws:dynamodb:us-east-1:011934824531:table/PaymentsDashboard/*"
]
},
{
"Sid": "DynamoDBList",
"Effect": "Allow",
"Action": [
"dynamodb:ListTables"
],
"Resource": [
"*"
]
},
{
"Sid": "SqsDlq",
"Effect": "Allow",
"Action": [
"sqs:*"
],
"Resource": [
"arn:aws:sqs:us-east-1:011934824531:payments-processPaymentCsv-async-dlq"
]
},
{
"Sid": "SqsList",
"Effect": "Allow",
"Action": [
"sqs:ListQueues"
],
"Resource": [
"*"
]
},
{
"Sid": "HttpApiManage",
"Effect": "Allow",
"Action": [
"apigateway:*"
],
"Resource": [
"arn:aws:apigateway:us-east-1::/apis",
"arn:aws:apigateway:us-east-1::/apis/*",
"arn:aws:apigateway:us-east-1::/tags/*",
"arn:aws:apigateway:us-east-1::/vpclinks",
"arn:aws:apigateway:us-east-1::/vpclinks/*"
]
},
{
"Sid": "PaymentsSsm",
"Effect": "Allow",
"Action": [
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:PutParameter",
"ssm:DeleteParameter",
"ssm:AddTagsToResource",
"ssm:RemoveTagsFromResource",
"ssm:ListTagsForResource"
],
"Resource": [
"arn:aws:ssm:us-east-1:011934824531:parameter/payments-dashboard/*",
"arn:aws:ssm:us-east-1:011934824531:parameter/seahaven/dynamodb/cmk-arn"
]
},
{
"Sid": "SsmDescribeParameters",
"Effect": "Allow",
"Action": [
"ssm:DescribeParameters"
],
"Resource": [
"*"
]
},
{
"Sid": "SecretsManagerRead",
"Effect": "Allow",
"Action": [
"secretsmanager:DescribeSecret",
"secretsmanager:GetResourcePolicy",
"secretsmanager:ListSecretVersionIds",
"secretsmanager:TagResource",
"secretsmanager:UntagResource"
],
"Resource": [
"arn:aws:secretsmanager:us-east-1:011934824531:secret:payments-dashboard/*"
]
},
{
"Sid": "SecretsManagerList",
"Effect": "Allow",
"Action": [
"secretsmanager:ListSecrets"
],
"Resource": [
"*"
]
},
{
"Sid": "KmsTableCmk",
"Effect": "Allow",
"Action": [
"kms:DescribeKey",
"kms:GetKeyPolicy",
"kms:ListResourceTags",
"kms:CreateGrant",
"kms:ListGrants",
"kms:RetireGrant",
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey",
"kms:GenerateDataKeyWithoutPlaintext"
],
"Resource": [
"arn:aws:kms:us-east-1:011934824531:key/be5fa4cb-c546-40fe-a13d-c7bec79f5d12"
]
},
{
"Sid": "CloudWatchAlarms",
"Effect": "Allow",
"Action": [
"cloudwatch:PutMetricAlarm",
"cloudwatch:DeleteAlarms",
"cloudwatch:DescribeAlarms",
"cloudwatch:TagResource",
"cloudwatch:UntagResource",
"cloudwatch:ListTagsForResource"
],
"Resource": [
"arn:aws:cloudwatch:us-east-1:011934824531:alarm:payments-*"
]
},
{
"Sid": "CloudWatchDescribeAlarms",
"Effect": "Allow",
"Action": [
"cloudwatch:DescribeAlarms"
],
"Resource": [
"*"
]
},
{
"Sid": "SnsPublishSiteAlerts",
"Effect": "Allow",
"Action": [
"sns:Publish",
"sns:GetTopicAttributes",
"sns:ListTagsForResource"
],
"Resource": [
"arn:aws:sns:us-east-1:011934824531:site-alerts"
]
},
{
"Sid": "ManageTfManagedBoundary",
"Effect": "Allow",
"Action": [
"iam:GetPolicy",
"iam:GetPolicyVersion",
"iam:ListPolicyVersions",
"iam:ListPolicyTags",
"iam:TagPolicy",
"iam:UntagPolicy"
],
"Resource": [
"arn:aws:iam::011934824531:policy/tf-managed/payments-dashboard-*"
]
},
{
"Sid": "Ec2VpcManagement",
"Effect": "Allow",
"Action": [
"ec2:AllocateAddress",
"ec2:AssociateRouteTable",
"ec2:AttachInternetGateway",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:CreateInternetGateway",
"ec2:CreateNatGateway",
"ec2:CreateRoute",
"ec2:CreateRouteTable",
"ec2:CreateSecurityGroup",
"ec2:CreateSubnet",
"ec2:CreateVpc",
"ec2:CreateVpcEndpoint",
"ec2:CreateTags",
"ec2:DeleteInternetGateway",
"ec2:DeleteNatGateway",
"ec2:DeleteRoute",
"ec2:DeleteRouteTable",
"ec2:DeleteSecurityGroup",
"ec2:DeleteSubnet",
"ec2:DeleteVpc",
"ec2:DeleteVpcEndpoints",
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeAddressesAttribute",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeInternetGateways",
"ec2:DescribeNatGateways",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroupRules",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeTags",
"ec2:DescribeVpcAttribute",
"ec2:DescribeVpcEndpoints",
"ec2:DescribeVpcs",
"ec2:DescribePrefixLists",
"ec2:DetachInternetGateway",
"ec2:DisassociateAddress",
"ec2:DisassociateRouteTable",
"ec2:ModifySubnetAttribute",
"ec2:ModifyVpcAttribute",
"ec2:ModifyVpcEndpoint",
"ec2:ReleaseAddress",
"ec2:RevokeSecurityGroupEgress",
"ec2:RevokeSecurityGroupIngress",
"ec2:UpdateSecurityGroupRuleDescriptionsEgress",
"ec2:UpdateSecurityGroupRuleDescriptionsIngress"
],
"Resource": [
"*"
]
}
]
}, account, target);
}
function planDocument(
account: string,
target: SeahavenHcptfStackProps,
): object {
return retarget({
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RefreshIamRoles",
"Effect": "Allow",
"Action": [
"iam:GetRole",
"iam:GetRolePolicy",
"iam:ListRolePolicies",
"iam:ListAttachedRolePolicies",
"iam:ListRoleTags"
],
"Resource": [
"arn:aws:iam::011934824531:role/tf-managed/payments-dashboard-*",
"arn:aws:iam::011934824531:role/tf-managed/githubdeploy-payments-dashboard",
"arn:aws:iam::011934824531:role/hcptf-payments-dashboard",
"arn:aws:iam::011934824531:role/hcptf-payments-dashboard-plan"
]
},
{
"Sid": "RefreshManagedPolicies",
"Effect": "Allow",
"Action": [
"iam:GetPolicy",
"iam:GetPolicyVersion"
],
"Resource": [
"*"
]
},
{
"Sid": "RefreshLambda",
"Effect": "Allow",
"Action": [
"lambda:GetFunction",
"lambda:GetFunctionConfiguration",
"lambda:GetPolicy",
"lambda:GetFunctionCodeSigningConfig",
"lambda:GetFunctionConcurrency",
"lambda:GetFunctionEventInvokeConfig",
"lambda:GetFunctionUrlConfig",
"lambda:GetRuntimeManagementConfig",
"lambda:GetFunctionRecursionConfig",
"lambda:ListTags",
"lambda:ListVersionsByFunction",
"lambda:ListAliases"
],
"Resource": [
"arn:aws:lambda:us-east-1:011934824531:function:payments-*"
]
},
{
"Sid": "RefreshLambdaList",
"Effect": "Allow",
"Action": [
"lambda:ListFunctions",
"lambda:ListLayers",
"lambda:GetAccountSettings"
],
"Resource": [
"*"
]
},
{
"Sid": "RefreshBuckets",
"Effect": "Allow",
"Action": [
"s3:GetAccelerateConfiguration",
"s3:GetAnalyticsConfiguration",
"s3:GetBucketAcl",
"s3:GetBucketCORS",
"s3:GetBucketLifecycleConfiguration",
"s3:GetBucketLocation",
"s3:GetBucketLogging",
"s3:GetBucketNotification",
"s3:GetBucketObjectLockConfiguration",
"s3:GetBucketOwnershipControls",
"s3:GetBucketPolicy",
"s3:GetBucketPolicyStatus",
"s3:GetBucketPublicAccessBlock",
"s3:GetBucketReplication",
"s3:GetBucketRequestPayment",
"s3:GetBucketTagging",
"s3:GetBucketVersioning",
"s3:GetBucketWebsite",
"s3:GetEncryptionConfiguration",
"s3:GetIntelligentTieringConfiguration",
"s3:GetInventoryConfiguration",
"s3:GetLifecycleConfiguration",
"s3:GetMetricsConfiguration",
"s3:GetObject",
"s3:GetObjectTagging",
"s3:GetObjectVersion",
"s3:GetReplicationConfiguration",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::payments-dashboard-artifacts-011934824531",
"arn:aws:s3:::payments-dashboard-artifacts-011934824531/*",
"arn:aws:s3:::seahaven-payments-csv-011934824531",
"arn:aws:s3:::seahaven-payments-csv-011934824531/*",
"arn:aws:s3:::seahaven-payments-boa-raw-011934824531",
"arn:aws:s3:::seahaven-payments-boa-raw-011934824531/*"
]
},
{
"Sid": "RefreshDynamoDB",
"Effect": "Allow",
"Action": [
"dynamodb:DescribeTable",
"dynamodb:DescribeTimeToLive",
"dynamodb:DescribeContinuousBackups",
"dynamodb:DescribeKinesisStreamingDestination",
"dynamodb:ListTagsOfResource"
],
"Resource": [
"arn:aws:dynamodb:us-east-1:011934824531:table/PaymentsDashboard"
]
},
{
"Sid": "RefreshEventBridge",
"Effect": "Allow",
"Action": [
"events:DescribeRule",
"events:ListTargetsByRule",
"events:ListTagsForResource"
],
"Resource": [
"arn:aws:events:us-east-1:011934824531:rule/payments-dashboard-*"
]
},
{
"Sid": "RefreshLogs",
"Effect": "Allow",
"Action": [
"logs:DescribeLogGroups",
"logs:ListTagsForResource"
],
"Resource": [
"*"
]
},
{
"Sid": "RefreshHttpApi",
"Effect": "Allow",
"Action": [
"apigateway:GET"
],
"Resource": [
"arn:aws:apigateway:us-east-1::/apis",
"arn:aws:apigateway:us-east-1::/apis/*",
"arn:aws:apigateway:us-east-1::/tags/*"
]
},
{
"Sid": "RefreshSsm",
"Effect": "Allow",
"Action": [
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:ListTagsForResource"
],
"Resource": [
"arn:aws:ssm:us-east-1:011934824531:parameter/payments-dashboard/*",
"arn:aws:ssm:us-east-1:011934824531:parameter/seahaven/dynamodb/cmk-arn"
]
},
{
"Sid": "RefreshSsmDescribeParameters",
"Effect": "Allow",
"Action": [
"ssm:DescribeParameters"
],
"Resource": [
"*"
]
},
{
"Sid": "RefreshSecrets",
"Effect": "Allow",
"Action": [
"secretsmanager:DescribeSecret",
"secretsmanager:GetResourcePolicy",
"secretsmanager:ListSecretVersionIds"
],
"Resource": [
"arn:aws:secretsmanager:us-east-1:011934824531:secret:payments-dashboard/*"
]
},
{
"Sid": "RefreshSecretsList",
"Effect": "Allow",
"Action": [
"secretsmanager:ListSecrets"
],
"Resource": [
"*"
]
},
{
"Sid": "RefreshAlarms",
"Effect": "Allow",
"Action": [
"cloudwatch:DescribeAlarms",
"cloudwatch:ListTagsForResource"
],
"Resource": [
"*"
]
},
{
"Sid": "RefreshSns",
"Effect": "Allow",
"Action": [
"sns:GetTopicAttributes",
"sns:ListTagsForResource"
],
"Resource": [
"arn:aws:sns:us-east-1:011934824531:site-alerts"
]
},
{
"Sid": "RefreshSqs",
"Effect": "Allow",
"Action": [
"sqs:GetQueueAttributes",
"sqs:GetQueueUrl",
"sqs:ListQueueTags"
],
"Resource": [
"arn:aws:sqs:us-east-1:011934824531:payments-processPaymentCsv-async-dlq"
]
},
{
"Sid": "RefreshKms",
"Effect": "Allow",
"Action": [
"kms:DescribeKey",
"kms:GetKeyPolicy",
"kms:ListResourceTags",
"kms:CreateGrant",
"kms:ListGrants"
],
"Resource": [
"arn:aws:kms:us-east-1:011934824531:key/be5fa4cb-c546-40fe-a13d-c7bec79f5d12"
]
},
{
"Sid": "RefreshEc2",
"Effect": "Allow",
"Action": [
"ec2:DescribeAccountAttributes",
"ec2:DescribeAddresses",
"ec2:DescribeAddressesAttribute",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeInternetGateways",
"ec2:DescribeNatGateways",
"ec2:DescribeNetworkInterfaces",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroupRules",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeTags",
"ec2:DescribeVpcAttribute",
"ec2:DescribeVpcEndpoints",
"ec2:DescribeVpcs",
"ec2:DescribePrefixLists"
],
"Resource": [
"*"
]
}
]
}, account, target);
}
function boundaryDocument(
account: string,
target: SeahavenHcptfStackProps,
): object {
return retarget({
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CloudWatchLogsWrite",
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
],
"Resource": [
"arn:aws:logs:us-east-1:011934824531:log-group:/aws/lambda*"
]
},
{
"Sid": "CloudWatchLogsDescribe",
"Effect": "Allow",
"Action": [
"logs:DescribeLogGroups"
],
"Resource": [
"*"
]
},
{
"Sid": "XRay",
"Effect": "Allow",
"Action": [
"xray:PutTraceSegments",
"xray:PutTelemetryRecords"
],
"Resource": [
"*"
]
},
{
"Sid": "Ec2Eni",
"Effect": "Allow",
"Action": [
"ec2:CreateNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface",
"ec2:DescribeSubnets",
"ec2:DescribeSecurityGroups",
"ec2:DescribeVpcs"
],
"Resource": [
"*"
]
},
{
"Sid": "PaymentsSecrets",
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": [
"arn:aws:secretsmanager:us-east-1:011934824531:secret:payments-dashboard/slack-bot-token-0pAM3S",
"arn:aws:secretsmanager:us-east-1:011934824531:secret:payments-dashboard/slack-signing-secret-u0T6h8",
"arn:aws:secretsmanager:us-east-1:011934824531:secret:payments-dashboard/boa-check-mgmt-LEbC65",
"arn:aws:secretsmanager:us-east-1:011934824531:secret:payments-dashboard/boa-reporting-JoR9lq",
"arn:aws:secretsmanager:us-east-1:011934824531:secret:payments-dashboard/expense-slack-token-SeMg3s",
"arn:aws:secretsmanager:us-east-1:011934824531:secret:payments-dashboard/expense-slack-signing-secret-lbb78J"
]
},
{
"Sid": "PaymentsDynamoDB",
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:BatchGetItem",
"dynamodb:BatchWriteItem",
"dynamodb:DescribeTable",
"dynamodb:ConditionCheckItem"
],
"Resource": [
"arn:aws:dynamodb:us-east-1:011934824531:table/PaymentsDashboard",
"arn:aws:dynamodb:us-east-1:011934824531:table/PaymentsDashboard/*"
]
},
{
"Sid": "PaymentsCmk",
"Effect": "Allow",
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey",
"kms:DescribeKey"
],
"Resource": [
"arn:aws:kms:us-east-1:011934824531:key/be5fa4cb-c546-40fe-a13d-c7bec79f5d12"
],
"Condition": {
"StringEquals": {
"kms:ViaService": [
"dynamodb.us-east-1.amazonaws.com"
]
}
}
},
{
"Sid": "PaymentsCsvRead",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion"
],
"Resource": [
"arn:aws:s3:::seahaven-payments-csv-011934824531/*"
]
},
{
"Sid": "PaymentsBoaRawPut",
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::seahaven-payments-boa-raw-011934824531/*"
]
},
{
"Sid": "PaymentsDlqSend",
"Effect": "Allow",
"Action": [
"sqs:SendMessage"
],
"Resource": [
"arn:aws:sqs:us-east-1:011934824531:payments-processPaymentCsv-async-dlq"
]
},
{
"Sid": "PaymentsInvokeExpenseProcessor",
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": [
"arn:aws:lambda:us-east-1:011934824531:function:payments-expenseProcessor"
]
}
]
}, account, target);
}