seahaven-org-baseline/lib/engineering-access-stack.ts
Adam Moussa 7ab7346f78
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
feat(iam): add engineering view-only permission sets (PLAT-235) (#169)
Give the engineering group view-only access in seahaven-dev and seahaven-prod without secret, object, or item reads.
2026-10-01 19:35:17 +00:00

122 lines
3.5 KiB
TypeScript

import * as cdk from "aws-cdk-lib";
import * as identitystore from "aws-cdk-lib/aws-identitystore";
import * as sso from "aws-cdk-lib/aws-sso";
import { Construct } from "constructs";
const IDENTITY_CENTER_INSTANCE_ARN =
"arn:aws:sso:::instance/ssoins-722321f42ca610e4";
const IDENTITY_STORE_ID = "d-9067ec8e26";
const VIEW_ONLY_ACCESS_ARN =
"arn:aws:iam::aws:policy/job-function/ViewOnlyAccess";
/**
* Blocks secret, object, and item reads if a broader managed policy is
* attached later. ViewOnlyAccess is the allow. ReadOnlyAccess is not used.
*/
const DATA_PLANE_DENY = {
Version: "2012-10-17",
Statement: [
{
Sid: "DenyDataPlaneReads",
Effect: "Deny",
Action: [
"secretsmanager:GetSecretValue",
"secretsmanager:BatchGetSecretValue",
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:GetParametersByPath",
"kms:Decrypt",
"s3:GetObject",
"dynamodb:GetItem",
"dynamodb:BatchGetItem",
"dynamodb:Query",
"dynamodb:Scan",
],
Resource: "*",
},
],
};
export interface EngineeringAccessStackProps extends cdk.StackProps {
devAccountId: string;
prodAccountId: string;
}
/**
* Identity Center group and view-only permission sets for the engineering
* team (PLAT-235).
*
* Assigned to seahaven-dev and seahaven-prod only. The group has no members.
* People are added after the roster exists, outside this stack. This is not
* an SCP exemption and cannot assume OrganizationAccountAccessRole.
*
* A later SCIM sync of engineering@seahaven.com must adopt this group. A
* second group with display name engineering will collide.
*/
export class EngineeringAccessStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: EngineeringAccessStackProps) {
super(scope, id, props);
const group = new identitystore.CfnGroup(this, "EngineeringGroup", {
identityStoreId: IDENTITY_STORE_ID,
displayName: "engineering",
description:
"Engineering team. View-only in seahaven-dev and seahaven-prod. No members until the roster exists.",
});
const devPermissionSet = this.permissionSet(
"EngineeringDevPermissionSet",
"EngineeringDev",
"View-only in seahaven-dev. No secret, object, or item reads.",
);
const prodPermissionSet = this.permissionSet(
"EngineeringProdPermissionSet",
"EngineeringProd",
"View-only in seahaven-prod. No secret, object, or item reads.",
);
this.assignment(
"EngineeringDevAssignment",
devPermissionSet,
group,
props.devAccountId,
);
this.assignment(
"EngineeringProdAssignment",
prodPermissionSet,
group,
props.prodAccountId,
);
}
private permissionSet(
id: string,
name: string,
description: string,
): sso.CfnPermissionSet {
return new sso.CfnPermissionSet(this, id, {
instanceArn: IDENTITY_CENTER_INSTANCE_ARN,
name,
description,
sessionDuration: "PT8H",
managedPolicies: [VIEW_ONLY_ACCESS_ARN],
inlinePolicy: DATA_PLANE_DENY,
});
}
private assignment(
id: string,
permissionSet: sso.CfnPermissionSet,
group: identitystore.CfnGroup,
targetId: string,
): void {
new sso.CfnAssignment(this, id, {
instanceArn: IDENTITY_CENTER_INSTANCE_ARN,
permissionSetArn: permissionSet.attrPermissionSetArn,
principalId: group.attrGroupId,
principalType: "GROUP",
targetId,
targetType: "AWS_ACCOUNT",
});
}
}