AWS Fundamentals: Region, IAM and Key Services

1. Overview

This article helps you quickly grasp the three most important pillars when getting started with AWS: region (where to place your infrastructure), IAM (who can do what) and key services (EC2, S3, RDS, Lambda, VPC…). After reading, you’ll understand how to organize your AWS account securely, know how to create IAM roles for your applications, and be able to write Java code to access S3 using AWS SDK v2.

2. Region and Availability Zone

AWS divides its infrastructure into regions (geographic areas, such as ap-southeast-1 in Singapore) and each region contains multiple Availability Zones (AZ) — data centers that are independent in terms of power and network, located close together to keep latency low but still maintain fault isolation.

aws ec2 describe-availability-zones --region ap-southeast-1

The command above lists the AZs in the Singapore region. The output typically looks like:

ZoneName: ap-southeast-1a
ZoneName: ap-southeast-1b
ZoneName: ap-southeast-1c

When designing your system, you should deploy across at least 2 AZs for fault tolerance (high availability). For example, an Auto Scaling Group or a Multi-AZ RDS instance both rely on this principle.

Note: Some services are global (not tied to a specific region) such as IAM, Route 53, and CloudFront. Most other services (EC2, S3, RDS…) are regional, and data does not automatically replicate to other regions unless you configure it.

2.1. How to Choose a Region

Three main criteria: latency (proximity to your users), compliance (data residency requirements), and cost (service prices vary across regions). For Vietnamese users, ap-southeast-1 (Singapore) is typically the default choice due to low latency and comprehensive services.

3. IAM: User, Group, Role, Policy

