Skip to content

Instantly share code, notes, and snippets.

@markilott
Last active July 3, 2020 08:19
Show Gist options
  • Save markilott/d4f6fc2614748c078794781b39eb8319 to your computer and use it in GitHub Desktop.
Save markilott/d4f6fc2614748c078794781b39eb8319 to your computer and use it in GitHub Desktop.
AWS Transfer CloundFormation Demo
AWSTemplateFormatVersion: '2010-09-09'
Description: >-
AWS Transfer SFTP with EIP and Security Group
You can choose a fully automated and self-contained demo by creating a new VPC.
Or you can select an existing VPC. The VPC must have 2 public subnets with internet access.
There is one manual step in the Console required for an existing VPC after the CloudFormation script-
- Open the VPC Service page
- Go to Endpoints
- Select the new transfer Endpoint
- Select Security Groups tab, and Edit
- Attach the new SFTP Security Group
Optionally create a DNS CNAME - requires a Route53 hosted domain.
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
-
Label:
default: Select Options
Parameters:
- CreateDnsFlag
- CreateVpcFlag
-
Label:
default: DNS Settings (skip if not required)
Parameters:
- Hostname
- Route53Domain
-
Label:
default: New VPC Options (skip if using existing)
Parameters:
- VpcCidr
- SubCidr1
- SubCidr2
-
Label:
default: Existing VPC Options (you must select here, they will be ignored if you are creating a new VPC)
Parameters:
- ExistingVpc
- Subnet1
- Subnet2
-
Label:
default: Test User and Network
Parameters:
- TestUserName
- TestUserKey
- TestCidr
ParameterLabels:
CreateDnsFlag:
default: Create CNAME for the SFTP server (requires Route53 Domain)
CreateVpcFlag:
default: Create a new VPC for the server (recommended for a demo)
Hostname:
default: DNS Hostname (FQDN)
Route53Domain:
default: Route53 Domain Id
VpcCidr:
default: CIDR Range for the new VPC
SubCidr1:
default: CIDR Range for new Subnet1
SubCidr2:
default: CIDR Range for new Subnet2
ExistingVpc:
default: Existing VPC Id
Subnet1:
default: Existing Subnet1 Id
Subnet2:
default: Existing Subnet2 Id
TestUserName:
default: Test Username
TestUserKey:
default: Test User SSH Public Key
TestCidr:
default: IP Range for SFTP Whitelist
Parameters:
CreateVpcFlag:
Description: Create new isolated VPC for SFTP
Default: 'Yes'
Type: String
AllowedValues:
- 'Yes'
- 'No'
ConstraintDescription: Must specify Yes or No
ExistingVpc:
Description: Select the VPC
Type: AWS::EC2::VPC::Id
Default: Select...
Subnet1:
Description: Select a subnet from the VPC
Type: AWS::EC2::Subnet::Id
Default: Select...
Subnet2:
Description: Select a second subnet from the VPC
Type: AWS::EC2::Subnet::Id
Default: Select...
VpcCidr:
Description: CIDR Block for VPC
Type: String
Default: 192.168.0.0/16
SubCidr1:
Description: CIDR Block for Subnet1
Type: String
Default: 192.168.100.0/24
SubCidr2:
Description: CIDR Block for Subnet2
Type: String
Default: 192.168.101.0/24
CreateDnsFlag:
Description: Create DNS alias (requires Route53 Domain)
Default: 'No'
Type: String
AllowedValues:
- 'Yes'
- 'No'
ConstraintDescription: Must specify Yes or No
Hostname:
Type: String
Default: sftp-public.mydomain.com
Description: Enter a FQDN for the SFTP server (optional)
Route53Domain:
# using String rather than AWS::Route53::HostedZone::Id so that it can be left blank
Type: String
Description: Enter the Route53 Domain ID (optional)
Default: Hosted Zone ID
TestUserName:
Type: String
Description: Username for the test user
Default: test-user
TestUserKey:
Type: String
Description: SSH public key for the test user
Default: ssh-rsa - copy your key here
TestCidr:
Type: String
Description: CIDR Range
Default: 0.0.0.0/0
Conditions:
CreateDns:
!Equals [ !Ref CreateDnsFlag, 'Yes' ]
NoDns:
!Equals [ !Ref CreateDnsFlag, 'No' ]
CreateVpc:
!Equals [ !Ref CreateVpcFlag, 'Yes' ]
ExistingVpc:
!Equals [ !Ref CreateVpcFlag, 'No' ]
Resources:
# New VPC ----------------------------------------------------------
NewVpc:
Condition: CreateVpc
Type: 'AWS::EC2::VPC'
Properties:
CidrBlock: !Ref VpcCidr
Tags:
- Key: Name
Value: SFTPVPC
NewSubnet1:
Condition: CreateVpc
Type: 'AWS::EC2::Subnet'
Properties:
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs: ""
CidrBlock: !Ref SubCidr1
MapPublicIpOnLaunch: false
VpcId: !Ref NewVpc
Tags:
- Key: Name
Value: SFTP-1
NewSubnet2:
Condition: CreateVpc
Type: 'AWS::EC2::Subnet'
Properties:
AvailabilityZone:
Fn::Select:
- 1
- Fn::GetAZs: ""
CidrBlock: !Ref SubCidr2
MapPublicIpOnLaunch: false
VpcId: !Ref NewVpc
Tags:
- Key: Name
Value: SFTP-2
SftpInternetGateway:
Condition: CreateVpc
Type: AWS::EC2::InternetGateway
SftpGatewayAttach:
Condition: CreateVpc
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref SftpInternetGateway
VpcId: !Ref NewVpc
PublicRouteTable:
Condition: CreateVpc
Type: 'AWS::EC2::RouteTable'
Properties:
VpcId:
Ref: NewVpc
Tags:
- Key: Name
Value: Public
PublicDefaultRoute:
Condition: CreateVpc
Type: 'AWS::EC2::Route'
Properties:
RouteTableId:
Ref: PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId:
Ref: SftpInternetGateway
PublicRouteTableAssociation1:
Condition: CreateVpc
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId:
Ref: NewSubnet1
RouteTableId:
Ref: PublicRouteTable
PublicRouteTableAssociation2:
Condition: CreateVpc
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId:
Ref: NewSubnet2
RouteTableId:
Ref: PublicRouteTable
SftpIngress:
# added to default VPC SG => allows public SSH to all resources in the new VPC
Condition: CreateVpc
Type: AWS::EC2::SecurityGroupIngress
DependsOn: NewVpc
Properties:
CidrIp: !Ref TestCidr
Description: Inbound to SFTP
FromPort: 22
GroupId: !GetAtt NewVpc.DefaultSecurityGroup
IpProtocol: tcp
ToPort: 22
# Existing VPC ----------------------------------------------------------
# The security group must be manually attached to the SFTP Network interface
# after the creation of the server.
SftpSg:
Condition: ExistingVpc
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: SFTPGroup
GroupDescription: SFTP Access
VpcId: !Ref ExistingVpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: !Ref TestCidr
# Wait Handles -----------------------------------------------------------
# Cannot use !If in DependsOn, so using this hack instead
# SftpServer is dependent on SftpGatewayAttach when we create a new VPC
NewWaitHandle:
Condition: CreateVpc
DependsOn: SftpGatewayAttach
Type: AWS::CloudFormation::WaitConditionHandle
ExistingWaitHandle:
Type: AWS::CloudFormation::WaitConditionHandle
WaitCondition:
Type: AWS::CloudFormation::WaitCondition
Properties:
Handle: !If [CreateVpc, !Ref NewWaitHandle, !Ref ExistingWaitHandle]
Timeout: '1'
Count: 0
# Server -----------------------------------------------------------------
SftpServer:
Type: AWS::Transfer::Server
DependsOn: WaitCondition
Properties:
EndpointDetails:
AddressAllocationIds:
- !GetAtt EIP1.AllocationId
- !GetAtt EIP2.AllocationId
SubnetIds:
- !If [CreateVpc, !Ref NewSubnet1, !Ref Subnet1]
- !If [CreateVpc, !Ref NewSubnet2, !Ref Subnet2]
VpcId: !If [CreateVpc, !Ref NewVpc, !Ref ExistingVpc]
EndpointType: VPC
IdentityProviderType: SERVICE_MANAGED
LoggingRole: !GetAtt LoggingRole.Arn
Protocols:
- SFTP
EIP1:
Type: 'AWS::EC2::EIP'
Properties:
Domain: vpc
EIP2:
Type: 'AWS::EC2::EIP'
Properties:
Domain: vpc
Dns:
Condition: CreateDns
Type: AWS::Route53::RecordSet
Properties:
HostedZoneId : !Ref Route53Domain
Name: !Sub '${Hostname}'
ResourceRecords:
- !Sub ${SftpServer.ServerId}.server.transfer.${AWS::Region}.amazonaws.com
Type: CNAME
TTL: 300
# IAM ----------------------------------------------------------
LoggingRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
-
Effect: Allow
Principal:
Service:
- transfer.amazonaws.com
Action:
- sts:AssumeRole
Path: '/'
# ManagedPolicyArns:
# -
LoggingPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: CloudWatch logs for SFTP
Path: '/'
PolicyDocument:
Version: '2012-10-17'
Statement:
-
Effect: Allow
Action:
- logs:CreateLogStream
- logs:DescribeLogStreams
- logs:CreateLogGroup
- logs:PutLogEvents
Resource:
- '*'
Roles:
- Ref: LoggingRole
UserRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- transfer.amazonaws.com
Action:
- sts:AssumeRole
Path: /
UserPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
Description: CloudWatch logs for SFTP
Path: '/'
PolicyDocument:
Version: '2012-10-17'
Statement:
-
Effect: Allow
Action:
- s3:ListBucket
Resource:
- !GetAtt SftpBucket.Arn
-
Effect: Allow
Action:
- s3:PutObject
- s3:GetObject
- s3:GetObjectVersion
Resource:
- !Sub '${SftpBucket.Arn}/*'
Roles:
- Ref: UserRole
# S3 ----------------------------------------------------------
SftpBucket:
Type: AWS::S3::Bucket
Properties:
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
# Users ---------------------------------------------------------
TestUser:
Type: AWS::Transfer::User
DependsOn: SftpServer
Properties:
ServerId: !GetAtt SftpServer.ServerId
UserName: !Ref TestUserName
HomeDirectory: !Sub "/${SftpBucket}/home/${TestUserName}"
Policy: >
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowListingOfUserFolder",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::${transfer:HomeBucket}",
"Condition": {
"StringLike": {
"s3:prefix": [
"home/${transfer:UserName}/*",
"home/${transfer:UserName}"
]
}
}
},
{
"Sid": "HomeDirObjectAccess",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:GetObjectVersion"
],
"Resource": "arn:aws:s3:::${transfer:HomeDirectory}*"
}
]
}
Role: !GetAtt UserRole.Arn
SshPublicKeys:
- !Ref TestUserKey
# Outputs -----------------------------------------------------------------------------
Outputs:
EIP1:
Description: SFTP Server EIP1
Value: !Ref EIP1
Export:
Name: !Sub '${AWS::StackName}-EIP1'
EIP2:
Description: SFTP Server EIP1
Value: !Ref EIP2
Export:
Name: !Sub '${AWS::StackName}-EIP2'
SftpHostName:
Description: DNS hostname for the server
Condition: NoDns
Value: !Sub ${SftpServer.ServerId}.server.transfer.${AWS::Region}.amazonaws.com
Export:
Name: !Sub '${AWS::StackName}-ServerDNS'
SftpCustomHostName:
Description: DNS hostname for the server
Condition: CreateDns
Value: !Sub ${Hostname}
Export:
Name: !Sub '${AWS::StackName}-ServerDNS'
S3Bucket:
Description: the SFTP home S3 bucket
Value: !Ref SftpBucket
Export:
Name: !Sub '${AWS::StackName}-S3Bucket'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment