AWSTemplateFormatVersion: 2010-09-09 Mappings: RegionMap: us-east-1: AMI: ami-004593940846dd0af us-east-2: AMI: ami-055b8224508879f3c us-west-1: AMI: ami-03b3169915d81c514 us-west-2: AMI: ami-03abaad0a0125fbb4 ca-central-1: AMI: ami-0b4e425890074ac4b eu-central-1: AMI: ami-074ebb799ac5a5ff0 eu-west-1: AMI: ami-04a02a87f4703ac7f eu-west-2: AMI: ami-0af5e4120716942e8 eu-west-3: AMI: ami-00c3b17b0b06703ec ap-southeast-1: AMI: ami-0f174c3e3876c9cbe ap-southeast-2: AMI: ami-0c2b3c19b75f8848d ap-south-1: AMI: ami-0462393a733d5b5a9 ap-northeast-1: AMI: ami-0a8895ef45fb8351d ap-northeast-2: AMI: ami-0e2a0c104cf363dac sa-east-1: AMI: ami-0302bddcaa9415eaf eu-north-1: AMI: ami-fe28a380 us-gov-west-1: AMI: ami-59a3eb38 Conditions: S3IsOpen: !Equals ["open", !Ref BucketAccess] S3IsRestricted: !Equals ["restricted", !Ref BucketAccess] Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Bucket Parameters: - DefaultBucket - BucketAccess - Label: default: Instance and storage options Parameters: - EC2Type - DiskVolumeSize - KeyPair - EFSEncryption - DesiredCapacity - Label: default: Network Parameters: - VPC - PublicSubnetA - PublicSubnetB - Label: default: Access and authentication Parameters: - InputCIDR - WebAdminPassword Parameters: EC2Type: Description: SFTP Gateway instance type. You can use a t2.medium for testing, but m4.large is recommended for Production. Type: String Default: t2.medium AllowedValues: - t2.nano - t2.micro - t2.small - t2.medium - t2.large - t2.xlarge - t2.2xlarge - t3.nano - t3.micro - t3.small - t3.medium - t3.large - t3.xlarge - t3.2xlarge - m3.medium - m3.large - m3.xlarge - m3.2xlarge - m4.large - m4.xlarge - m4.2xlarge - m4.4xlarge - m4.10xlarge - m4.16xlarge - m5.large - m5.xlarge - m5.2xlarge - m5.4xlarge - m5.12xlarge - m5.24xlarge - c3.large - c3.xlarge - c3.2xlarge - c3.4xlarge - c3.8xlarge - c4.large - c4.xlarge - c4.2xlarge - c4.4xlarge - c4.8xlarge - c5.large - c5.xlarge - c5.2xlarge - c5.4xlarge - c5.9xlarge - c5.18xlarge - f1.2xlarge - f1.16xlarge - g2.2xlarge - g2.8xlarge - g3.4xlarge - g3.8xlarge - g3.16xlarge - p2.xlarge - p2.8xlarge - p2.16xlarge - p3.2xlarge - p3.8xlarge - p3.16xlarge - cg1.4xlarge - cr1.8xlarge - r3.large - r3.xlarge - r3.2xlarge - r3.4xlarge - r3.8xlarge - r4.large - r4.xlarge - r4.2xlarge - r4.4xlarge - r4.8xlarge - r4.16xlarge - r5.large - r5.xlarge - r5.2xlarge - r5.4xlarge - r5.12xlarge - r5.24xlarge - r5d.large - r5d.xlarge - r5d.2xlarge - r5d.4xlarge - r5d.12xlarge - r5d.24xlarge - z1d.large - z1d.xlarge - z1d.2xlarge - z1d.3xlarge - z1d.6xlarge - z1d.12xlarge - x1.16xlarge - x1.32xlarge - x1e.xlarge - x1e.2xlarge - x1e.4xlarge - x1e.8xlarge - x1e.16xlarge - x1e.32xlarge - i2.xlarge - i2.2xlarge - i2.4xlarge - i2.8xlarge - i3.large - i3.xlarge - i3.2xlarge - i3.4xlarge - i3.8xlarge - i3.16xlarge - h1.2xlarge - h1.4xlarge - h1.8xlarge - h1.16xlarge - hs1.8xlarge - d2.xlarge - d2.2xlarge - d2.4xlarge - d2.8xlarge - a1.medium - a1.large - a1.xlarge - a1.2xlarge - a1.4xlarge - c5n.large - c5n.xlarge - c5n.2xlarge - c5n.4xlarge - c5n.9xlarge - c5n.18xlarge VPC: Description: SFTP Gateway will launch into this VPC Type: AWS::EC2::VPC::Id PublicSubnetA: Description: Make sure this subnet belongs to the VPC above. Type: AWS::EC2::Subnet::Id PublicSubnetB: Description: A different subnet that belongs to the VPC above. Type: AWS::EC2::Subnet::Id DiskVolumeSize: Default: 32 Description: Disk volume size in GB. Must be at least 32. ConstraintDescription: Must be a number greater or equal to 32 MinValue: 32 Type: Number DefaultBucket: Type: String MaxLength: 63 Description: Set an S3 bucket (cannot be blank) MinLength: 3 AllowedPattern: ([a-z0-9])([a-z0-9-.]){2,62} ConstraintDescription: Must contain lowercase characters, numbers, hyphens, or periods. Must contain between 3 and 63 characters. WebAdminPassword: Description: Set the admin user's password for the web interface. Must be at least 6 charcters. NoEcho: true MinLength: 6 Type: String InputCIDR: Description: Public IP address range for SSH and SFTP access. Use 0.0.0.0/0 to allow any IP address. Use a CIDR range to restrict access. To get your local machine's IP, see http://checkip.dyndns.org/. (Remember to append /32 for a single IP e.g. 12.34.56.78/32) AllowedPattern: (\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2}) ConstraintDescription: Must be a valid IP CIDR range in the form of x.x.x.x/x. Type: String EFSEncryption: Description: Encrypt EFS mount Default: true Type: String AllowedValues: - true - false DesiredCapacity: Description: Desired capacity Type: Number Default: 2 KeyPair: Description: Make sure you have access to this EC2 key pair. Otherwise, create a new key pair before proceeding. Type: AWS::EC2::KeyPair::KeyName ConstraintDescription: Existing EC2 KeyPair. BucketAccess: Description: Allow full S3 access to support multiple buckets, or restrict S3 permissions to your default bucket Default: open Type: String AllowedValues: - open - restricted Resources: NetworkLoadBalancer: Type: AWS::ElasticLoadBalancingV2::LoadBalancer Properties: Type: network LoadBalancerAttributes: - Key: load_balancing.cross_zone.enabled Value: true Subnets: - !Ref PublicSubnetA - !Ref PublicSubnetB NLBListener22: Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - TargetGroupArn: !Ref NLBTargetGroup22 Type: forward LoadBalancerArn: !Ref NetworkLoadBalancer Port: 22 Protocol: TCP NLBListener80: Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - TargetGroupArn: !Ref NLBTargetGroup80 Type: forward LoadBalancerArn: !Ref NetworkLoadBalancer Port: 80 Protocol: TCP NLBListener443: Type: AWS::ElasticLoadBalancingV2::Listener Properties: DefaultActions: - TargetGroupArn: !Ref NLBTargetGroup443 Type: forward LoadBalancerArn: !Ref NetworkLoadBalancer Port: 443 Protocol: TCP NLBTargetGroup22: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Port: 22 Protocol: TCP VpcId: !Ref VPC HealthCheckPort: 80 HealthCheckProtocol: TCP HealthyThresholdCount: 4 UnhealthyThresholdCount: 4 NLBTargetGroup80: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Port: 80 Protocol: TCP VpcId: !Ref VPC HealthCheckPort: 80 HealthCheckProtocol: TCP HealthyThresholdCount: 4 UnhealthyThresholdCount: 4 NLBTargetGroup443: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: Port: 443 Protocol: TCP VpcId: !Ref VPC HealthCheckPort: 80 HealthCheckProtocol: TCP HealthyThresholdCount: 4 UnhealthyThresholdCount: 4 AutoScalingGroup: DependsOn: - EFS - EFSMountTargetA - EFSMountTargetB - RolePolicies Type: AWS::AutoScaling::AutoScalingGroup Properties: DesiredCapacity: !Ref DesiredCapacity LaunchConfigurationName: !Ref LaunchConfiguration MaxSize: 4 MinSize: 1 TargetGroupARNs: - !Ref NLBTargetGroup22 - !Ref NLBTargetGroup80 - !Ref NLBTargetGroup443 VPCZoneIdentifier: - !Ref PublicSubnetA - !Ref PublicSubnetB Tags: - Key: Name Value: SFTPGateway PropagateAtLaunch: true CreationPolicy: ResourceSignal: Count: !Ref DesiredCapacity Timeout: PT15M CloudWatchRole: Type: AWS::IAM::Role Properties: Path: /service-role/ AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: events.amazonaws.com Action: sts:AssumeRole CloudWatchRolePolicy: Type: AWS::IAM::Policy Properties: PolicyName: CloudWatchRunCommand PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: ssm:SendCommand Resource: !Sub 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:instance/*' Condition: StringEquals: 'ec2:ResourceTag/*': - !Ref AutoScalingGroup - Effect: Allow Action: ssm:SendCommand Resource: !Sub 'arn:aws:ssm:${AWS::Region}:*:document/AWS-RunShellScript' Roles: - !Ref CloudWatchRole CloudWatchRule: Type: AWS::Events::Rule Properties: Description: Scheduled task for s3sync ScheduleExpression: rate(5 minutes) State: ENABLED Targets: - Arn: !Sub arn:aws:ssm:${AWS::Region}::document/AWS-RunShellScript Id: SSM-ASG Input: '{"workingDirectory":["/usr/local/bin"],"commands":["s3sync"]}' RoleArn: !GetAtt CloudWatchRole.Arn RunCommandParameters: RunCommandTargets: - Key: tag:aws:autoscaling:groupName Values: - !Ref AutoScalingGroup InternalSecurityGroupIngress: Type: AWS::EC2::SecurityGroupIngress Properties: IpProtocol: tcp FromPort: 389 ToPort: 389 SourceSecurityGroupId: !Ref SFTPGatewaySG GroupId: !Ref SFTPGatewaySG EFS: Type: AWS::EFS::FileSystem Properties: Encrypted: !Ref EFSEncryption EFSMountTargetA: Type: AWS::EFS::MountTarget Properties: FileSystemId: !Ref EFS SecurityGroups: - !GetAtt EFSSG.GroupId SubnetId: !Ref PublicSubnetA EFSMountTargetB: Type: AWS::EFS::MountTarget Properties: FileSystemId: !Ref EFS SecurityGroups: - !GetAtt EFSSG.GroupId SubnetId: !Ref PublicSubnetB EFSSG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: EFS Security Group VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 2049 ToPort: 2049 SourceSecurityGroupId: !GetAtt SFTPGatewaySG.GroupId LaunchConfiguration: Type: AWS::AutoScaling::LaunchConfiguration Properties: AssociatePublicIpAddress: true IamInstanceProfile: !Ref EC2InstanceProfile ImageId: !FindInMap - RegionMap - !Ref AWS::Region - AMI KeyName: !Ref KeyPair InstanceType: !Ref EC2Type SecurityGroups: - !Ref SFTPGatewaySG UserData: Fn::Base64: !Sub | #!/bin/bash # Magic variable read by cloud-init sftpgatewaysetup script SFTPGW_ENV=AWS_HA BUCKET_NAME=${DefaultBucket} WEB_ADMIN_PASSWORD="${WebAdminPassword}" /usr/local/bin/resetadminpassword -p "$WEB_ADMIN_PASSWORD" /opt/aws/bin/cfn-signal -e 0 --stack ${AWS::StackName} --resource AutoScalingGroup --region ${AWS::Region} LogGroup: Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub sftpgw-${AWS::StackName} EC2InstanceProfile: Type: AWS::IAM::InstanceProfile Properties: Path: / Roles: - !If [S3IsRestricted, !Ref RestrictedSFTPGWRole, !Ref OpenSFTPGWRole] RestrictedSFTPGWRole: Type: AWS::IAM::Role Condition: S3IsRestricted Properties: Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: ec2.amazonaws.com Action: sts:AssumeRole OpenSFTPGWRole: Type: AWS::IAM::Role Condition: S3IsOpen Properties: Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonS3FullAccess - arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: ec2.amazonaws.com Action: sts:AssumeRole RolePolicies: Type: AWS::IAM::Policy Properties: PolicyName: SFTPGatewayPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: 's3:*' Resource: - !Sub arn:aws:s3:::${DefaultBucket} - !Sub 'arn:aws:s3:::${DefaultBucket}/*' - Effect: Allow Action: - logs:CreateLogStream - logs:PutLogEvents - logs:DescribeLogStreams - logs:CreateLogGroup Resource: '*' - Effect: Allow Action: - ec2:DescribeAvailabilityZones - ec2:DescribeInstances - ec2:DescribeTags - s3:ListAllMyBuckets Resource: '*' - Effect: Allow Action: - autoscaling:DescribeAutoScalingInstances - autoscaling:DescribeAutoScalingGroups - cloudformation:DescribeStacks - cloudformation:ListStackResources Resource: '*' - Effect: Allow Action: - kms:ListAliases - kms:ListKeys Resource: '*' Roles: - !If [S3IsRestricted, !Ref RestrictedSFTPGWRole, !Ref OpenSFTPGWRole] SFTPGatewaySG: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: SFTPGateway Security Group VpcId: !Ref VPC SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: !Ref InputCIDR - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: !Ref InputCIDR - IpProtocol: tcp FromPort: 443 ToPort: 443 CidrIp: !Ref InputCIDR Outputs: Hostname: Value: !GetAtt NetworkLoadBalancer.DNSName Description: Network Load Balancer DNS CloudWatchLogs: Value: !Ref LogGroup Description: CloudWatch logs