IAM (Identity and Access Management) controls who can access which resources. Four core concepts:

  • User: a specific identity (person or service) with its own credentials.
  • Group: a collection of users with a common policy attached for easier management.
  • Role: a set of temporary permissions not tied to fixed credentials — used for EC2, Lambda, or cross-account access.
  • Policy: a JSON document describing permissions (allow/deny) on actions and resources.
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::order-invoices-bucket/*"
    }
  ]
}

This policy allows reading/writing objects in the order-invoices-bucket bucket. Attach the policy to a role and then attach that role to an EC2 instance or Lambda function running the InvoiceStorageService service — the application will automatically have the necessary permissions without needing to hard-code access key/secret key.

aws iam create-role \
  --role-name InvoiceServiceRole \
  --assume-role-policy-document file://trust-policy.json

aws iam attach-role-policy \
  --role-name InvoiceServiceRole \
  --policy-arn arn:aws:iam::123456789012:policy/InvoiceBucketAccess

The first command creates a role with trust-policy.json that specifies who can “assume” this role (for example, the EC2 service). The second command attaches the specific permission policy to the role.

Note: Never embed access key/secret key directly in code or commit to Git. Use IAM Role (for resources running on AWS) or IAM Identity Center/SSO (for users).

3.1. Principle of Least Privilege

Grant only the necessary permissions, don’t use AdministratorAccess policy for service accounts. For example, if a Lambda only needs to read from one bucket, you shouldn’t grant s3:* across your entire account.

{
  "Effect": "Deny",
  "Action": "s3:DeleteBucket",
  "Resource": "*"
}

A Deny statement always takes precedence over Allow in case of conflict — this is a way to add a “safety net” against accidentally deleting important buckets.

4. Key Services

4.1. Compute

  • EC2: virtual machines rented by the hour/second that you manage yourself.
  • Lambda: runs code based on events (event-driven), no server management, billed per invocation and execution time.
  • ECS/EKS: runs containers (Docker) with orchestration.
aws lambda invoke \
  --function-name CalculateOrderTotal \
  --payload '{"orderId":"ORD-1001"}' \
  response.json

This command invokes the CalculateOrderTotal function with a JSON payload, and the result is written to response.json.

4.2. Storage

  • S3: object storage for storing any type of file (images, logs, backups) with 99.999999999% durability.
  • EBS: block storage volumes attached to EC2 instances.
  • EFS: a shared file system that multiple EC2 instances can mount.

4.3. Database

  • RDS: managed relational database (MySQL, PostgreSQL…), automatic backup and patching.
  • DynamoDB: NoSQL, automatic horizontal scaling, single-digit millisecond latency.

4.4. Network

  • VPC: virtual private network where you define subnets, route tables, and security groups.
  • Security Group: stateful firewall at the instance level.
Resources:
  OrderServiceSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow HTTPS inbound
      VpcId: vpc-0123456789abcdef0
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0

This CloudFormation template creates a Security Group named OrderServiceSG that only opens port 443 (HTTPS) to all IPs — all other inbound rules are denied by default.

5. Calling S3 from a Java Application

This section demonstrates how a Spring Boot service uploads files to S3 using AWS SDK for Java v2.

<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>s3</artifactId>
    <version>2.25.16</version>
</dependency>
public class InvoiceStorageService {

    private final S3Client s3Client;
    private final String bucketName;

    public InvoiceStorageService(S3Client s3Client, String bucketName) {
        this.s3Client = s3Client;
        this.bucketName = bucketName;
    }

    public void uploadInvoice(String orderId, byte[] content) {
        PutObjectRequest request = PutObjectRequest.builder()
                .bucket(bucketName)
                .key("invoices/" + orderId + ".pdf")
                .build();

        s3Client.putObject(request, RequestBody.fromBytes(content));
    }
}

S3Client is injected through the constructor (credentials are automatically pulled from the IAM Role if running on EC2/Lambda, no need to hard-code). The uploadInvoice method creates a PutObjectRequest with the key as the object path in the bucket, then calls putObject to push the content to S3.

Configuring the S3Client bean:

@Bean
public S3Client s3Client() {
    return S3Client.builder()
            .region(Region.AP_SOUTHEAST_1)
            .build();
}

There’s no need to explicitly pass a credentialsProvider — the SDK automatically uses the Default Credentials Provider Chain: it looks in order at environment variables, the ~/.aws/credentials file, and then the IAM Role attached to EC2/ECS/Lambda.

5.1. Testing with a Mock S3Client

@Test
void shouldCallPutObjectWithCorrectKey() {
    S3Client mockS3 = mock(S3Client.class);
    InvoiceStorageService service = new InvoiceStorageService(mockS3, "order-invoices-bucket");

    service.uploadInvoice("ORD-1001", "invoice content".getBytes());

    ArgumentCaptor<PutObjectRequest> captor = ArgumentCaptor.forClass(PutObjectRequest.class);
    verify(mockS3).putObject(captor.capture(), any(RequestBody.class));

    assertThat(captor.getValue().bucket()).isEqualTo("order-invoices-bucket");
    assertThat(captor.getValue().key()).isEqualTo("invoices/ORD-1001.pdf");
}

This test mocks the S3Client (using Mockito) to avoid calling AWS during CI. ArgumentCaptor captures the actual PutObjectRequest that was sent, and AssertJ verifies that bucket and key are correct as expected — ensuring the request building logic is correct without incurring network costs.

Note: For true integration tests with S3, use LocalStack or Testcontainers libraries instead of calling actual production buckets — this avoids unexpected costs and data risks.

6. Common Mistakes

  • Accidentally making S3 buckets public: always enable “Block Public Access” at the account level unless you really need a public bucket (for example, for a static website).
  • Attaching overly broad policies ("Action": "*", "Resource": "*") to test roles and forgetting to remove them in production.
  • Not enabling MFA for the root account: the root account has all permissions, losing root access is the most serious risk.
  • Hard-coding access keys in code: use IAM Roles, Secrets Manager, or environment variables via CI/CD secrets instead.
  • Choosing the wrong region leading to high latency for end users or violating data residency requirements.

7. Conclusion

Region helps you choose the right infrastructure location balancing latency, cost, and compliance. IAM is your first and most important defense layer — always apply the principle of least privilege and prioritize Roles over static access keys. Understanding the core services (EC2, S3, RDS, Lambda, VPC) and how to integrate them via AWS SDK provides the foundation you need to start deploying real systems on AWS.

awscloudiamdevopsjava