--- AWSTemplateFormatVersion: 2010-09-09 Description: Setup AWS CloudProvider for Spinnaker Metadata: Author: Description: Sandeep Palavalasa License: Description: 'Copyright 2017 Amazon.com, Inc. and its affiliates. All Rights Reserved. Licensed under the Amazon Software License (the "License"). You may not use this file except in compliance with the License. A copy of the License is located at http://aws.amazon.com/asl/ or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.' AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Spinnaker Instance Configurations Parameters: - EC2KeyPairName - CurrentIP - SpinnakerVersion - SpinnakerS3BucketName - Label: default: Deployment VPC Configurations Parameters: - AvailabilityZones - NumberOfAZs - VPCCIDR - PublicSubnet1CIDR - PublicSubnet2CIDR - PublicSubnet3CIDR - PublicSubnet4CIDR - CreatePrivateSubnets - PrivateSubnet1ACIDR - PrivateSubnet2ACIDR - PrivateSubnet3ACIDR - PrivateSubnet4ACIDR - CreateSecurityGroupsForElbAndInstances ParameterLabels: AvailabilityZones: default: Availability Zones CreatePrivateSubnets: default: Create private subnets NumberOfAZs: default: Number of Availability Zones PrivateSubnet1ACIDR: default: Private subnet 1A CIDR PrivateSubnet2ACIDR: default: Private subnet 2A CIDR PrivateSubnet3ACIDR: default: Private subnet 3A CIDR PrivateSubnet4ACIDR: default: Private subnet 4A CIDR PublicSubnet1CIDR: default: Public subnet 1 CIDR PublicSubnet2CIDR: default: Public subnet 2 CIDR PublicSubnet3CIDR: default: Public subnet 3 CIDR PublicSubnet4CIDR: default: Public subnet 4 CIDR CreateSecurityGroupsForElbAndInstances: default: Create Security Groups for ELB and EC2 Instances VPCCIDR: default: VPC CIDR EC2KeyPairName: default: EC2 Key Pair for Spinnaker Instance CurrentIP: default: Your current IP address (used to limit access to SSH services on Spinnaker instances) SpinnakerVersion: default: The version of the Spinnaker to be installed on the Instance SpinnakerS3BucketName: default: The Name of the S3 bucket Spinnaker will creates for its persistent storage Parameters: AvailabilityZones: Description: 'List of Availability Zones to use for the subnets in the VPC. Note: The logical order is preserved.' Type: List NumberOfAZs: AllowedValues: - '2' - '3' - '4' Default: '3' Description: Number of Availability Zones to use in the VPC. This must match your selections in the list of Availability Zones parameter. Type: String VPCCIDR: 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])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.100.0.0/16 Description: CIDR block for the VPC Type: String PublicSubnet1CIDR: 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])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.100.128.0/20 Description: CIDR block for the public DMZ subnet 1 located in Availability Zone 1 Type: String PublicSubnet2CIDR: 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])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.100.144.0/20 Description: CIDR block for the public DMZ subnet 2 located in Availability Zone 2 Type: String PublicSubnet3CIDR: 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])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.100.160.0/20 Description: CIDR block for the public DMZ subnet 3 located in Availability Zone 3 Type: String PublicSubnet4CIDR: 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])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.100.176.0/20 Description: CIDR block for the public DMZ subnet 4 located in Availability Zone 4 Type: String CreatePrivateSubnets: AllowedValues: - 'true' - 'false' Default: 'true' Description: Set to false to create only public subnets. If false, the CIDR parameters for ALL private subnets will be ignored. Type: String PrivateSubnet1ACIDR: 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])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.100.0.0/19 Description: CIDR block for private subnet 1A located in Availability Zone 1 Type: String PrivateSubnet2ACIDR: 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])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.100.32.0/19 Description: CIDR block for private subnet 2A located in Availability Zone 2 Type: String PrivateSubnet3ACIDR: 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])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.100.64.0/19 Description: CIDR block for private subnet 3A located in Availability Zone 3 Type: String PrivateSubnet4ACIDR: 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])(\/(1[6-9]|2[0-8]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28 Default: 10.100.96.0/19 Description: CIDR block for private subnet 4A located in Availability Zone 4 Type: String CreateSecurityGroupsForElbAndInstances: AllowedValues: - 'true' - 'false' Default: 'true' Description: Set to true to create Security Groups for ELB and EC2 Instances for a sample application Type: String EC2KeyPairName: Description: "The name of an existing EC2 KeyPair to enable SSH access." Type: "AWS::EC2::KeyPair::KeyName" AllowedPattern: ".+" CurrentIP: Description: Your current IP address (used to limit access to SSH services on EC2 instances) Type: String AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\/([0-9]|[1-2][0-9]|3[0-2])$ ConstraintDescription: must be specified in CIDR notation (e.g, 123.45.67.0/24) Default: 0.0.0.0/0 SpinnakerVersion: Description: Your current IP address (used to limit access to SSH services on EC2 instances) Type: String AllowedPattern: ^\d{1,}\.\d{1,}\.\d{1,}$ ConstraintDescription: must be valid Spinnaker version (e.g, 1.25.0) Default: 1.25.0 SpinnakerS3BucketName: Description: The Name of the S3 bucket Spinnaker will creates for its persistent storage Type: String Default: spin-persitent-storage Conditions: CreateSGForElbAndInstancesCondition: !Equals [ !Ref 'CreateSecurityGroupsForElbAndInstances', 'true' ] Mappings: # ubuntu/images/hvm-ssd/ubuntu-xenial-16.04-amd64-server-20190320 AMI: ap-northeast-1: AMI: ami-023a7615a07affbe5 ap-northeast-2: AMI: ami-0e67aff698cb24c1d ap-northeast-3: AMI: ami-049b6e4838d9ae7a4 ap-south-1: AMI: ami-0db0b3ab7df22e366 ap-southeast-1: AMI: ami-06fb5332e8e3e577a ap-southeast-2: AMI: ami-0987943c813a8426b ca-central-1: AMI: ami-0e625dfca3e5a33bd eu-central-1: AMI: ami-0e1ce3e0deb8896d2 eu-north-1: AMI: ami-01996625fff6b8fcc eu-west-1: AMI: ami-0dc8d444ee2a42d8a eu-west-2: AMI: ami-0e169fa5b2b2f88ae eu-west-3: AMI: ami-089d839e690b09b28 sa-east-1: AMI: ami-0f2c5d4cfd5301fac us-east-1: AMI: ami-00ddb0e5626798373 us-east-2: AMI: ami-0dd9f0e7df0f0a138 us-gov-east-1: AMI: ami-79d13708 us-gov-west-1: AMI: ami-36bbd557 us-west-1: AMI: ami-0a741b782c2c8632d us-west-2: AMI: ami-0ac73f33a1888c64a Resources: ### VPC resources for deploying applications ### DeploymentVPCStack: Type: AWS::CloudFormation::Stack Properties: Parameters: AvailabilityZones: !Join - ',' - !Ref AvailabilityZones NumberOfAZs: !Ref NumberOfAZs VPCCIDR: !Ref VPCCIDR PublicSubnet1CIDR: !Ref PublicSubnet1CIDR PublicSubnet2CIDR: !Ref PublicSubnet2CIDR PublicSubnet3CIDR: !Ref PublicSubnet3CIDR PublicSubnet4CIDR: !Ref PublicSubnet4CIDR CreatePrivateSubnets: !Ref CreatePrivateSubnets PrivateSubnet1ACIDR: !Ref PrivateSubnet1ACIDR PrivateSubnet2ACIDR: !Ref PrivateSubnet2ACIDR PrivateSubnet3ACIDR: !Ref PrivateSubnet3ACIDR PrivateSubnet4ACIDR: !Ref PrivateSubnet4ACIDR PublicSubnetTag2: 'immutable_metadata={"purpose":"public-subnet"}' # Spinnaker need these immutable metadata to detech the Subnets PrivateSubnetATag2: 'immutable_metadata={"purpose":"private-subnet"}' TemplateURL: https://aws-quickstart.s3.amazonaws.com/quickstart-aws-vpc/templates/aws-vpc.template.yaml ### Spinnaker Instance Resources ### SpinnakerInstanceSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: Spinnaker Instance Security Group GroupDescription: A Security Group that allows ingress access for SSH to Spinnaker Instance SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !Ref CurrentIP SpinnakerInstanceRole: Type: AWS::IAM::Role Properties: RoleName: Fn::Join: - '' - - SpinnakerInstanceRole- - Ref: AWS::StackName AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - ec2.amazonaws.com Action: - sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonS3FullAccess - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly SpinnakerInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: Fn::Join: - '' - - SpinnakerInstanceProfile- - Ref: AWS::StackName Roles: - !Ref SpinnakerInstanceRole InstanceProfileName: SpinnakerInstanceRole SpinnakerInstancePolicy: Type: AWS::IAM::Policy Properties: Roles: - !Ref SpinnakerInstanceRole PolicyDocument: Version: '2012-10-17' Statement: - Action: sts:AssumeRole Effect: Allow Resource: !GetAtt SpinnakerManagedRole.Arn - Action: - "ec2:DescribeRegions" - "ec2:DescribeAvailabilityZones" Effect: Allow Resource: "*" PolicyName: Fn::Join: - '' - - SpinnakerInstanceAssumeManagedRole- - Ref: AWS::StackName ### Spinnaker Managed Account Setup ### SpinnakerManagedRole: Type: AWS::IAM::Role Properties: RoleName: Fn::Join: - '' - - spinnakerManaged- - Ref: AWS::StackName AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Action: - sts:AssumeRole Effect: Allow Principal: AWS: !GetAtt SpinnakerInstanceRole.Arn ManagedPolicyArns: - arn:aws:iam::aws:policy/PowerUserAccess SpinnakerManagedPolicy: Type: AWS::IAM::Policy DependsOn: SpinnakerManagedRole Properties: Roles: - !Ref SpinnakerManagedRole PolicyDocument: Version: '2012-10-17' Statement: - Action: iam:PassRole Effect: Allow Resource: "*" # You should restrict this only to certain set of roles, if required PolicyName: Fn::Join: - '' - - SpinnakerPassRole- - Ref: AWS::StackName ## Spinnaker Managing Setup ### BaseIAMRole: Type: AWS::IAM::Role Properties: RoleName: BaseIAMRole AssumeRolePolicyDocument: Statement: - Action: - sts:AssumeRole Effect: Allow Principal: Service: - ec2.amazonaws.com Version: "2012-10-17" Path: / # Creates Instance Profile to be used by any APP created by Spinnaker. Spinnaker has passRole access only to this instance Profile BaseInstanceProfile: Type: AWS::IAM::InstanceProfile Properties: InstanceProfileName: BaseIAMRole Path: / Roles: - !Ref BaseIAMRole SpinnakerAssumeRolePolicy: Type: AWS::IAM::Policy DependsOn: SpinnakerManagedRole Properties: Roles: - !Ref SpinnakerInstanceRole PolicyDocument: Version: "2012-10-17" Statement: - Action: sts:AssumeRole Effect: Allow Resource: - !GetAtt SpinnakerManagedRole.Arn # This is the current account PolicyName: Fn::Join: - '' - - SpinnakerAssumeRolePolicy- - Ref: AWS::StackName SecurityGroupDemoWebAppALB: # A Security Group that allows ingress access for HTTP on ALBs and used to access the DemoWebApp environment Condition: CreateSGForElbAndInstancesCondition Type: AWS::EC2::SecurityGroup DependsOn: DeploymentVPCStack Properties: GroupName: Demo-ALB-SecurityGroup GroupDescription: A Security Group that allows ingress access for HTTP on ALBs and used to access the DemoWebApp test environment SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: 0.0.0.0/0 VpcId: !GetAtt DeploymentVPCStack.Outputs.VPCID SecurityGroupDemoWebAppEC2: # A Security Group that allows ingress access for SSH and the default port that the DemoWebApp application will run on Condition: CreateSGForElbAndInstancesCondition Type: AWS::EC2::SecurityGroup DependsOn: SecurityGroupDemoWebAppALB Properties: GroupName: Demo-EC2-SecurityGroup GroupDescription: A Security Group that allows ingress access for SSH and the default port that a Jenkins Master will run on SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !Ref CurrentIP - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: !Ref CurrentIP - IpProtocol: tcp FromPort: 80 ToPort: 80 SourceSecurityGroupId: !Ref SecurityGroupDemoWebAppALB VpcId: !GetAtt DeploymentVPCStack.Outputs.VPCID SpinnakerInstance: Type: AWS::EC2::Instance DependsOn: - SpinnakerInstanceSecurityGroup - SpinnakerManagedRole Properties: ImageId: !FindInMap [AMI, !Ref 'AWS::Region', 'AMI'] InstanceType: "m5.4xlarge" BlockDeviceMappings: - DeviceName: "/dev/sda1" Ebs: VolumeSize: 100 DeleteOnTermination: "true" IamInstanceProfile: "SpinnakerInstanceRole" KeyName: !Ref "EC2KeyPairName" SecurityGroupIds: - !Ref SpinnakerInstanceSecurityGroup Tags: - Key: Name Value: Spinnaker UserData: Fn::Base64: !Sub | #!/bin/bash set -ex # Install dependencies for installation sudo apt-get update sudo apt-get -y install openjdk-11-jdk awscli jq # Install halyard with the use ubuntu sudo curl --silent --location -o /home/ubuntu/InstallHalyard.sh https://raw.githubusercontent.com/spinnaker/halyard/master/install/debian/InstallHalyard.sh sudo chmod +x /home/ubuntu/InstallHalyard.sh sudo bash /home/ubuntu/InstallHalyard.sh -y source ~/.bashrc sudo -H -u ubuntu hal -v sudo service halyard start sleep 10 export ACCOUNT_ID=$(aws sts get-caller-identity --output text --query Account) export AWS_REGION=$(curl -s 169.254.169.254/latest/dynamic/instance-identity/document | jq -r '.region') echo "export ACCOUNT_ID=${!ACCOUNT_ID}" >> /home/ubuntu/.bash_profile echo "export AWS_REGION=${!AWS_REGION}" >> /home/ubuntu/.bash_profile sudo service halyard status # Configure and enable the AWS account to deploy on EC2 sudo -H -u ubuntu hal config provider aws account add my-aws-account --account-id ${!ACCOUNT_ID} --assume-role role/spinnakerManaged-${AWS::StackName} --regions ${!AWS_REGION} sudo -H -u ubuntu hal config provider aws enable # Choose the Spinnaker sudo -H -u ubuntu hal config version edit --version ${SpinnakerVersion} # Choose Persistent Storage to S3 Using Halyard sudo -H -u ubuntu hal config storage s3 edit --bucket ${SpinnakerS3BucketName} sudo -H -u ubuntu hal config storage edit --type s3 # Deploy Spinnaker locally sudo hal deploy apply # Enable Launch Templates for all applications on clouddriver sudo -H -u ubuntu cat << EOF > /home/spinnaker/.hal/default/profiles/clouddriver-local.yml aws: features: launch-templates: enabled: true all-applications: enabled: true EOF # Enable Launch Templates on the UI sudo -H -u spinnaker cp /opt/deck/html/settings.js /home/spinnaker/.hal/default/profiles/ sudo -H -u spinnaker sed -i -e "s/var aws = {/var aws = { serverGroups: { enableLaunchTemplates: true, enableIMDSv2: true, enableCpuCredits: true, },/g" /home/spinnaker/.hal/default/profiles/settings.js # Apply the Changes sudo hal deploy apply systemctl daemon-reload Outputs: SpinnakerInstance: Value: !Sub ${SpinnakerInstance.PublicDnsName} Description: 'Spinnaker instance public name (connect via SSH using ssh -A -L 9000:localhost:9000 -L 8084:localhost:8084 -L 8087:localhost:8087 -i ubuntu@)' VPCID: Value: !GetAtt DeploymentVPCStack.Outputs.VPCID