From 463e59935444b74b6c8c2f6ede7e4923a9baa5a5 Mon Sep 17 00:00:00 2001 From: Julian Michel Date: Sun, 31 Oct 2021 10:54:53 +0100 Subject: [PATCH 1/5] feat(ec2): lookup security group by name --- packages/@aws-cdk/aws-ec2/README.md | 27 +++ packages/@aws-cdk/aws-ec2/lib/index.ts | 1 + .../aws-ec2/lib/security-group-lookup.ts | 35 +++ .../@aws-cdk/aws-ec2/lib/security-group.ts | 24 +- .../aws-ec2/test/security-group.test.ts | 169 +++++++++++++ .../lib/cloud-assembly/context-queries.ts | 18 +- .../schema/cloud-assembly.schema.json | 13 +- .../schema/cloud-assembly.version.json | 2 +- .../lib/context-providers/security-groups.ts | 29 ++- .../context-providers/security-groups.test.ts | 222 ++++++++++++++++++ 10 files changed, 532 insertions(+), 8 deletions(-) create mode 100644 packages/@aws-cdk/aws-ec2/lib/security-group-lookup.ts diff --git a/packages/@aws-cdk/aws-ec2/README.md b/packages/@aws-cdk/aws-ec2/README.md index ca30cc28fe9bd..11d827869a5d8 100644 --- a/packages/@aws-cdk/aws-ec2/README.md +++ b/packages/@aws-cdk/aws-ec2/README.md @@ -577,6 +577,33 @@ const mySecurityGroupWithoutInlineRules = new ec2.SecurityGroup(this, 'SecurityG mySecurityGroupWithoutInlineRules.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(22), 'allow ssh access from the world'); ``` +### Importing an existing security group + +If you know the ID and the configuration of the security group to import, you can use `SecurityGroup.fromSecurityGroupId`: + +```ts +const sg = ec2.SecurityGroup.fromSecurityGroupId(this, 'SecurityGroupImport', 'security-group-import', { + allowAllOutbound: true, +}); +``` + +Alternatively, use method `SecurityGroup.fromLookupAttributes` to import security groups if you do not know the ID or the configuration details. You can lookup a security group by `securityGroupId` or by `securityGroupName`. + +The result of the `SecurityGroup.fromLookupAttributes` operation will be written to a file called `cdk.context.json`. You must commit this file to source control so that the lookup values are available in non-privileged environments such as CI build steps, and to ensure your template builds are repeatable. + +```ts fixture=with-vpc +const sg = ec2.SecurityGroup.fromLookupAttributes( + this, 'SecurityGroupLookup', { + // This imports a security group by name but you can also + // specify a 'securityGroupId. + securityGroupName: 'security-group-lookup', + + // optional, lookup in specified VPC + vpc, + } +); +``` + ## Machine Images (AMIs) AMIs control the OS that gets launched when you start your EC2 instance. The EC2 diff --git a/packages/@aws-cdk/aws-ec2/lib/index.ts b/packages/@aws-cdk/aws-ec2/lib/index.ts index 4b0741044e4dd..c06159ded4e3c 100644 --- a/packages/@aws-cdk/aws-ec2/lib/index.ts +++ b/packages/@aws-cdk/aws-ec2/lib/index.ts @@ -12,6 +12,7 @@ export * from './network-acl'; export * from './network-acl-types'; export * from './port'; export * from './security-group'; +export * from './security-group-lookup'; export * from './subnet'; export * from './peer'; export * from './volume'; diff --git a/packages/@aws-cdk/aws-ec2/lib/security-group-lookup.ts b/packages/@aws-cdk/aws-ec2/lib/security-group-lookup.ts new file mode 100644 index 0000000000000..a0e6739cf8f8b --- /dev/null +++ b/packages/@aws-cdk/aws-ec2/lib/security-group-lookup.ts @@ -0,0 +1,35 @@ +import { IVpc } from '.'; + +/** + * Properties for looking up an existing SecurityGroup. + * + * Either `securityGroupName` or `securityGroupId` hast to be specified, otherwise an error is raised. + */ +export interface SecurityGroupLookupOptions { + /** + * The name of the security group + * + * If given, will import the SecurityGroup with this name. + * + * @default Don't filter on securityGroupName + */ + readonly securityGroupName?: string; + + /** + * The ID of the security group + * + * If given, will import the SecurityGroup with this ID. + * + * @default Don't filter on securityGroupId + */ + readonly securityGroupId?: string; + + /** + * The VPC of the security group + * + * If given, will filter the SecurityGroup based on the VPC. + * + * @default Don't filter on VPC + */ + readonly vpc?: IVpc, +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2/lib/security-group.ts b/packages/@aws-cdk/aws-ec2/lib/security-group.ts index 54695f2d17b71..51557a5e5e7c7 100644 --- a/packages/@aws-cdk/aws-ec2/lib/security-group.ts +++ b/packages/@aws-cdk/aws-ec2/lib/security-group.ts @@ -6,6 +6,7 @@ import { Connections } from './connections'; import { CfnSecurityGroup, CfnSecurityGroupEgress, CfnSecurityGroupIngress } from './ec2.generated'; import { IPeer, Peer } from './peer'; import { Port } from './port'; +import { SecurityGroupLookupOptions } from './security-group-lookup'; import { IVpc } from './vpc'; const SECURITY_GROUP_SYMBOL = Symbol.for('@aws-cdk/iam.SecurityGroup'); @@ -318,13 +319,32 @@ export class SecurityGroup extends SecurityGroupBase { * Look up a security group by id. */ public static fromLookup(scope: Construct, id: string, securityGroupId: string) { - if (Token.isUnresolved(securityGroupId)) { + return this.fromLookupAttributes(scope, id, { securityGroupId }); + } + + /** + * Look up a security group. + */ + public static fromLookupAttributes(scope: Construct, id: string, options: SecurityGroupLookupOptions) { + if (Token.isUnresolved(options.securityGroupId) || Token.isUnresolved(options.securityGroupName) || Token.isUnresolved(options.vpc?.vpcId)) { throw new Error('All arguments to look up a security group must be concrete (no Tokens)'); } + if (options.securityGroupId && options.securityGroupName) { + throw new Error('\'securityGroupId\' and \'securityGroupName\' can not be specified both when looking up a security group'); + } + + if (!options.securityGroupId && !options.securityGroupName) { + throw new Error('\'securityGroupId\' or \'securityGroupName\' must be specified to look up a security group'); + } + const attributes: cxapi.SecurityGroupContextResponse = ContextProvider.getValue(scope, { provider: cxschema.ContextProvider.SECURITY_GROUP_PROVIDER, - props: { securityGroupId }, + props: { + securityGroupId: options.securityGroupId, + securityGroupName: options.securityGroupName, + vpcId: options.vpc?.vpcId, + }, dummyValue: { securityGroupId: 'sg-12345', allowAllOutbound: true, diff --git a/packages/@aws-cdk/aws-ec2/test/security-group.test.ts b/packages/@aws-cdk/aws-ec2/test/security-group.test.ts index 09ccc6cdc682e..15bab88939cf4 100644 --- a/packages/@aws-cdk/aws-ec2/test/security-group.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/security-group.test.ts @@ -326,8 +326,177 @@ describe('security group', () => { expect(securityGroup.securityGroupId).toEqual('sg-12345'); expect(securityGroup.allowAllOutbound).toEqual(true); + }); + + test('can look up a security group by id', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'stack', { + env: { + account: '1234', + region: 'us-east-1', + }, + }); + + // WHEN + const securityGroup = SecurityGroup.fromLookupAttributes(stack, 'stack', { + securityGroupId: 'sg-12345', + }); + + // THEN + expect(securityGroup.securityGroupId).toEqual('sg-12345'); + expect(securityGroup.allowAllOutbound).toEqual(true); + + }); + + test('can look up a security group by name', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'stack', { + env: { + account: '1234', + region: 'us-east-1', + }, + }); + + // WHEN + const securityGroup = SecurityGroup.fromLookupAttributes(stack, 'stack', { + securityGroupName: 'my-security-group', + }); + + // THEN + expect(securityGroup.securityGroupId).toEqual('sg-12345'); + expect(securityGroup.allowAllOutbound).toEqual(true); + + }); + + test('can look up a security group by name and vpc', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'stack', { + env: { + account: '1234', + region: 'us-east-1', + }, + }); + + const vpc = Vpc.fromVpcAttributes(stack, 'VPC', { + vpcId: 'vpc-1234', + availabilityZones: ['dummy1a', 'dummy1b', 'dummy1c'], + }); + + // WHEN + const securityGroup = SecurityGroup.fromLookupAttributes(stack, 'stack', { + securityGroupName: 'my-security-group', + vpc, + }); + + // THEN + expect(securityGroup.securityGroupId).toEqual('sg-12345'); + expect(securityGroup.allowAllOutbound).toEqual(true); + + }); + + test('throws when securityGroupId and securityGroupName are specified both', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'stack', { + env: { + account: '1234', + region: 'us-east-1', + }, + }); + + // WHEN + expect(() => { + SecurityGroup.fromLookupAttributes(stack, 'stack', { + securityGroupId: 'sg-12345', + securityGroupName: 'my-security-group', + }); + }).toThrow(/\'securityGroupId\' and \'securityGroupName\' can not be specified both when looking up a security group/); + + }); + + test('throws when neither securityGroupId nor securityGroupName are specified', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'stack', { + env: { + account: '1234', + region: 'us-east-1', + }, + }); + + // WHEN + expect(() => { + SecurityGroup.fromLookupAttributes(stack, 'stack', {}); + }).toThrow(/\'securityGroupId\' or \'securityGroupName\' must be specified to look up a security group/); + + }); + + test('throws if securityGroupId is tokenized', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'stack', { + env: { + account: '1234', + region: 'us-east-1', + }, + }); + + // WHEN + expect(() => { + SecurityGroup.fromLookupAttributes(stack, 'stack', { + securityGroupId: Lazy.string({ produce: () => 'sg-12345' }), + }); + }).toThrow('All arguments to look up a security group must be concrete (no Tokens)'); }); + + test('throws if securityGroupName is tokenized', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'stack', { + env: { + account: '1234', + region: 'us-east-1', + }, + }); + + // WHEN + expect(() => { + SecurityGroup.fromLookupAttributes(stack, 'stack', { + securityGroupName: Lazy.string({ produce: () => 'my-security-group' }), + }); + }).toThrow('All arguments to look up a security group must be concrete (no Tokens)'); + + }); + + test('throws if vpc id is tokenized', () => { + // GIVEN + const app = new App(); + const stack = new Stack(app, 'stack', { + env: { + account: '1234', + region: 'us-east-1', + }, + }); + + const vpc = Vpc.fromVpcAttributes(stack, 'VPC', { + vpcId: Lazy.string({ produce: () => 'vpc-1234' }), + availabilityZones: ['dummy1a', 'dummy1b', 'dummy1c'], + }); + + // WHEN + expect(() => { + SecurityGroup.fromLookupAttributes(stack, 'stack', { + securityGroupName: 'my-security-group', + vpc, + }); + }).toThrow('All arguments to look up a security group must be concrete (no Tokens)'); + + }); + }); function testRulesAreInlined(contextDisableInlineRules: boolean | undefined | null, optionsDisableInlineRules: boolean | undefined) { diff --git a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts index ebed927f1828e..97ede25af2498 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts +++ b/packages/@aws-cdk/cloud-assembly-schema/lib/cloud-assembly/context-queries.ts @@ -416,8 +416,24 @@ export interface SecurityGroupContextQuery { /** * Security group id + * + * @default - None + */ + readonly securityGroupId?: string; + + /** + * Security group name + * + * @default - None */ - readonly securityGroupId: string; + readonly securityGroupName?: string; + + /** + * VPC ID + * + * @default - None + */ + readonly vpcId?: string; } /** diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json index d52512102e9a6..9241ae62ef0ff 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.schema.json @@ -761,14 +761,21 @@ "type": "string" }, "securityGroupId": { - "description": "Security group id", + "description": "Security group id (Default - None)", + "type": "string" + }, + "securityGroupName": { + "description": "Security group name (Default - None)", + "type": "string" + }, + "vpcId": { + "description": "VPC ID (Default - None)", "type": "string" } }, "required": [ "account", - "region", - "securityGroupId" + "region" ] }, "KeyContextQuery": { diff --git a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json index c1ee7b9a1f8ad..01d4f111912e9 100644 --- a/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json +++ b/packages/@aws-cdk/cloud-assembly-schema/schema/cloud-assembly.version.json @@ -1 +1 @@ -{"version":"14.0.0"} \ No newline at end of file +{"version":"15.0.0"} \ No newline at end of file diff --git a/packages/aws-cdk/lib/context-providers/security-groups.ts b/packages/aws-cdk/lib/context-providers/security-groups.ts index 7edde696fba45..f4c85d07c7ba3 100644 --- a/packages/aws-cdk/lib/context-providers/security-groups.ts +++ b/packages/aws-cdk/lib/context-providers/security-groups.ts @@ -12,11 +12,34 @@ export class SecurityGroupContextProviderPlugin implements ContextProviderPlugin const account: string = args.account!; const region: string = args.region!; + if (args.securityGroupId && args.securityGroupName) { + throw new Error('\'securityGroupId\' and \'securityGroupName\' can not be specified both when looking up a security group'); + } + + if (!args.securityGroupId && !args.securityGroupName) { + throw new Error('\'securityGroupId\' or \'securityGroupName\' must be specified to look up a security group'); + } + const options = { assumeRoleArn: args.lookupRoleArn }; const ec2 = (await this.aws.forEnvironment(cxapi.EnvironmentUtils.make(account, region), Mode.ForReading, options)).ec2(); + const filters: AWS.EC2.FilterList = []; + if (args.vpcId) { + filters.push({ + Name: 'vpc-id', + Values: [args.vpcId], + }); + } + if (args.securityGroupName) { + filters.push({ + Name: 'group-name', + Values: [args.securityGroupName], + }); + } + const response = await ec2.describeSecurityGroups({ - GroupIds: [args.securityGroupId], + GroupIds: args.securityGroupId ? [args.securityGroupId] : undefined, + Filters: filters.length > 0 ? filters : undefined, }).promise(); const securityGroups = response.SecurityGroups ?? []; @@ -24,6 +47,10 @@ export class SecurityGroupContextProviderPlugin implements ContextProviderPlugin throw new Error(`No security groups found matching ${JSON.stringify(args)}`); } + if (securityGroups.length > 1) { + throw new Error(`More than one security groups found matching ${JSON.stringify(args)}`); + } + const [securityGroup] = securityGroups; return { diff --git a/packages/aws-cdk/test/context-providers/security-groups.test.ts b/packages/aws-cdk/test/context-providers/security-groups.test.ts index 7f24e684819b6..2d64961af7fe4 100644 --- a/packages/aws-cdk/test/context-providers/security-groups.test.ts +++ b/packages/aws-cdk/test/context-providers/security-groups.test.ts @@ -74,6 +74,157 @@ describe('security group context provider plugin', () => { expect(res.allowAllOutbound).toEqual(true); }); + test('looks up by security group id and vpc id', async () => { + // GIVEN + const provider = new SecurityGroupContextProviderPlugin(mockSDK); + + AWS.mock('EC2', 'describeSecurityGroups', (_params: aws.EC2.DescribeSecurityGroupsRequest, cb: AwsCallback) => { + expect(_params).toEqual({ + GroupIds: ['sg-1234'], + Filters: [ + { + Name: 'vpc-id', + Values: ['vpc-1234567'], + }, + ], + }); + cb(null, { + SecurityGroups: [ + { + GroupId: 'sg-1234', + IpPermissionsEgress: [ + { + IpProtocol: '-1', + IpRanges: [ + { CidrIp: '0.0.0.0/0' }, + ], + }, + { + IpProtocol: '-1', + Ipv6Ranges: [ + { CidrIpv6: '::/0' }, + ], + }, + ], + }, + ], + }); + }); + + // WHEN + const res = await provider.getValue({ + account: '1234', + region: 'us-east-1', + securityGroupId: 'sg-1234', + vpcId: 'vpc-1234567', + }); + + // THEN + expect(res.securityGroupId).toEqual('sg-1234'); + expect(res.allowAllOutbound).toEqual(true); + }); + + test('looks up by security group name', async () => { + // GIVEN + const provider = new SecurityGroupContextProviderPlugin(mockSDK); + + AWS.mock('EC2', 'describeSecurityGroups', (_params: aws.EC2.DescribeSecurityGroupsRequest, cb: AwsCallback) => { + expect(_params).toEqual({ + Filters: [ + { + Name: 'group-name', + Values: ['my-security-group'], + }, + ], + }); + cb(null, { + SecurityGroups: [ + { + GroupId: 'sg-1234', + IpPermissionsEgress: [ + { + IpProtocol: '-1', + IpRanges: [ + { CidrIp: '0.0.0.0/0' }, + ], + }, + { + IpProtocol: '-1', + Ipv6Ranges: [ + { CidrIpv6: '::/0' }, + ], + }, + ], + }, + ], + }); + }); + + // WHEN + const res = await provider.getValue({ + account: '1234', + region: 'us-east-1', + securityGroupName: 'my-security-group', + }); + + // THEN + expect(res.securityGroupId).toEqual('sg-1234'); + expect(res.allowAllOutbound).toEqual(true); + }); + + test('looks up by security group name and vpc id', async () => { + // GIVEN + const provider = new SecurityGroupContextProviderPlugin(mockSDK); + + AWS.mock('EC2', 'describeSecurityGroups', (_params: aws.EC2.DescribeSecurityGroupsRequest, cb: AwsCallback) => { + expect(_params).toEqual({ + Filters: [ + { + Name: 'vpc-id', + Values: ['vpc-1234567'], + }, + { + Name: 'group-name', + Values: ['my-security-group'], + }, + ], + }); + cb(null, { + SecurityGroups: [ + { + GroupId: 'sg-1234', + IpPermissionsEgress: [ + { + IpProtocol: '-1', + IpRanges: [ + { CidrIp: '0.0.0.0/0' }, + ], + }, + { + IpProtocol: '-1', + Ipv6Ranges: [ + { CidrIpv6: '::/0' }, + ], + }, + ], + }, + ], + }); + }); + + // WHEN + const res = await provider.getValue({ + account: '1234', + region: 'us-east-1', + securityGroupName: 'my-security-group', + vpcId: 'vpc-1234567', + }); + + // THEN + expect(res.securityGroupId).toEqual('sg-1234'); + expect(res.allowAllOutbound).toEqual(true); + }); + test('detects non all-outbound egress', async () => { // GIVEN const provider = new SecurityGroupContextProviderPlugin(mockSDK); @@ -109,6 +260,77 @@ describe('security group context provider plugin', () => { expect(res.allowAllOutbound).toEqual(false); }); + test('errors when more than one security group is found', async () => { + // GIVEN + const provider = new SecurityGroupContextProviderPlugin(mockSDK); + + AWS.mock('EC2', 'describeSecurityGroups', (_params: aws.EC2.DescribeSecurityGroupsRequest, cb: AwsCallback) => { + expect(_params).toEqual({ GroupIds: ['sg-1234'] }); + cb(null, { + SecurityGroups: [ + { + GroupId: 'sg-1234', + IpPermissionsEgress: [ + { + IpProtocol: '-1', + IpRanges: [ + { CidrIp: '10.0.0.0/16' }, + ], + }, + ], + }, + { + GroupId: 'sg-1234', + IpPermissionsEgress: [ + { + IpProtocol: '-1', + IpRanges: [ + { CidrIp: '10.0.0.0/16' }, + ], + }, + ], + }, + ], + }); + }); + // WHEN + await expect( + provider.getValue({ + account: '1234', + region: 'us-east-1', + securityGroupId: 'sg-1234', + }), + ).rejects.toThrow(/\More than one security groups found matching/i); + }); + + test('errors when securityGroupId and securityGroupName are specified both', async () => { + // GIVEN + const provider = new SecurityGroupContextProviderPlugin(mockSDK); + + // WHEN + await expect( + provider.getValue({ + account: '1234', + region: 'us-east-1', + securityGroupId: 'sg-1234', + securityGroupName: 'my-security-group', + }), + ).rejects.toThrow(/\'securityGroupId\' and \'securityGroupName\' can not be specified both when looking up a security group/i); + }); + + test('errors when neither securityGroupId nor securityGroupName are specified', async () => { + // GIVEN + const provider = new SecurityGroupContextProviderPlugin(mockSDK); + + // WHEN + await expect( + provider.getValue({ + account: '1234', + region: 'us-east-1', + }), + ).rejects.toThrow(/\'securityGroupId\' or \'securityGroupName\' must be specified to look up a security group/i); + }); + test('identifies allTrafficEgress from SecurityGroup permissions', () => { expect( hasAllTrafficEgress({ From 927ba53b70500e725ffe7b2f23f8af57f1a62a90 Mon Sep 17 00:00:00 2001 From: Julian Michel Date: Sun, 31 Oct 2021 19:22:01 +0100 Subject: [PATCH 2/5] insert change to breaking changes --- allowed-breaking-changes.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/allowed-breaking-changes.txt b/allowed-breaking-changes.txt index 6def9776b6dc9..ffd902b5ff460 100644 --- a/allowed-breaking-changes.txt +++ b/allowed-breaking-changes.txt @@ -77,4 +77,8 @@ strengthened:@aws-cdk/aws-lambda-event-sources.ManagedKafkaEventSourceProps # Remove IO2 from autoscaling EbsDeviceVolumeType. This value is not supported # at the moment and was not supported in the past. -removed:@aws-cdk/aws-autoscaling.EbsDeviceVolumeType.IO2 \ No newline at end of file +removed:@aws-cdk/aws-autoscaling.EbsDeviceVolumeType.IO2 + +# Changed property securityGroupId to optional because either securityGroupId or +# securityGroupName is required. Therefore securityGroupId is no longer mandatory. +weakened:@aws-cdk/cloud-assembly-schema.SecurityGroupContextQuery \ No newline at end of file From a16809998982098721387ec2621afadc84cd1a0a Mon Sep 17 00:00:00 2001 From: Julian Michel Date: Sun, 14 Nov 2021 10:57:26 +0100 Subject: [PATCH 3/5] introduce methods fromLookupById, fromLookupByName --- packages/@aws-cdk/aws-ec2/README.md | 25 +++---- packages/@aws-cdk/aws-ec2/lib/index.ts | 1 - .../aws-ec2/lib/security-group-lookup.ts | 4 +- .../@aws-cdk/aws-ec2/lib/security-group.ts | 68 ++++++++++-------- .../aws-ec2/test/security-group.test.ts | 72 ++++--------------- 5 files changed, 64 insertions(+), 106 deletions(-) diff --git a/packages/@aws-cdk/aws-ec2/README.md b/packages/@aws-cdk/aws-ec2/README.md index 11d827869a5d8..88e9e4285697c 100644 --- a/packages/@aws-cdk/aws-ec2/README.md +++ b/packages/@aws-cdk/aws-ec2/README.md @@ -582,28 +582,25 @@ mySecurityGroupWithoutInlineRules.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tc If you know the ID and the configuration of the security group to import, you can use `SecurityGroup.fromSecurityGroupId`: ```ts -const sg = ec2.SecurityGroup.fromSecurityGroupId(this, 'SecurityGroupImport', 'security-group-import', { +const sg = ec2.SecurityGroup.fromSecurityGroupId(this, 'SecurityGroupImport', 'sg-1234', { allowAllOutbound: true, }); ``` -Alternatively, use method `SecurityGroup.fromLookupAttributes` to import security groups if you do not know the ID or the configuration details. You can lookup a security group by `securityGroupId` or by `securityGroupName`. - -The result of the `SecurityGroup.fromLookupAttributes` operation will be written to a file called `cdk.context.json`. You must commit this file to source control so that the lookup values are available in non-privileged environments such as CI build steps, and to ensure your template builds are repeatable. +Alternatively, use lookup methods to import security groups if you do not know the ID or the configuration details. Method `SecurityGroup.fromLookupByName` looks up a security group if the secruity group ID is unknown. ```ts fixture=with-vpc -const sg = ec2.SecurityGroup.fromLookupAttributes( - this, 'SecurityGroupLookup', { - // This imports a security group by name but you can also - // specify a 'securityGroupId. - securityGroupName: 'security-group-lookup', - - // optional, lookup in specified VPC - vpc, - } -); +const sg = ec2.SecurityGroup.fromLookupByName(stack, 'SecurityGroupLookup', 'security-group-name', vpc); +``` + +If the security group ID is known and configuration details are unknown, use method `SecurityGroup.fromLookupById` instead. This method will lookup property `allowAllOutbound` from the current configuration of the security group. + +```ts +const sg = ec2.SecurityGroup.fromLookupById(stack, 'SecurityGroupLookup', 'sg-1234'); ``` +The result of `SecurityGroup.fromLookupByName` and `SecurityGroup.fromLookupById` operations will be written to a file called `cdk.context.json`. You must commit this file to source control so that the lookup values are available in non-privileged environments such as CI build steps, and to ensure your template builds are repeatable. + ## Machine Images (AMIs) AMIs control the OS that gets launched when you start your EC2 instance. The EC2 diff --git a/packages/@aws-cdk/aws-ec2/lib/index.ts b/packages/@aws-cdk/aws-ec2/lib/index.ts index c06159ded4e3c..4b0741044e4dd 100644 --- a/packages/@aws-cdk/aws-ec2/lib/index.ts +++ b/packages/@aws-cdk/aws-ec2/lib/index.ts @@ -12,7 +12,6 @@ export * from './network-acl'; export * from './network-acl-types'; export * from './port'; export * from './security-group'; -export * from './security-group-lookup'; export * from './subnet'; export * from './peer'; export * from './volume'; diff --git a/packages/@aws-cdk/aws-ec2/lib/security-group-lookup.ts b/packages/@aws-cdk/aws-ec2/lib/security-group-lookup.ts index a0e6739cf8f8b..f39a17e07e4d0 100644 --- a/packages/@aws-cdk/aws-ec2/lib/security-group-lookup.ts +++ b/packages/@aws-cdk/aws-ec2/lib/security-group-lookup.ts @@ -1,9 +1,9 @@ -import { IVpc } from '.'; +import { IVpc } from './vpc'; /** * Properties for looking up an existing SecurityGroup. * - * Either `securityGroupName` or `securityGroupId` hast to be specified, otherwise an error is raised. + * Either `securityGroupName` or `securityGroupId` has to be specified. */ export interface SecurityGroupLookupOptions { /** diff --git a/packages/@aws-cdk/aws-ec2/lib/security-group.ts b/packages/@aws-cdk/aws-ec2/lib/security-group.ts index 51557a5e5e7c7..c8eb8aead7b65 100644 --- a/packages/@aws-cdk/aws-ec2/lib/security-group.ts +++ b/packages/@aws-cdk/aws-ec2/lib/security-group.ts @@ -317,44 +317,25 @@ export interface SecurityGroupImportOptions { export class SecurityGroup extends SecurityGroupBase { /** * Look up a security group by id. + * + * @deprecated Use `fromLookupById()` instead */ public static fromLookup(scope: Construct, id: string, securityGroupId: string) { return this.fromLookupAttributes(scope, id, { securityGroupId }); } /** - * Look up a security group. + * Look up a security group by id. */ - public static fromLookupAttributes(scope: Construct, id: string, options: SecurityGroupLookupOptions) { - if (Token.isUnresolved(options.securityGroupId) || Token.isUnresolved(options.securityGroupName) || Token.isUnresolved(options.vpc?.vpcId)) { - throw new Error('All arguments to look up a security group must be concrete (no Tokens)'); - } - - if (options.securityGroupId && options.securityGroupName) { - throw new Error('\'securityGroupId\' and \'securityGroupName\' can not be specified both when looking up a security group'); - } - - if (!options.securityGroupId && !options.securityGroupName) { - throw new Error('\'securityGroupId\' or \'securityGroupName\' must be specified to look up a security group'); - } - - const attributes: cxapi.SecurityGroupContextResponse = ContextProvider.getValue(scope, { - provider: cxschema.ContextProvider.SECURITY_GROUP_PROVIDER, - props: { - securityGroupId: options.securityGroupId, - securityGroupName: options.securityGroupName, - vpcId: options.vpc?.vpcId, - }, - dummyValue: { - securityGroupId: 'sg-12345', - allowAllOutbound: true, - } as cxapi.SecurityGroupContextResponse, - }).value; + public static fromLookupById(scope: Construct, id: string, securityGroupId: string) { + return this.fromLookupAttributes(scope, id, { securityGroupId }); + } - return SecurityGroup.fromSecurityGroupId(scope, id, attributes.securityGroupId, { - allowAllOutbound: attributes.allowAllOutbound, - mutable: true, - }); + /** + * Look up a security group by name. + */ + public static fromLookupByName(scope: Construct, id: string, securityGroupName: string, vpc: IVpc) { + return this.fromLookupAttributes(scope, id, { securityGroupName, vpc }); } /** @@ -398,6 +379,33 @@ export class SecurityGroup extends SecurityGroupBase { : new ImmutableImport(scope, id); } + /** + * Look up a security group. + */ + private static fromLookupAttributes(scope: Construct, id: string, options: SecurityGroupLookupOptions) { + if (Token.isUnresolved(options.securityGroupId) || Token.isUnresolved(options.securityGroupName) || Token.isUnresolved(options.vpc?.vpcId)) { + throw new Error('All arguments to look up a security group must be concrete (no Tokens)'); + } + + const attributes: cxapi.SecurityGroupContextResponse = ContextProvider.getValue(scope, { + provider: cxschema.ContextProvider.SECURITY_GROUP_PROVIDER, + props: { + securityGroupId: options.securityGroupId, + securityGroupName: options.securityGroupName, + vpcId: options.vpc?.vpcId, + }, + dummyValue: { + securityGroupId: 'sg-12345', + allowAllOutbound: true, + } as cxapi.SecurityGroupContextResponse, + }).value; + + return SecurityGroup.fromSecurityGroupId(scope, id, attributes.securityGroupId, { + allowAllOutbound: attributes.allowAllOutbound, + mutable: true, + }); + } + /** * An attribute that represents the security group name. * diff --git a/packages/@aws-cdk/aws-ec2/test/security-group.test.ts b/packages/@aws-cdk/aws-ec2/test/security-group.test.ts index 15bab88939cf4..d7ca9ab79a695 100644 --- a/packages/@aws-cdk/aws-ec2/test/security-group.test.ts +++ b/packages/@aws-cdk/aws-ec2/test/security-group.test.ts @@ -339,9 +339,7 @@ describe('security group', () => { }); // WHEN - const securityGroup = SecurityGroup.fromLookupAttributes(stack, 'stack', { - securityGroupId: 'sg-12345', - }); + const securityGroup = SecurityGroup.fromLookupById(stack, 'SG1', 'sg-12345'); // THEN expect(securityGroup.securityGroupId).toEqual('sg-12345'); @@ -349,7 +347,7 @@ describe('security group', () => { }); - test('can look up a security group by name', () => { + test('can look up a security group by name and vpc', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'stack', { @@ -359,18 +357,21 @@ describe('security group', () => { }, }); - // WHEN - const securityGroup = SecurityGroup.fromLookupAttributes(stack, 'stack', { - securityGroupName: 'my-security-group', + const vpc = Vpc.fromVpcAttributes(stack, 'VPC', { + vpcId: 'vpc-1234', + availabilityZones: ['dummy1a', 'dummy1b', 'dummy1c'], }); + // WHEN + const securityGroup = SecurityGroup.fromLookupByName(stack, 'SG1', 'sg-12345', vpc); + // THEN expect(securityGroup.securityGroupId).toEqual('sg-12345'); expect(securityGroup.allowAllOutbound).toEqual(true); }); - test('can look up a security group by name and vpc', () => { + test('can look up a security group by id and vpc', () => { // GIVEN const app = new App(); const stack = new Stack(app, 'stack', { @@ -386,10 +387,7 @@ describe('security group', () => { }); // WHEN - const securityGroup = SecurityGroup.fromLookupAttributes(stack, 'stack', { - securityGroupName: 'my-security-group', - vpc, - }); + const securityGroup = SecurityGroup.fromLookupByName(stack, 'SG1', 'my-security-group', vpc); // THEN expect(securityGroup.securityGroupId).toEqual('sg-12345'); @@ -397,43 +395,6 @@ describe('security group', () => { }); - test('throws when securityGroupId and securityGroupName are specified both', () => { - // GIVEN - const app = new App(); - const stack = new Stack(app, 'stack', { - env: { - account: '1234', - region: 'us-east-1', - }, - }); - - // WHEN - expect(() => { - SecurityGroup.fromLookupAttributes(stack, 'stack', { - securityGroupId: 'sg-12345', - securityGroupName: 'my-security-group', - }); - }).toThrow(/\'securityGroupId\' and \'securityGroupName\' can not be specified both when looking up a security group/); - - }); - - test('throws when neither securityGroupId nor securityGroupName are specified', () => { - // GIVEN - const app = new App(); - const stack = new Stack(app, 'stack', { - env: { - account: '1234', - region: 'us-east-1', - }, - }); - - // WHEN - expect(() => { - SecurityGroup.fromLookupAttributes(stack, 'stack', {}); - }).toThrow(/\'securityGroupId\' or \'securityGroupName\' must be specified to look up a security group/); - - }); - test('throws if securityGroupId is tokenized', () => { // GIVEN const app = new App(); @@ -446,9 +407,7 @@ describe('security group', () => { // WHEN expect(() => { - SecurityGroup.fromLookupAttributes(stack, 'stack', { - securityGroupId: Lazy.string({ produce: () => 'sg-12345' }), - }); + SecurityGroup.fromLookupById(stack, 'stack', Lazy.string({ produce: () => 'sg-12345' })); }).toThrow('All arguments to look up a security group must be concrete (no Tokens)'); }); @@ -465,9 +424,7 @@ describe('security group', () => { // WHEN expect(() => { - SecurityGroup.fromLookupAttributes(stack, 'stack', { - securityGroupName: Lazy.string({ produce: () => 'my-security-group' }), - }); + SecurityGroup.fromLookupById(stack, 'stack', Lazy.string({ produce: () => 'my-security-group' })); }).toThrow('All arguments to look up a security group must be concrete (no Tokens)'); }); @@ -489,10 +446,7 @@ describe('security group', () => { // WHEN expect(() => { - SecurityGroup.fromLookupAttributes(stack, 'stack', { - securityGroupName: 'my-security-group', - vpc, - }); + SecurityGroup.fromLookupByName(stack, 'stack', 'my-security-group', vpc); }).toThrow('All arguments to look up a security group must be concrete (no Tokens)'); }); From 379dac81aca20817ad1ec678804767b8ae78efe1 Mon Sep 17 00:00:00 2001 From: Julian Michel Date: Sun, 14 Nov 2021 14:17:02 +0100 Subject: [PATCH 4/5] fix readme --- packages/@aws-cdk/aws-ec2/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-ec2/README.md b/packages/@aws-cdk/aws-ec2/README.md index 88e9e4285697c..f673e24111e9f 100644 --- a/packages/@aws-cdk/aws-ec2/README.md +++ b/packages/@aws-cdk/aws-ec2/README.md @@ -590,13 +590,13 @@ const sg = ec2.SecurityGroup.fromSecurityGroupId(this, 'SecurityGroupImport', 's Alternatively, use lookup methods to import security groups if you do not know the ID or the configuration details. Method `SecurityGroup.fromLookupByName` looks up a security group if the secruity group ID is unknown. ```ts fixture=with-vpc -const sg = ec2.SecurityGroup.fromLookupByName(stack, 'SecurityGroupLookup', 'security-group-name', vpc); +const sg = ec2.SecurityGroup.fromLookupByName(this, 'SecurityGroupLookup', 'security-group-name', vpc); ``` If the security group ID is known and configuration details are unknown, use method `SecurityGroup.fromLookupById` instead. This method will lookup property `allowAllOutbound` from the current configuration of the security group. ```ts -const sg = ec2.SecurityGroup.fromLookupById(stack, 'SecurityGroupLookup', 'sg-1234'); +const sg = ec2.SecurityGroup.fromLookupById(this, 'SecurityGroupLookup', 'sg-1234'); ``` The result of `SecurityGroup.fromLookupByName` and `SecurityGroup.fromLookupById` operations will be written to a file called `cdk.context.json`. You must commit this file to source control so that the lookup values are available in non-privileged environments such as CI build steps, and to ensure your template builds are repeatable. From a1ca5d8c7ca625a67f701cb6ba549a12572e2f45 Mon Sep 17 00:00:00 2001 From: Julian Michel Date: Mon, 15 Nov 2021 20:03:05 +0100 Subject: [PATCH 5/5] move SecurityGroupLookupOptions to security-group.ts --- .../aws-ec2/lib/security-group-lookup.ts | 35 ------------------- .../@aws-cdk/aws-ec2/lib/security-group.ts | 35 ++++++++++++++++++- 2 files changed, 34 insertions(+), 36 deletions(-) delete mode 100644 packages/@aws-cdk/aws-ec2/lib/security-group-lookup.ts diff --git a/packages/@aws-cdk/aws-ec2/lib/security-group-lookup.ts b/packages/@aws-cdk/aws-ec2/lib/security-group-lookup.ts deleted file mode 100644 index f39a17e07e4d0..0000000000000 --- a/packages/@aws-cdk/aws-ec2/lib/security-group-lookup.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { IVpc } from './vpc'; - -/** - * Properties for looking up an existing SecurityGroup. - * - * Either `securityGroupName` or `securityGroupId` has to be specified. - */ -export interface SecurityGroupLookupOptions { - /** - * The name of the security group - * - * If given, will import the SecurityGroup with this name. - * - * @default Don't filter on securityGroupName - */ - readonly securityGroupName?: string; - - /** - * The ID of the security group - * - * If given, will import the SecurityGroup with this ID. - * - * @default Don't filter on securityGroupId - */ - readonly securityGroupId?: string; - - /** - * The VPC of the security group - * - * If given, will filter the SecurityGroup based on the VPC. - * - * @default Don't filter on VPC - */ - readonly vpc?: IVpc, -} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-ec2/lib/security-group.ts b/packages/@aws-cdk/aws-ec2/lib/security-group.ts index c806f83dd7f85..163cb8079ccdb 100644 --- a/packages/@aws-cdk/aws-ec2/lib/security-group.ts +++ b/packages/@aws-cdk/aws-ec2/lib/security-group.ts @@ -6,7 +6,6 @@ import { Connections } from './connections'; import { CfnSecurityGroup, CfnSecurityGroupEgress, CfnSecurityGroupIngress } from './ec2.generated'; import { IPeer, Peer } from './peer'; import { Port } from './port'; -import { SecurityGroupLookupOptions } from './security-group-lookup'; import { IVpc } from './vpc'; const SECURITY_GROUP_SYMBOL = Symbol.for('@aws-cdk/iam.SecurityGroup'); @@ -724,3 +723,37 @@ function egressRulesEqual(a: CfnSecurityGroup.EgressProperty, b: CfnSecurityGrou function isAllTrafficRule(rule: any) { return rule.cidrIp === '0.0.0.0/0' && rule.ipProtocol === '-1'; } + +/** + * Properties for looking up an existing SecurityGroup. + * + * Either `securityGroupName` or `securityGroupId` has to be specified. + */ +interface SecurityGroupLookupOptions { + /** + * The name of the security group + * + * If given, will import the SecurityGroup with this name. + * + * @default Don't filter on securityGroupName + */ + readonly securityGroupName?: string; + + /** + * The ID of the security group + * + * If given, will import the SecurityGroup with this ID. + * + * @default Don't filter on securityGroupId + */ + readonly securityGroupId?: string; + + /** + * The VPC of the security group + * + * If given, will filter the SecurityGroup based on the VPC. + * + * @default Don't filter on VPC + */ + readonly vpc?: IVpc, +}