=============================================================================== S3 Role External Access =============================================================================== It provides the mechanism to create an IAM role with access on a specific location (path) within a S3 bucket. The access policy is assigned to the root of the external account, which is responsible for creating the internal policies to allow its internal roles to assume the role. Features ------------------------------------------------------------------------------- - **Fine-grained S3 access**: Grant access to a specific bucket and prefix, not the entire bucket - **External account trust**: Only the specified AWS account ID can assume the role - **Optional External ID**: Support for External ID to prevent confused deputy attacks (recommended for third-party access) - **Configurable access level**: Choose between ``READ``, ``WRITE``, or ``READ_WRITE`` permissions - **Optional bucket listing**: Optionally allow the external account to list all buckets - **Secure by default**: No public access, and permissions are limited to the specified path - **Tagging support**: Apply custom tags to IAM resources for organization and cost tracking Outputs ------------------------------------------------------------------------------- This module exports the following outputs: .. list-table:: :header-rows: 1 :widths: 30 70 * - Name - Description * - iam_role_arn - The ARN of the IAM role created for external access * - iam_policy_arn - The ARN of the IAM policy attached to the role Access Levels ------------------------------------------------------------------------------- - ``READ``: Allows listing and reading objects under the specified prefix - ``WRITE``: Allows uploading objects under the specified prefix - ``READ_WRITE``: Allows both reading and writing (full access) under the specified prefix .. list-table:: :header-rows: 1 :widths: 25 25 25 25 * - Access Level - s3:GetObject - s3:PutObject - s3:ListBucket * - READ - ✅ - ❌ - ✅ * - WRITE - ❌ - ✅ - ❌ * - READ_WRITE - ✅ - ✅ - ✅ Security Considerations ------------------------------------------------------------------------------- - The role can only be assumed by the specified external AWS account - The policy restricts access to the given bucket and prefix only - Use ``external_id`` parameter for third-party access to prevent confused deputy attacks - The external account should create internal policies limiting which users/roles can assume this role - Account ID validation ensures only valid 12-digit AWS account IDs are accepted How to Use ------------------------------------------------------------------------------- Variables ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. list-table:: :header-rows: 1 :widths: 20 40 15 15 10 * - Name - Description - Type - Default - Required * - role_name - Name of the IAM role - string - n/a - Yes * - aws_external_account - AWS Account ID (12 digits) of the external account - string - n/a - Yes * - bucket_name - Name of the S3 bucket to grant access to - string - n/a - Yes * - prefix - Prefix/path within the bucket where access is granted - string - n/a - Yes * - access_level - Access level: READ, WRITE, or READ_WRITE - string - "READ" - No * - allow_list_buckets - Allow listing all S3 buckets - bool - false - No * - external_id - Optional External ID for additional security - string - null - No * - tags - Tags to apply to IAM resources - map(string) - {} - No Basic Example ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: terraform resource "aws_s3_bucket" "s3_bucket" { bucket = "shared-bucket-xcfu-uysters" } resource "aws_s3_bucket_public_access_block" "block_public_access" { bucket = aws_s3_bucket.s3_bucket.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } module "some_external_access" { source = "./modules/s3/role_external_access" aws_external_account = "123456789012" role_name = "role_for_client" bucket_name = aws_s3_bucket.s3_bucket.bucket prefix = "uploads/client" access_level = "READ" } Advanced Example with External ID and Tags ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: terraform module "third_party_access" { source = "./modules/s3/role_external_access" aws_external_account = "987654321098" role_name = "ThirdPartyDataAccess" bucket_name = "company-shared-data" prefix = "external/partner-a" access_level = "READ" # Security: External ID for confused deputy protection external_id = "unique-external-id-from-third-party" # Optional: Allow them to list all buckets allow_list_buckets = true # Tags for organization tags = { Environment = "production" ManagedBy = "terraform" Partner = "partner-a" CostCenter = "data-sharing" } } # Use the output output "partner_role_arn" { value = module.third_party_access.iam_role_arn } Configuration on the External Account ------------------------------------------------------------------------------- Because the access is granted to the root of the external account, the administrator of it could create an IAM policy allowing other users to access the bucket location. IAM Policy ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: json { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::{{account_own_bucket}}:role/{{role_name}}" } ] } AWS CLI Commands ------------------------------------------------------------------------------- Accessing the bucket location using the AWS CLI console commands. Assuming the Role ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: shell aws sts assume-role --role-arn arn:aws:iam::060923676874:role/role_for_nissan --role-session-name testing-s3-role Listing the Buckets ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: shell aws s3 ls We will get an error because the lack of permission, we can grant it via: ``allow_list_buckets = true``. .. code-block:: text An error occurred (AccessDenied) when calling the ListBuckets operation: User: arn:aws:sts::******:assumed-role/role_for_nissan/testing-s3-role is not authorized to perform: s3:ListAllMyBuckets because no identity-based policy allows the s3:ListAllMyBuckets action. Listing Objects in Unauthorized Locations ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: shell aws s3 ls s3://shared_bucket aws s3 ls s3://shared_bucket/uploads Errors will be raised: .. code-block:: text An error occurred (AccessDenied) when calling the ListObjects operation: User: arn:aws:sts::******:assumed-role/role_for_nissan/testing-s3-role is not authorized to perform: s3:ListBucket on resource: "arn:aws:s3:::shared_bucket" because no identity-based policy allows the s3:ListBucket action. Listing Objects in Authorized Locations ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: shell aws s3 ls s3://shared_bucket/uploads/nissan/ Outputs example: .. code-block:: text 2024-01-10 15:30:09 0 2025-01-15 10:21:32 21 report.txt 2025-01-21 10:28:06 19565 sample_data.csv Downloading Files ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: shell aws s3 cp s3://shared_bucket/uploads/nissan/report.txt . For ``READ`` and ``READ_WRITE`` permissions we will get ``download: s3://shared_bucket/uploads/nissan/report.txt to ./report.txt``, otherwise ``fatal error: An error occurred (403) when calling the HeadObject operation: Forbidden``. Uploading Files ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: shell aws s3 cp ./report.txt s3://shared_bucket/uploads/nissan/report.txt For ``WRITE`` and ``READ_WRITE`` permissions we will get ``upload: ./report.txt to s3://shared_bucket/uploads/nissan/report.txt``, otherwise: .. code-block:: text upload failed: ./report.txt to s3://shared_bucket/uploads/nissan/report.txt An error occurred (AccessDenied) when calling the PutObject operation: User: arn:aws:sts::******:assumed-role/role_for_nissan/testing-s3-role is not authorized to perform: s3:PutObject on resource: "arn:aws:s3:::shared_bucket/uploads/nissan/report.txt" because no identity-based policy allows the s3:PutObject action.