UNPKG

cfn-forge

Version:

CloudFormation deployment automation tool with git-based workflows

592 lines (513 loc) 17.9 kB
name: vpc-network description: Complete VPC setup with public/private subnets, NAT Gateway, and security groups version: 1.0.0 author: CFN-Forge cfnForgeVersion: 1.0.0 variables: projectName: prompt: "Project name" default: "my-vpc-network" description: prompt: "Project description" default: "VPC network infrastructure for AWS applications" vpcCidr: prompt: "VPC CIDR block" default: "10.0.0.0/16" enableNatGateway: prompt: "Enable NAT Gateway for private subnets?" default: "true" type: "boolean" multiAz: prompt: "Deploy across multiple Availability Zones?" default: "true" type: "boolean" files: include: - "**/*" exclude: - "template.yaml" - ".git/**" structure: cloudformation.yaml: | AWSTemplateFormatVersion: '2010-09-09' Description: {{description}} Parameters: Environment: Type: String Default: dev AllowedValues: [dev, staging, prod] Description: Environment name Conditions: EnableNatGateway: !Equals ["{{enableNatGateway}}", "true"] MultiAZ: !Equals ["{{multiAz}}", "true"] CreateSecondAZ: !And [!Condition MultiAZ, !Not [!Equals [!Select [1, !GetAZs ""], ""]]] Resources: # VPC VPC: Type: AWS::EC2::VPC Properties: CidrBlock: {{vpcCidr}} EnableDnsHostnames: true EnableDnsSupport: true Tags: - Key: Name Value: !Sub "${AWS::StackName}-vpc" - Key: Environment Value: !Ref Environment # Internet Gateway InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub "${AWS::StackName}-igw" InternetGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: InternetGatewayId: !Ref InternetGateway VpcId: !Ref VPC # Public Subnets PublicSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [0, !GetAZs ""] CidrBlock: !Select [0, !Cidr [{{vpcCidr}}, 6, 8]] MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub "${AWS::StackName}-public-subnet-1" - Key: Type Value: Public PublicSubnet2: Type: AWS::EC2::Subnet Condition: CreateSecondAZ Properties: VpcId: !Ref VPC AvailabilityZone: !Select [1, !GetAZs ""] CidrBlock: !Select [1, !Cidr [{{vpcCidr}}, 6, 8]] MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Sub "${AWS::StackName}-public-subnet-2" - Key: Type Value: Public # Private Subnets PrivateSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [0, !GetAZs ""] CidrBlock: !Select [2, !Cidr [{{vpcCidr}}, 6, 8]] Tags: - Key: Name Value: !Sub "${AWS::StackName}-private-subnet-1" - Key: Type Value: Private PrivateSubnet2: Type: AWS::EC2::Subnet Condition: CreateSecondAZ Properties: VpcId: !Ref VPC AvailabilityZone: !Select [1, !GetAZs ""] CidrBlock: !Select [3, !Cidr [{{vpcCidr}}, 6, 8]] Tags: - Key: Name Value: !Sub "${AWS::StackName}-private-subnet-2" - Key: Type Value: Private # Database Subnets DatabaseSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC AvailabilityZone: !Select [0, !GetAZs ""] CidrBlock: !Select [4, !Cidr [{{vpcCidr}}, 6, 8]] Tags: - Key: Name Value: !Sub "${AWS::StackName}-database-subnet-1" - Key: Type Value: Database DatabaseSubnet2: Type: AWS::EC2::Subnet Condition: CreateSecondAZ Properties: VpcId: !Ref VPC AvailabilityZone: !Select [1, !GetAZs ""] CidrBlock: !Select [5, !Cidr [{{vpcCidr}}, 6, 8]] Tags: - Key: Name Value: !Sub "${AWS::StackName}-database-subnet-2" - Key: Type Value: Database # NAT Gateway NatGateway1EIP: Type: AWS::EC2::EIP Condition: EnableNatGateway DependsOn: InternetGatewayAttachment Properties: Domain: vpc Tags: - Key: Name Value: !Sub "${AWS::StackName}-nat-eip-1" NatGateway2EIP: Type: AWS::EC2::EIP Condition: !And [EnableNatGateway, CreateSecondAZ] DependsOn: InternetGatewayAttachment Properties: Domain: vpc Tags: - Key: Name Value: !Sub "${AWS::StackName}-nat-eip-2" NatGateway1: Type: AWS::EC2::NatGateway Condition: EnableNatGateway Properties: AllocationId: !GetAtt NatGateway1EIP.AllocationId SubnetId: !Ref PublicSubnet1 Tags: - Key: Name Value: !Sub "${AWS::StackName}-nat-1" NatGateway2: Type: AWS::EC2::NatGateway Condition: !And [EnableNatGateway, CreateSecondAZ] Properties: AllocationId: !GetAtt NatGateway2EIP.AllocationId SubnetId: !Ref PublicSubnet2 Tags: - Key: Name Value: !Sub "${AWS::StackName}-nat-2" # Route Tables PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${AWS::StackName}-public-routes" DefaultPublicRoute: Type: AWS::EC2::Route DependsOn: InternetGatewayAttachment Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway PublicSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PublicRouteTable SubnetId: !Ref PublicSubnet1 PublicSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Condition: CreateSecondAZ Properties: RouteTableId: !Ref PublicRouteTable SubnetId: !Ref PublicSubnet2 PrivateRouteTable1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${AWS::StackName}-private-routes-1" DefaultPrivateRoute1: Type: AWS::EC2::Route Condition: EnableNatGateway Properties: RouteTableId: !Ref PrivateRouteTable1 DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway1 PrivateSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: RouteTableId: !Ref PrivateRouteTable1 SubnetId: !Ref PrivateSubnet1 PrivateRouteTable2: Type: AWS::EC2::RouteTable Condition: CreateSecondAZ Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${AWS::StackName}-private-routes-2" DefaultPrivateRoute2: Type: AWS::EC2::Route Condition: !And [EnableNatGateway, CreateSecondAZ] Properties: RouteTableId: !Ref PrivateRouteTable2 DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway2 PrivateSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Condition: CreateSecondAZ Properties: RouteTableId: !Ref PrivateRouteTable2 SubnetId: !Ref PrivateSubnet2 # Database Subnet Group DatabaseSubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: Subnet group for RDS database SubnetIds: - !Ref DatabaseSubnet1 - !If [CreateSecondAZ, !Ref DatabaseSubnet2, !Ref DatabaseSubnet1] Tags: - Key: Name Value: !Sub "${AWS::StackName}-db-subnet-group" # Security Groups WebSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: !Sub "${AWS::StackName}-web-sg" GroupDescription: Security group for web servers VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 Description: HTTP - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: 0.0.0.0/0 Description: HTTPS Tags: - Key: Name Value: !Sub "${AWS::StackName}-web-sg" ApplicationSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: !Sub "${AWS::StackName}-app-sg" GroupDescription: Security group for application servers VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 8080 ToPort: 8080 SourceSecurityGroupId: !Ref WebSecurityGroup Description: Application port from web tier Tags: - Key: Name Value: !Sub "${AWS::StackName}-app-sg" DatabaseSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: !Sub "${AWS::StackName}-db-sg" GroupDescription: Security group for database servers VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 3306 ToPort: 3306 SourceSecurityGroupId: !Ref ApplicationSecurityGroup Description: MySQL/Aurora from application tier - IpProtocol: tcp FromPort: 5432 ToPort: 5432 SourceSecurityGroupId: !Ref ApplicationSecurityGroup Description: PostgreSQL from application tier Tags: - Key: Name Value: !Sub "${AWS::StackName}-db-sg" Outputs: VPC: Description: VPC ID Value: !Ref VPC Export: Name: !Sub "${AWS::StackName}-VPC" PublicSubnets: Description: Public subnet IDs Value: !If - CreateSecondAZ - !Sub "${PublicSubnet1},${PublicSubnet2}" - !Ref PublicSubnet1 Export: Name: !Sub "${AWS::StackName}-PublicSubnets" PrivateSubnets: Description: Private subnet IDs Value: !If - CreateSecondAZ - !Sub "${PrivateSubnet1},${PrivateSubnet2}" - !Ref PrivateSubnet1 Export: Name: !Sub "${AWS::StackName}-PrivateSubnets" DatabaseSubnets: Description: Database subnet IDs Value: !If - CreateSecondAZ - !Sub "${DatabaseSubnet1},${DatabaseSubnet2}" - !Ref DatabaseSubnet1 Export: Name: !Sub "${AWS::StackName}-DatabaseSubnets" DatabaseSubnetGroup: Description: Database subnet group name Value: !Ref DatabaseSubnetGroup Export: Name: !Sub "${AWS::StackName}-DatabaseSubnetGroup" WebSecurityGroup: Description: Web security group ID Value: !Ref WebSecurityGroup Export: Name: !Sub "${AWS::StackName}-WebSecurityGroup" ApplicationSecurityGroup: Description: Application security group ID Value: !Ref ApplicationSecurityGroup Export: Name: !Sub "${AWS::StackName}-ApplicationSecurityGroup" DatabaseSecurityGroup: Description: Database security group ID Value: !Ref DatabaseSecurityGroup Export: Name: !Sub "${AWS::StackName}-DatabaseSecurityGroup" .cfn-forge.yaml: | project: name: {{projectName}} description: {{description}} type: vpc-network # Global AWS settings (optional - can be overridden per environment) aws: profile: default # Default AWS profile for all environments region: us-east-1 # Default region environments: dev: stackName: "{{projectName}}-dev" template: cloudformation.yaml region: us-east-1 # aws: # profile: dev-account # Override profile for dev environment # region: us-east-1 # Override region for dev environment parameters: Environment: dev staging: stackName: "{{projectName}}-staging" template: cloudformation.yaml region: us-east-1 # aws: # profile: staging-account # Override profile for staging # region: us-west-2 # Override region for staging parameters: Environment: staging prod: stackName: "{{projectName}}-prod" template: cloudformation.yaml region: us-east-1 # aws: # profile: prod-account # Override profile for production # region: us-west-2 # Override region for production # accountId: "123456789012" # Verify account ID for safety parameters: Environment: prod deployment: packagingBucket: "{{projectName}}-deployment-artifacts" createBucket: true git: watchBranches: ["main", "develop"] deployOnPush: true README.md: | # {{projectName}} {{description}} ## Architecture This template creates a complete VPC infrastructure with: - **VPC**: {{vpcCidr}} CIDR block - **Public Subnets**: For load balancers, NAT gateways - **Private Subnets**: For application servers, Lambda functions - **Database Subnets**: Isolated subnets for RDS databases - **Internet Gateway**: Internet access for public subnets - **NAT Gateway**: Outbound internet access for private subnets {{#if enableNatGateway}}✅{{else}}❌{{/if}} - **Multi-AZ**: High availability across multiple zones {{#if multiAz}}✅{{else}}❌{{/if}} ## Quick Start 1. Deploy the VPC infrastructure: ```bash cfn-forge deploy ``` 2. Verify the deployment: ```bash cfn-forge validate ``` ## Network Layout ``` {{projectName}} VPC ({{vpcCidr}}) ├── Public Subnets (Internet Gateway) ├── Public Subnet 1 (AZ-a) └── Public Subnet 2 (AZ-b) {{#if multiAz}}✅{{else}}❌{{/if}} ├── Private Subnets (NAT Gateway) ├── Private Subnet 1 (AZ-a) └── Private Subnet 2 (AZ-b) {{#if multiAz}}✅{{else}}❌{{/if}} └── Database Subnets (No Internet) ├── Database Subnet 1 (AZ-a) └── Database Subnet 2 (AZ-b) {{#if multiAz}}✅{{else}}❌{{/if}} ``` ## Security Groups ### Web Security Group - **HTTP (80)**: Open to internet (0.0.0.0/0) - **HTTPS (443)**: Open to internet (0.0.0.0/0) ### Application Security Group - **Port 8080**: Access from Web Security Group only ### Database Security Group - **MySQL (3306)**: Access from Application Security Group only - **PostgreSQL (5432)**: Access from Application Security Group only ## Deployment ### Environments - **dev**: Development environment - **staging**: Staging environment - **prod**: Production environment ### Commands - `cfn-forge deploy` - Deploy VPC infrastructure - `cfn-forge deploy --env prod` - Deploy to production - `cfn-forge validate` - Validate CloudFormation template ## Usage Examples ### Launch EC2 Instances ```bash # Web servers in public subnets aws ec2 run-instances \ --subnet-id $(cfn-forge output PublicSubnets | cut -d',' -f1) \ --security-group-ids $(cfn-forge output WebSecurityGroup) # App servers in private subnets aws ec2 run-instances \ --subnet-id $(cfn-forge output PrivateSubnets | cut -d',' -f1) \ --security-group-ids $(cfn-forge output ApplicationSecurityGroup) ``` ### Create RDS Database ```bash aws rds create-db-instance \ --db-subnet-group-name $(cfn-forge output DatabaseSubnetGroup) \ --vpc-security-group-ids $(cfn-forge output DatabaseSecurityGroup) \ --db-instance-identifier myapp-db \ --engine mysql ``` ### Deploy Lambda Functions ```yaml # In your Lambda CloudFormation template VpcConfig: SubnetIds: - !ImportValue {{projectName}}-dev-PrivateSubnets SecurityGroupIds: - !ImportValue {{projectName}}-dev-ApplicationSecurityGroup ``` ## Costs ### Fixed Costs (24/7) {{#if enableNatGateway}} - **NAT Gateway**: ~$32-45/month per gateway {{#if multiAz}} - **Total NAT Gateways**: 2 (~$64-90/month) {{else}} - **Total NAT Gateways**: 1 (~$32-45/month) {{/if}} {{else}} - **NAT Gateway**: Disabled (no fixed costs) {{/if}} ### Variable Costs - **Data Transfer**: $0.045/GB through NAT Gateway - **VPC, Subnets, Route Tables**: Free - **Security Groups, NACLs**: Free ## Best Practices - ✅ Separate tiers into different subnets - ✅ Use security groups for fine-grained access control - ✅ Deploy across multiple AZs for high availability - ✅ Use private subnets for application/database servers - ✅ Monitor NAT Gateway costs and data transfer hooks: postInstall: - type: message content: "VPC network template created! This provides the foundation for your AWS infrastructure."