packages/@aws-cdk/aws-ec2-alpha/lib/ipam.ts (218 lines of code) (raw):
import { CfnIPAM, CfnIPAMPool, CfnIPAMPoolCidr, CfnIPAMScope } from 'aws-cdk-lib/aws-ec2';
import { Construct } from 'constructs';
import { Lazy, Names, Resource, Stack, Tags } from 'aws-cdk-lib';
import { addConstructMetadata, MethodMetadata } from 'aws-cdk-lib/core/lib/metadata-resource';
/**
* Represents the address family for IP addresses in an IPAM pool.
* IP_V4 - Represents the IPv4 address family.
* IP_V6 - Represents the IPv6 address family.
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-ipampool.html#cfn-ec2-ipampool-addressfamily
*/
export enum AddressFamily {
/**
* Represents the IPv4 address family.
* Allowed under public and private pool.
*/
IP_V4 = 'ipv4',
/**
* Represents the IPv6 address family.
* Only allowed under public pool.
*/
IP_V6 = 'ipv6',
}
/**
* The IP address source for pools in the public scope.
* Only used for provisioning IP address CIDRs to pools in the public scope.
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-ipampool.html#cfn-ec2-ipampool-publicipsource
*/
export enum IpamPoolPublicIpSource {
/**
* BYOIP Ipv6 to be registered under IPAM
*/
BYOIP = 'byoip',
/**
* Amazon Provided Ipv6 range
*/
AMAZON = 'amazon',
}
/**
* Limits which service in AWS that the pool can be used in
*/
export enum AwsServiceName {
/**
* Allows users to use space for Elastic IP addresses and VPCs
*/
EC2 = 'ec2',
}
/**
* Options to create a new Ipam in the account
*/
export interface IpamProps {
/**
* The operating Regions for an IPAM.
* Operating Regions are AWS Regions where the IPAM is allowed to manage IP address CIDRs
* For more information about operating Regions, see [Create an IPAM](https://docs.aws.amazon.com//vpc/latest/ipam/create-ipam.html) in the *Amazon VPC IPAM User Guide* .
* @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-ipam.html#cfn-ec2-ipam-operatingregions
*
* @default - Stack.region if defined in the stack
*/
readonly operatingRegions?: string[];
/**
* Name of IPAM that can be used for tagging resource
*
* @default - If no name provided, no tags will be added to the IPAM
*/
readonly ipamName?: string;
}
/**
* Refers to two possible scope types under IPAM
*/
export enum IpamScopeType {
/**
* Default scopes created by IPAM
*/
DEFAULT = 'default',
/**
* Custom scope created using method
*/
CUSTOM = 'custom',
}
/**
* Options for configuring an IPAM pool.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-ipampool.html
*/
export interface PoolOptions {
/**
* addressFamily - The address family of the pool (ipv4 or ipv6).
*/
readonly addressFamily: AddressFamily;
/**
* Information about the CIDRs provisioned to the pool.
*
* @default - No CIDRs are provisioned
*/
readonly ipv4ProvisionedCidrs?: string[];
/**
* The locale (AWS Region) of the pool. Should be one of the IPAM operating region.
* Only resources in the same Region as the locale of the pool can get IP address allocations from the pool.
* You can only allocate a CIDR for a VPC, for example, from an IPAM pool that shares a locale with the VPC’s Region.
* Note that once you choose a Locale for a pool, you cannot modify it. If you choose an AWS Region for locale that has not been configured as an operating Region for the IPAM, you'll get an error.
* @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-ipampool.html#cfn-ec2-ipampool-locale
*
* @default - Current operating region of IPAM
*/
readonly locale?: string;
/**
* The IP address source for pools in the public scope.
* Only used for IPv6 address
* Only allowed values to this are 'byoip' or 'amazon'
*
* @default amazon
*/
readonly publicIpSource?: IpamPoolPublicIpSource;
/**
* Limits which service in AWS that the pool can be used in.
*
* "ec2", for example, allows users to use space for Elastic IP addresses and VPCs.
*
* @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-ipampool.html#cfn-ec2-ipampool-awsservice
*
* @default - required in case of an IPv6, throws an error if not provided.
*/
readonly awsService?: AwsServiceName;
/**
* IPAM Pool resource name to be used for tagging
*
* @default - autogenerated by CDK if not provided
*/
readonly ipamPoolName?: string;
}
const NAME_TAG: string = 'Name';
/**
* Properties for creating an IPAM pool.
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-ipampool.html
*/
interface IpamPoolProps extends PoolOptions {
/**
* Scope id where pool needs to be created
*/
readonly ipamScopeId: string;
}
/**
* Options to provision CIDRs to an IPAM pool.
* Used to create a new IpamPoolCidr
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-ipampoolcidr.html
*/
export interface IpamPoolCidrProvisioningOptions {
/**
* Ipv6 Netmask length for the CIDR
*
* @default - pool provisioned without netmask length, need cidr range in this case
*/
readonly netmaskLength?: number;
/**
* Ipv6 CIDR block for the IPAM pool
*
* @default - pool provisioned without netmask length, need netmask length in this case
*/
readonly cidr?: string;
}
/**
* Definition used to add or create a new IPAM pool
*/
export interface IIpamPool {
/**
* Pool ID to be passed to the VPC construct
* @attribute IpamPoolId
*/
readonly ipamPoolId: string;
/**
* Pool CIDR for IPv6 to be provisioned with Public IP source set to 'Amazon'
*/
readonly ipamCidrs: CfnIPAMPoolCidr[];
/**
* Pool CIDR for IPv4 to be provisioned using IPAM
* Required to check for subnet IP range is within the VPC range
*/
readonly ipamIpv4Cidrs?: string[];
/**
* Function to associate a IPv6 address with IPAM pool
*/
provisionCidr(id: string, options: IpamPoolCidrProvisioningOptions): CfnIPAMPoolCidr;
}
/**
* IPAM scope is the highest-level container within IPAM. An IPAM contains two default scopes.
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-ipamscope.html
*/
interface IpamScopeProps extends IpamScopeOptions {
/**
* IPAM id to which scope needs to be added
*/
readonly ipamId: string;
/**
* Operating regions for the Ipam
* Required in order to validate the locale being set on pool
*/
readonly ipamOperatingRegions: string[];
/**
* Custom ipam scope id to add a pool in order to support default scopes
*
* @default - throws an error if no scope id is provided
*/
readonly ipamScopeId?: string;
}
/**
* Being used in IPAM class to add pools to default scope created by IPAM.
*/
export interface IpamScopeOptions {
/**
* IPAM scope name that will be used for tagging
*
* @default - no tags will be added to the scope
*/
readonly ipamScopeName?: string;
}
/**
* Options for configuring an IP Address Manager (IPAM).
*
* For more information, see the {@link https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-ipam.html}.
*/
export interface IpamOptions {
/**
* CIDR Mask for Vpc
* Only required when using AWS Ipam
*
* @default - no netmask length for IPAM attached to VPC secondary address
*/
readonly netmaskLength?: number;
/**
* Ipv4 or an Ipv6 IPAM pool
* Only required when using AWS Ipam
*
* @default - no pool attached to VPC secondary address
*/
readonly ipamPool?: IIpamPool;
/**
* Required to set Secondary cidr block resource name
* in order to generate unique logical id for the resource.
*/
readonly cidrBlockName: string;
}
/**
* Interface for IpamScope Class
*/
export interface IIpamScopeBase {
/**
* Reference to the current scope of stack to be passed in order to create
* a new IPAM pool
*/
readonly scope: Construct;
/**
* Default Scope ids created by the IPAM or a new Resource id
*/
readonly scopeId: string;
/**
* Defines scope type can be either default or custom
*/
readonly scopeType?: IpamScopeType;
/**
* Function to add a new pool to an IPAM scope
*/
addPool(id: string, options: PoolOptions): IIpamPool;
}
/**
* Creates new IPAM Pool
* Pools enable you to organize your IP addresses according to your routing and security needs
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-ipampool.html
* @resource AWS::EC2::IPAMPool
* @internal
*/
class IpamPool extends Resource implements IIpamPool {
/**
* Pool ID to be passed to the VPC construct
* @attribute IpamPoolId
*/
public readonly ipamPoolId: string;
/**
* Pool CIDR for IPv6 to be provisioned with Public IP source set to 'Amazon'
*/
public readonly ipamCidrs: CfnIPAMPoolCidr[] = [];
/**
* Pool CIDR for IPv4 to be provisioned using IPAM
* Required to check for subnet IP range is within the VPC range
*/
public readonly ipamIpv4Cidrs: string[] = [];
/**
* Reference to ipamPool resource created in this class
*/
private readonly _ipamPool: CfnIPAMPool;
constructor(scope: Construct, id: string, props: IpamPoolProps) {
super(scope, id, {
physicalName: props.ipamPoolName ?? Lazy.string({
produce: () => Names.uniqueResourceName(this, { maxLength: 128, allowedSpecialCharacters: '_' }),
}),
});
// Enhanced CDK Analytics Telemetry
addConstructMetadata(this, props);
if (props.addressFamily === AddressFamily.IP_V6 && !props.awsService) {
throw new Error('awsService is required when addressFamily is set to ipv6');
}
// Add tags to the IPAM Pool if name is provided
if (props.ipamPoolName) {
Tags.of(this).add(NAME_TAG, props.ipamPoolName);
}
this._ipamPool = new CfnIPAMPool(this, id, {
addressFamily: props.addressFamily,
provisionedCidrs: props.ipv4ProvisionedCidrs?.map(cidr => ({ cidr })),
locale: props.locale,
ipamScopeId: props.ipamScopeId,
publicIpSource: props.publicIpSource,
awsService: props.awsService,
});
this.ipamPoolId = this._ipamPool.attrIpamPoolId;
// Populating to check for subnet range against all IPv4 ranges assigned to VPC including IPAM
props.ipv4ProvisionedCidrs?.map(cidr => (this.ipamIpv4Cidrs.push(cidr)));
this.node.defaultChild = this._ipamPool;
}
/**
* A CIDR provisioned to an IPAM pool.
* @param id Name of Resource
* @param options Either a CIDR or netmask length must be provided
* @returns AWS::EC2::IPAMPoolCidr
*/
@MethodMetadata()
public provisionCidr(id: string, options: IpamPoolCidrProvisioningOptions): CfnIPAMPoolCidr {
const cidr = new CfnIPAMPoolCidr(this, id, {
...options,
ipamPoolId: this.ipamPoolId,
});
this.ipamCidrs.push(cidr);
return cidr;
}
}
/**
* Creates custom Ipam Scope, custom IPAM scopes can only be private
* (can be used for adding custom scopes to an existing IPAM)
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-ipamscope.html
* @resource AWS::EC2::IPAMScope
*/
class IpamScope extends Resource implements IIpamScopeBase {
/**
* Stores the reference to newly created Resource
*/
private readonly _ipamScope: CfnIPAMScope;
/**
* ID for Resource IpamScope
* @attribute IpamScopeId
*/
public readonly scopeId: string;
/**
* Properties to configure ipam scope
*/
private readonly props: IpamScopeProps;
/**
* Reference to stack scope to be passed through addPool method inorder to create a new IpamPool
*/
public readonly scope: Construct;
/**
* Defines scope type can be either default or custom
*/
public readonly scopeType: IpamScopeType;
constructor(scope: Construct, id: string, props: IpamScopeProps) {
super(scope, id);
// Enhanced CDK Analytics Telemetry
addConstructMetadata(this, props);
this._ipamScope = new CfnIPAMScope(scope, 'IpamScope', {
ipamId: props.ipamId,
});
Tags.of(this._ipamScope).add(NAME_TAG, props.ipamScopeName ?? 'CustomIpamScope');
this.scopeId = this._ipamScope.attrIpamScopeId;
this.scopeType = IpamScopeType.CUSTOM;
this.scope = scope;
this.props = props;
}
/**
* Adds a pool to the IPAM scope.
* @external
*/
addPool(id: string, options: PoolOptions): IIpamPool {
return createIpamPool(this.scope, id, this.props, options, this.scopeId);
}
}
/**
* Base class for IPAM default scopes.
*/
class IpamScopeBase implements IIpamScopeBase {
constructor(
readonly scope: Construct,
readonly scopeId: string,
private readonly props: IpamScopeProps,
readonly scopeType?: IpamScopeType,
) {
this.scopeType = IpamScopeType.DEFAULT;
if (!props.ipamScopeId) {
throw new Error('ipamScopeId is required');
} else {
this.scopeId = props.ipamScopeId;
}
}
/**
* Adds a pool to the IPAM scope.
* @external
*/
addPool(id: string, options: PoolOptions): IIpamPool {
return createIpamPool(this.scope, id, this.props, options, this.scopeId);
}
}
/**
* Creates new IPAM with default public and private scope
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-ipamscope.html
* @resource AWS::EC2::IPAM
*/
export class Ipam extends Resource {
/**
* Provides access to default public IPAM scope through add pool method.
* Usage: To add an Ipam Pool to a default public scope
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-ipamscope.html
*/
public readonly publicScope: IIpamScopeBase;
/**
* Provides access to default private IPAM scope through add pool method.
* Usage: To add an Ipam Pool to a default private scope
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-ipamscope.html
* */
public readonly privateScope: IIpamScopeBase;
// Resource IPAM
private readonly _ipam: CfnIPAM;
/**
* Access to Ipam resource id that can be used later to add a custom private scope to this IPAM
* @attribute IpamId
*/
public readonly ipamId: string;
/**
* List of operating regions for IPAM
*/
public readonly operatingRegions: string[];
/**
* List of scopes created under this IPAM
*/
public readonly scopes: IIpamScopeBase[] = [];
/**
* IPAM name to be used for tagging
* @default no tag specified
* @attribute IpamName
*/
public readonly ipamName?: string;
constructor(scope: Construct, id: string, props?: IpamProps) {
super(scope, id);
// Enhanced CDK Analytics Telemetry
addConstructMetadata(this, props);
if (props?.ipamName) {
Tags.of(this).add(NAME_TAG, props.ipamName);
}
if (props?.operatingRegions && (props.operatingRegions.length === 0)) {
throw new Error('Please provide at least one operating region');
}
this.operatingRegions = props?.operatingRegions ?? [Stack.of(this).region];
this.ipamName = props?.ipamName;
this._ipam = new CfnIPAM(this, 'Ipam', {
operatingRegions: this.operatingRegions ? this.operatingRegions.map(region => ({ regionName: region })) : [],
});
this.node.defaultChild = this._ipam;
this.ipamId = this._ipam.attrIpamId;
this.publicScope = new IpamScopeBase(this, 'DefaultPublicScope', {
ipamOperatingRegions: this.operatingRegions,
ipamId: this._ipam.attrIpamId,
ipamScopeId: this._ipam.attrPublicDefaultScopeId,
});
this.privateScope = new IpamScopeBase(this, 'DefaultPrivateScope', {
ipamOperatingRegions: this.operatingRegions,
ipamId: this._ipam.attrIpamId,
ipamScopeId: this._ipam.attrPrivateDefaultScopeId,
});
this.scopes.push(this.publicScope, this.privateScope);
}
/**
* Function to add custom scope to an existing IPAM
* Custom scopes can only be private
*/
@MethodMetadata()
public addScope(scope: Construct, id: string, options: IpamScopeOptions): IIpamScopeBase {
const ipamScope = new IpamScope(scope, id, {
...options,
ipamId: this.ipamId,
ipamOperatingRegions: this.operatingRegions,
});
this.scopes.push(ipamScope);
return ipamScope;
}
}
/**
* Function to create IpamPool under scope
* @internal
*/
function createIpamPool(
scope: Construct,
id: string,
scopeOptions: IpamScopeProps,
poolOptions: PoolOptions,
scopeId: string,
): IpamPool {
// Only check locale if it's provided since it's an optional field
if (poolOptions.locale) {
const isLocaleInOperatingRegions = scopeOptions.ipamOperatingRegions
? scopeOptions.ipamOperatingRegions.map(region => ({ regionName: region }))
.some(region => region.regionName === poolOptions.locale)
: false;
if (!isLocaleInOperatingRegions) {
throw new Error(`The provided locale '${poolOptions.locale}' is not in the operating regions (${scopeOptions.ipamOperatingRegions}). ` +
'If specified, locale must be configured as an operating region for the IPAM.');
}
}
return new IpamPool(scope, id, {
ipamPoolName: poolOptions.ipamPoolName,
addressFamily: poolOptions.addressFamily,
ipv4ProvisionedCidrs: poolOptions.ipv4ProvisionedCidrs,
ipamScopeId: scopeId,
locale: poolOptions.locale,
publicIpSource: poolOptions.publicIpSource,
awsService: poolOptions.awsService,
});
}