Blog
Wie Sie mit Rubycfn eine vollständige VPC mit automatischer Subnetzberechnung erstellen

In diesem Blogbeitrag wird beschrieben, wie Sie mit Rubycfn eine vollständige VPC erstellen können, einschließlich der Berechnung von Subnetzen mit Hilfe der intrinsischen Funktion Fn::Cidr, eines Internet-Gateways, Routentabellen, Routen und Subnetzzuordnungen. Das Produkt dieses Rubycfn-Skripts ist eine CloudFormation-Vorlage, die Sie bereitstellen können. Besuchen Sie Rubycfn unter dennisvink - rubycfn oder probieren Sie den Online-Rubycfn-Compiler unter rubycfn.com/
Einführung
Rubycfn ist eine CloudFormation-Abstraktionsschicht und ein Tool zur Orchestrierung der Bereitstellung. In diesem Blogbeitrag zeige ich Ihnen, wie Sie mit Rubycfn eine VPC erstellen können, für die alle Subnetz-CIDRs automatisch berechnet und alle abhängigen Ressourcen für Sie erstellt werden. Das Ergebnis ist eine CloudFormation-Vorlage, mit der Sie in wenigen Minuten eine komplette VPC einrichten können.
Voraussetzungen
Sie müssen ein ruby installiert haben. Außerdem müssen Sie das Gem rubycfn installiert haben: gem install rubycfn
Das Drehbuch
Speichern Sie das folgende Skript unter vpc.rb oder einem anderen geeigneten Namen. Geben Sie dann ein:
Standardmäßig generiert das Skript eine VPC mit einem CIDR-Block von 10.0.0.0/16. Wenn Sie den CIDR-Block ändern möchten, geben Sie einfach ein:
export VPC_CIDR_BLOCK="10.100.0.0/16"
cat vpc.rb | rubycfn
Hier ist das vollständige Skript:
# Definition of subnets to create. The offset must be unique.
def subnets
[
{
"es_private": {
"owner": "binx",
"public": false,
"offset": 1
}
},
{
"ec2_public": {
"owner": "binx",
"public": true,
"offset": 2
}
},
{
"ec2_private": {
"owner": "binx",
"public": false,
"offset": 3
}
},
{
"bastion_public": {
"owner": "binx",
"public": true,
"offset": 4
}
}
]
end
# export VPC_CIDR_BLOCK to desired CIDR range.
# Defaults to 10.0.0.0/16.
variable :cidr_block,
default: "10.0.0.0/16",
value: ENV["VPC_CIDR_BLOCK"]
# Set the Stack content
content "Rubycfn Generated VPC Stack (#{cidr_block})"
# Create the VPC
resource :vpc,
type: "AWS::EC2::VPC" do |r|
r.property(:cidr_block) { cidr_block }
r.property(:enable_dns_support) { true }
r.property(:enable_dns_hostnames) { true }
end
# Create the Internet Gateway
resource :internet_gateway,
type: "AWS::EC2::InternetGateway"
# Create route
resource :route,
type: "AWS::EC2::Route" do |r|
r.property(:destination_cidr_block) { "0.0.0.0/0" }
r.property(:gateway_id) { :internet_gateway.ref }
r.property(:route_table_id) { :route_table.ref }
end
# Create and tag route table
resource :route_table,
type: "AWS::EC2::RouteTable" do |r|
r.property(:vpc_id) { :vpc.ref }
r.property(:tags) do
[
{
"Key": "Environment",
"Value": "VPC Route Table"
}
]
end
end
# Attach the VPC to the Gateway
resource :vpc_gateway_attachment,
type: "AWS::EC2::VPCGatewayAttachment" do |r|
r.depends_on %w(Vpc)
r.property(:internet_gateway_id) { :internet_gateway.ref }
r.property(:vpc_id) { :vpc.ref }
end
# Create 3 subnets for each defined subnet (1 per AZ)
subnets.each_with_index do |subnet, _subnet_count|
subnet.each do |subnet_name, arguments|
resource "#{subnet_name}_subnet".cfnize,
type: "AWS::EC2::Subnet",
amount: 3 do |r, index|
r.property(:availability_zone) do
{
"Fn::GetAZs": ""
}.fnselect(index)
end
r.property(:cidr_block) do
[
:vpc.ref("CidrBlock"),
(3 * arguments[:offset]).to_s,
(Math.log(256) / Math.log(2)).floor.to_s
].fncidr.fnselect(index + (3 * arguments[:offset]) - 3)
end
r.property(:map_public_ip_on_launch) { arguments[:public] }
r.property(:tags) do
[
{
"Key": "owner",
"Value": arguments[:owner].to_s.cfnize
},
{
"Key": "resource_type",
"Value": subnet_name.to_s.cfnize
}
]
end
r.property(:vpc_id) { :vpc.ref }
end
# Create subnet route table associations
resource "#{subnet_name}_subnet_route_table_association".cfnize,
amount: 3,
type: "AWS::EC2::SubnetRouteTableAssociation" do |r, index|
r.property(:route_table_id) { :route_table.ref }
r.property(:subnet_id) { "#{subnet_name}_subnet#{index.zero? && "" || index + 1}".cfnize.ref }
end
# Generate outputs for these subnets
3.times do |i|
output "#{subnet_name}_subnet#{i.positive? ? (i + 1) : ""}_name".cfnize,
value: "#{subnet_name}_subnet#{i.positive? ? (i + 1) : ""}".cfnize.ref
end
end
end
# Output the VPC CIDR range and VPC Id
output :vpc_cidr,
value: :vpc.ref("CidrBlock)
output :vpc_id,
value: :vpc.ref
Subnetz-Definitionen
Dieses Skript generiert Subnetze für 4 Dienste: 3 private Subnetze für
ElasticSearch, 3 öffentliche Subnetze für EC2, 3 private Subnetze für EC2 und 3 öffentliche
Subnetze für einen Bastion-Host.
Die Eigenschaft owner markiert die erstellten Subnetze mit owner als Schlüssel und binx
als Wert.
public kann entweder auf true oder false gesetzt werden, um anzugeben, ob die in das Subnetz gestarteten Dienste
direkt aus dem Internet erreichbar sein sollen oder nicht.
offset muss für jedes definierte Subnetz eindeutig sein. Er wird verwendet, um den
CIDR-Bereich für die Subnetze zu berechnen.
def subnets
[
{
"es_private": {
"owner": "binx",
"public": false,
"offset": 1
}
},
{
"ec2_public": {
"owner": "binx",
"public": true,
"offset": 2
}
},
{
"ec2_private": {
"owner": "binx",
"public": false,
"offset": 3
}
},
{
"bastion_public": {
"owner": "binx",
"public": true,
"offset": 4
}
}
]
end
Resultierende CloudFormation-Vorlage
Das Artefakt dieses Rubycfn-Skripts ist eine CloudFormation-Vorlage, die deutlich größer und weit weniger freundlich für das menschliche Auge ist.
Angesichts der Standard-CIDR von 10.0.0.0/16 sieht die resultierende Vorlage wie folgt aus:
{
"AWSTemplateFormatVersion": "2010-09-09",
"content": "Rubycfn Generated VPC Stack (10.0.0.0/16)",
"Resources": {
"BastionPublicSubnet": {
"Properties": {
"AvailabilityZone": {
"Fn::Select": [
0,
{
"Fn::GetAZs": ""
}
]
},
"CidrBlock": {
"Fn::Select": [
9,
{
"Fn::Cidr": [
{
"Fn::GetAtt": [
"Vpc",
"CidrBlock"
]
},
"12",
"8"
]
}
]
},
"MapPublicIpOnLaunch": true,
"Tags": [
{
"Key": "owner",
"Value": "Binx"
},
{
"Key": "resource_type",
"Value": "BastionPublic"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::Subnet"
},
"BastionPublicSubnet2": {
"Properties": {
"AvailabilityZone": {
"Fn::Select": [
1,
{
"Fn::GetAZs": ""
}
]
},
"CidrBlock": {
"Fn::Select": [
10,
{
"Fn::Cidr": [
{
"Fn::GetAtt": [
"Vpc",
"CidrBlock"
]
},
"12",
"8"
]
}
]
},
"MapPublicIpOnLaunch": true,
"Tags": [
{
"Key": "owner",
"Value": "Binx"
},
{
"Key": "resource_type",
"Value": "BastionPublic"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::Subnet"
},
"BastionPublicSubnet3": {
"Properties": {
"AvailabilityZone": {
"Fn::Select": [
2,
{
"Fn::GetAZs": ""
}
]
},
"CidrBlock": {
"Fn::Select": [
11,
{
"Fn::Cidr": [
{
"Fn::GetAtt": [
"Vpc",
"CidrBlock"
]
},
"12",
"8"
]
}
]
},
"MapPublicIpOnLaunch": true,
"Tags": [
{
"Key": "owner",
"Value": "Binx"
},
{
"Key": "resource_type",
"Value": "BastionPublic"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::Subnet"
},
"BastionPublicSubnetRouteTableAssociation": {
"Properties": {
"RouteTableId": {
"Ref": "RouteTable"
},
"SubnetId": {
"Ref": "BastionPublicSubnet"
}
},
"Type": "AWS::EC2::SubnetRouteTableAssociation"
},
"BastionPublicSubnetRouteTableAssociation2": {
"Properties": {
"RouteTableId": {
"Ref": "RouteTable"
},
"SubnetId": {
"Ref": "BastionPublicSubnet2"
}
},
"Type": "AWS::EC2::SubnetRouteTableAssociation"
},
"BastionPublicSubnetRouteTableAssociation3": {
"Properties": {
"RouteTableId": {
"Ref": "RouteTable"
},
"SubnetId": {
"Ref": "BastionPublicSubnet3"
}
},
"Type": "AWS::EC2::SubnetRouteTableAssociation"
},
"Ec2PrivateSubnet": {
"Properties": {
"AvailabilityZone": {
"Fn::Select": [
0,
{
"Fn::GetAZs": ""
}
]
},
"CidrBlock": {
"Fn::Select": [
6,
{
"Fn::Cidr": [
{
"Fn::GetAtt": [
"Vpc",
"CidrBlock"
]
},
"9",
"8"
]
}
]
},
"MapPublicIpOnLaunch": false,
"Tags": [
{
"Key": "owner",
"Value": "Binx"
},
{
"Key": "resource_type",
"Value": "Ec2Private"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::Subnet"
},
"Ec2PrivateSubnet2": {
"Properties": {
"AvailabilityZone": {
"Fn::Select": [
1,
{
"Fn::GetAZs": ""
}
]
},
"CidrBlock": {
"Fn::Select": [
7,
{
"Fn::Cidr": [
{
"Fn::GetAtt": [
"Vpc",
"CidrBlock"
]
},
"9",
"8"
]
}
]
},
"MapPublicIpOnLaunch": false,
"Tags": [
{
"Key": "owner",
"Value": "Binx"
},
{
"Key": "resource_type",
"Value": "Ec2Private"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::Subnet"
},
"Ec2PrivateSubnet3": {
"Properties": {
"AvailabilityZone": {
"Fn::Select": [
2,
{
"Fn::GetAZs": ""
}
]
},
"CidrBlock": {
"Fn::Select": [
8,
{
"Fn::Cidr": [
{
"Fn::GetAtt": [
"Vpc",
"CidrBlock"
]
},
"9",
"8"
]
}
]
},
"MapPublicIpOnLaunch": false,
"Tags": [
{
"Key": "owner",
"Value": "Binx"
},
{
"Key": "resource_type",
"Value": "Ec2Private"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::Subnet"
},
"Ec2PrivateSubnetRouteTableAssociation": {
"Properties": {
"RouteTableId": {
"Ref": "RouteTable"
},
"SubnetId": {
"Ref": "Ec2PrivateSubnet"
}
},
"Type": "AWS::EC2::SubnetRouteTableAssociation"
},
"Ec2PrivateSubnetRouteTableAssociation2": {
"Properties": {
"RouteTableId": {
"Ref": "RouteTable"
},
"SubnetId": {
"Ref": "Ec2PrivateSubnet2"
}
},
"Type": "AWS::EC2::SubnetRouteTableAssociation"
},
"Ec2PrivateSubnetRouteTableAssociation3": {
"Properties": {
"RouteTableId": {
"Ref": "RouteTable"
},
"SubnetId": {
"Ref": "Ec2PrivateSubnet3"
}
},
"Type": "AWS::EC2::SubnetRouteTableAssociation"
},
"Ec2PublicSubnet": {
"Properties": {
"AvailabilityZone": {
"Fn::Select": [
0,
{
"Fn::GetAZs": ""
}
]
},
"CidrBlock": {
"Fn::Select": [
3,
{
"Fn::Cidr": [
{
"Fn::GetAtt": [
"Vpc",
"CidrBlock"
]
},
"6",
"8"
]
}
]
},
"MapPublicIpOnLaunch": true,
"Tags": [
{
"Key": "owner",
"Value": "Binx"
},
{
"Key": "resource_type",
"Value": "Ec2Public"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::Subnet"
},
"Ec2PublicSubnet2": {
"Properties": {
"AvailabilityZone": {
"Fn::Select": [
1,
{
"Fn::GetAZs": ""
}
]
},
"CidrBlock": {
"Fn::Select": [
4,
{
"Fn::Cidr": [
{
"Fn::GetAtt": [
"Vpc",
"CidrBlock"
]
},
"6",
"8"
]
}
]
},
"MapPublicIpOnLaunch": true,
"Tags": [
{
"Key": "owner",
"Value": "Binx"
},
{
"Key": "resource_type",
"Value": "Ec2Public"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::Subnet"
},
"Ec2PublicSubnet3": {
"Properties": {
"AvailabilityZone": {
"Fn::Select": [
2,
{
"Fn::GetAZs": ""
}
]
},
"CidrBlock": {
"Fn::Select": [
5,
{
"Fn::Cidr": [
{
"Fn::GetAtt": [
"Vpc",
"CidrBlock"
]
},
"6",
"8"
]
}
]
},
"MapPublicIpOnLaunch": true,
"Tags": [
{
"Key": "owner",
"Value": "Binx"
},
{
"Key": "resource_type",
"Value": "Ec2Public"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::Subnet"
},
"Ec2PublicSubnetRouteTableAssociation": {
"Properties": {
"RouteTableId": {
"Ref": "RouteTable"
},
"SubnetId": {
"Ref": "Ec2PublicSubnet"
}
},
"Type": "AWS::EC2::SubnetRouteTableAssociation"
},
"Ec2PublicSubnetRouteTableAssociation2": {
"Properties": {
"RouteTableId": {
"Ref": "RouteTable"
},
"SubnetId": {
"Ref": "Ec2PublicSubnet2"
}
},
"Type": "AWS::EC2::SubnetRouteTableAssociation"
},
"Ec2PublicSubnetRouteTableAssociation3": {
"Properties": {
"RouteTableId": {
"Ref": "RouteTable"
},
"SubnetId": {
"Ref": "Ec2PublicSubnet3"
}
},
"Type": "AWS::EC2::SubnetRouteTableAssociation"
},
"EsPrivateSubnet": {
"Properties": {
"AvailabilityZone": {
"Fn::Select": [
0,
{
"Fn::GetAZs": ""
}
]
},
"CidrBlock": {
"Fn::Select": [
0,
{
"Fn::Cidr": [
{
"Fn::GetAtt": [
"Vpc",
"CidrBlock"
]
},
"3",
"8"
]
}
]
},
"MapPublicIpOnLaunch": false,
"Tags": [
{
"Key": "owner",
"Value": "Binx"
},
{
"Key": "resource_type",
"Value": "EsPrivate"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::Subnet"
},
"EsPrivateSubnet2": {
"Properties": {
"AvailabilityZone": {
"Fn::Select": [
1,
{
"Fn::GetAZs": ""
}
]
},
"CidrBlock": {
"Fn::Select": [
1,
{
"Fn::Cidr": [
{
"Fn::GetAtt": [
"Vpc",
"CidrBlock"
]
},
"3",
"8"
]
}
]
},
"MapPublicIpOnLaunch": false,
"Tags": [
{
"Key": "owner",
"Value": "Binx"
},
{
"Key": "resource_type",
"Value": "EsPrivate"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::Subnet"
},
"EsPrivateSubnet3": {
"Properties": {
"AvailabilityZone": {
"Fn::Select": [
2,
{
"Fn::GetAZs": ""
}
]
},
"CidrBlock": {
"Fn::Select": [
2,
{
"Fn::Cidr": [
{
"Fn::GetAtt": [
"Vpc",
"CidrBlock"
]
},
"3",
"8"
]
}
]
},
"MapPublicIpOnLaunch": false,
"Tags": [
{
"Key": "owner",
"Value": "Binx"
},
{
"Key": "resource_type",
"Value": "EsPrivate"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::Subnet"
},
"EsPrivateSubnetRouteTableAssociation": {
"Properties": {
"RouteTableId": {
"Ref": "RouteTable"
},
"SubnetId": {
"Ref": "EsPrivateSubnet"
}
},
"Type": "AWS::EC2::SubnetRouteTableAssociation"
},
"EsPrivateSubnetRouteTableAssociation2": {
"Properties": {
"RouteTableId": {
"Ref": "RouteTable"
},
"SubnetId": {
"Ref": "EsPrivateSubnet2"
}
},
"Type": "AWS::EC2::SubnetRouteTableAssociation"
},
"EsPrivateSubnetRouteTableAssociation3": {
"Properties": {
"RouteTableId": {
"Ref": "RouteTable"
},
"SubnetId": {
"Ref": "EsPrivateSubnet3"
}
},
"Type": "AWS::EC2::SubnetRouteTableAssociation"
},
"InternetGateway": {
"Type": "AWS::EC2::InternetGateway"
},
"Route": {
"Properties": {
"DestinationCidrBlock": "0.0.0.0/0",
"GatewayId": {
"Ref": "InternetGateway"
},
"RouteTableId": {
"Ref": "RouteTable"
}
},
"Type": "AWS::EC2::Route"
},
"RouteTable": {
"Properties": {
"Tags": [
{
"Key": "Environment",
"Value": "VPC Route Table"
}
],
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::RouteTable"
},
"Vpc": {
"Properties": {
"CidrBlock": "10.0.0.0/16",
"EnableDnsHostnames": true,
"EnableDnsSupport": true
},
"Type": "AWS::EC2::VPC"
},
"VpcGatewayAttachment": {
"DependsOn": [
"Vpc"
],
"Properties": {
"InternetGatewayId": {
"Ref": "InternetGateway"
},
"VpcId": {
"Ref": "Vpc"
}
},
"Type": "AWS::EC2::VPCGatewayAttachment"
}
},
"Outputs": {
"BastionPublicSubnet2Name": {
"Value": {
"Ref": "BastionPublicSubnet2"
}
},
"BastionPublicSubnet3Name": {
"Value": {
"Ref": "BastionPublicSubnet3"
}
},
"BastionPublicSubnetName": {
"Value": {
"Ref": "BastionPublicSubnet"
}
},
"Ec2PrivateSubnet2Name": {
"Value": {
"Ref": "Ec2PrivateSubnet2"
}
},
"Ec2PrivateSubnet3Name": {
"Value": {
"Ref": "Ec2PrivateSubnet3"
}
},
"Ec2PrivateSubnetName": {
"Value": {
"Ref": "Ec2PrivateSubnet"
}
},
"Ec2PublicSubnet2Name": {
"Value": {
"Ref": "Ec2PublicSubnet2"
}
},
"Ec2PublicSubnet3Name": {
"Value": {
"Ref": "Ec2PublicSubnet3"
}
},
"Ec2PublicSubnetName": {
"Value": {
"Ref": "Ec2PublicSubnet"
}
},
"EsPrivateSubnet2Name": {
"Value": {
"Ref": "EsPrivateSubnet2"
}
},
"EsPrivateSubnet3Name": {
"Value": {
"Ref": "EsPrivateSubnet3"
}
},
"EsPrivateSubnetName": {
"Value": {
"Ref": "EsPrivateSubnet"
}
},
"VpcCidr": {
"Value": {
"Fn::GetAtt": [
"Vpc",
"CidrBlock"
]
}
},
"VpcId": {
"Value": {
"Ref": "Vpc"
}
}
}
}
Verfasst von

Dennis Vink
Crafting digital leaders through innovative AI & cloud solutions. Empowering businesses with cutting-edge strategies for growth and transformation.
Unsere Ideen
Weitere Blogs
Contact



