Skip to content

Commit 5672cc1

Browse files
committed
Add transfer-ftp-s3 sample
1 parent f93b55a commit 5672cc1

File tree

13 files changed

+575
-0
lines changed

13 files changed

+575
-0
lines changed

transfer-ftp-s3/python/cdk/app.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Transfer FTP to S3 CDK application.
4+
"""
5+
6+
from aws_cdk import (
7+
App,
8+
CfnOutput,
9+
RemovalPolicy,
10+
Stack,
11+
aws_iam as iam,
12+
aws_s3 as s3,
13+
aws_transfer as transfer,
14+
)
15+
from constructs import Construct
16+
17+
18+
class TransferFtpS3Stack(Stack):
19+
"""Stack for Transfer FTP server resources."""
20+
21+
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
22+
super().__init__(scope, construct_id, **kwargs)
23+
24+
bucket_name = "transfer-files"
25+
username = "ftpuser"
26+
27+
# S3 bucket for file storage
28+
bucket = s3.Bucket(
29+
self,
30+
"TransferBucket",
31+
bucket_name=bucket_name,
32+
removal_policy=RemovalPolicy.DESTROY,
33+
auto_delete_objects=True,
34+
)
35+
36+
# IAM role for Transfer service
37+
transfer_role = iam.Role(
38+
self,
39+
"TransferRole",
40+
role_name="transfer-role",
41+
assumed_by=iam.ServicePrincipal("transfer.amazonaws.com"),
42+
)
43+
44+
# Grant the role access to the bucket
45+
bucket.grant_read_write(transfer_role)
46+
47+
# Transfer server with FTP protocol
48+
server = transfer.CfnServer(
49+
self,
50+
"TransferServer",
51+
endpoint_type="PUBLIC",
52+
identity_provider_type="SERVICE_MANAGED",
53+
protocols=["FTP"],
54+
)
55+
56+
# Transfer user
57+
user = transfer.CfnUser(
58+
self,
59+
"TransferUser",
60+
server_id=server.attr_server_id,
61+
user_name=username,
62+
role=transfer_role.role_arn,
63+
home_directory_type="PATH",
64+
home_directory=f"/{bucket_name}",
65+
)
66+
67+
# Outputs
68+
CfnOutput(self, "ServerId", value=server.attr_server_id)
69+
CfnOutput(self, "BucketName", value=bucket.bucket_name)
70+
CfnOutput(self, "Username", value=username)
71+
72+
73+
app = App()
74+
TransferFtpS3Stack(app, "TransferFtpS3Stack")
75+
app.synth()
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"app": "python3 app.py",
3+
"context": {
4+
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
5+
"@aws-cdk/core:checkSecretUsage": true,
6+
"@aws-cdk/core:target-partitions": ["aws"]
7+
}
8+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
# Transfer FTP to S3 CDK deployment script
5+
6+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7+
SAMPLE_DIR="$(dirname "$SCRIPT_DIR")"
8+
ENV_FILE="$SAMPLE_DIR/scripts/.env"
9+
STACK_NAME="TransferFtpS3Stack"
10+
11+
echo "Deploying Transfer FTP server with CDK..."
12+
13+
cd "$SCRIPT_DIR"
14+
15+
# Install CDK dependencies
16+
pip install -q -r requirements.txt
17+
18+
# Bootstrap CDK (if needed)
19+
cdklocal bootstrap --quiet 2>/dev/null || true
20+
21+
# Deploy
22+
cdklocal deploy "$STACK_NAME" --require-approval never --outputs-file outputs.json
23+
24+
# Extract outputs
25+
SERVER_ID=$(jq -r ".\"$STACK_NAME\".ServerId" outputs.json)
26+
BUCKET_NAME=$(jq -r ".\"$STACK_NAME\".BucketName" outputs.json)
27+
USERNAME=$(jq -r ".\"$STACK_NAME\".Username" outputs.json)
28+
29+
# Extract port from server ID (format: s-xxxNNNNN where NNNNN is the port)
30+
FTP_PORT=$(echo "$SERVER_ID" | sed 's/s-[a-z]*//')
31+
32+
echo ""
33+
echo "Transfer FTP server deployed successfully!"
34+
echo " Server ID: $SERVER_ID"
35+
echo " FTP Port: $FTP_PORT"
36+
echo " Bucket: $BUCKET_NAME"
37+
echo " Username: $USERNAME"
38+
39+
# Write environment variables
40+
mkdir -p "$(dirname "$ENV_FILE")"
41+
cat > "$ENV_FILE" << EOF
42+
SERVER_ID=$SERVER_ID
43+
FTP_PORT=$FTP_PORT
44+
BUCKET_NAME=$BUCKET_NAME
45+
USERNAME=$USERNAME
46+
FTP_PASSWORD=12345
47+
EOF
48+
49+
echo ""
50+
echo "Environment written to $ENV_FILE"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
aws-cdk-lib>=2.100.0
2+
constructs>=10.0.0
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
# Transfer FTP to S3 CDK teardown script
5+
6+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7+
SAMPLE_DIR="$(dirname "$SCRIPT_DIR")"
8+
ENV_FILE="$SAMPLE_DIR/scripts/.env"
9+
STACK_NAME="TransferFtpS3Stack"
10+
11+
echo "Tearing down Transfer FTP CDK resources..."
12+
13+
cd "$SCRIPT_DIR"
14+
15+
# Destroy stack
16+
cdklocal destroy "$STACK_NAME" --force 2>/dev/null || true
17+
18+
# Clean up
19+
rm -f "$ENV_FILE"
20+
rm -f outputs.json
21+
rm -rf cdk.out
22+
23+
echo "Teardown complete"
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
# Transfer FTP to S3 CloudFormation deployment script
5+
6+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7+
SAMPLE_DIR="$(dirname "$SCRIPT_DIR")"
8+
ENV_FILE="$SAMPLE_DIR/scripts/.env"
9+
STACK_NAME="transfer-ftp-s3"
10+
11+
echo "Deploying Transfer FTP server with CloudFormation..."
12+
13+
cd "$SCRIPT_DIR"
14+
15+
# Deploy CloudFormation stack
16+
awslocal cloudformation deploy \
17+
--stack-name "$STACK_NAME" \
18+
--template-file template.yml \
19+
--capabilities CAPABILITY_NAMED_IAM \
20+
--no-fail-on-empty-changeset
21+
22+
# Get outputs
23+
SERVER_ID=$(awslocal cloudformation describe-stacks \
24+
--stack-name "$STACK_NAME" \
25+
--query "Stacks[0].Outputs[?OutputKey=='ServerId'].OutputValue" \
26+
--output text)
27+
28+
BUCKET_NAME=$(awslocal cloudformation describe-stacks \
29+
--stack-name "$STACK_NAME" \
30+
--query "Stacks[0].Outputs[?OutputKey=='BucketName'].OutputValue" \
31+
--output text)
32+
33+
USERNAME=$(awslocal cloudformation describe-stacks \
34+
--stack-name "$STACK_NAME" \
35+
--query "Stacks[0].Outputs[?OutputKey=='Username'].OutputValue" \
36+
--output text)
37+
38+
# Extract port from server ID (format: s-xxxNNNNN where NNNNN is the port)
39+
FTP_PORT=$(echo "$SERVER_ID" | sed 's/s-[a-z]*//')
40+
41+
echo ""
42+
echo "Transfer FTP server deployed successfully!"
43+
echo " Stack: $STACK_NAME"
44+
echo " Server ID: $SERVER_ID"
45+
echo " FTP Port: $FTP_PORT"
46+
echo " Bucket: $BUCKET_NAME"
47+
echo " Username: $USERNAME"
48+
49+
# Write environment variables
50+
mkdir -p "$(dirname "$ENV_FILE")"
51+
cat > "$ENV_FILE" << EOF
52+
SERVER_ID=$SERVER_ID
53+
FTP_PORT=$FTP_PORT
54+
BUCKET_NAME=$BUCKET_NAME
55+
USERNAME=$USERNAME
56+
FTP_PASSWORD=12345
57+
EOF
58+
59+
echo ""
60+
echo "Environment written to $ENV_FILE"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
# Transfer FTP to S3 CloudFormation teardown script
5+
6+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7+
SAMPLE_DIR="$(dirname "$SCRIPT_DIR")"
8+
ENV_FILE="$SAMPLE_DIR/scripts/.env"
9+
STACK_NAME="transfer-ftp-s3"
10+
11+
echo "Tearing down Transfer FTP CloudFormation resources..."
12+
13+
# Delete stack
14+
awslocal cloudformation delete-stack --stack-name "$STACK_NAME" 2>/dev/null || true
15+
16+
# Wait for deletion
17+
awslocal cloudformation wait stack-delete-complete --stack-name "$STACK_NAME" 2>/dev/null || true
18+
19+
# Clean up
20+
rm -f "$ENV_FILE"
21+
22+
echo "Teardown complete"
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
AWSTemplateFormatVersion: '2010-09-09'
2+
Description: Transfer FTP to S3 - CloudFormation template
3+
4+
Parameters:
5+
BucketName:
6+
Type: String
7+
Default: transfer-files
8+
Username:
9+
Type: String
10+
Default: ftpuser
11+
12+
Resources:
13+
# S3 bucket for file storage
14+
TransferBucket:
15+
Type: AWS::S3::Bucket
16+
Properties:
17+
BucketName: !Ref BucketName
18+
19+
# IAM role for Transfer service
20+
TransferRole:
21+
Type: AWS::IAM::Role
22+
Properties:
23+
RoleName: transfer-role
24+
AssumeRolePolicyDocument:
25+
Version: '2012-10-17'
26+
Statement:
27+
- Effect: Allow
28+
Principal:
29+
Service: transfer.amazonaws.com
30+
Action: sts:AssumeRole
31+
Policies:
32+
- PolicyName: transfer-s3-access
33+
PolicyDocument:
34+
Version: '2012-10-17'
35+
Statement:
36+
- Effect: Allow
37+
Action:
38+
- s3:ListBucket
39+
- s3:GetBucketLocation
40+
Resource: !GetAtt TransferBucket.Arn
41+
- Effect: Allow
42+
Action:
43+
- s3:PutObject
44+
- s3:GetObject
45+
- s3:DeleteObject
46+
Resource: !Sub ${TransferBucket.Arn}/*
47+
48+
# Transfer server with FTP protocol
49+
TransferServer:
50+
Type: AWS::Transfer::Server
51+
Properties:
52+
EndpointType: PUBLIC
53+
IdentityProviderType: SERVICE_MANAGED
54+
Protocols:
55+
- FTP
56+
57+
# Transfer user
58+
TransferUser:
59+
Type: AWS::Transfer::User
60+
Properties:
61+
ServerId: !GetAtt TransferServer.ServerId
62+
UserName: !Ref Username
63+
Role: !GetAtt TransferRole.Arn
64+
HomeDirectoryType: PATH
65+
HomeDirectory: !Sub /${BucketName}
66+
67+
Outputs:
68+
ServerId:
69+
Description: Transfer server ID
70+
Value: !GetAtt TransferServer.ServerId
71+
BucketName:
72+
Description: S3 bucket name
73+
Value: !Ref TransferBucket
74+
Username:
75+
Description: Transfer username
76+
Value: !Ref Username
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
SAMPLE_DIR="$(dirname "$SCRIPT_DIR")"
6+
7+
SUFFIX=$(date +%s)
8+
BUCKET_NAME="transfer-files-${SUFFIX}"
9+
USERNAME="user-${SUFFIX}"
10+
11+
echo "Creating S3 bucket: $BUCKET_NAME"
12+
awslocal s3 mb "s3://$BUCKET_NAME"
13+
14+
echo "Creating Transfer FTP server..."
15+
SERVER_RESPONSE=$(awslocal transfer create-server \
16+
--endpoint-type PUBLIC \
17+
--identity-provider-type SERVICE_MANAGED \
18+
--protocols FTP)
19+
20+
SERVER_ID=$(echo "$SERVER_RESPONSE" | jq -r '.ServerId')
21+
22+
# Extract port from server ID (format: s-xxxNNNNN where NNNNN is the port)
23+
FTP_PORT=$(echo "$SERVER_ID" | sed 's/s-[a-z]*//')
24+
25+
echo "Creating Transfer user: $USERNAME"
26+
awslocal transfer create-user \
27+
--server-id "$SERVER_ID" \
28+
--user-name "$USERNAME" \
29+
--home-directory "$BUCKET_NAME" \
30+
--home-directory-type PATH \
31+
--role "arn:aws:iam::000000000000:role/transfer-role"
32+
33+
# Save configuration for tests
34+
cat > "$SCRIPT_DIR/.env" << EOF
35+
BUCKET_NAME=$BUCKET_NAME
36+
SERVER_ID=$SERVER_ID
37+
FTP_PORT=$FTP_PORT
38+
USERNAME=$USERNAME
39+
FTP_PASSWORD=12345
40+
EOF
41+
42+
echo ""
43+
echo "Deployment complete!"
44+
echo "Server ID: $SERVER_ID"
45+
echo "FTP Port: $FTP_PORT"
46+
echo "Bucket: $BUCKET_NAME"
47+
echo "Username: $USERNAME"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
6+
if [ -f "$SCRIPT_DIR/.env" ]; then
7+
source "$SCRIPT_DIR/.env"
8+
fi
9+
10+
if [ -n "${SERVER_ID:-}" ] && [ -n "${USERNAME:-}" ]; then
11+
echo "Deleting Transfer user: $USERNAME"
12+
awslocal transfer delete-user --server-id "$SERVER_ID" --user-name "$USERNAME" 2>/dev/null || true
13+
fi
14+
15+
if [ -n "${SERVER_ID:-}" ]; then
16+
echo "Deleting Transfer server: $SERVER_ID"
17+
awslocal transfer delete-server --server-id "$SERVER_ID" 2>/dev/null || true
18+
fi
19+
20+
if [ -n "${BUCKET_NAME:-}" ]; then
21+
echo "Deleting S3 bucket: $BUCKET_NAME"
22+
awslocal s3 rb "s3://$BUCKET_NAME" --force 2>/dev/null || true
23+
fi
24+
25+
rm -f "$SCRIPT_DIR/.env"
26+
echo "Teardown complete"

0 commit comments

Comments
 (0)