diff --git a/API.md b/API.md index 256f712..f7b8b84 100644 --- a/API.md +++ b/API.md @@ -343,6 +343,7 @@ Any object. | **Name** | **Type** | **Description** | | --- | --- | --- | | node | constructs.Node | The tree node. | +| controlTowerAdminRole | aws-cdk-lib.aws_iam.IRole | The Control Tower admin role (either created or referenced). | | landingZoneArn | string | The ARN of the Control Tower landing zone. | | landingZoneId | string | The unique identifier of the Control Tower landing zone. | | logArchiveAccountId | string | The AWS account ID of the log archive account. | @@ -363,6 +364,18 @@ The tree node. --- +##### `controlTowerAdminRole`Required + +```typescript +public readonly controlTowerAdminRole: IRole; +``` + +- *Type:* aws-cdk-lib.aws_iam.IRole + +The Control Tower admin role (either created or referenced). + +--- + ##### `landingZoneArn`Required ```typescript @@ -1123,6 +1136,7 @@ const controlTowerLandingZoneProps: ControlTowerLandingZoneProps = { ... } | --- | --- | --- | | controlTowerStackName | string | The name of the Control Tower stack. | | accessLoggingBucketRetentionDays | number | Number of days to retain access logs in the access logging bucket. | +| createControlTowerAdminRole | boolean | Whether to create the Control Tower admin role if it doesn't already exist. | | governedRegions | string[] | AWS regions where Control Tower governance will be applied. | | logArchiveAccountEmail | string | Email address for the log archive account. | | logArchiveAccountId | string | AWS account ID for the log archive account. | @@ -1163,6 +1177,21 @@ Number of days to retain access logs in the access logging bucket. --- +##### `createControlTowerAdminRole`Optional + +```typescript +public readonly createControlTowerAdminRole: boolean; +``` + +- *Type:* boolean +- *Default:* true + +Whether to create the Control Tower admin role if it doesn't already exist. + +If false, the construct will attempt to reference an existing role with the name 'AWSControlTowerAdmin'. + +--- + ##### `governedRegions`Optional ```typescript diff --git a/src/control-tower/control-tower-landing-zone.ts b/src/control-tower/control-tower-landing-zone.ts index 6b72b7c..068647c 100644 --- a/src/control-tower/control-tower-landing-zone.ts +++ b/src/control-tower/control-tower-landing-zone.ts @@ -98,6 +98,12 @@ export interface ControlTowerLandingZoneProps { * @default - AWS best practices organizational structure */ readonly organizationStructure?: { [key: string]: OrganizationalUnit }; + /** + * Whether to create the Control Tower admin role if it doesn't already exist. + * If false, the construct will attempt to reference an existing role with the name 'AWSControlTowerAdmin'. + * @default - true + */ + readonly createControlTowerAdminRole?: boolean; } /** @@ -127,6 +133,8 @@ export class ControlTowerLandingZone extends Construct { public readonly logArchiveAccountId?: string; /** The AWS account ID of the security audit account */ public readonly securityAuditAccountId?: string; + /** The Control Tower admin role (either created or referenced) */ + public readonly controlTowerAdminRole: iam.IRole; /** * Creates a new Control Tower Landing Zone. @@ -225,6 +233,15 @@ export class ControlTowerLandingZone extends Construct { }, }; + const controlTowerAdminRole = props.createControlTowerAdminRole !== false + ? new iam.Role(this, 'ControlTowerAdminRole', { + roleName: 'AWSControlTowerAdmin', + assumedBy: new iam.ServicePrincipal('controltower.amazonaws.com'), + managedPolicies: [ + iam.ManagedPolicy.fromAwsManagedPolicyName('AWSControlTowerAdmin'), + ], + }) + : iam.Role.fromRoleName(this, 'ControlTowerAdminRole', 'AWSControlTowerAdmin'); const baseManifest = { governedRegions: props.governedRegions ?? [Stack.of(this).region], @@ -251,7 +268,7 @@ export class ControlTowerLandingZone extends Construct { version: '3.3', }); - // Hacky but I want to be sure the KMS key and grant exists before moving forward. + // Hacky but I want to be sure the KMS key and grant exists before moving forward. if (logArchiveAccount) { landingZone.node.addDependency(logArchiveAccount); if (loggingKmsKey) { @@ -268,6 +285,7 @@ export class ControlTowerLandingZone extends Construct { landingZone.node.addDependency(loggingKmsKey); } + this.controlTowerAdminRole = controlTowerAdminRole; this.landingZoneArn = landingZone.attrArn; this.landingZoneId = landingZone.attrLandingZoneIdentifier; this.loggingKmsKeyArn = props.loggingBucketKmsKeyArn ?? loggingKmsKey?.keyArn;