David Martin Calalang
07/29/2025, 3:32 PMMarvin
07/29/2025, 3:32 PMMarvin
07/29/2025, 3:33 PMbash
aws ecs create-cluster --cluster-name prefect-server-cluster
2. Create ECR Repository
bash
aws ecr create-repository --repository-name prefect-server
3. Create Required IAM Roles
- Create Task Execution Role
- Create Task Role
4. Create Task Definition
- Create a task definition JSON file that includes:
json
{
"family": "prefect-server",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"containerDefinitions": [
{
"name": "prefect-server",
"image": "${ECR_REPO_URI}:latest",
"essential": true,
"portMappings": [
{
"containerPort": 4200,
"protocol": "tcp"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/prefect-server",
"awslogs-region": "${AWS_REGION}",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}
5. Create ECS Service
bash
aws ecs create-service \
--cluster prefect-server-cluster \
--service-name prefect-server \
--task-definition prefect-server \
--desired-count 1 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-xxxxx],securityGroups=[sg-xxxxx]}"
### Required IAM Permissions
1. Task Execution Role (minimum permissions):
- Managed Policy: AmazonECSTaskExecutionRolePolicy
- This includes permissions for:
- ECR image pulling
- CloudWatch Logs creation and management
json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
2. Task Role (minimum permissions):
json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
### Additional Configuration Considerations
1. Networking
- Place the service in a private subnet with a NAT Gateway
- Configure security groups to allow inbound traffic on port 4200
- Consider using an Application Load Balancer for HTTPS termination
2. Environment Variables
- Set PREFECT_SERVER_API_HOST="0.0.0.0"
- Set PREFECT_SERVER_API_PORT="4200"
3. Health Checks
- Configure health check endpoint at /api/health
- Set appropriate health check grace periods in your service definition
4. Monitoring
- Set up CloudWatch Logs monitoring
- Configure CloudWatch Alarms for service metrics
### Best Practices
1. Use Infrastructure as Code (Terraform/CloudFormation) to manage the deployment
2. Implement proper logging and monitoring
3. Use secrets management for sensitive values
4. Configure auto-scaling policies based on your needs
5. Regular backup of Prefect database
6. Use proper tagging for resource managementMarvin
07/29/2025, 3:33 PMDavid Martin Calalang
07/29/2025, 3:37 PMMarvin
07/29/2025, 3:37 PMMarvin
07/29/2025, 3:37 PMAmazonECSTaskExecutionRolePolicy
AND additional ECS permissions:
json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogStream",
"logs:PutLogEvents",
"ecs:StartTask",
"ecs:StopTask",
"ecs:DescribeTasks"
],
"Resource": "*"
}
]
}
### Task Role
The task role needs permissions to interact with ECS services and describe its own resources:
json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:DescribeTasks",
"ecs:ListTasks",
"ecs:DescribeContainerInstances",
"ecs:DescribeServices",
"ecs:UpdateService",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
],
"Resource": "*"
}
]
}
The reason these permissions are necessary:
1. Task Execution Role needs:
- ecs:StartTask
, ecs:StopTask
, `ecs:DescribeTasks`: To manage the lifecycle of the ECS tasks
- ECR permissions: To pull container images
- CloudWatch Logs permissions: For logging
2. Task Role needs:
- ecs:Describe*
permissions: To allow the task to query its own metadata and status
- `ecs:ListTasks`: To enumerate running tasks
- `ecs:UpdateService`: To allow the service to be updated if needed
- CloudWatch Logs permissions: For the application's runtime logging
Without these permissions, the ECS service won't be able to:
- Start and stop tasks properly
- Access container images
- Write logs
- Manage its own lifecycle
- Query task metadata (which Prefect needs for proper operation)
Would you like me to elaborate on any of these permissions or explain why specific ones are needed?David Martin Calalang
07/29/2025, 3:42 PMMarvin
07/29/2025, 3:42 PMMarvin
07/29/2025, 3:43 PMMarvin
07/29/2025, 3:44 PMjson
{
"family": "prefect-server",
"requiresCompatibilities": ["FARGATE"],
"networkMode": "awsvpc",
"cpu": "1024",
"memory": "2048",
"executionRoleArn": "arn:aws:iam::<YOUR-ACCOUNT-ID>:role/PrefectServerTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::<YOUR-ACCOUNT-ID>:role/PrefectServerTaskRole",
"containerDefinitions": [
{
"name": "prefect-server",
"image": "prefecthq/prefect:2-latest",
"essential": true,
"portMappings": [
{
"containerPort": 4200,
"protocol": "tcp"
}
],
"environment": [
{
"name": "PREFECT_SERVER_API_HOST",
"value": "0.0.0.0"
},
{
"name": "PREFECT_SERVER_API_PORT",
"value": "4200"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/prefect-server",
"awslogs-region": "<YOUR-REGION>",
"awslogs-stream-prefix": "prefect-server",
"awslogs-create-group": "true"
}
},
"healthCheck": {
"command": [
"CMD-SHELL",
"curl -f <http://localhost:4200/api/health> || exit 1"
],
"interval": 30,
"timeout": 5,
"retries": 3,
"startPeriod": 60
}
}
]
}
Key components explained:
1. Role References:
json
"executionRoleArn": "arn:aws:iam::<YOUR-ACCOUNT-ID>:role/PrefectServerTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::<YOUR-ACCOUNT-ID>:role/PrefectServerTaskRole",
- Replace <YOUR-ACCOUNT-ID>
with your AWS account ID
- These ARNs reference the two roles we discussed earlier
2. Fargate Requirements:
json
"requiresCompatibilities": ["FARGATE"],
"networkMode": "awsvpc",
"cpu": "1024",
"memory": "2048",
- awsvpc
network mode is required for Fargate
- CPU and memory values can be adjusted based on your needs
3. Container Configuration:
json
"containerDefinitions": [
{
"name": "prefect-server",
"image": "prefecthq/prefect:2-latest",
"portMappings": [
{
"containerPort": 4200,
"protocol": "tcp"
}
]
}
]
- Exposes port 4200 for the Prefect API
- Uses the official Prefect Docker image
4. Logging Configuration:
json
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/prefect-server",
"awslogs-region": "<YOUR-REGION>",
"awslogs-stream-prefix": "prefect-server",
"awslogs-create-group": "true"
}
}
- Replace <YOUR-REGION>
with your AWS region
- Automatically creates the log group if it doesn't exist
5. Health Check:
json
"healthCheck": {
"command": [
"CMD-SHELL",
"curl -f <http://localhost:4200/api/health> || exit 1"
],
"interval": 30,
"timeout": 5,
"retries": 3,
"startPeriod": 60
}
- Monitors the Prefect server's health endpoint
- Allows 60 seconds for initial startup
To register this task definition, you would use:
bash
aws ecs register-task-definition --cli-input-json <file://task-definition.json>
Remember to:
1. Replace <YOUR-ACCOUNT-ID>
with your AWS account ID
2. Replace <YOUR-REGION>
with your AWS regionMarvin
07/29/2025, 3:44 PMDavid Martin Calalang
07/29/2025, 3:44 PMMarvin
07/29/2025, 3:44 PMMarvin
07/29/2025, 3:45 PMPrefectServerTaskExecutionRole
)
json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"ecs:StartTask",
"ecs:StopTask",
"ecs:DescribeTasks",
"iam:GetRole",
"iam:PassRole"
],
"Resource": "*"
}
]
}
You can also attach the AWS-managed policy AmazonECSTaskExecutionRolePolicy
to this role, which includes most of these permissions.
### 2. Task Role (PrefectServerTaskRole
)
json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:DescribeTasks",
"ecs:ListTasks",
"ecs:DescribeContainerInstances",
"ecs:DescribeServices",
"ecs:UpdateService",
"ecs:ListServices",
"ecs:DescribeClusters",
"servicediscovery:ListServices",
"servicediscovery:GetService",
"servicediscovery:ListInstances",
"servicediscovery:RegisterInstance",
"servicediscovery:DeregisterInstance",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:DescribeLogStreams"
],
"Resource": "*"
}
]
}
Let me create these roles using AWS CLI commands for you:
1. First, create the trust relationships (assume role policies) that both roles will need:
bash
# Create trust policy JSON for both roles (they're the same for Fargate tasks)
cat > trust-policy.json << 'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "<http://ecs-tasks.amazonaws.com|ecs-tasks.amazonaws.com>"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
2. Create the IAM roles and their policies:
```bash
# Create the Task Execution Role
aws iam create-role \
--role-name PrefectServerTaskExecutionRole \
--assume-role-policy-document file://trust-policy.json
# Attach the AWS managed policy for ECS task execution
aws iam attach-role-policy \
--role-name PrefectServerTaskExecutionRole \
--policy-arn arnawsiam:awspolicy/service-role/AmazonECSTaskExecutionRolePolicy
# Create and attach the custom policy for Task Execution Role
aws iam put-role-policy \
--role-name PrefectServerTaskExecutionRole \
--policy-name PrefectServerTaskExecutionPolicy \
--policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:GetRole",
"iam:PassRole"
],
"Resource": "*"
}
]
}'
# Create the Task Role
aws iam create-role \
--role-name PrefectServerTaskRole \
--assume-role-policy-document file://trust-policy.json
# Create and attach the custom policy for Task Role
aws iam put-role-policy \
--role-name PrefectServerTaskRole \
--policy-name PrefectServerTaskPolicy \
--policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:DescribeTasks",
"ecs:ListTasks",Marvin
07/29/2025, 3:45 PMDavid Martin Calalang
07/29/2025, 3:53 PMMarvin
07/29/2025, 3:53 PMMarvin
07/29/2025, 3:54 PMiam:PassRole
permission should indeed be scoped to only the specific roles that the task execution role needs to pass, which in this case is primarily the task role itself.
Here's the properly scoped task execution role policy:
json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"ecs:StartTask",
"ecs:StopTask",
"ecs:DescribeTasks"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"iam:PassRole",
"iam:GetRole"
],
"Resource": [
"arn:aws:iam::<YOUR-ACCOUNT-ID>:role/PrefectServerTaskRole"
],
"Condition": {
"StringEquals": {
"iam:PassedToService": "<http://ecs-tasks.amazonaws.com|ecs-tasks.amazonaws.com>"
}
}
}
]
}
Key security improvements in this policy:
1. Separated Statements: Separates the iam:PassRole
permission into its own statement for better clarity and control
2. Specific Resource: Limits the PassRole
permission to only the specific task role ARN that needs to be passed
3. Service Condition: Adds a condition that the role can only be passed to the ECS tasks service
4. Scope Limitation: Only allows passing roles within the specific path needed for Prefect server operation
To create this more secure policy using AWS CLI:
bash
aws iam put-role-policy \
--role-name PrefectServerTaskExecutionRole \
--policy-name PrefectServerTaskExecutionPolicy \
--policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:GetAuthorizationToken",
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage",
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"ecs:StartTask",
"ecs:StopTask",
"ecs:DescribeTasks"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"iam:PassRole",
"iam:GetRole"
],
"Resource": [
"arn:aws:iam::'${AWS_ACCOUNT_ID}':role/PrefectServerTaskRole"
],
"Condition": {
"StringEquals": {
"iam:PassedToService": "<http://ecs-tasks.amazonaws.com|ecs-tasks.amazonaws.com>"
}
}
}
]
}'
Additional security considerations:
1. You could further scope the ECR permissions to specific repositories:
json
{
"Effect": "Allow",
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:GetDownloadUrlForLayer",
"ecr:BatchGetImage"
],
"Resource": "arn:aws:ecr:<region>:<account-id>:repository/prefect-server"
}
2. CloudWatch logs can be scoped to specific log groups:
json
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:<region>:<account-id>:log-group:/ecs/prefect-server:*"
}
3. ECS task permissions can be scoped to specific clusters:
```json
{
"Effect": "Allow",
"Action": [
"ecs:StartTask",
"ecs:StopTask",
"ecs:DescribeTasks"
],Marvin
07/29/2025, 3:54 PM