AWS Console Manual Web Application Baseline Manual
This runbook provisions the baseline AWS infrastructure for one web application in the AWS Console. Region: ap-northeast-1 (Asia Pacific - Tokyo).
Target Architecture
The baseline infrastructure for a single web application in AWS Console.
| Component | Configuration |
|---|---|
| Region | ap-northeast-1 (Asia Pacific - Tokyo) |
| Compute | One Ubuntu Server 24.04 LTS EC2 instance in a public subnet |
| Public access | Elastic IP directly associated with EC2; TCP 22, 80, and 443 allowed inbound |
| Database | Publicly accessible Amazon RDS for PostgreSQL 16 in public subnets; PostgreSQL exposed on TCP 5432 |
| Object storage | One S3 bucket; only public/* can be read anonymously |
| Amazon SES verified identities and SMTP credentials | |
| Load balancer | Not part of this baseline. Add an ALB only when multiple instances or managed TLS termination is needed. |
Values To Decide Before Starting
Use one consistent application prefix, such as myapp-prod.
| Value | Example / required value |
|---|---|
| Application prefix | <APP> |
| VPC CIDR | <VPC_CIDR> |
| Public subnet CIDRs | <PUBLIC_SUBNET_A_CIDR> , <PUBLIC_SUBNET_C_CIDR> |
| EC2 instance type | <EC2_INSTANCE_TYPE> |
| EBS root-volume size | <EBS_GIB> GiB |
| Database instance class and storage | <DB_INSTANCE_CLASS>, <DB_STORAGE_GIB> GiB initial, <DB_MAX_STORAGE_GIB> GiB maximum |
| Database name | <DB_NAME> |
| SSH source CIDR | <SSH_SOURCE_CIDR> ; 0.0.0.0/0 |
| S3 bucket name | <GLOBALLY_UNIQUE_BUCKET_NAME> |
| SES sending domain or email | <SENDING_IDENTITY> |
Required Console Permissions
Give the client-provided IAM user the three inline policies below before beginning. Together they cover every create and required configuration action in this manual, plus the read-only actions the AWS Console uses to populate its forms. Create each policy under IAM > Users > the provisioning user > Add permissions > Create inline policy > JSON, naming them <APP>-infrastructure-provisioning, <APP>-iam-provisioning, and <APP>-ses-support-provisioning. Replace <ACCOUNT_ID>, <APP>, <PROVISIONING_IAM_USER>, and <GLOBALLY_UNIQUE_BUCKET_NAME> first.
These are provisioning policies, not application policies. They intentionally permit resource creation only in ap-northeast-1, except IAM and S3 actions, which are global services. Some create actions do not support resource-level permissions, so their resource must remain *. Remove the policies or user when provisioning is complete.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "cloudwatch:GetMetricData",
"Resource": "*",
"Condition": {
"StringEquals": { "aws:RequestedRegion": "ap-northeast-1" }
}
},
{
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"ec2:GetConsole*",
"rds:Describe*",
"rds:ListTagsForResource"
],
"Resource": "*",
"Condition": {
"StringEquals": { "aws:RequestedRegion": "ap-northeast-1" }
}
},
{
"Effect": "Allow",
"Action": [
"s3:GetAccountPublicAccessBlock",
"s3:ListAllMyBuckets"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"s3:GetBucket*",
"s3:ListBucket"
],
"Resource": "arn:aws:s3:::<GLOBALLY_UNIQUE_BUCKET_NAME>"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::<GLOBALLY_UNIQUE_BUCKET_NAME>/*"
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateVpc",
"ec2:ModifyVpcAttribute",
"ec2:CreateSubnet",
"ec2:ModifySubnetAttribute",
"ec2:CreateInternetGateway",
"ec2:AttachInternetGateway",
"ec2:CreateRouteTable",
"ec2:AssociateRouteTable",
"ec2:CreateRoute",
"ec2:CreateSecurityGroup",
"ec2:AuthorizeSecurityGroupIngress",
"ec2:AuthorizeSecurityGroupEgress",
"ec2:CreateTags"
],
"Resource": "*",
"Condition": {
"StringEquals": { "aws:RequestedRegion": "ap-northeast-1" }
}
},
{
"Effect": "Allow",
"Action": [
"ec2:CreateKeyPair",
"ec2:RunInstances",
"ec2:AllocateAddress",
"ec2:AssociateAddress",
"ec2:ModifyVolume",
"ec2:StartInstances",
"ec2:StopInstances",
"ec2:RebootInstances",
"ec2:CreateTags"
],
"Resource": "*",
"Condition": {
"StringEquals": { "aws:RequestedRegion": "ap-northeast-1" }
}
},
{
"Effect": "Allow",
"Action": [
"kms:DescribeKey",
"kms:ListAliases"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "kms:CreateGrant",
"Resource": "arn:aws:kms:ap-northeast-1:<ACCOUNT_ID>:key/*",
"Condition": {
"ForAnyValue:StringEquals": {
"kms:ViaService": [
"ec2.ap-northeast-1.amazonaws.com",
"rds.ap-northeast-1.amazonaws.com"
]
},
"Bool": { "kms:GrantIsForAWSResource": "true" }
}
},
{
"Effect": "Allow",
"Action": [
"kms:DescribeKey",
"kms:GenerateDataKeyWithoutPlaintext"
],
"Resource": "arn:aws:kms:ap-northeast-1:<ACCOUNT_ID>:key/*",
"Condition": {
"ForAnyValue:StringEquals": {
"kms:ViaService": [
"ec2.ap-northeast-1.amazonaws.com",
"rds.ap-northeast-1.amazonaws.com"
]
}
}
},
{
"Effect": "Allow",
"Action": [
"rds:CreateDBSubnetGroup",
"rds:CreateDBParameterGroup",
"rds:CreateDBInstance",
"rds:ModifyDBParameterGroup",
"rds:ModifyDBInstance",
"rds:StartDBInstance",
"rds:StopDBInstance",
"rds:RebootDBInstance",
"rds:AddTagsToResource"
],
"Resource": "*",
"Condition": {
"StringEquals": { "aws:RequestedRegion": "ap-northeast-1" }
}
},
{
"Effect": "Allow",
"Action": [
"s3:CreateBucket",
"s3:PutBucketOwnershipControls",
"s3:PutBucketVersioning",
"s3:PutEncryptionConfiguration",
"s3:PutBucketPublicAccessBlock",
"s3:PutBucketPolicy",
"s3:PutBucketCors"
],
"Resource": [
"arn:aws:s3:::<GLOBALLY_UNIQUE_BUCKET_NAME>",
"arn:aws:s3:::<GLOBALLY_UNIQUE_BUCKET_NAME>/*"
]
}
]
}Managed-Policy Alternative
Use the inline policies above for a client-facing provisioning user. If maintaining them is disproportionate to this one-off setup, attach these AWS managed policies temporarily instead:
| Manual workflow | AWS managed policy |
|---|---|
| VPC, subnets, routes, and security groups | AmazonVPCFullAccess |
| Key pair, EC2, EBS, and Elastic IP | AmazonEC2FullAccess |
| RDS subnet group, parameter group, and database | AmazonRDSFullAccess |
| S3 bucket, policy, encryption, versioning, CORS, and object management | AmazonS3FullAccess |
| Application IAM user/access key and SES SMTP IAM user | IAMFullAccess |
| IAM Access Analyzer policy validation | IAMAccessAnalyzerFullAccess |
| RDS Console metrics | CloudWatchReadOnlyAccess |
| SES identity and production-access request | AmazonSESFullAccess |
| SES quota increase and AWS Support case escalation | ServiceQuotasFullAccess , AWSSupportAccess |
AdministratorAccess is the convenient single-policy alternative, but it is broader than all of the above combined. Do not give it to the running application, and detach it from the provisioning user after handoff. The application IAM user created later receives only the S3 policy shown below.
The KMS permissions in the inline policy are deliberately not replaced with an AWS managed policy. If the account requires customer-managed KMS keys instead of the default EBS and RDS keys, the key administrator must also authorize this user in the KMS key policy. Do not compensate by attaching a broad KMS managed policy.
If Enhanced Monitoring is enabled during RDS creation, choose Create new role and name it `rds-monitoring-role`. Its trusted entity must be `monitoring.rds.amazonaws.com`, and it must use `AmazonRDSEnhancedMonitoringRole`. The IAM provisioning policy can create, read, and pass only that named role and attach only that AWS managed policy.
Permission Mapping
| Manual action | Create/configure permissions |
|---|---|
| VPC, subnets, internet gateway, routes, and security groups | ec2:CreateVpc , CreateSubnet , CreateInternetGateway , AttachInternetGateway , CreateRouteTable , AssociateRouteTable , CreateRoute , CreateSecurityGroup , ingress/egress authorization, and required VPC/subnet changes and tags |
| SSH key pair, EC2 instance/EBS, Elastic IP | ec2:CreateKeyPair , RunInstances , AllocateAddress , AssociateAddress , ModifyVolume for EBS resizing, StartInstances , StopInstances , RebootInstances , and tags |
| Encrypted EBS and RDS resources | Discover the default keys in the Console; grant and data-key use only through EC2 or RDS in Tokyo |
| RDS subnet group, parameter group, and database | Create the subnet group, parameter group, and DB instance; modify it, including its storage size; start, stop, and reboot it; add resource tags; and create only the RDS service-linked role when it does not already exist |
| S3 bucket, public-prefix policy, CORS, and object management | s3:CreateBucket , ownership controls, versioning, encryption, public-access block, bucket policy, CORS, and s3:GetObject , s3:PutObject , s3:DeleteObject |
| Application S3 user/access key and SES SMTP user | Create the S3/SMTP users and access keys; create and attach only <APP>-s3-bucket-access to <APP>-s3-user; create and configure only AWSSESSendingGroupDoNotRename; add only generated SMTP users to that group |
| Optional RDS Enhanced Monitoring role | Create, list IAM users/groups/roles and managed policies; read and pass only rds-monitoring-role; and attach only AmazonRDSEnhancedMonitoringRole |
| RDS Console metrics | cloudwatch:GetMetricData in Tokyo |
| SES configuration set, identity, DKIM, and production access | ses:CreateConfigurationSet , CreateEmailIdentity , PutEmailIdentityDkimAttributes , and PutAccountDetails |
| SES quota increase and Support escalation | Service Quotas read/request actions; create, read, and reply to AWS Support cases |
1. Create The Network
- Open VPC in ap-northeast-1 and select Your VPCs > Create VPC.
- Select VPC and more and use these settings:
| Setting | Value |
|---|---|
| Name tag auto-generation | <APP> |
| IPv4 CIDR block | <VPC_CIDR> |
| Number of Availability Zones | 2 |
| Number of public subnets | 2 |
| Public subnet CIDRs | <PUBLIC_SUBNET_A_CIDR> , <PUBLIC_SUBNET_C_CIDR> |
| Number of private subnets | 0 |
| NAT gateways | None |
| VPC endpoints | None (do not create an S3 endpoint) |
| DNS options | Keep Enable DNS hostnames and Enable DNS resolution selected |
- Create the VPC. The EC2 instance and publicly accessible RDS instance use the two public subnets. Confirm each public route table has a 0.0.0.0/0 route to the internet gateway.
- In Subnets, rename the public subnets to <APP>-public-a and <APP>-public-c.
- Verify both public subnets have Auto-assign public IPv4 address enabled and a route to the internet gateway.
Create Security Groups
- Open EC2 > Security Groups > Create security group.
- Create <APP>-ec2-sg in <APP>-vpc with these inbound rules:
| Type | Protocol | Port | Source |
|---|---|---|---|
| SSH | TCP | 22 | <SSH_SOURCE_CIDR> |
| HTTP | TCP | 80 | 0.0.0.0/0 and ::/0 |
| HTTPS | TCP | 443 | 0.0.0.0/0 and ::/0 |
- Leave the default outbound rule allowing all outbound traffic.
- Create <APP>-rds-sg in the same VPC. Add one inbound rule: PostgreSQL, TCP 5432, source 0.0.0.0/0. Leave its default outbound rule.
Record the VPC ID, public subnet IDs, and both security-group IDs.
2. Create An SSH Key Pair
- In EC2 > Key Pairs, select Create key pair.
- Name it <APP>-ec2-key, choose ED25519 and .pem, then create it.
- Download the private key immediately and store it in the approved secret store with owner and recovery information. AWS cannot download it again.
Record key-pair name and secure location of the .pem file. Do not commit the key to source control or place it in shared storage.
3. Launch EC2 And Attach An Elastic IP
- In EC2 > Instances, select Launch instances.
- Name the instance <APP>-web-1.
- Under Application and OS Images, select Ubuntu Server 24.04 LTS from Canonical.
- Select <EC2_INSTANCE_TYPE>, the <APP>-ec2-key key pair, and <APP>-vpc.
- Select one public subnet, enable automatic public IP assignment, and select <APP>-ec2-sg.
- Set root EBS volume to <EBS_GIB> GiB, type gp3, with encryption enabled. Launch the instance.
- Wait until both instance status checks pass. Record its instance ID.
- Open EC2 > Elastic IP addresses > Allocate Elastic IP address and allocate an address in this region.
- Select the new address, choose Actions > Associate Elastic IP address, select <APP>-web-1, and associate it.
Record instance ID, Availability Zone, Elastic IP allocation ID, and Elastic IP address.
4. Create PostgreSQL 16 In RDS
Create The DB Subnet Group
- Open RDS > Subnet groups > Create DB subnet group.
- Name it <APP>-db-subnets, choose <APP>-vpc, and add <APP>-public-a and <APP>-public-c in different Availability Zones.
- Create the subnet group.
Create The Parameter Group
- Open RDS > Parameter groups > Create parameter group.
- Select PostgreSQL, family postgres16, type DB Parameter Group, and name it <APP>-postgres16-parameters.
- Create the parameter group.
- Select the new group, choose Edit, set rds.force_ssl to 0, and save. PostgreSQL 16 defaults this parameter to 1; setting it to 0 allows non-SSL client connections.
Create The Database
- Open RDS > Databases > Create database.
- Select Standard create, PostgreSQL, and engine version PostgreSQL 16.
- Select a template matching the environment. For production, enable Multi-AZ only if the availability requirement and budget support it.
- Set DB instance identifier to <APP>-postgres16 and master username to <DB_MASTER_USERNAME>. Choose Auto generate a password, then copy the generated master password to the approved secret store when RDS displays it. Do not place it in this manual or source control.
- Set DB instance class to <DB_INSTANCE_CLASS>. Under Storage, select General Purpose SSD (gp3), set Allocated storage to <DB_STORAGE_GIB> GiB, and enable storage encryption. Enable storage autoscaling and set Maximum storage threshold to <DB_MAX_STORAGE_GIB> GiB, sized for expected growth and budget.
- Under Connectivity, select <APP>-vpc, <APP>-db-subnets, Public access: Yes, and existing security group <APP>-rds-sg. Remove the default security group if it was added automatically.
- Expand Additional configuration, then select <APP>-postgres16-parameters as the DB parameter group. Set initial database name to <DB_NAME> if the application requires it.
- Enable automated backups and deletion protection for production. Review the estimated cost and create the database.
- Wait for the creation process to finish and the DB instance status to become Available. In the completion page, select View connection details and record the endpoint and port. If the completion page was dismissed, open the DB instance and use its Connectivity & security tab instead.
Record DB identifier, endpoint hostname, port 5432, database name, master username, parameter-group name, and secret-store reference for the password.
If RDS reports Unable to create the resource. Verify that you have permission to create service linked role, the provisioning IAM policy must allow iam:CreateServiceLinkedRole for AWSServiceRoleForRDS, as included above. If that role was just created, wait a few minutes for IAM propagation and retry. An AWS Organizations service control policy, permissions boundary, or session policy can still deny this action.
5. Create The S3 Bucket And Application Credentials
Create The Bucket
- Open S3 > Buckets > Create bucket.
- Name it <GLOBALLY_UNIQUE_BUCKET_NAME> and select ap-northeast-1.
- Keep Object Ownership set to ACLs disabled (recommended).
- Keep versioning enabled if uploads must be recoverable. Enable default encryption.
- Under Block Public Access settings, clear only the settings that prevent a public bucket policy: Block public access to buckets and objects granted through new public bucket or access point policies and Block public and cross-account access to buckets and objects through any public bucket or access point policies.
- Acknowledge that objects under public/ will be public, then create the bucket. If account-level Block Public Access prevents this, an account administrator must change that setting or use the private alternative.
Add The Public-Prefix Bucket Policy
- Open the bucket's Permissions tab > Bucket policy > Edit.
- Replace <GLOBALLY_UNIQUE_BUCKET_NAME> and save this policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadOnlyForPublicPrefix",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<GLOBALLY_UNIQUE_BUCKET_NAME>/public/*"
}
]
}Add The Requested Permissive CORS Rule
- Open the bucket's Permissions tab > Cross-origin resource sharing (CORS) > Edit.
- Save this configuration:
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
"AllowedOrigins": ["*"],
"ExposeHeaders": [],
"MaxAgeSeconds": 3000
}
]Create The Application IAM User
- Open IAM > Users > Create user and name it <APP>-s3-user.
- Do not enable AWS Management Console access.
- Open IAM > Policies > Create policy > JSON.
- Replace the bucket name and create this customer-managed policy as <APP>-s3-bucket-access:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListThisBucketOnly",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::<GLOBALLY_UNIQUE_BUCKET_NAME>"
},
{
"Sid": "ManageObjectsInThisBucketOnly",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::<GLOBALLY_UNIQUE_BUCKET_NAME>/*"
}
]
}- Return to <APP>-s3-user, select Add permissions > Attach policies directly, select <APP>-s3-bucket-access, and add the permission.
- Open the user's Security credentials tab > Create access key. Select Application running outside AWS if prompted, confirm the acknowledgement, and create the key.
- Download the CSV or copy the access key ID and secret access key into the approved secret store. The secret access key is shown once only.
Record bucket name & ARN, IAM user ARN, access-key ID, and secret store reference. Never record or transmit the secret key in tickets, chat, or source control.
6. Configure Amazon SES
- Open Amazon SES in ap-northeast-1 > Configuration > Verified identities > Create identity.
- Prefer Domain and enter <SENDING_IDENTITY>. Add the DNS verification records shown by SES at the domain's DNS provider. Enable DKIM and add all DKIM CNAME records.
- Wait until the identity status is Verified. An email-address identity is acceptable only when a sending domain cannot be verified.
- In SES, open Account dashboard and select Request production access. Provide the real sending use case, website, mail type, expected volume, bounce/complaint handling, and opt-out process. Wait for approval before sending to unverified recipients.
- Open SMTP settings > Create SMTP credentials. Name the IAM user <APP>-ses-smtp, create it, and download the SMTP username and password.
Record verified identity, DNS-record completion status, SES production-access status, SMTP endpoint for ap-northeast-1, SMTP username, and secret-store reference for the SMTP password.
Submit the production-access request in English when practical so AWS Support can review the use case clearly. Approval depends on the quality and legitimacy of the sending-use-case, compliance, and bounce/complaint handling information, not the request language alone. SMTP credentials are region-specific; keep them separate from the S3 IAM access key and rotate them when an operator with access leaves the project.
Final Handoff Checklist
Interactive verification before handing over infrastructure
Need Custom AWS Architecture & Automation?
Our senior DevOps & Cloud Architects design scalable, secure, and cost-efficient cloud baselines tailored to your enterprise.
