AWS

AWS IAM Roles Explained for Java Developers

By Utility Zone · 2026-10-03T15:56:34.079235

AWS article series — Security, Monitoring & Reliability
Audience: Java developers, Spring Boot engineers, tech leads, and DevOps engineers
Goal: Understand how IAM roles let Java applications access AWS services securely without embedding long-lived AWS credentials in code.

1. Why Java developers need to understand IAM roles

A Spring Boot service may need to upload files to Amazon S3, read secrets from AWS Secrets Manager, publish messages to Amazon SQS, or write telemetry to CloudWatch. The application needs AWS permissions, but placing access keys directly in source code or configuration creates avoidable risk.

An IAM role is an AWS identity with permissions that can be assumed by a trusted principal. When a workload assumes the role, AWS provides temporary credentials that the AWS SDK can use to sign requests.

The key idea is simple:

Give the application an identity with only the permissions it needs, and let the AWS environment provide temporary credentials.

2. IAM users, roles, and policies: the difference

ConceptPurposeJava application example
IAM userAn identity that can have long-term credentials, though other authentication options existA human developer identity; generally not the identity to embed in a deployed service
IAM roleAn assumable identity with permissions and a trust policyAn EC2 instance role or ECS task role used by a Spring Boot service
IAM policyA JSON document describing allowed or denied actions on resourcesAllow the service to upload objects to one S3 bucket prefix
Trust policyDefines which principals can assume a roleAllow EC2, ECS tasks, or a specific federated principal to assume a role
Temporary credentialsShort-lived access key, secret key, and session token issued for a role sessionCredentials automatically obtained by the AWS SDK credential provider chain

A role's permissions policy answers “what can this identity do?” Its trust policy answers “who can assume this role?” Both matter.

3. How role-based access works

AWS IAM role flow for Java applications

  1. An AWS compute service is configured with a role appropriate to the workload.
  2. The runtime exposes temporary credentials through its supported credential mechanism.
  3. The AWS SDK for Java retrieves those credentials using its default credential provider chain.
  4. The SDK signs the request to the AWS service.
  5. IAM evaluates the identity's permissions together with applicable resource policies, organization controls, permissions boundaries, and other policy constraints.

The application should not need to know the temporary access key values or manually refresh them when using a supported SDK credential provider.

4. Use the AWS SDK for Java 2.x default credentials provider chain

For most deployed Java applications, prefer the AWS SDK's default credential provider chain. It checks supported credential sources in a defined order, which can include JVM properties, environment variables, web identity, shared AWS config/credentials files, container credentials, and EC2 instance-profile credentials. The exact chain and precedence depend on the SDK version and configuration.

Maven dependencies

For an application that uses Amazon S3, use the AWS SDK for Java 2.x. Manage versions consistently, preferably with the AWS SDK BOM.

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>${aws.sdk.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3</artifactId>
    </dependency>
</dependencies>

Set ${aws.sdk.version} to a current, tested AWS SDK v2 release in your project properties or dependency management. Keep related AWS SDK modules aligned to compatible versions.

Java example: access S3 without supplying credentials

import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Request;
import software.amazon.awssdk.services.s3.model.ListObjectsV2Response;

public class S3ListingExample {
    public static void main(String[] args) {
        String bucketName = System.getenv("S3_BUCKET");

        try (S3Client s3 = S3Client.builder()
                .region(Region.AP_SOUTH_1)
                // No static credentials configured here.
                // The SDK resolves credentials through its provider chain.
                .build()) {

            ListObjectsV2Response response = s3.listObjectsV2(
                ListObjectsV2Request.builder()
                    .bucket(bucketName)
                    .maxKeys(10)
                    .build()
            );

            response.contents().forEach(
                object -> System.out.println(object.key())
            );
        }
    }
}

The example assumes S3_BUCKET is configured and the role has permission to list the bucket. In a real application, avoid printing sensitive object names if they could reveal private information.

For a Spring-managed application, create the SDK client as a singleton bean and inject it into services. Avoid creating a new client for every request.

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;

@Configuration
public class AwsClientConfiguration {

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

The AWS SDK's default credentials provider is used when no explicit credentials provider is supplied.

5. Which role should your Java application use?

Where the application runsTypical role mechanismWhat to configure
Amazon EC2EC2 instance profileAttach a role to the instance; the SDK can obtain temporary credentials from instance metadata
Amazon ECS / FargateECS task rolePut application permissions on the task role; distinguish it from the task execution role used by the platform to pull images and write logs
Amazon EKSEKS Pod Identity or IAM Roles for Service Accounts (IRSA), depending on setupAssociate a role with the workload's Kubernetes service account using the chosen mechanism
AWS LambdaLambda execution roleGrant the function's role access to required services
Local developmentAWS IAM Identity Center or another approved developer credential flowAuthenticate with the approved developer profile; do not copy production credentials to a laptop
CI/CD pipelineOIDC federation or the platform's approved role-assumption mechanismUse short-lived federated credentials rather than storing long-lived AWS keys in pipeline variables

Do not automatically give an entire EC2 instance or ECS task every permission used by every application. Separate roles by service and environment when practical.

6. Example: allow read/write access to a specific S3 prefix

Suppose the application needs to read and write objects under uploads/ in one bucket. A policy can scope object actions to that prefix. Replace the example bucket name with your actual bucket.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadWriteApplicationUploads",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject"
      ],
      "Resource": "arn:aws:s3:::example-app-bucket/uploads/*"
    }
  ]
}

