--- AWSTemplateFormatVersion: "2010-09-09" Description: "Cloudformation Template to create a VPC with public and private subnets in 3 AZs" Metadata: "AWS::CloudFormation::Interface": ParameterGroups: - Label: default: "VPC Parameters" Parameters: - ClassB - Label: default: "EC2 KeyPair" Parameters: - KeyName - Label: default: "Allowed Bastion External Access CIDR" Parameters: - RemoteAccessCIDR ParameterLabels: ClassB: default: ClassB 2nd Octet KeyName: default: EC2 KeyPair RemoteAccessCIDR: default: Allowed Bastion External Access CIDR Parameters: ClassB: Description: "Specify the 2nd Octet of IPv4 CIDR block for the VPC (10.XXX.0.0/16) in the range [0-255]" Type: Number Default: 0 ConstraintDescription: "Must be in the range [0-255]" MinValue: 0 MaxValue: 255 KeyName: Description: Name of an existing EC2 KeyPair to enable SSH access to the instance Type: AWS::EC2::KeyPair::KeyName ConstraintDescription: must be the name of an existing EC2 KeyPair. Default: myOhio AmiID: Type: AWS::SSM::Parameter::Value Description: "The ID of the AMI." Default: /aws/service/ami-windows-latest/Windows_Server-2019-English-Full-Base RemoteAccessCIDR: AllowedPattern: >- ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/x Description: Allowed CIDR block in the x.x.x.x/x format for external SSH access to the bastion host Type: String Default: '52.95.4.1/32' Resources: VPC: Type: "AWS::EC2::VPC" Properties: CidrBlock: !Sub "10.${ClassB}.0.0/16" EnableDnsSupport: true EnableDnsHostnames: true InstanceTenancy: default Tags: - Key: Name Value: !Sub "${AWS::StackName}-VPC" - Key: Project Value: pgBadger - Key: Category Value: network - Key: Stage Value: all InternetGateway: Type: 'AWS::EC2::InternetGateway' Properties: Tags: - Key: Name Value: !Sub '${AWS::StackName}-igw' - Key: Project Value: pgBadger - Key: Category Value: network - Key: Stage Value: all VPCGatewayAttachment: Type: 'AWS::EC2::VPCGatewayAttachment' Properties: VpcId: !Ref VPC InternetGatewayId: !Ref InternetGateway SubnetAPrivate: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: !Select [0, !GetAZs ""] CidrBlock: !Sub "10.${ClassB}.16.0/20" VpcId: !Ref VPC Tags: - Key: Name Value: !Join - "_" - - !Sub "10.${ClassB}.0.0/16" - !Select [0, !GetAZs ""] - "Private" - Key: Reach Value: private - Key: Project Value: pgBadger - Key: Category Value: network - Key: Stage Value: consumer SubnetBPrivate: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: !Select [1, !GetAZs ""] CidrBlock: !Sub "10.${ClassB}.48.0/20" VpcId: !Ref VPC Tags: - Key: Name Value: !Join - "_" - - !Sub "10.${ClassB}.0.0/16" - !Select [1, !GetAZs ""] - "Private" - Key: Reach Value: private - Key: Project Value: pgBadger - Key: Category Value: network - Key: Stage Value: consumer SubnetCPrivate: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: !Select [2, !GetAZs ""] CidrBlock: !Sub "10.${ClassB}.80.0/20" VpcId: !Ref VPC Tags: - Key: Name Value: !Join - "_" - - !Sub "10.${ClassB}.0.0/16" - !Select [2, !GetAZs ""] - "Private" - Key: Reach Value: private - Key: Project Value: pgBadger - Key: Category Value: network - Key: Stage Value: consumer SubnetAPublic: Type: "AWS::EC2::Subnet" Properties: AvailabilityZone: !Select [0, !GetAZs ""] CidrBlock: !Sub "10.${ClassB}.0.0/20" VpcId: !Ref VPC MapPublicIpOnLaunch: true Tags: - Key: Name Value: !Join - "_" - - !Sub "10.${ClassB}.0.0/16" - !Select [0, !GetAZs ""] - "Public" - Key: Reach Value: public - Key: Project Value: pgBadger - Key: Category Value: network - Key: Stage Value: consumer RouteTableAPrivate: Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Join - "_" - - !Sub "10.${ClassB}.0.0/16" - !Select [0, !GetAZs ""] - "Private" - Key: Project Value: pgBadger - Key: Category Value: network - Key: Stage Value: consumer RouteTableBPrivate: Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Join - "_" - - !Sub "10.${ClassB}.0.0/16" - !Select [1, !GetAZs ""] - "Private" - Key: Project Value: pgBadger - Key: Category Value: network - Key: Stage Value: consumer RouteTableCPrivate: Type: "AWS::EC2::RouteTable" Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Join - "_" - - !Sub "10.${ClassB}.0.0/16" - !Select [2, !GetAZs ""] - "Private" - Key: Project Value: pgBadger - Key: Category Value: network - Key: Stage Value: consumer RouteTablePublic: Type: 'AWS::EC2::RouteTable' Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Join - "_" - - !Sub "10.${ClassB}.0.0/16" - !Select [0, !GetAZs ""] - "Public" - Key: Project Value: pgBadger - Key: Category Value: network - Key: Stage Value: consumer RouteTablePublicInternetRoute: Type: 'AWS::EC2::Route' DependsOn: VPCGatewayAttachment Properties: RouteTableId: !Ref RouteTablePublic DestinationCidrBlock: 0.0.0.0/0 GatewayId: !Ref InternetGateway RouteTableAssociationAPublic: Type: 'AWS::EC2::SubnetRouteTableAssociation' Properties: SubnetId: !Ref SubnetAPublic RouteTableId: !Ref RouteTablePublic RouteTableAssociationAPrivate: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref SubnetAPrivate RouteTableId: !Ref RouteTableAPrivate RouteTableAssociationBPrivate: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref SubnetBPrivate RouteTableId: !Ref RouteTableBPrivate RouteTableAssociationCPrivate: Type: "AWS::EC2::SubnetRouteTableAssociation" Properties: SubnetId: !Ref SubnetCPrivate RouteTableId: !Ref RouteTableCPrivate NetworkAclPrivate: Type: "AWS::EC2::NetworkAcl" Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Join - "_" - - !Sub "10.${ClassB}.0.0/16" - "NACL" - "Private" - Key: Project Value: pgBadger - Key: Category Value: network - Key: Stage Value: all NetworkAclPublic: Type: "AWS::EC2::NetworkAcl" Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Join - "_" - - !Sub "10.${ClassB}.0.0/16" - "NACL" - "Public" - Key: Project Value: pgBadger - Key: Category Value: network - Key: Stage Value: all SubnetNetworkAclAssociationAPrivate: Type: "AWS::EC2::SubnetNetworkAclAssociation" Properties: SubnetId: !Ref SubnetAPrivate NetworkAclId: !Ref NetworkAclPrivate SubnetNetworkAclAssociationBPrivate: Type: "AWS::EC2::SubnetNetworkAclAssociation" Properties: SubnetId: !Ref SubnetBPrivate NetworkAclId: !Ref NetworkAclPrivate SubnetNetworkAclAssociationCPrivate: Type: "AWS::EC2::SubnetNetworkAclAssociation" Properties: SubnetId: !Ref SubnetCPrivate NetworkAclId: !Ref NetworkAclPrivate SubnetNetworkAclAssociationAPublic: Type: "AWS::EC2::SubnetNetworkAclAssociation" Properties: SubnetId: !Ref SubnetAPublic NetworkAclId: !Ref NetworkAclPublic NetworkAclEntryInPublicAllowVPC: Type: "AWS::EC2::NetworkAclEntry" Properties: NetworkAclId: !Ref NetworkAclPublic RuleNumber: 99 Protocol: -1 RuleAction: allow Egress: false CidrBlock: "0.0.0.0/0" NetworkAclEntryOutPublicAllowVPC: Type: "AWS::EC2::NetworkAclEntry" Properties: NetworkAclId: !Ref NetworkAclPublic RuleNumber: 99 Protocol: -1 RuleAction: allow Egress: true CidrBlock: "0.0.0.0/0" NetworkAclEntryInPrivateAllowVPC: Type: "AWS::EC2::NetworkAclEntry" Properties: NetworkAclId: !Ref NetworkAclPrivate RuleNumber: 99 Protocol: -1 RuleAction: allow Egress: false CidrBlock: "0.0.0.0/0" NetworkAclEntryOutPrivateAllowVPC: Type: "AWS::EC2::NetworkAclEntry" Properties: NetworkAclId: !Ref NetworkAclPrivate RuleNumber: 99 Protocol: -1 RuleAction: allow Egress: true CidrBlock: "0.0.0.0/0" S3VPCEndpoint: Type: "AWS::EC2::VPCEndpoint" Properties: PolicyDocument: Version: 2012-10-17 Statement: - Action: "*" Effect: Allow Resource: "*" Principal: "*" RouteTableIds: - !Ref RouteTableAPrivate - !Ref RouteTableBPrivate - !Ref RouteTableCPrivate ServiceName: !Join - "" - - com.amazonaws. - !Ref "AWS::Region" - .s3 VpcId: !Ref VPC LambdaSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: !Join [ " - ", [ "Security group to allow Lambda to access S3", !Ref "AWS::StackName", ], ] VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${AWS::StackName}-LambdaSecurityGroup" - Key: Project Value: pgBadger - Key: Category Value: network - Key: Stage Value: consumer LambdaSecurityGroupHTTPSIngress: Type: "AWS::EC2::SecurityGroupIngress" Properties: GroupId: !GetAtt "LambdaSecurityGroup.GroupId" IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: !Sub "10.${ClassB}.0.0/16" Description: "Allow access for HTTPS" LambdaSecurityGroupEgress: Type: "AWS::EC2::SecurityGroupEgress" Properties: GroupId: !GetAtt "LambdaSecurityGroup.GroupId" IpProtocol: "-1" CidrIp: "0.0.0.0/0" Description: "Egress rules" EFSSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: !Join [ " - ", [ "Security group to allow Lambda to access EFS", !Ref "AWS::StackName", ], ] VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${AWS::StackName}-EFSSecurityGroup" - Key: Project Value: pgBadger - Key: Category Value: network - Key: Stage Value: consumer EFSSecurityGroupNFSIngress: Type: "AWS::EC2::SecurityGroupIngress" Properties: GroupId: !GetAtt "EFSSecurityGroup.GroupId" IpProtocol: tcp FromPort: 2049 ToPort: 2049 CidrIp: !Sub "10.${ClassB}.0.0/16" Description: "Allow access for NFS" EFSSecurityGroupNFSEgress: Type: "AWS::EC2::SecurityGroupEgress" Properties: GroupId: !GetAtt "EFSSecurityGroup.GroupId" IpProtocol: "-1" CidrIp: "0.0.0.0/0" Description: "Egress rules" EC2InstanceRole: Type: AWS::IAM::Role Properties: RoleName: !Sub "${AWS::StackName}-EC2Role" AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - 'sts:AssumeRole' # EC2InstancePolicy: # Type: AWS::IAM::Policy # Properties: # PolicyName: !Sub "${AWS::StackName}-EC2Policy" # Roles: # - !Ref EC2InstanceRole # PolicyDocument: # Version: "2012-10-17" # Statement: # - Effect: Allow # Action: # - "s3:GetObject" # - "s3:ListBucket" # Resource: "arn:aws:s3:::" EC2InstanceProfile: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: !Sub "${AWS::StackName}-EC2IProfile" Path: / Roles: - !Ref EC2InstanceRole EC2Instance: Type: AWS::EC2::Instance Properties: SubnetId: !Ref SubnetAPublic KeyName: !Ref KeyName InstanceType: t2.micro IamInstanceProfile: !Ref EC2InstanceProfile ImageId: !Ref AmiID SecurityGroupIds: - !GetAtt "EC2SecurityGroup.GroupId" EC2SecurityGroup: Type: 'AWS::EC2::SecurityGroup' Properties: GroupDescription: !Sub "EC2 Bastion Secuirty Group for ${AWS::StackName}" GroupName: !Sub '${AWS::StackName}-EC2SecurityGroup' VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: '3389' ToPort: '3389' CidrIp: !Ref RemoteAccessCIDR - IpProtocol: icmp FromPort: '-1' ToPort: '-1' CidrIp: !Ref RemoteAccessCIDR - IpProtocol: tcp FromPort: '0' ToPort: '65535' CidrIp: !Sub "10.${ClassB}.0.0/16" SecurityGroupEgress: - IpProtocol: "-1" CidrIp: "0.0.0.0/0" Tags: - Key: Name Value: !Sub '${AWS::StackName}-BastionSecurityGroup' Outputs: TemplateID: Description: "Template ID" Value: "VPC-3AZs" StackName: Description: "Stack name" Value: !Sub "${AWS::StackName}" VPC: Description: "VPC" Value: !Ref VPC Export: Name: !Sub "${AWS::StackName}-VPC" ClassB: Description: "Class B" Value: !Ref ClassB Export: Name: !Sub "${AWS::StackName}-ClassB" CidrBlock: Description: "The set of IP addresses for the VPC" Value: !GetAtt "VPC.CidrBlock" Export: Name: !Sub "${AWS::StackName}-CidrBlock" AZs: Description: "AZs" Value: 3 Export: Name: !Sub "${AWS::StackName}-AZs" AZA: Description: "AZ of A" Value: !Select [0, !GetAZs ""] Export: Name: !Sub "${AWS::StackName}-AZA" AZB: Description: "AZ of B" Value: !Select [1, !GetAZs ""] Export: Name: !Sub "${AWS::StackName}-AZB" AZC: Description: "AZ of C" Value: !Select [2, !GetAZs ""] Export: Name: !Sub "${AWS::StackName}-AZC" SubnetsPrivate: Description: "Subnets private" Value: !Join [ ",", [!Ref SubnetAPrivate, !Ref SubnetBPrivate, !Ref SubnetCPrivate], ] Export: Name: !Sub "${AWS::StackName}-SubnetsPrivate" RouteTablesPrivate: Description: "Route tables private" Value: !Join [ ",", [ !Ref RouteTableAPrivate, !Ref RouteTableBPrivate, !Ref RouteTableCPrivate, ], ] Export: Name: !Sub "${AWS::StackName}-RouteTablesPrivate" SubnetAPrivate: Description: "Subnet A private" Value: !Ref SubnetAPrivate Export: Name: !Sub "${AWS::StackName}-SubnetAPrivate" RouteTableAPrivate: Description: "Route table A private" Value: !Ref RouteTableAPrivate Export: Name: !Sub "${AWS::StackName}-RouteTableAPrivate" SubnetBPrivate: Description: "Subnet B private" Value: !Ref SubnetBPrivate Export: Name: !Sub "${AWS::StackName}-SubnetBPrivate" RouteTableBPrivate: Description: "Route table B private" Value: !Ref RouteTableBPrivate Export: Name: !Sub "${AWS::StackName}-RouteTableBPrivate" SubnetCPrivate: Description: "Subnet C private" Value: !Ref SubnetCPrivate Export: Name: !Sub "${AWS::StackName}-SubnetCPrivate" RouteTableCPrivate: Description: "Route table C private" Value: !Ref RouteTableCPrivate Export: Name: !Sub "${AWS::StackName}-RouteTableCPrivate" S3VPCEndpoint: Description: S3 VPC Endpoint Value: !Ref S3VPCEndpoint Export: Name: !Sub "${AWS::StackName}-S3VPCEndpoint" LambdaSecurityGroup: Description: Lambda SecurityGroup Value: !Ref LambdaSecurityGroup Export: Name: !Sub "${AWS::StackName}-LambdaSecurityGroup" EFSSecurityGroup: Description: EFS SecurityGroup Value: !Ref EFSSecurityGroup Export: Name: !Sub "${AWS::StackName}-EFSSecurityGroup" SubnetAPrivateARN: Description: "Subnet A private ARN" Value: !Join - "" - - "arn:aws:ec2:" - !Ref "AWS::Region" - ":" - !Ref "AWS::AccountId" - ":subnet/" - !Ref "SubnetAPrivate" Export: Name: !Sub "${AWS::StackName}-SubnetAPrivate-ARN" SubnetBPrivateARN: Description: "Subnet B private ARN" Value: !Join - "" - - "arn:aws:ec2:" - !Ref "AWS::Region" - ":" - !Ref "AWS::AccountId" - ":subnet/" - !Ref "SubnetBPrivate" Export: Name: !Sub "${AWS::StackName}-SubnetBPrivate-ARN" SubnetCPrivateARN: Description: "Subnet C private ARN" Value: !Join - "" - - "arn:aws:ec2:" - !Ref "AWS::Region" - ":" - !Ref "AWS::AccountId" - ":subnet/" - !Ref "SubnetCPrivate" #Value: !Join [ ':', [ "arn:aws:ec2", !Ref AWS::Region, !Ref AWS::AccountId,subnet, !Ref SubnetCPrivate ] ] Export: Name: !Sub "${AWS::StackName}-SubnetCPrivate-ARN" LambdaSecurityGroupARN: Description: "LambdaSecurityGroup ARN" Value: !Join - "" - - "arn:aws:ec2:" - !Ref "AWS::Region" - ":" - !Ref "AWS::AccountId" - ":security-group/" - !Ref "LambdaSecurityGroup" Export: Name: !Sub "${AWS::StackName}-LambdaSecurityGroup-ARN" EFSSecurityGroupARN: Description: "EFSSecurityGroup ARN" Value: !Join - "" - - "arn:aws:ec2:" - !Ref "AWS::Region" - ":" - !Ref "AWS::AccountId" - ":security-group/" - !Ref "EFSSecurityGroup" Export: Name: !Sub "${AWS::StackName}-EFSSecurityGroup-ARN" VPCS3EndPoint: Description: "VPC S3 Endpoint" Value: !Ref S3VPCEndpoint Export: Name: !Sub "${AWS::StackName}-VPCS3EndPoint" EC2PublicIp: Description: "The EC2 Public IP dddress" Value: !GetAtt "EC2Instance.PublicIp" Export: Name: !Sub "${AWS::StackName}-EC2PublicIp"