Skip to content

Commit 0e01de8

Browse files
authored
Merge branch 'main' into merge-back/2.140.0
2 parents 46168aa + 0a3cb94 commit 0e01de8

File tree

9 files changed

+940
-7
lines changed

9 files changed

+940
-7
lines changed

packages/@aws-cdk-testing/cli-integ/lib/aws.ts

+2
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export class AwsClients {
1919
public readonly s3: AwsCaller<AWS.S3>;
2020
public readonly ecr: AwsCaller<AWS.ECR>;
2121
public readonly ecs: AwsCaller<AWS.ECS>;
22+
public readonly sso: AwsCaller<AWS.SSO>;
2223
public readonly sns: AwsCaller<AWS.SNS>;
2324
public readonly iam: AwsCaller<AWS.IAM>;
2425
public readonly lambda: AwsCaller<AWS.Lambda>;
@@ -36,6 +37,7 @@ export class AwsClients {
3637
this.s3 = makeAwsCaller(AWS.S3, this.config);
3738
this.ecr = makeAwsCaller(AWS.ECR, this.config);
3839
this.ecs = makeAwsCaller(AWS.ECS, this.config);
40+
this.sso = makeAwsCaller(AWS.SSO, this.config);
3941
this.sns = makeAwsCaller(AWS.SNS, this.config);
4042
this.iam = makeAwsCaller(AWS.IAM, this.config);
4143
this.lambda = makeAwsCaller(AWS.Lambda, this.config);

packages/@aws-cdk-testing/cli-integ/resources/cdk-apps/app/app.js

+65
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ if (process.env.PACKAGE_LAYOUT_VERSION === '1') {
1111
var sns = require('@aws-cdk/aws-sns');
1212
var sqs = require('@aws-cdk/aws-sqs');
1313
var lambda = require('@aws-cdk/aws-lambda');
14+
var sso = require('@aws-cdk/aws-sso');
1415
var docker = require('@aws-cdk/aws-ecr-assets');
1516
} else {
1617
var cdk = require('aws-cdk-lib');
@@ -19,6 +20,7 @@ if (process.env.PACKAGE_LAYOUT_VERSION === '1') {
1920
LegacyStackSynthesizer,
2021
aws_ec2: ec2,
2122
aws_ecs: ecs,
23+
aws_sso: sso,
2224
aws_s3: s3,
2325
aws_ssm: ssm,
2426
aws_iam: iam,
@@ -68,6 +70,62 @@ class YourStack extends cdk.Stack {
6870
}
6971
}
7072

73+
class SsoPermissionSetNoPolicy extends Stack {
74+
constructor(scope, id) {
75+
super(scope, id);
76+
77+
new sso.CfnPermissionSet(this, "permission-set-without-managed-policy", {
78+
instanceArn: 'arn:aws:sso:::instance/testvalue',
79+
name: 'testName',
80+
permissionsBoundary: { customerManagedPolicyReference: { name: 'why', path: '/how/' }},
81+
})
82+
}
83+
}
84+
85+
class SsoPermissionSetManagedPolicy extends Stack {
86+
constructor(scope, id) {
87+
super(scope, id);
88+
new sso.CfnPermissionSet(this, "permission-set-with-managed-policy", {
89+
managedPolicies: ['arn:aws:iam::aws:policy/administratoraccess'],
90+
customerManagedPolicyReferences: [{ name: 'forSSO' }],
91+
permissionsBoundary: { managedPolicyArn: 'arn:aws:iam::aws:policy/AdministratorAccess' },
92+
instanceArn: 'arn:aws:sso:::instance/testvalue',
93+
name: 'niceWork',
94+
})
95+
}
96+
}
97+
98+
class SsoAssignment extends Stack {
99+
constructor(scope, id) {
100+
super(scope, id);
101+
new sso.CfnAssignment(this, "assignment", {
102+
instanceArn: 'arn:aws:sso:::instance/testvalue',
103+
permissionSetArn: 'arn:aws:sso:::testvalue',
104+
principalId: '11111111-2222-3333-4444-test',
105+
principalType: 'USER',
106+
targetId: '111111111111',
107+
targetType: 'AWS_ACCOUNT'
108+
});
109+
}
110+
}
111+
112+
class SsoInstanceAccessControlConfig extends Stack {
113+
constructor(scope, id) {
114+
super(scope, id);
115+
new sso.CfnInstanceAccessControlAttributeConfiguration(this, 'instanceAccessControlConfig', {
116+
instanceArn: 'arn:aws:sso:::instance/testvalue',
117+
accessControlAttributes: [
118+
{ key: 'first', value: { source: ['a'] } },
119+
{ key: 'second', value: { source: ['b'] } },
120+
{ key: 'third', value: { source: ['c'] } },
121+
{ key: 'fourth', value: { source: ['d'] } },
122+
{ key: 'fifth', value: { source: ['e'] } },
123+
{ key: 'sixth', value: { source: ['f'] } },
124+
]
125+
})
126+
}
127+
}
128+
71129
class ListMultipleDependentStack extends Stack {
72130
constructor(scope, id) {
73131
super(scope, id);
@@ -591,6 +649,13 @@ switch (stackSet) {
591649
new EcsHotswapStack(app, `${stackPrefix}-ecs-hotswap`);
592650
new DockerStack(app, `${stackPrefix}-docker`);
593651
new DockerStackWithCustomFile(app, `${stackPrefix}-docker-with-custom-file`);
652+
653+
// SSO stacks
654+
new SsoInstanceAccessControlConfig(app, `${stackPrefix}-sso-access-control`);
655+
new SsoAssignment(app, `${stackPrefix}-sso-assignment`);
656+
new SsoPermissionSetManagedPolicy(app, `${stackPrefix}-sso-perm-set-with-managed-policy`);
657+
new SsoPermissionSetNoPolicy(app, `${stackPrefix}-sso-perm-set-without-managed-policy`);
658+
594659
const failed = new FailedStack(app, `${stackPrefix}-failed`)
595660

596661
// A stack that depends on the failed stack -- used to test that '-e' does not deploy the failing stack

packages/@aws-cdk-testing/cli-integ/tests/cli-integ-tests/cli.integtest.ts

+152
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,158 @@ integTest('cdk diff --fail with multiple stack exits with if any of the stacks c
775775
await expect(fixture.cdk(['diff', '--fail', fixture.fullStackName('test-1'), fixture.fullStackName('test-2')])).rejects.toThrow('exited with error');
776776
}));
777777

778+
integTest('cdk diff --security-only successfully outputs sso-permission-set-without-managed-policy information', withDefaultFixture(async (fixture) => {
779+
const diff = await fixture.cdk(
780+
['diff', '--security-only', fixture.fullStackName('sso-perm-set-without-managed-policy')],
781+
);
782+
`┌───┬──────────────────────────────────────────┬──────────────────────────────────┬────────────────────┬───────────────────────────────────┬─────────────────────────────────┐
783+
│ │ Resource │ InstanceArn │ PermissionSet name │ PermissionsBoundary │ CustomerManagedPolicyReferences │
784+
├───┼──────────────────────────────────────────┼──────────────────────────────────┼────────────────────┼───────────────────────────────────┼─────────────────────────────────┤
785+
│ + │\${permission-set-without-managed-policy} │ arn:aws:sso:::instance/testvalue │ testName │ CustomerManagedPolicyReference: { │ │
786+
│ │ │ │ │ Name: why, Path: /how/ │ │
787+
│ │ │ │ │ } │ │
788+
`;
789+
expect(diff).toContain('Resource');
790+
expect(diff).toContain('permission-set-without-managed-policy');
791+
792+
expect(diff).toContain('InstanceArn');
793+
expect(diff).toContain('arn:aws:sso:::instance/testvalue');
794+
795+
expect(diff).toContain('PermissionSet name');
796+
expect(diff).toContain('testName');
797+
798+
expect(diff).toContain('PermissionsBoundary');
799+
expect(diff).toContain('CustomerManagedPolicyReference: {');
800+
expect(diff).toContain('Name: why, Path: /how/');
801+
expect(diff).toContain('}');
802+
803+
expect(diff).toContain('CustomerManagedPolicyReferences');
804+
}));
805+
806+
integTest('cdk diff --security-only successfully outputs sso-permission-set-with-managed-policy information', withDefaultFixture(async (fixture) => {
807+
const diff = await fixture.cdk(
808+
['diff', '--security-only', fixture.fullStackName('sso-perm-set-with-managed-policy')],
809+
);
810+
`┌───┬──────────────────────────────────────────┬──────────────────────────────────┬────────────────────┬───────────────────────────────────────────────────────────────┬─────────────────────────────────┐
811+
│ │ Resource │ InstanceArn │ PermissionSet name │ PermissionsBoundary │ CustomerManagedPolicyReferences │
812+
├───┼──────────────────────────────────────────┼──────────────────────────────────┼────────────────────┼───────────────────────────────────────────────────────────────┼─────────────────────────────────┤
813+
│ + │\${permission-set-with-managed-policy} │ arn:aws:sso:::instance/testvalue │ niceWork │ ManagedPolicyArn: arn:aws:iam::aws:policy/AdministratorAccess │ Name: forSSO, Path: │
814+
`;
815+
816+
expect(diff).toContain('Resource');
817+
expect(diff).toContain('permission-set-with-managed-policy');
818+
819+
expect(diff).toContain('InstanceArn');
820+
expect(diff).toContain('arn:aws:sso:::instance/testvalue');
821+
822+
expect(diff).toContain('PermissionSet name');
823+
expect(diff).toContain('niceWork');
824+
825+
expect(diff).toContain('PermissionsBoundary');
826+
expect(diff).toContain('ManagedPolicyArn: arn:aws:iam::aws:policy/AdministratorAccess');
827+
828+
expect(diff).toContain('CustomerManagedPolicyReferences');
829+
expect(diff).toContain('Name: forSSO, Path:');
830+
}));
831+
832+
integTest('cdk diff --security-only successfully outputs sso-assignment information', withDefaultFixture(async (fixture) => {
833+
const diff = await fixture.cdk(
834+
['diff', '--security-only', fixture.fullStackName('sso-assignment')],
835+
);
836+
`┌───┬───────────────┬──────────────────────────────────┬─────────────────────────┬──────────────────────────────┬───────────────┬──────────────┬─────────────┐
837+
│ │ Resource │ InstanceArn │ PermissionSetArn │ PrincipalId │ PrincipalType │ TargetId │ TargetType │
838+
├───┼───────────────┼──────────────────────────────────┼─────────────────────────┼──────────────────────────────┼───────────────┼──────────────┼─────────────┤
839+
│ + │\${assignment} │ arn:aws:sso:::instance/testvalue │ arn:aws:sso:::testvalue │ 11111111-2222-3333-4444-test │ USER │ 111111111111 │ AWS_ACCOUNT │
840+
└───┴───────────────┴──────────────────────────────────┴─────────────────────────┴──────────────────────────────┴───────────────┴──────────────┴─────────────┘
841+
`;
842+
expect(diff).toContain('Resource');
843+
expect(diff).toContain('assignment');
844+
845+
expect(diff).toContain('InstanceArn');
846+
expect(diff).toContain('arn:aws:sso:::instance/testvalue');
847+
848+
expect(diff).toContain('PermissionSetArn');
849+
expect(diff).toContain('arn:aws:sso:::testvalue');
850+
851+
expect(diff).toContain('PrincipalId');
852+
expect(diff).toContain('11111111-2222-3333-4444-test');
853+
854+
expect(diff).toContain('PrincipalType');
855+
expect(diff).toContain('USER');
856+
857+
expect(diff).toContain('TargetId');
858+
expect(diff).toContain('111111111111');
859+
860+
expect(diff).toContain('TargetType');
861+
expect(diff).toContain('AWS_ACCOUNT');
862+
}));
863+
864+
integTest('cdk diff --security-only successfully outputs sso-access-control information', withDefaultFixture(async (fixture) => {
865+
const diff = await fixture.cdk(
866+
['diff', '--security-only', fixture.fullStackName('sso-access-control')],
867+
);
868+
`┌───┬────────────────────────────────┬────────────────────────┬─────────────────────────────────┐
869+
│ │ Resource │ InstanceArn │ AccessControlAttributes │
870+
├───┼────────────────────────────────┼────────────────────────┼─────────────────────────────────┤
871+
│ + │\${instanceAccessControlConfig} │ arn:aws:test:testvalue │ Key: first, Values: [a] │
872+
│ │ │ │ Key: second, Values: [b] │
873+
│ │ │ │ Key: third, Values: [c] │
874+
│ │ │ │ Key: fourth, Values: [d] │
875+
│ │ │ │ Key: fifth, Values: [e] │
876+
│ │ │ │ Key: sixth, Values: [f] │
877+
└───┴────────────────────────────────┴────────────────────────┴─────────────────────────────────┘
878+
`;
879+
expect(diff).toContain('Resource');
880+
expect(diff).toContain('instanceAccessControlConfig');
881+
882+
expect(diff).toContain('InstanceArn');
883+
expect(diff).toContain('arn:aws:sso:::instance/testvalue');
884+
885+
expect(diff).toContain('AccessControlAttributes');
886+
expect(diff).toContain('Key: first, Values: [a]');
887+
expect(diff).toContain('Key: second, Values: [b]');
888+
expect(diff).toContain('Key: third, Values: [c]');
889+
expect(diff).toContain('Key: fourth, Values: [d]');
890+
expect(diff).toContain('Key: fifth, Values: [e]');
891+
expect(diff).toContain('Key: sixth, Values: [f]');
892+
}));
893+
894+
integTest('cdk diff --security-only --fail exits when security diff for sso access control config', withDefaultFixture(async (fixture) => {
895+
await expect(
896+
fixture.cdk(
897+
['diff', '--security-only', '--fail', fixture.fullStackName('sso-access-control')],
898+
),
899+
).rejects
900+
.toThrow('exited with error');
901+
}));
902+
903+
integTest('cdk diff --security-only --fail exits when security diff for sso-perm-set-without-managed-policy', withDefaultFixture(async (fixture) => {
904+
await expect(
905+
fixture.cdk(
906+
['diff', '--security-only', '--fail', fixture.fullStackName('sso-perm-set-without-managed-policy')],
907+
),
908+
).rejects
909+
.toThrow('exited with error');
910+
}));
911+
912+
integTest('cdk diff --security-only --fail exits when security diff for sso-perm-set-with-managed-policy', withDefaultFixture(async (fixture) => {
913+
await expect(
914+
fixture.cdk(
915+
['diff', '--security-only', '--fail', fixture.fullStackName('sso-perm-set-with-managed-policy')],
916+
),
917+
).rejects
918+
.toThrow('exited with error');
919+
}));
920+
921+
integTest('cdk diff --security-only --fail exits when security diff for sso-assignment', withDefaultFixture(async (fixture) => {
922+
await expect(
923+
fixture.cdk(
924+
['diff', '--security-only', '--fail', fixture.fullStackName('sso-assignment')],
925+
),
926+
).rejects
927+
.toThrow('exited with error');
928+
}));
929+
778930
integTest('cdk diff --security-only --fail exits when security changes are present', withDefaultFixture(async (fixture) => {
779931
const stackName = 'iam-test';
780932
await expect(fixture.cdk(['diff', '--security-only', '--fail', fixture.fullStackName(stackName)])).rejects.toThrow('exited with error');

packages/@aws-cdk/cloudformation-diff/lib/diff/types.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,13 @@ export class TemplateDiff implements ITemplateDiff {
128128
continue;
129129
}
130130

131-
if (!resourceChange.newResourceType) {
131+
if (!resourceChange.resourceType) {
132+
// We use resourceChange.resourceType to loadResourceModel so that we can inspect the
133+
// properties of a resource even after the resource is removed from the template.
132134
continue;
133135
}
134136

135-
const newTypeProps = loadResourceModel(resourceChange.newResourceType)?.properties || {};
137+
const newTypeProps = loadResourceModel(resourceChange.resourceType)?.properties || {};
136138
for (const [propertyName, prop] of Object.entries(newTypeProps)) {
137139
const propScrutinyType = prop.scrutinizable || PropertyScrutinyType.None;
138140
if (scrutinyTypes.includes(propScrutinyType)) {

packages/@aws-cdk/cloudformation-diff/lib/format.ts

+13
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,19 @@ class Formatter {
382382
this.printSectionHeader('IAM Policy Changes');
383383
this.print(formatTable(this.deepSubstituteBracedLogicalIds(changes.summarizeManagedPolicies()), this.stream.columns));
384384
}
385+
386+
if (changes.ssoPermissionSets.hasChanges || changes.ssoInstanceACAConfigs.hasChanges || changes.ssoAssignments.hasChanges) {
387+
this.printSectionHeader('IAM Identity Center Changes');
388+
if (changes.ssoPermissionSets.hasChanges) {
389+
this.print(formatTable(this.deepSubstituteBracedLogicalIds(changes.summarizeSsoPermissionSets()), this.stream.columns));
390+
}
391+
if (changes.ssoInstanceACAConfigs.hasChanges) {
392+
this.print(formatTable(this.deepSubstituteBracedLogicalIds(changes.summarizeSsoInstanceACAConfigs()), this.stream.columns));
393+
}
394+
if (changes.ssoAssignments.hasChanges) {
395+
this.print(formatTable(this.deepSubstituteBracedLogicalIds(changes.summarizeSsoAssignments()), this.stream.columns));
396+
}
397+
}
385398
}
386399

387400
public formatSecurityGroupChanges(changes: SecurityGroupChanges) {

0 commit comments

Comments
 (0)