This object-level policy does not grant s3:ListBucket on the bucket. Add that action separately only if the application needs to list objects, with the bucket ARN as the resource and an appropriate prefix condition. Depending on your design, multipart uploads, object tags, encryption settings, or deletes may require additional permissions.

A policy allowing s3:PutObject is not automatically a policy allowing object reads, deletes, bucket listing, or bucket administration. Grant each operation deliberately.

7. Trust policy versus permissions policy

A simplified EC2 role trust relationship allows the EC2 service to assume the role:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

This trust policy alone does not give the application S3 access. The role also needs an appropriate permissions policy, and the role must be attached to the EC2 instance through an instance profile.

For ECS tasks, Lambda, EKS workloads, and federated identities, the trust relationship differs. Use the trust policy pattern documented for that specific service and avoid broad principals unless there is a documented reason.

8. What about local development?

On a developer machine, use your organization's approved authentication method, commonly AWS IAM Identity Center. The SDK can use a named profile when configured for it.

For example, a developer may sign in using the AWS CLI and then run the application with the matching profile:

aws sso login --profile dev
AWS_PROFILE=dev ./mvnw spring-boot:run

This is an example for shells that support the shown environment-variable syntax. On Windows, set the environment variable using the appropriate PowerShell or Command Prompt syntax. Configure the profile with aws configure sso if it has not already been created.

Do not commit the shared AWS credentials file, SSO cache, access keys, or profile-specific secrets to source control. A local developer profile should have development permissions, not unrestricted production access.

9. Common mistakes to avoid

  • Hardcoding access keys: credentials can leak through repositories, logs, images, or build artifacts.
  • Using administrator permissions for an application: broad permissions increase the impact of a compromise.
  • Confusing the ECS task role with the task execution role: the application normally uses the task role; the execution role is used by ECS for platform operations such as image pulls and log delivery.
  • Assuming a trust policy grants service permissions: trust controls who can assume the role; permissions policies control what the role can do.
  • Forgetting resource policies: an S3 bucket policy, KMS key policy, VPC endpoint policy, service control policy, or permissions boundary may affect the final result.
  • Using the wrong region: credentials can be valid while the client points at the wrong region or resource.
  • Using an old SDK or unsupported credential configuration: keep SDK versions updated and use the provider documented for the target runtime.
  • Logging credentials: never print authorization headers, session tokens, or secrets while troubleshooting.
  • Giving every service the same role: isolate permissions where practical so one workload's compromise does not automatically expose another workload's resources.

10. Troubleshooting access denied errors

When an AWS SDK call fails, capture the service, operation, region, request ID, and sanitized exception details. Do not log secrets or complete signed requests.

Check these items in order:

  1. Which identity is the application using? Verify the runtime role or developer profile, using approved diagnostics. For example, aws sts get-caller-identity is useful when run with the same credentials and environment as the application.
  2. Is the expected role attached or associated? Check the EC2 instance profile, ECS task definition, Lambda execution role, or EKS workload identity configuration.
  3. Does the role's permissions policy allow the exact action and resource? Check bucket versus object ARNs and any prefix conditions.
  4. Does the trust policy allow the expected principal? This is especially important for role assumption and federated identity.
  5. Is there an explicit deny or higher-level restriction? Review resource policies, permissions boundaries, session policies, and AWS Organizations service control policies as applicable.
  6. Does the resource require another permission? For example, S3 objects encrypted with a customer-managed KMS key may require appropriate KMS permissions and key-policy access.
  7. Is the request using the correct region and account? Confirm resource ARN, account, region, and endpoint.
  8. Are credentials available and current? For workloads, check role association, metadata/network access, container configuration, and SDK version rather than copying static keys into the application.

Use IAM Access Analyzer and policy simulation tools where appropriate, but remember that simulations may not capture every runtime condition or every resource policy interaction.

11. A practical production checklist

  • Use a workload role for each deployed service or clearly bounded group of workloads.
  • Use the AWS SDK for Java 2.x default credentials provider chain where appropriate.
  • Grant only required actions and resource scopes.
  • Keep trust policies restricted to the intended service or identity.
  • Separate developer, test, staging, and production access.
  • Use federation or an approved identity system for CI/CD and local development.
  • Avoid static credentials in source code, images, configuration files, and pipeline variables.
  • Review bucket, KMS, and other resource policies alongside role policies.
  • Monitor access and review unused permissions periodically.
  • Document the role owner, purpose, resources, and process for changing permissions.

12. Key takeaways

IAM roles provide Java applications with a managed identity and temporary credentials. In most AWS-hosted deployments, the application can use the AWS SDK's default credentials provider chain rather than managing access keys itself.

Remember the distinction: trust policy = who can assume the role; permissions policy = what the role can do. Combine that model with least privilege, workload-specific roles, and careful review of resource policies to reduce credential-management risk.

References


Educational example: validate policy scope, trust relationships, SDK versions, and workload identity configuration in a non-production environment before applying changes to production.