Skip to content
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

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions python/Ipam-L2-vpc/.gitignore
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
67 changes: 67 additions & 0 deletions python/Ipam-L2-vpc/README.md
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
Comment on lines +4 to +7
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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
This project demonstrates using IPAM pool in Amazon EC2 VPCs.


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

Copy link
Contributor

Choose a reason for hiding this comment

The 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!
39 changes: 39 additions & 0 deletions python/Ipam-L2-vpc/app.py
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"],
Copy link
Contributor

Choose a reason for hiding this comment

The 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: os.getenv(k, default) does not throw an exception and provides a default value.

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()
37 changes: 37 additions & 0 deletions python/Ipam-L2-vpc/cdk.json
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"
]
}
}
Empty file.
61 changes: 61 additions & 0 deletions python/Ipam-L2-vpc/ipam_l2_vpc/ipam_stack.py
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")
83 changes: 83 additions & 0 deletions python/Ipam-L2-vpc/ipam_l2_vpc/vpc_stack.py
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())




1 change: 1 addition & 0 deletions python/Ipam-L2-vpc/requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pytest==6.2.5
2 changes: 2 additions & 0 deletions python/Ipam-L2-vpc/requirements.txt
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
13 changes: 13 additions & 0 deletions python/Ipam-L2-vpc/source.bat
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
2 changes: 1 addition & 1 deletion python/package.json
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"
}
}