-
Notifications
You must be signed in to change notification settings - Fork 2.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add a new cdk example that allow us to use ipam pool id in a L2 vpc construct (ec2.vpc) #708
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
*.swp | ||
package-lock.json | ||
__pycache__ | ||
.pytest_cache | ||
.venv | ||
*.egg-info | ||
|
||
# CDK asset staging directory | ||
.cdk.staging | ||
cdk.out |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
|
||
# Welcome to your CDK Python project! | ||
|
||
This is cdk example project for CDK development with Python. | ||
|
||
The project provides a solution that I already suggested for the issues reported from aws-cdk issues about using IPAM pool id in the ec2.vpc L2 construct : | ||
https://github.com/aws/aws-cdk/issues/21333 | ||
|
||
The deployement provides a VPC with 2 subnets ( private and public ) on each availability zone ( you could customize your VPC by modifying the vpc_stack.py code ) | ||
The Ipam_stack have a cidr_range for the top level ipam pool as "11.0.0.0/8" and "11.0.0.0/12" for the region ipam pool you can modify the two variable in app.py | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tell the user what the value of this is. Tell the user what they should be able to see/do with this. how can the customer see the two VPCs in each AZ?. |
||
The `cdk.json` file tells the CDK Toolkit how to execute your app. | ||
|
||
This project is set up like a standard Python project. The initialization | ||
process also creates a virtualenv within this project, stored under the `.venv` | ||
directory. To create the virtualenv it assumes that there is a `python3` | ||
(or `python` for Windows) executable in your path with access to the `venv` | ||
package. If for any reason the automatic creation of the virtualenv fails, | ||
you can create the virtualenv manually. | ||
|
||
To manually create a virtualenv on MacOS and Linux: | ||
|
||
``` | ||
$ python3 -m venv .venv | ||
``` | ||
|
||
After the init process completes and the virtualenv is created, you can use the following | ||
step to activate your virtualenv. | ||
|
||
``` | ||
$ source .venv/bin/activate | ||
``` | ||
|
||
If you are a Windows platform, you would activate the virtualenv like this: | ||
|
||
``` | ||
% .venv\Scripts\activate.bat | ||
``` | ||
|
||
Once the virtualenv is activated, you can install the required dependencies. | ||
|
||
``` | ||
$ pip install -r requirements.txt | ||
``` | ||
|
||
At this point you can now synthesize the CloudFormation template for this code. | ||
|
||
``` | ||
$ cdk synth | ||
``` | ||
|
||
To add additional dependencies, for example other CDK libraries, just add | ||
them to your `setup.py` file and rerun the `pip install -r requirements.txt` | ||
command. | ||
|
||
## Useful commands | ||
|
||
* `cdk ls` list all stacks in the app | ||
* `cdk synth` emits the synthesized CloudFormation template | ||
* `cdk deploy "*" --require-approval never --outputs-file ./cdk-exports.json` deploy this stack to your default AWS account/region | ||
* `cdk diff` compare deployed stack with current state | ||
* `cdk docs` open CDK documentation | ||
* `cat cdk-exports.json | grep ipamid | cut -d\" -f4 | tr -d "\n" > ipamdelid` to destroy the ipam without wainting for release | ||
* `aws ec2 delete-ipam --cascade --ipam-id $(<ipamdelid)` to destroy the ipam without wainting for release | ||
* `cdk destroy "*"` to destroy all the stacks | ||
|
||
Enjoy! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
#!/usr/bin/env python3 | ||
import os | ||
|
||
import aws_cdk as cdk | ||
|
||
from ipam_l2_vpc.ipam_stack import IpamStack | ||
from ipam_l2_vpc.vpc_stack import VpcStack | ||
|
||
cidr_range = "11.0.0.0/8" | ||
region_cidr_range = "11.0.0.0/12" | ||
|
||
app = cdk.App() | ||
|
||
|
||
# IPAM stack | ||
IPAM_stack = IpamStack(app, "IPAM-iac", | ||
cidr_range = cidr_range, | ||
region_cidr_range = region_cidr_range, | ||
env=cdk.Environment( | ||
account=os.environ["CDK_DEFAULT_ACCOUNT"], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will result in a KeyError if this environment variable is not set. Handle this better: |
||
region=os.environ["CDK_DEFAULT_REGION"])) | ||
|
||
# VPC stack | ||
vpc_stack_1 = VpcStack(app, "VPC-iac-1", | ||
env=cdk.Environment( | ||
account=os.environ["CDK_DEFAULT_ACCOUNT"], | ||
region=os.environ["CDK_DEFAULT_REGION"])) | ||
|
||
# VPC stack | ||
vpc_stack_2 = VpcStack(app, "VPC-iac-2", | ||
env=cdk.Environment( | ||
account=os.environ["CDK_DEFAULT_ACCOUNT"], | ||
region=os.environ["CDK_DEFAULT_REGION"])) | ||
|
||
|
||
vpc_stack_1.add_dependency(IPAM_stack) | ||
vpc_stack_2.add_dependency(IPAM_stack) | ||
|
||
app.synth() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
{ | ||
"app": "python3 app.py", | ||
"watch": { | ||
"include": [ | ||
"**" | ||
], | ||
"exclude": [ | ||
"README.md", | ||
"cdk*.json", | ||
"requirements*.txt", | ||
"source.bat", | ||
"**/__init__.py", | ||
"python/__pycache__", | ||
"tests" | ||
] | ||
}, | ||
"context": { | ||
"@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, | ||
"@aws-cdk/core:stackRelativeExports": true, | ||
"@aws-cdk/aws-rds:lowercaseDbIdentifier": true, | ||
"@aws-cdk/aws-lambda:recognizeVersionProps": true, | ||
"@aws-cdk/aws-lambda:recognizeLayerVersion": true, | ||
"@aws-cdk/aws-cloudfront:defaultSecurityPolicyTLSv1.2_2021": true, | ||
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, | ||
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, | ||
"@aws-cdk/core:checkSecretUsage": true, | ||
"@aws-cdk/aws-iam:minimizePolicies": true, | ||
"@aws-cdk/core:validateSnapshotRemovalPolicy": true, | ||
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, | ||
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, | ||
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, | ||
"@aws-cdk/core:target-partitions": [ | ||
"aws", | ||
"aws-cn" | ||
] | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
from constructs import Construct | ||
from aws_cdk import ( | ||
CfnOutput, | ||
Stack, | ||
aws_ec2 as ec2, | ||
) | ||
|
||
|
||
class IpamStack(Stack): | ||
|
||
def __init__(self, scope: Construct, construct_id: str,cidr_range : str,region_cidr_range : str, **kwargs) -> None: | ||
super().__init__(scope, construct_id, **kwargs) | ||
|
||
# The code that defines your stack goes here | ||
|
||
|
||
# Ipam creation | ||
cfn_iPAM = ec2.CfnIPAM(self, "MyCfnIPAM", | ||
description="description", | ||
operating_regions=[ec2.CfnIPAM.IpamOperatingRegionProperty(region_name=self.region)] | ||
) | ||
|
||
# Top level ipam pool creation used by accounts or regions | ||
cfn_Top_IpamPool = ec2.CfnIPAMPool(self, "TOP-CfnIPAMPool", | ||
address_family="ipv4", | ||
ipam_scope_id=cfn_iPAM.attr_private_default_scope_id, | ||
|
||
description="top-level-pool", | ||
provisioned_cidrs=[ec2.CfnIPAMPool.ProvisionedCidrProperty( | ||
cidr=cidr_range | ||
)], | ||
) | ||
|
||
# region level ipam pool used by regions | ||
|
||
cfn_Region_iPAMPool = ec2.CfnIPAMPool(self, "Local-CfnIPAMPool", | ||
address_family="ipv4", | ||
ipam_scope_id=cfn_iPAM.attr_private_default_scope_id, | ||
source_ipam_pool_id=cfn_Top_IpamPool.attr_ipam_pool_id, | ||
|
||
description="region-level-pool", | ||
locale=self.region, | ||
provisioned_cidrs=[ec2.CfnIPAMPool.ProvisionedCidrProperty( | ||
cidr=region_cidr_range | ||
)], | ||
auto_import= True | ||
|
||
) | ||
|
||
|
||
# dependencies | ||
cfn_Region_iPAMPool.add_depends_on(cfn_Top_IpamPool) | ||
cfn_Top_IpamPool.add_depends_on(cfn_iPAM) | ||
|
||
# output ressources | ||
ipam_pool_id = cfn_Region_iPAMPool.attr_ipam_pool_id | ||
self.ipam_pool_id = ipam_pool_id | ||
|
||
|
||
CfnOutput(self,"ipampoolid",value=ipam_pool_id,description="Ipam pool id",export_name="Poolid") | ||
CfnOutput(self,"ipamid",value=cfn_iPAM.attr_ipam_id,description="Ipamid",export_name="ipamid") |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
import math | ||
from constructs import Construct | ||
from aws_cdk import ( | ||
|
||
Fn, | ||
Stack, | ||
aws_ec2 as ec2, | ||
|
||
) | ||
|
||
|
||
class VpcStack(Stack): | ||
|
||
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: | ||
super().__init__(scope, construct_id, **kwargs) | ||
|
||
# The code that defines your stack goes here | ||
|
||
max_azs = 2 | ||
self.vpc = ec2.Vpc(self, 'VPC', | ||
max_azs = max_azs, | ||
enable_dns_hostnames = True, | ||
enable_dns_support = True, | ||
# configuration will create 2 subnets in a 2 AZ. | ||
subnet_configuration=[ | ||
ec2.SubnetConfiguration( | ||
name = 'Public-Subnet', | ||
subnet_type = ec2.SubnetType.PUBLIC, | ||
), | ||
ec2.SubnetConfiguration( | ||
name = 'Private-Subnet', | ||
subnet_type = ec2.SubnetType.PRIVATE_WITH_NAT, | ||
) | ||
], | ||
nat_gateways = 2, | ||
nat_gateway_subnets=ec2.SubnetSelection(subnet_group_name="Public-Subnet"), | ||
|
||
) | ||
|
||
#import the pool id | ||
Ipam_pool_id = Fn.import_value("Poolid") | ||
Ipv4NetmaskLength = 16 | ||
Ipv4NetmaskLength = Ipv4NetmaskLength.numerator | ||
# Override vpc construct | ||
cfn_vpc: ec2.CfnVPC = self.vpc.node.default_child | ||
cfn_vpc.add_property_deletion_override("CidrBlock") | ||
cfn_vpc.add_property_override("Ipv4IpamPoolId", Ipam_pool_id) | ||
cfn_vpc.add_property_override("Ipv4NetmaskLength", Ipv4NetmaskLength) | ||
|
||
# make sure to have dynamically the max division of subnets from vpc cidr block (it's just a choice you could use your own division) | ||
# if N is a power of two simply return it | ||
N = max_azs*2 | ||
if not (N & (N - 1)): | ||
size_max = str(32-(Ipv4NetmaskLength+int(math.log2(N)))) | ||
else : | ||
# else set only the left bit of most significant bit | ||
size_max = str(32-(Ipv4NetmaskLength+int(math.log2(int("1" + (len(bin(N)) - 2) * "0", 2))))) | ||
|
||
# count=max_azs*2 the *2 because I create 2 subnets in each Availability zone | ||
cidrs_subnet = Fn.cidr(ip_block=cfn_vpc.attr_cidr_block,count=max_azs*2,size_mask= size_max) | ||
|
||
# cidrs_subnet is a list of subnets cidr we could allocate by overriding as much as we need | ||
|
||
# i is the number of availability zone | ||
for i in range(max_azs) : | ||
cidr_pub_subnet = Fn.select(i,cidrs_subnet) | ||
cfn_subnet: ec2.CfnSubnet = self.vpc.public_subnets[i].node.default_child | ||
cfn_subnet.add_property_deletion_override("CidrBlock") | ||
cfn_subnet.add_property_override("CidrBlock", cidr_pub_subnet) | ||
|
||
cidr_priv_subnet = Fn.select(i+max_azs,cidrs_subnet) | ||
cfn_subnet: ec2.CfnSubnet = self.vpc.private_subnets[i].node.default_child | ||
cfn_subnet.add_property_deletion_override("CidrBlock") | ||
cfn_subnet.add_property_override("CidrBlock", cidr_priv_subnet) | ||
|
||
|
||
# Security group creation | ||
self.VpcSG = ec2.SecurityGroup(self,'VPC-sg',vpc=self.vpc,allow_all_outbound=True,security_group_name='VPC4sg') | ||
self.VpcSG.add_ingress_rule(peer=ec2.Peer.any_ipv4(),connection=ec2.Port.all_traffic()) | ||
|
||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pytest==6.2.5 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
aws-cdk-lib==2.35.0 | ||
constructs>=10.0.0,<11.0.0 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
@echo off | ||
|
||
rem The sole purpose of this script is to make the command | ||
rem | ||
rem source .venv/bin/activate | ||
rem | ||
rem (which activates a Python virtualenv on Linux or Mac OS X) work on Windows. | ||
rem On Windows, this command just runs this batch file (the argument is ignored). | ||
rem | ||
rem Now we don't need to document a Windows command for activating a virtualenv. | ||
|
||
echo Executing .venv\Scripts\activate.bat for you | ||
.venv\Scripts\activate.bat |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
{ | ||
"devDependencies": { | ||
"aws-cdk": "^2.0.0-rc.32" | ||
"aws-cdk": "^2.35.0" | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.