Skip to main content
Version: v2.10.0

Prerequisites

The template consumes existing networking rather than provisioning it, so a handful of things have to be in place in your account before the stack will launch. None of it is exotic. Most AWS environments already have the VPC and the subnet configured, and the piece organisations are usually missing is the deployment policy attached to the principal running create-stack.

We've ordered the five items below the way we'd tackle them.

1. A VPC and at least one subnet

VpcId and Ec2SubnetId are typed as AWS::EC2::VPC::Id and AWS::EC2::Subnet::Id, which means the console renders them as pickers and CloudFormation rejects an ID that doesn't exist in the region you're deploying to. The subnet you pick hosts both the EC2 instance and the EFS mount target, so those two always land in the same Availability Zone.

If you're planning to set AssignPublicIp to true, that subnet needs public IP mapping enabled on it. We'd check this before anything else, because CloudFormation won't warn you.

Creating the PostgreSQL database adds two more subnets. RdsSubnet1Id and RdsSubnet2Id are collected into a DB subnet group for a Multi-AZ instance, so they have to sit in separate Availability Zones. Both are plain strings validated against the pattern ^$|subnet-[0-9a-fA-F]+$, which means a mistyped identifier is rejected at parameter validation rather than halfway through provisioning.

2. Outbound internet access from the subnet

This one bites people. The stack contains no internet gateway, no NAT gateway and no VPC endpoints, so whatever egress path the subnet already has is the one the bootstrap will use. During initialisation the instance reaches out to the Ubuntu package mirrors, to amazoncloudwatch-agent.s3.amazonaws.com for the CloudWatch agent, to awscli.amazonaws.com for AWS CLI v2, to GitHub for Docker Compose 1.29.2, and to s3://yeedu-softwares/releases/ for the Yeedu release itself. A subnet with no route outwards produces a stack that reports success while the platform never actually starts.

3. An Ubuntu 22.04 AMI and, optionally, a key pair

Ec2ImageId has no default. Look up the current Ubuntu 22.04 LTS AMI published for your region and pass its identifier. The parameter is checked against ^(?!.*\s)ami-[0-9a-fA-F]{8,}$, so a trailing space becomes a validation error rather than a mystery.

Ec2KeyName defaults to an empty string and the HasKeyPair condition removes the KeyName property completely when you leave it that way. Supply the name of an existing key pair if you want SSH access to the machine, and remember that the instance security group already permits port 22 from 0.0.0.0/0.

4. Certificate and key in S3, if you want HTTPS

Setting EnableHttps to true makes three additional parameters mandatory: CustomDnsName, HttpsCertS3Path and HttpsKeyS3Path. The two S3 paths are complete URIs of the form s3://bucket/key, one referencing a certificate file and the other its private key. The bootstrap copies both to /opt/Core-Services-Formation/ssl/, then writes the SSL-enabled hostnames for the REST API, the UI, the history server and the functions proxy into yeedu-connection.properties.

The instance role carries AmazonS3FullAccess, so the bucket holding those files simply needs to live in the same account.

5. Deployment permissions

