in src/deployments/cdk/src/common/iam-assets.ts [38:323]
constructor(scope: cdk.Construct, id: string, props: IamAssetsProps) {
super(scope, id);
const { accountKey, iamConfig, iamPoliciesDefinition, accounts, userPasswords, logBucket } = props;
const customerManagedPolicies: { [policyName: string]: iam.ManagedPolicy } = {};
// method to create IAM Policy
const createIamPolicy = (policyName: string, policy: string): void => {
console.log(`Creating Policy "${policyName}" in account "${accountKey}"`);
const iamPolicyDef = iamPoliciesDefinition[policyName];
const iamPolicyJson = JSON.parse(iamPolicyDef);
const statementArray = iamPolicyJson.Statement;
const iamPolicy = new iam.ManagedPolicy(this, `IAM-Policy-${policyName}-${accountKey}`, {
managedPolicyName: policyName,
description: `PBMM - ${policyName}`,
});
for (const statement of statementArray) {
iamPolicy.addStatements(
new iam.PolicyStatement({
effect: statement.Effect === 'Allow' ? iam.Effect.ALLOW : iam.Effect.DENY,
actions: typeof statement.Action === 'string' ? [statement.Action] : statement.Action,
resources: typeof statement.Resource === 'string' ? [statement.Resource] : statement.Resource,
}),
);
}
customerManagedPolicies[policyName] = iamPolicy;
new CfnIamPolicyOutput(this, `IamPolicy${policyName}Output`, {
policyName: iamPolicy.managedPolicyName,
policyArn: iamPolicy.managedPolicyArn,
policyKey: 'IamCustomerManagedPolicy',
});
};
// method to create IAM User & Group
const createIamUser = (userIds: string[], groupName: string, policies: string[], boundaryPolicy: string): void => {
const iamGroup = new iam.Group(this, `IAM-Group-${groupName}-${accountKey}`, {
groupName,
managedPolicies: policies.map(x => iam.ManagedPolicy.fromAwsManagedPolicyName(x)),
});
new CfnIamGroupOutput(this, `IamGroup${groupName}Output`, {
groupName: iamGroup.groupName,
groupArn: iamGroup.groupArn,
groupKey: 'IamAccountGroup',
});
for (const userId of userIds) {
const iamUser = new iam.User(this, `IAM-User-${userId}-${accountKey}`, {
userName: userId,
password: userPasswords[userId],
groups: [iamGroup],
permissionsBoundary: customerManagedPolicies[boundaryPolicy],
});
new CfnIamUserOutput(this, `IamUser${userId}Output`, {
userName: iamUser.userName,
userArn: iamUser.userArn,
userKey: 'IamAccountUser',
});
}
};
// method to create IAM Role
const createIamRole = (
role: string,
type: string,
policies: string[],
boundaryPolicy: string,
sourceAccount?: string,
sourceAccountRole?: string,
trustPolicy?: string,
): iam.Role => {
let newRole: iam.Role | undefined;
if (sourceAccount && sourceAccountRole) {
const sourceAccountId = getAccountId(accounts, sourceAccount);
newRole = new iam.Role(this, `IAM-Role-${role}-${accountKey}`, {
roleName: role,
description: `PBMM - ${role}`,
assumedBy: new iam.ArnPrincipal(`arn:aws:iam::${sourceAccountId}:role/${sourceAccountRole}`),
managedPolicies: policies.map(
x => customerManagedPolicies[x] ?? iam.ManagedPolicy.fromAwsManagedPolicyName(x),
),
permissionsBoundary: customerManagedPolicies[boundaryPolicy],
});
if (trustPolicy) {
const newRoleTrustPolicy = newRole.assumeRolePolicy;
const statements = JSON.parse(trustPolicy).Statement as any[];
statements.map(statement => {
if (!Array.isArray(statement.Principal.AWS)) {
statement.Principal.AWS = [statement.Principal.AWS as string];
}
const principalAccount: string[] = statement.Principal.AWS;
const newPrincipals = principalAccount.map(principal => {
return new iam.ArnPrincipal(principal);
});
newRoleTrustPolicy?.addStatements(
new iam.PolicyStatement({
principals: newPrincipals,
actions: ['sts:AssumeRole'],
conditions: statement.Condition,
notActions: statement.NotAction,
effect: iam.Effect.ALLOW,
}),
);
});
}
} else {
newRole = new iam.Role(this, `IAM-Role-${role}`, {
roleName: role,
description: `PBMM - ${role}`,
assumedBy: new iam.ServicePrincipal(`${type}.amazonaws.com`),
managedPolicies: policies.map(
x => customerManagedPolicies[x] ?? iam.ManagedPolicy.fromAwsManagedPolicyName(x),
),
permissionsBoundary: customerManagedPolicies[boundaryPolicy],
});
}
return newRole;
};
const createIamSSMLogArchiveWritePolicy = (): iam.ManagedPolicy => {
const policyName = createPolicyName('SSMWriteAccessPolicy');
const iamSSMLogArchiveWriteAccessPolicy = new iam.ManagedPolicy(
this,
`IAM-SSM-LogArchive-Write-Policy-${accountKey}`,
{
managedPolicyName: policyName,
description: policyName,
},
);
iamSSMLogArchiveWriteAccessPolicy.addStatements(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['kms:DescribeKey', 'kms:GenerateDataKey*', 'kms:Decrypt', 'kms:Encrypt', 'kms:ReEncrypt*'],
resources: [logBucket.encryptionKey?.keyArn || '*'],
}),
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['kms:Decrypt'],
resources: ['*'], // TODO: limit resource to be SSM key only
}),
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['s3:GetEncryptionConfiguration'],
resources: [logBucket.bucketArn],
}),
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['s3:PutObject', 's3:PutObjectAcl'],
resources: [logBucket.arnForObjects('*')],
}),
);
new CfnIamPolicyOutput(this, `IamSsmPolicyOutput`, {
policyName: iamSSMLogArchiveWriteAccessPolicy.managedPolicyName,
policyArn: iamSSMLogArchiveWriteAccessPolicy.managedPolicyArn,
policyKey: 'IamSsmWriteAccessPolicy',
});
return iamSSMLogArchiveWriteAccessPolicy;
};
const createIamSSMLogArchiveReadOnlyPolicy = (): iam.ManagedPolicy => {
const policyName = createPolicyName('SSMReadOnlyAccessPolicy');
const iamSSMLogArchiveReadOnlyAccessPolicy = new iam.ManagedPolicy(
this,
`IAM-SSM-LogArchive-ReadOnly-Policy-${accountKey}`,
{
managedPolicyName: policyName,
description: policyName,
},
);
iamSSMLogArchiveReadOnlyAccessPolicy.addStatements(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['kms:Decrypt', 'kms:DescribeKey', 'kms:GenerateDataKey'],
resources: [logBucket.encryptionKey?.keyArn || '*'],
}),
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ['s3:GetObject', 's3:ListBucket'],
resources: [logBucket.bucketArn, logBucket.arnForObjects('*')],
}),
);
new CfnIamPolicyOutput(this, `IamSsmReadOnlyPolicyOutput`, {
policyName: iamSSMLogArchiveReadOnlyAccessPolicy.managedPolicyName,
policyArn: iamSSMLogArchiveReadOnlyAccessPolicy.managedPolicyArn,
policyKey: 'IamSsmReadOnlyAccessPolicy',
});
return iamSSMLogArchiveReadOnlyAccessPolicy;
};
if (!IamConfigType.is(iamConfig)) {
console.log(
`IAM config is not defined for account with key - ${accountKey}. Skipping Policies/Users/Roles creation.`,
);
} else {
const iamPolicies = iamConfig.policies;
for (const iamPolicy of iamPolicies!) {
if (!IamPolicyConfigType.is(iamPolicy)) {
console.log(
`IAM config - policies is not defined for account with key - ${accountKey}. Skipping Policies creation.`,
);
} else {
createIamPolicy(iamPolicy['policy-name'], iamPolicy.policy);
}
}
const iamUsers = iamConfig.users;
for (const iamUser of iamUsers!) {
if (!IamUserConfigType.is(iamUser)) {
console.log(
`IAM config - users is not defined for account with key - ${accountKey}. Skipping Users creation.`,
);
} else {
createIamUser(iamUser['user-ids'], iamUser.group, iamUser.policies, iamUser['boundary-policy']);
}
}
const iamRoles = iamConfig.roles;
if (!iamRoles) {
return;
}
const ssmLogArchiveWritePolicy =
iamRoles.filter(i => i['ssm-log-archive-write-access'] || i['ssm-log-archive-access']).length > 0
? createIamSSMLogArchiveWritePolicy()
: undefined;
const ssmLogArchiveReadOnlyPolicy =
iamRoles.filter(i => i['ssm-log-archive-read-only-access']).length > 0
? createIamSSMLogArchiveReadOnlyPolicy()
: undefined;
for (const iamRole of iamRoles) {
if (!IamRoleConfigType.is(iamRole)) {
console.log(
`IAM config - roles is not defined for account with key - ${accountKey}. Skipping Roles creation.`,
);
} else {
const role = createIamRole(
iamRole.role,
iamRole.type,
iamRole.policies,
iamRole['boundary-policy'],
iamRole['source-account'],
iamRole['source-account-role'],
iamRole['trust-policy'],
);
if (iamRole.type === 'ec2') {
new iam.CfnInstanceProfile(this, `IAM-Instance-Profile-${iamRole.role}-${accountKey}`, {
path: '/',
roles: [role.roleName],
instanceProfileName: createIamInstanceProfileName(iamRole.role),
});
}
new CfnIamRoleOutput(this, `IamRole${iamRole.role}Output`, {
roleName: role.roleName,
roleArn: role.roleArn,
roleKey: 'IamAccountRole',
});
if (
(iamRole['ssm-log-archive-write-access'] || iamRole['ssm-log-archive-access']) &&
ssmLogArchiveWritePolicy
) {
role.addManagedPolicy(ssmLogArchiveWritePolicy);
}
if (iamRole['ssm-log-archive-read-only-access'] && ssmLogArchiveReadOnlyPolicy) {
role.addManagedPolicy(ssmLogArchiveReadOnlyPolicy);
}
}
}
}
}