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;