The principal that creates the stack needs a genuinely broad policy, because CloudFormation performs every underlying API call on its behalf. What follows is the policy shipped alongside the template as automation/aws/aws-policy.json.

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cloudformation:CreateStack",
"cloudformation:DescribeStacks",
"cloudformation:DescribeStackEvents",
"cloudformation:DescribeStackResources",
"cloudformation:GetTemplate",
"cloudformation:ValidateTemplate",
"cloudformation:DeleteStack",
"ec2:DescribeInstances",
"ec2:DescribeSecurityGroups",
"ec2:DescribeSubnets",
"ec2:DescribeVpcs",
"ec2:RunInstances",
"ec2:StartInstances",
"ec2:StopInstances",
"ec2:CreateSecurityGroup",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:RevokeSecurityGroupEgress",
"ec2:DeleteSecurityGroup",
"ec2:CreateTags",
"ec2:DescribeImages",
"ec2:DescribeKeyPairs",
"ec2:TerminateInstances",
"ec2:DescribeInstanceTypes",
"ec2:CreateNetworkInterface",
"ec2:DeleteNetworkInterface",
"ec2:ModifyNetworkInterfaceAttribute",
"ec2:DescribeAvailabilityZones",
"ec2:CreateVolume",
"ec2:DeleteVolume",
"iam:CreateRole",
"iam:AttachRolePolicy",
"iam:GetRole",
"iam:PassRole",
"iam:PutRolePolicy",
"iam:CreatePolicy",
"iam:DeletePolicy",
"iam:DeleteAccessKey",
"iam:DeleteRolePolicy",
"iam:CreateInstanceProfile",
"iam:AddRoleToInstanceProfile",
"iam:RemoveRoleFromInstanceProfile",
"iam:DeleteInstanceProfile",
"iam:DeleteRole",
"iam:CreateUser",
"iam:GetUser",
"iam:TagUser",
"iam:DeleteUser",
"iam:CreateAccessKey",
"iam:ListAccessKeys",
"iam:DetachRolePolicy",
"iam:PutUserPolicy",
"iam:GetUserPolicy",
"iam:DeleteUserPolicy",
"iam:TagRole",
"iam:CreateServiceLinkedRole",
"s3:CreateBucket",
"s3:DeleteBucket",
"s3:PutBucketVersioning",
"s3:PutBucketPolicy",
"s3:PutEncryptionConfiguration",
"s3:PutBucketPublicAccessBlock",
"s3:PutObject",
"s3:PutBucketTagging",
"logs:CreateLogGroup",
"logs:PutRetentionPolicy",
"logs:TagResource",
"logs:DeleteLogGroup",
"secretsmanager:CreateSecret",
"secretsmanager:PutSecretValue",
"secretsmanager:TagResource",
"secretsmanager:GetRandomPassword",
"secretsmanager:DeleteSecret",
"elasticfilesystem:CreateFileSystem",
"elasticfilesystem:DeleteFileSystem",
"elasticfilesystem:DescribeFileSystems",
"elasticfilesystem:DescribeReplicationConfigurations",
"elasticfilesystem:DescribeLifecycleConfiguration",
"elasticfilesystem:DescribeBackupPolicy",
"elasticfilesystem:DescribeFileSystemPolicy",
"elasticfilesystem:CreateMountTarget",
"elasticfilesystem:DeleteMountTarget",
"elasticfilesystem:DescribeMountTargets",
"elasticfilesystem:CreateAccessPoint",
"elasticfilesystem:DeleteAccessPoint",
"elasticfilesystem:DescribeAccessPoints",
"elasticfilesystem:TagResource",
"ecr:CreateRepository",
"ecr:PutLifecyclePolicy",
"ecr:SetRepositoryPolicy",
"ecr:TagResource",
"ecr:DeleteRepository",
"rds:CreateDBInstance",
"rds:DeleteDBInstance",
"rds:DescribeDBInstances",
"rds:CreateDBSubnetGroup",
"rds:DeleteDBSubnetGroup",
"rds:CreateDBParameterGroup",
"rds:DeleteDBParameterGroup",
"rds:ModifyDBParameterGroup",
"rds:DescribeDBSubnetGroups",
"rds:DescribeDBParameterGroups",
"rds:AddTagsToResource",
"rds:DescribeEngineDefaultParameters",
"rds:CreateDBSnapshot",
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": "*"
}
]
}

The ecr:* actions matter only when CreateContainerRepositories is true, and the rds:* actions only when RdsCreate is true. You can remove them if you never intend to enable those repositories, though we'd recommend keeping the whole policy for an initial deployment and tightening it afterwards.

Acknowledging the IAM capability

The stack creates an IAM user and an IAM policy under names it generates itself, which CloudFormation classifies as named IAM resources. Nothing deploys until you acknowledge that explicitly.

On the command line, pass the flag --capabilities CAPABILITY_NAMED_IAM. In the console, tick the acknowledgement checkbox on the Configure stack options page before submitting.