#https://github.com/aws-samples/startup-kit-templates/blob/master/templates/vpc.cfn.yml #--- AWSTemplateFormatVersion: 2010-09-09 Description: VPC Aurora PostgreSQL # This VPC stack should be created first before any other # CloudFormation stacks, such as a bastion stack, database # stack and application stack Parameters: AvailabilityZone1: Description: The first availability zone in the region Type: AWS::EC2::AvailabilityZone::Name ConstraintDescription: Must be a valid availability zone AvailabilityZone2: Description: The second availability zone in the region Type: AWS::EC2::AvailabilityZone::Name ConstraintDescription: Must be a valid availability zone ELBIngressPort: Description: The ELB ingress port used by security groups Type: Number MinValue: 0 MaxValue: 65535 ConstraintDescription: TCP ports must be between 0 - 65535 Default: 80 AppIngressPort: Description: The application ingress port used by security groups Type: Number MinValue: 0 MaxValue: 65535 ConstraintDescription: TCP ports must be between 0 - 65535 Default: 80 SingleNatGateway: Description: Set to true to only install one NAT gateway Type: String ConstraintDescription: Value must be true or false Default: true AllowedValues: - true - false Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Region Availability Zones Parameters: - AvailabilityZone1 - AvailabilityZone2 - Label: default: Ingress Ports Parameters: - ELBIngressPort - AppIngressPort ParameterLabels: AvailabilityZone1: default: Availability Zone 1 AvailabilityZone2: default: Availability Zone 2 ELBIngressPort: default: Load Balancer Port AppIngressPort: default: Application Port Conditions: CreateSingleNatGateway: !Equals [ !Ref SingleNatGateway, true ] CreateMultipleNatGateways: !Not [ Condition: CreateSingleNatGateway ] Mappings: # Maps CIDR blocks to VPC and various subnets CIDRMap: VPC: CIDR: 10.50.0.0/16 Public1: CIDR: 10.50.0.0/24 Public2: CIDR: 10.50.1.0/24 Private1: CIDR: 10.50.64.0/19 Private2: CIDR: 10.50.96.0/19 Resources: VPC: Type: AWS::EC2::VPC Properties: CidrBlock: !FindInMap [CIDRMap, VPC, CIDR] EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Ref "AWS::StackName" VPCFLogGroup: Type: 'AWS::Logs::LogGroup' Properties: RetentionInDays: 7 VPCFlowLogIAMRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: 'vpc-flow-logs.amazonaws.com' Action: 'sts:AssumeRole' Policies: - PolicyName: 'flowlogs-policy' PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - 'logs:CreateLogStream' - 'logs:PutLogEvents' - 'logs:DescribeLogGroups' - 'logs:DescribeLogStreams' Resource: !GetAtt 'VPCFLogGroup.Arn' VPCFlowLog: Type: AWS::EC2::FlowLog Properties: DeliverLogsPermissionArn: !GetAtt VPCFlowLogIAMRole.Arn LogGroupName: !Ref VPCFLogGroup ResourceId: !Ref VPC ResourceType: VPC TrafficType: ALL PublicSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: !FindInMap [ CIDRMap, Public1, CIDR ] AvailabilityZone: !Ref AvailabilityZone1 Tags: - Key: Name Value: !Sub "${AWS::StackName}-PublicSubnet1" PublicSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: !FindInMap [ CIDRMap, Public2, CIDR ] AvailabilityZone: !Ref AvailabilityZone2 Tags: - Key: Name Value: !Sub "${AWS::StackName}-PublicSubnet2" PrivateSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: !FindInMap [ CIDRMap, Private1, CIDR ] AvailabilityZone: !Ref AvailabilityZone1 Tags: - Key: Name Value: !Sub "${AWS::StackName}-PrivateSubnet1" PrivateSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: !FindInMap [ CIDRMap, Private2, CIDR ] AvailabilityZone: !Ref AvailabilityZone2 Tags: - Key: Name Value: !Sub "${AWS::StackName}-PrivateSubnet2" InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub "${AWS::StackName}-igw" VPCGatewayAttachment: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref VPC InternetGatewayId: !Ref InternetGateway PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${AWS::StackName}-public-igw" PublicRoute: Type: AWS::EC2::Route DependsOn: VPCGatewayAttachment Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway PublicSubnetRouteTableAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet1 RouteTableId: !Ref PublicRouteTable PublicSubnetRouteTableAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet2 RouteTableId: !Ref PublicRouteTable PublicSubnetNetworkAclAssociation1: Type: AWS::EC2::SubnetNetworkAclAssociation Properties: SubnetId: !Ref PublicSubnet1 NetworkAclId: !GetAtt VPC.DefaultNetworkAcl PublicSubnetNetworkAclAssociation2: Type: AWS::EC2::SubnetNetworkAclAssociation Properties: SubnetId: !Ref PublicSubnet2 NetworkAclId: !GetAtt VPC.DefaultNetworkAcl ELBSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable HTTP/HTTPs ingress VpcId: !Ref VPC SecurityGroupIngress: - CidrIp: 0.0.0.0/0 IpProtocol: tcp ToPort: !Ref ELBIngressPort FromPort: !Ref ELBIngressPort Tags: - Key: Name Value: !Sub "${AWS::StackName}-ELBSecurityGroup" ELBSecurityGroupToAppEgress: Type: AWS::EC2::SecurityGroupEgress # prevent security group circular references Properties: GroupId: !Ref ELBSecurityGroup IpProtocol: tcp ToPort: !Ref AppIngressPort FromPort: !Ref AppIngressPort DestinationSecurityGroupId: !Ref AppSecurityGroup AppSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable access from ELB to app VpcId: !Ref VPC SecurityGroupIngress: - SourceSecurityGroupId: !Ref ELBSecurityGroup IpProtocol: tcp ToPort: !Ref AppIngressPort FromPort: !Ref AppIngressPort - SourceSecurityGroupId: !Ref BastionSecurityGroup IpProtocol: tcp ToPort: 22 FromPort: 22 Tags: - Key: Name Value: !Sub "${AWS::StackName}-AppSecurityGroup" AppSecurityGroupFromELBIngress: Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references Properties: GroupId: !Ref AppSecurityGroup IpProtocol: tcp ToPort: !Ref AppIngressPort FromPort: !Ref AppIngressPort SourceSecurityGroupId: !Ref ELBSecurityGroup AppSecurityGroupFromBastionIngress: Type: AWS::EC2::SecurityGroupIngress # prevent security group circular references Properties: GroupId: !Ref AppSecurityGroup IpProtocol: tcp ToPort: 22 FromPort: 22 SourceSecurityGroupId: !Ref BastionSecurityGroup #Customize as necessary https://docs.aws.amazon.com/elasticloadbalancing/latest/network/target-group-register-targets.html#target-security-groups AppSecurityGroupFromVPCIngress: Type: AWS::EC2::SecurityGroupIngress Properties: Description: Ingress from internal clients GroupId: !Ref AppSecurityGroup CidrIp: 0.0.0.0/0 FromPort: !Ref AppIngressPort ToPort: !Ref AppIngressPort IpProtocol: tcp BastionSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable access to the bastion host VpcId: !Ref VPC SecurityGroupEgress: - CidrIp: 0.0.0.0/0 IpProtocol: tcp ToPort: 80 FromPort: 80 - CidrIp: 0.0.0.0/0 IpProtocol: tcp ToPort: 443 FromPort: 443 - CidrIp: 0.0.0.0/0 IpProtocol: udp ToPort: 123 FromPort: 123 Tags: - Key: Name Value: !Sub "${AWS::StackName}-BastionSecurityGroup" BastionSecurityGroupToAppEgress: Type: AWS::EC2::SecurityGroupEgress # prevent security group circular references Properties: GroupId: !Ref BastionSecurityGroup IpProtocol: tcp ToPort: 22 FromPort: 22 DestinationSecurityGroupId: !Ref AppSecurityGroup BastionSecurityGroupToPostgreSqlDbEgress: Type: AWS::EC2::SecurityGroupEgress Properties: GroupId: !Ref BastionSecurityGroup IpProtocol: tcp ToPort: 5432 FromPort: 5432 DestinationSecurityGroupId: !Ref DbSecurityGroup BastionSecurityGroupToPostgreMySqlDbEgress: Type: AWS::EC2::SecurityGroupEgress Properties: GroupId: !Ref BastionSecurityGroup IpProtocol: tcp ToPort: 3306 FromPort: 3306 DestinationSecurityGroupId: !Ref DbSecurityGroup DbSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Enable access to the RDS DB VpcId: !Ref VPC SecurityGroupEgress: - CidrIp: 0.0.0.0/0 IpProtocol: tcp ToPort: 3306 FromPort: 3306 - CidrIp: 0.0.0.0/0 IpProtocol: tcp ToPort: 5432 FromPort: 5432 Tags: - Key: Name Value: !Sub "${AWS::StackName}-DbSecurityGroup" DbSecurityGroupFromBastionPostgreSqlIngress: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref DbSecurityGroup IpProtocol: tcp ToPort: 5432 FromPort: 5432 SourceSecurityGroupId: !Ref BastionSecurityGroup DbSecurityGroupFromBastionMySqlIngress: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref DbSecurityGroup IpProtocol: tcp ToPort: 3306 FromPort: 3306 SourceSecurityGroupId: !Ref BastionSecurityGroup DbSecurityGroupFromAppPostgreSqlIngress: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref DbSecurityGroup IpProtocol: tcp ToPort: 5432 FromPort: 5432 SourceSecurityGroupId: !Ref AppSecurityGroup DbSecurityGroupFromAppMySqlIngress: Type: AWS::EC2::SecurityGroupIngress Properties: GroupId: !Ref DbSecurityGroup IpProtocol: tcp ToPort: 3306 FromPort: 3306 SourceSecurityGroupId: !Ref AppSecurityGroup # NAT-related resources # # NAT is used to allow instances in private subnets to communicate with AWS # services, and pull down code and updates. NatGateway1: DependsOn: VPCGatewayAttachment Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt NatEIP1.AllocationId SubnetId: !Ref PublicSubnet1 NatGateway2: DependsOn: VPCGatewayAttachment Condition: CreateMultipleNatGateways Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt NatEIP2.AllocationId SubnetId: !Ref PublicSubnet2 NatEIP1: DependsOn: VPCGatewayAttachment Type: AWS::EC2::EIP Properties: Domain: vpc NatEIP2: DependsOn: VPCGatewayAttachment Condition: CreateMultipleNatGateways Type: AWS::EC2::EIP Properties: Domain: vpc NatRouteTable1: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${AWS::StackName}-private-nat-1" NatRouteTable2: Type: AWS::EC2::RouteTable Condition: CreateMultipleNatGateways Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${AWS::StackName}-private-nat-2" NatRoute1: Type: AWS::EC2::Route DependsOn: VPCGatewayAttachment Properties: RouteTableId: !Ref NatRouteTable1 DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway1 NatRoute2: Type: AWS::EC2::Route DependsOn: VPCGatewayAttachment Condition: CreateMultipleNatGateways Properties: RouteTableId: !Ref NatRouteTable2 DestinationCidrBlock: 0.0.0.0/0 NatGatewayId: !Ref NatGateway2 PrivateSubnetRouteTableAssociation1: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PrivateSubnet1 RouteTableId: !Ref NatRouteTable1 PrivateSubnetRouteTableAssociationSingleNatGateway: Type: AWS::EC2::SubnetRouteTableAssociation Condition: CreateSingleNatGateway Properties: SubnetId: !Ref PrivateSubnet2 RouteTableId: !Ref NatRouteTable1 PrivateSubnetRouteTableAssociation2: Type: AWS::EC2::SubnetRouteTableAssociation Condition: CreateMultipleNatGateways Properties: SubnetId: !Ref PrivateSubnet2 RouteTableId: !Ref NatRouteTable2 Outputs: Name: Description: VPC Stack Name Value: !Ref AWS::StackName Export: Name: !Sub ${AWS::StackName}-Name VPCId: Description: VPC ID Value: !Ref VPC Export: Name: !Sub "${AWS::StackName}-VpcID" VpcCidr: Description: Vpc cidr block Value: !FindInMap [ CIDRMap, VPC, CIDR ] Export: Name: !Sub "${AWS::StackName}-vpc-cidr" PublicSubnet1: Description: Public subnet 1 ID Value: !Ref PublicSubnet1 Export: Name: !Sub "${AWS::StackName}-PublicSubnet1ID" PublicSubnet2: Description: Public subnet 2 ID Value: !Ref PublicSubnet2 Export: Name: !Sub "${AWS::StackName}-PublicSubnet2ID" PrivateSubnet1: Description: Private subnet 1 ID Value: !Ref PrivateSubnet1 Export: Name: !Sub "${AWS::StackName}-PrivateSubnet1ID" PrivateSubnet2: Description: Private subnet 2 ID Value: !Ref PrivateSubnet2 Export: Name: !Sub "${AWS::StackName}-PrivateSubnet2ID" ELBSecurityGroup: Description: Security group ID for Internet-facing ELB Value: !GetAtt ELBSecurityGroup.GroupId Export: Name: !Sub "${AWS::StackName}-ELBSecurityGroupID" AppSecurityGroup: Description: Security group ID for app behind ELB Value: !GetAtt AppSecurityGroup.GroupId Export: Name: !Sub "${AWS::StackName}-AppSecurityGroupID" BastionSecurityGroup: Description: Security group ID for bastion host Value: !GetAtt BastionSecurityGroup.GroupId Export: Name: !Sub "${AWS::StackName}-BastionGroupID" DatabaseSecurityGroup: Description: Security group ID for RDS database Value: !GetAtt DbSecurityGroup.GroupId Export: Name: !Sub "${AWS::StackName}-DatabaseGroupID" ELBIngressPort: Description: ELB ingress port Value: !Ref ELBIngressPort Export: Name: !Sub "${AWS::StackName}-ELBIngressPort" AppIngressPort: Description: App ingress port Value: !Ref AppIngressPort Export: Name: !Sub "${AWS::StackName}-AppIngressPort" StackName: Value: !Ref AWS::StackName Export: Name: !Sub "${AWS::StackName}-Stackname"