diff --git a/create_stack.sh b/create_stack.sh new file mode 100644 index 0000000..e1083a5 --- /dev/null +++ b/create_stack.sh @@ -0,0 +1,6 @@ +aws cloudformation create-stack \ +--stack-name $1 \ +--template-body file://$2 \ +--parameters file://$3 \ +--region=us-west-2 +read -p "Press any key..." \ No newline at end of file diff --git a/network-parameters.json b/network-parameters.json new file mode 100644 index 0000000..36c615a --- /dev/null +++ b/network-parameters.json @@ -0,0 +1,30 @@ +[ + { + "ParameterKey": "EnvironmentName", + "ParameterValue": "NDProject2" + }, + { + "ParameterKey": "VpcCidr", + "ParameterValue": "10.0.0.0/16" + }, + { + "ParameterKey": "SubnetBitmask", + "ParameterValue": "24" + }, + { + "ParameterKey": "PrivateSubnet1Ip", + "ParameterValue": "10.0.20.0" + }, + { + "ParameterKey": "PrivateSubnet2Ip", + "ParameterValue": "10.0.21.0" + }, + { + "ParameterKey": "PublicSubnet1Ip", + "ParameterValue": "10.0.10.0" + }, + { + "ParameterKey": "PublicSubnet2Ip", + "ParameterValue": "10.0.11.0" + } +] \ No newline at end of file diff --git a/network.yml b/network.yml new file mode 100644 index 0000000..fc890c7 --- /dev/null +++ b/network.yml @@ -0,0 +1,250 @@ +Description: > + Florian Born / Udacity Cloud DevOps ND + Project 2 + +Parameters: + EnvironmentName: + Type: String + VpcCidr: + Type: String + SubnetBitmask: + Type: String + PublicSubnet1Ip: + Type: String + PublicSubnet2Ip: + Type: String + PrivateSubnet1Ip: + Type: String + PrivateSubnet2Ip: + Type: String + +Resources: + Vpc: + Type: AWS::EC2::VPC + Properties: + CidrBlock: !Ref VpcCidr + Tags: + - Key: Name + Value: !Ref EnvironmentName + +# Subnets + + PublicSubnet1: + Type: AWS::EC2::Subnet + Properties: + AvailabilityZone: + !Select + - 0 + - !GetAZs + CidrBlock: + Fn::Join: ["/", [!Ref PublicSubnet1Ip, !Ref SubnetBitmask]] + Tags: + - Key: Name + Value: !Ref EnvironmentName + VpcId: !Ref Vpc + + PublicSubnet2: + Type: AWS::EC2::Subnet + Properties: + AvailabilityZone: + !Select + - 1 + - !GetAZs + CidrBlock: + Fn::Join: ["/", [!Ref PublicSubnet2Ip, !Ref SubnetBitmask]] + Tags: + - Key: Name + Value: !Ref EnvironmentName + VpcId: !Ref Vpc + + PrivateSubnet1: + Type: AWS::EC2::Subnet + Properties: + AvailabilityZone: + !Select + - 0 + - !GetAZs + CidrBlock: + Fn::Join: ["/", [!Ref PrivateSubnet1Ip, !Ref SubnetBitmask]] + Tags: + - Key: Name + Value: !Ref EnvironmentName + VpcId: !Ref Vpc + + PrivateSubnet2: + Type: AWS::EC2::Subnet + Properties: + AvailabilityZone: + !Select + - 1 + - !GetAZs + CidrBlock: + Fn::Join: ["/", [!Ref PrivateSubnet2Ip, !Ref SubnetBitmask]] + Tags: + - Key: Name + Value: !Ref EnvironmentName + VpcId: !Ref Vpc + +# EIPs + Eip1: + Type: AWS::EC2::EIP + DependsOn: IgwAttachment + Properties: + Domain: vpc + Tags: + - Key: Name + Value: !Ref EnvironmentName + + Eip2: + Type: AWS::EC2::EIP + DependsOn: IgwAttachment + Properties: + Domain: vpc + Tags: + - Key: Name + Value: !Ref EnvironmentName +# Gatways + Ngw1: + Type: AWS::EC2::NatGateway + Properties: + AllocationId: + Fn::GetAtt: + - Eip1 + - AllocationId + SubnetId: !Ref PublicSubnet1 + Tags: + - Key: Name + Value: !Ref EnvironmentName + + Ngw2: + Type: AWS::EC2::NatGateway + Properties: + AllocationId: + Fn::GetAtt: + - Eip2 + - AllocationId + SubnetId: !Ref PublicSubnet2 + Tags: + - Key: Name + Value: !Ref EnvironmentName + + Igw: + Type: AWS::EC2::InternetGateway + Properties: + Tags: + - Key: Name + Value: !Ref EnvironmentName + + IgwAttachment: + Type: AWS::EC2::VPCGatewayAttachment + Properties: + InternetGatewayId: !Ref Igw + VpcId: !Ref Vpc + +# Route Tables + + PrivateRouteTable1: + Type: AWS::EC2::RouteTable + Properties: + Tags: + - Key: Name + Value: !Ref EnvironmentName + VpcId: !Ref Vpc + + PrivateRouteTable2: + Type: AWS::EC2::RouteTable + Properties: + Tags: + - Key: Name + Value: !Ref EnvironmentName + VpcId: !Ref Vpc + + PublicRouteTable1: + Type: AWS::EC2::RouteTable + Properties: + Tags: + - Key: Name + Value: !Ref EnvironmentName + VpcId: !Ref Vpc + +# Route Table Association + PrivateRouteTable1Association: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + RouteTableId: !Ref PrivateRouteTable1 + SubnetId: !Ref PrivateSubnet1 + + PrivateRouteTable2Association: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + RouteTableId: !Ref PrivateRouteTable2 + SubnetId: !Ref PrivateSubnet2 + + PublicRouteTable1Association: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + RouteTableId: !Ref PublicRouteTable1 + SubnetId: !Ref PublicSubnet1 + + PublicRouteTable2Association: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + RouteTableId: !Ref PublicRouteTable1 + SubnetId: !Ref PublicSubnet2 + +# Routes + + DefaultPrivateRoute1: + Type: AWS::EC2::Route + DependsOn: Igw + Properties: + DestinationCidrBlock: "0.0.0.0/0" + NatGatewayId: !Ref Ngw1 + RouteTableId: !Ref PrivateRouteTable1 + + DefaultPrivateRoute2: + Type: AWS::EC2::Route + Properties: + DestinationCidrBlock: "0.0.0.0/0" + NatGatewayId: !Ref Ngw2 + RouteTableId: !Ref PrivateRouteTable2 + + DefaultPublicRoute1: + Type: AWS::EC2::Route + Properties: + DestinationCidrBlock: "0.0.0.0/0" + GatewayId: !Ref Igw + RouteTableId: !Ref PublicRouteTable1 + +Outputs: + + Vpc: + Description: "my VPC" + Value: !Ref Vpc + Export: + Name: !Sub ${EnvironmentName}-Vpc + SubnetBitmask: + Description: "the subnet bitmask of all subnets" + Value: !Ref SubnetBitmask + Export: + Name: !Sub ${EnvironmentName}-Subnet-Bitmask + PrivateSubnet1: + Description: "ID of private Subnet 1" + Value: !Ref PrivateSubnet1 + Export: + Name: !Sub ${EnvironmentName}-Priv-Subnet-1 + PrivateSubnet2: + Description: "ID of private Subnet 2" + Value: !Ref PrivateSubnet2 + Export: + Name: !Sub ${EnvironmentName}-Priv-Subnet-2 + PublicSubnet1: + Description: "ID of public Subnet 1" + Value: !Ref PublicSubnet1 + Export: + Name: !Sub ${EnvironmentName}-Pub-Subnet-1 + PublicSubnet2: + Description: "ID of public Subnet 2" + Value: !Ref PublicSubnet2 + Export: + Name: !Sub ${EnvironmentName}-Pub-Subnet-2 \ No newline at end of file diff --git a/servers-parameters.json b/servers-parameters.json new file mode 100644 index 0000000..de64556 --- /dev/null +++ b/servers-parameters.json @@ -0,0 +1,10 @@ +[ + { + "ParameterKey": "EnvironmentName", + "ParameterValue": "NDProject2" + }, + { + "ParameterKey": "S3Repo", + "ParameterValue": "s3://udacity-project2-brn/udacity.zip" + } +] \ No newline at end of file diff --git a/servers.yml b/servers.yml new file mode 100644 index 0000000..2e0fcfd --- /dev/null +++ b/servers.yml @@ -0,0 +1,325 @@ +Description: > + Florian Born / Udacity ND Project 2 + Server Infrastructure + +Parameters: + + EnvironmentName: + Description: Name der Umgebung / des Projekts + Type: String + S3Repo: + Description: the name and path of the udacity zip-file + Type: String + + +Resources: + +# LB Security Group + LBSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: "Allow HTTP an LoadBalancer" + SecurityGroupEgress: # Why Egress Port 80? See: https://superuser.com/questions/669689/does-a-web-server-use-a-different-port-transport-data + - IpProtocol: tcp + FromPort: 80 + ToPort: 80 + CidrIp: 0.0.0.0/0 + SecurityGroupIngress: + - IpProtocol: tcp + FromPort: 80 + ToPort: 80 + CidrIp: 0.0.0.0/0 + VpcId: + Fn::ImportValue: !Sub ${EnvironmentName}-Vpc + + +# Listener & Listener Rule + Listener: + Type: AWS::ElasticLoadBalancingV2::Listener + Properties: + DefaultActions: + - Type: forward + TargetGroupArn: !Ref LbTargetGroup + LoadBalancerArn: !Ref ApplicationLoadBalancer + Port: '80' + Protocol: HTTP + + ListenerRule: + Type: AWS::ElasticLoadBalancingV2::ListenerRule + Properties: + Actions: + - Type: forward + TargetGroupArn: !Ref LbTargetGroup + Order: 1 + Conditions: + - Field: path-pattern + Values: + - / + ListenerArn: !Ref 'Listener' + Priority: 1 + + +# Load Balancer + ApplicationLoadBalancer: + Type: AWS::ElasticLoadBalancingV2::LoadBalancer + Properties: + Type: application + SecurityGroups: + - !Ref LBSecurityGroup + Subnets: + - Fn::ImportValue: + !Sub ${EnvironmentName}-Pub-Subnet-1 + - Fn::ImportValue: + !Sub ${EnvironmentName}-Pub-Subnet-2 + Tags: + - Key: Name + Value: !Sub ${EnvironmentName} + +# EC2 Security Group + Ec2SecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: "Allow Outbound Traffic, allow inbound http and ssh" + SecurityGroupEgress: + - CidrIp: 0.0.0.0/0 + FromPort: 0 + ToPort: 65535 + IpProtocol: tcp + SecurityGroupIngress: + - CidrIp: 0.0.0.0/0 + FromPort: 80 + ToPort: 80 + IpProtocol: tcp +# - CidrIp: 0.0.0.0/0 + - CidrIp: + Fn::Join: + - "/" + - - Fn::GetAtt: BastionHost1.PrivateIp + - Fn::ImportValue: + !Sub ${EnvironmentName}-Subnet-Bitmask + FromPort: 22 + ToPort: 22 + IpProtocol: tcp +# - CidrIp: 0.0.0.0/0 + - CidrIp: + Fn::Join: + - "/" + - - Fn::GetAtt: BastionHost2.PrivateIp + - Fn::ImportValue: + !Sub ${EnvironmentName}-Subnet-Bitmask + FromPort: 22 + ToPort: 22 + IpProtocol: tcp + VpcId: + Fn::ImportValue: !Sub ${EnvironmentName}-Vpc + +# InstanceProfiles, Roles and Policies + + ListBucketInstanceProfile: + Type: AWS::IAM::InstanceProfile + Properties: + Path: "/" + Roles: + - Ref: S3BucketRole + + S3BucketRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: + - ec2.amazonaws.com + Action: + - sts:AssumeRole + Path: "/" + + S3BucketsPolicy: + Type: AWS::IAM::Policy + Properties: + PolicyName: S3BucketsPolicy + PolicyDocument: + Statement: + - Effect: Allow + Action: + - s3:List* + - s3:Get* # is needed to download files from bucket + Resource: "*" + Roles: + - Ref: S3BucketRole +# Policy: +# Type: 'AWS::IAM::Policy' +# Properties: + +# IAM Profiles and Roles + # IamRole: + # Type: 'AWS::IAM::Role' + # Properties: + # AssumeRolePolicyDocument: + # Version: 2012-10-17 + # Statement: + # - + + # ProfileWithRolesForOurApp: + # Type: AWS::IAM::InstanceProfile + # Properties: + # Roles: + # - Fn:ImportValue: UdacityS3ReadOnlyEC2 + + + +# Application LaunchConfiguration + AppLaunchConfiguration: + Type: AWS::AutoScaling::LaunchConfiguration + Properties: + AssociatePublicIpAddress: false + BlockDeviceMappings: + - DeviceName: /dev/sdf # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/device_naming.html + Ebs: + VolumeSize: 10 + ImageId: ami-06d51e91cea0dac8d # Ubuntu Server 18.04 + InstanceMonitoring: false # to avoid additional costs + InstanceType: t2.micro # use free tier + IamInstanceProfile: !Ref ListBucketInstanceProfile + KeyName: foobar # to be able to connect via ssh + SecurityGroups: + - !Ref Ec2SecurityGroup + UserData: + Fn::Base64: !Sub | + #!/bin/bash + apt-get update -y + apt-get install unzip awscli -y + apt-get install apache2 -y + systemctl start apache2.service + cd /var/www/html + aws s3 cp ${S3Repo} . + unzip -o udacity.zip + +# Target Group + LbTargetGroup: + Type: AWS::ElasticLoadBalancingV2::TargetGroup + Properties: + HealthCheckEnabled: true + HealthCheckIntervalSeconds: 30 + HealthCheckPath: / + #HealthCheckPort: 80 + HealthCheckProtocol: HTTP + HealthCheckTimeoutSeconds: 10 + HealthyThresholdCount: 2 + UnhealthyThresholdCount: 2 + Port: 80 + Protocol: HTTP # protocol must be specified + Tags: + - Key: Name + Value: !Sub ${EnvironmentName} + VpcId: + Fn::ImportValue: !Sub ${EnvironmentName}-Vpc + + +# Auto Scaling Group + AutoScalingGroup: + Type: AWS::AutoScaling::AutoScalingGroup + Properties: + VPCZoneIdentifier: + - Fn::ImportValue: + !Sub ${EnvironmentName}-Priv-Subnet-1 + - Fn::ImportValue: + !Sub ${EnvironmentName}-Priv-Subnet-2 + # VPCZoneIdentifier: + # - Fn::ImportValue: + # !Sub ${EnvironmentName}-Pub-Subnet-1 + # - Fn::ImportValue: + # !Sub ${EnvironmentName}-Pub-Subnet-2 + DesiredCapacity: '1' + LaunchConfigurationName: !Ref AppLaunchConfiguration + MaxSize: '2' + MinSize: '1' + Tags: + - Key: Name + Value: !Sub ${EnvironmentName} + PropagateAtLaunch: true + + TargetGroupARNs: + - !Ref LbTargetGroup + + # Bastion Hosts and Security Groups + BastionHostSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: "Allow only ssh" + SecurityGroupEgress: + - CidrIp: 0.0.0.0/0 + FromPort: 22 + ToPort: 22 + IpProtocol: tcp + SecurityGroupIngress: + - CidrIp: 0.0.0.0/0 # should static (known) IP if possible (corporate Network) + FromPort: 22 + ToPort: 22 + IpProtocol: tcp + VpcId: + Fn::ImportValue: !Sub ${EnvironmentName}-Vpc + + BastionHost1: + Type: AWS::EC2::Instance + Properties: + AvailabilityZone: + !Select + - 0 + - !GetAZs + ImageId: ami-06d51e91cea0dac8d # Ubuntu Server 18.04 + InstanceType: t2.micro # use free tier + KeyName: foobar5 + NetworkInterfaces: + - AssociatePublicIpAddress: true + DeviceIndex: '0' + SubnetId: + Fn::ImportValue: + !Sub ${EnvironmentName}-Pub-Subnet-1 + GroupSet: # IDs of the security groups for the network interface + - !Ref BastionHostSecurityGroup +# SecurityGroupIds: +# - !Ref BastionHostSecurityGroup +# SubnetId: +# Fn::ImportValue: +# !Sub ${EnvironmentName}-Pub-Subnet-1 + Tags: + - Key: Name + Value: !Sub ${EnvironmentName} + + BastionHost2: + Type: AWS::EC2::Instance + Properties: + AvailabilityZone: + !Select + - 1 + - !GetAZs + ImageId: ami-06d51e91cea0dac8d # Ubuntu Server 18.04 + InstanceType: t2.micro # use free tier + KeyName: foobar5 + NetworkInterfaces: + - AssociatePublicIpAddress: true + DeviceIndex: '0' + SubnetId: + Fn::ImportValue: + !Sub ${EnvironmentName}-Pub-Subnet-2 + GroupSet: # IDs of the security groups for the network interface + - !Ref BastionHostSecurityGroup + #SecurityGroupIds: + # - !Ref BastionHostSecurityGroup +# SubnetId: +# Fn::ImportValue: +# !Sub ${EnvironmentName}-Pub-Subnet-2 + Tags: + - Key: Name + Value: !Sub ${EnvironmentName} + +Outputs: + ApplicationLoadBalancerDnsName: + Description: The DNS Name of the application load Balancer + Value: + Fn::Join: [ "", [ "http://", !GetAtt ApplicationLoadBalancer.DNSName ] ] + Export: + Name: !Sub ${EnvironmentName}-Lb-Dns-Name diff --git a/udacity.zip b/udacity.zip new file mode 100644 index 0000000..9320a46 Binary files /dev/null and b/udacity.zip differ diff --git a/update_stack.sh b/update_stack.sh new file mode 100644 index 0000000..c157fa4 --- /dev/null +++ b/update_stack.sh @@ -0,0 +1,6 @@ +aws cloudformation update-stack \ +--stack-name $1 \ +--template-body file://$2 \ +--parameters file://$3 \ +--region=us-west-2 +read -p "Press any key..." \ No newline at end of file