Skip to content

Cross region Same Account Glue Job ETL - from Parquet to iceberg format

0

Our current setup runs the AWS Glue job in us-west-1 while our raw Parquet data is stored there. However, our target AWS Glue Data Catalog (for Iceberg table definitions) resides in us-east-2. By default, both Glue and Apache Iceberg try to interact with the local region’s catalog, which in our case means they attempt to perform operations (e.g. glue:GetTable or glue:GetDatabase) in us-west-1. This leads to authorization errors, as our IAM policy only grants permissions on the us-east-2 catalog. The Glue Database and Crawler stay on the default provider which is us-east-2

How can we can override the default region in our Spark configurations so that all metadata operations (table creation, lookups, etc.) are directed to us-east-2 instead of us-west-1??

Code:

import sys
from awsglue.utils import getResolvedOptions
from pyspark.sql import SparkSession

args = getResolvedOptions(sys.argv, ['SOURCE_BUCKET', 'DEST_BUCKET', 'JOB_NAME'])
source_bucket = args['SOURCE_BUCKET']
dest_bucket   = args['DEST_BUCKET']

spark = (SparkSession.builder
    .appName("ParquetToIceberg")
    .config("spark.sql.extensions", "org.apache.iceberg.spark.extensions.IcebergSparkSessionExtensions")
    .config("spark.sql.catalog.glue_catalog", "org.apache.iceberg.spark.SparkCatalog")
    .config("spark.sql.catalog.glue_catalog.warehouse", f"s3://{dest_bucket}/iceberg-data/")
    .config("spark.sql.catalog.glue_catalog.catalog-impl", "org.apache.iceberg.aws.glue.GlueCatalog")
    .config("spark.sql.catalog.glue_catalog.io-impl", "org.apache.iceberg.aws.s3.S3FileIO")
    .config("spark.sql.catalog.my_catalog.client.assume-role.region", "us-east-2")
    .getOrCreate()
)

source_path = f"s3://{source_bucket}/raw-parquet/"
df = spark.read.parquet(source_path)

table_name = "glue_catalog.iceberg_database.my_iceberg_table"
df.writeTo(table_name) \
  .tableProperty("format-version", "2") \
  .partitionedBy("username", "year", "month", "day") \
  .createOrReplace()

spark.stop()

**Minimal Terraform Setup - iam role and Glue Data Catalog **

resource "aws_iam_role" "glue_role" {
  name               = "glue-cross-region-role-${var.environment}"
  assume_role_policy = data.aws_iam_policy_document.combined_trust_policy.json
}

data "aws_iam_policy_document" "glue_policy_doc" {
  # Allow S3 read from the source bucket
  statement {
    effect = "Allow"
    actions = [
      "s3:GetObject",
      "s3:ListBucket"
    ]
    resources = [
      "arn:aws:s3:::${var.source_bucket_name}",
      "arn:aws:s3:::${var.source_bucket_name}/*"
    ]
  }

  # Allow S3 write to the destination bucket
  statement {
    effect = "Allow"
    actions = [
      "s3:PutObject",
      "s3:GetObject",
      "s3:ListBucket"
    ]
    resources = [
      aws_s3_bucket.destination_bucket.arn,
      "${aws_s3_bucket.destination_bucket.arn}/*"
    ]
  }

  # Allow CloudWatch Logs actions
  statement {
    effect    = "Allow"
    actions   = ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"]
    resources = ["*"]
  }

  # Allow Glue Data Catalog operations dynamically using the current account ID
  statement {
    effect = "Allow"
    actions = [
      "glue:GetDatabase",
      "glue:GetTable",
      "glue:CreateTable",
      "glue:UpdateTable"
    ]
    resources = [
      "arn:aws:glue:us-east-2:${data.aws_caller_identity.current.account_id}:catalog",
      "arn:aws:glue:us-east-2:${data.aws_caller_identity.current.account_id}:database/*",
      "arn:aws:glue:us-east-2:${data.aws_caller_identity.current.account_id}:table/*"
    ]
  }
}

resource "aws_iam_policy" "glue_policy" {
  name        = "glue-cross-region-policy-${var.environment}"
  description = "Policy for AWS Glue to access S3 in us-west-1 and us-east-2 and to interact with the Glue Data Catalog"
  policy      = data.aws_iam_policy_document.glue_policy_doc.json
}

resource "aws_iam_role_policy_attachment" "glue_policy_attach" {
  role       = aws_iam_role.glue_role.name
  policy_arn = aws_iam_policy.glue_policy.arn
}

############################################################
# 4. Create a Glue Database in us-east-2
############################################################

resource "aws_glue_catalog_database" "iceberg_db" {
  name = var.glue_database_name
}

############################################################
# 5. AWS Glue Job (us-east-2) reading from us-west-1
############################################################

resource "aws_glue_job" "parquet_to_iceberg" {
  provider = aws.west
  name     = "parquet-to-iceberg-job-${var.environment}"
  role_arn = aws_iam_role.glue_role.arn

  command {
    name            = "glueetl"
    script_location = "s3://${aws_s3_bucket.destination_bucket.bucket}/scripts/glue_script.py"
    python_version  = "3"
  }

  default_arguments = {
    "--job-bookmark-option" = "job-bookmark-disable"
    "--TempDir"             = "s3://${aws_s3_bucket.destination_bucket.bucket}/temp/"
    "--enable-metrics"      = "true"
    "--SOURCE_BUCKET"       = var.source_bucket_name
    "--DEST_BUCKET"         = aws_s3_bucket.destination_bucket.bucket
  }

  glue_version      = "3.0"
  number_of_workers = 2
  worker_type       = "G.1X"
  max_retries       = 1

  tags = {
    Environment = var.environment
  }
}

############################################################
# 6. AWS Glue Crawler (us-east-2)
############################################################

resource "aws_glue_crawler" "iceberg_crawler" {
  name          = "iceberg-crawler-${var.environment}"
  role          = aws_iam_role.glue_role.arn
  database_name = aws_glue_catalog_database.iceberg_db.name

  s3_target {
    path = "s3://${aws_s3_bucket.destination_bucket.bucket}/iceberg-data/"
  }

  configuration = jsonencode({
    Version = 1.0,
    CrawlerOutput = {
      Partitions = {
        AddOrUpdateBehavior = "InheritFromTable"
      }
    }
  })

  schedule = "cron(0 3 * * ? *)"

  tags = {
    Environment = var.environment
  }
}

Error (Account and Request ID replaced with random values)

An error occurred while calling o111.createOrReplace. User: arn:aws:sts::1111:assumed-role/glue-cross-region-role-dev/GlueJobRunnerSession is not authorized to perform: glue:GetTable on resource: arn:aws:glue:us-west-1:1111:catalog because no identity-based policy allows the glue:GetTable action (Service: Glue, Status Code: 400, Request ID: 12121212)

We really don't require to operate on the DB within us-west-1 region, but we already have created the DB via terraform script in us-east-2. How can we overcome this issue and make sure that glue job should only run process in us-west-1 and transfer data to us-east-2??

Any help would be appreciated to solve this issue. We had a hard requirement to use Iceberg as the format

asked 2 years ago988 views

2 Answers
0

Hello,

To access a glue job in a cross region scenario "us-west-1" and "us-east-2", where the raw parquet data is in us-west-1 region and the glue data catalog(Iceberg table) is in the us-east-2 region.

There are only 2 methods that you can follow as the workaround to solve this issue:

  1. Using S3 Access Point:

When following Spark configs are provided, cross region S3 access works.

The S3 Access points is supported in Iceberg 0.14.0 & the later versions and you can only use these configurations on those versions. Additionally, you need to configure S3 access point for the S3 bucket in advance.

spark.sql.catalog.{catalog_name}.s3.use-arn-region-enabled=true
spark.sql.catalog.{catalog_name}.s3.access-points.{bucketName}=arn:aws:s3:{region}:{account}:accesspoint/{s3accessPointName})

Note: This suggestion requires to locate both Glue database/table and S3 in the same region. It is not possible to locate Glue table in us-east-2 and S3 bucket in us-west-1.

[+] https://aws.amazon.com/blogs/big-data/configure-cross-region-table-access-with-the-aws-glue-catalog-and-aws-lake-formation/

  1. Using AssumeRole:

The IAM role attached to the Glue job need to be able to assume the role specified in the below configuration.

-> The source IAM role (attached to the job): Need to have sts:AssumeRole action in IAM policy. -> The target IAM role (assumed by the config): Need to allow sts:AssumeRole action from the source IAM role in trust relationship.

spark.sql.catalog.{catalog_name}.client.factory=org.apache.iceberg.aws.AssumeRoleAwsClientFactory
spark.sql.catalog.{catalog_name}.client.assume-role.arn=arn:aws:iam::{account}:role/{roleName}
spark.sql.catalog.{catalog_name}.client.assume-role.region={region}

By implementing this based on the requirements of the job will resolve this issue.

Additionally, if your use-case permits, you can use Amazon Athena for reading the table, as it supports cross region without any conflicts.

However, if you continue to face any issue, to do a thorough analysis for your architecture and script I would suggest you create a case with technical support to help you with this.

AWS

answered 2 years ago

-1

To resolve this cross-region issue and ensure your Glue job runs correctly, you need to make a few modifications to your Spark configuration and Glue job setup:

  1. Update your Spark configuration to explicitly set the Glue catalog region:

Add the following configuration to your Spark session builder:

.config("spark.sql.catalog.glue_catalog.catalog-impl", "org.apache.iceberg.aws.glue.GlueCatalog")
.config("spark.sql.catalog.glue_catalog.warehouse", f"s3://{dest_bucket}/iceberg-data/")
.config("spark.sql.catalog.glue_catalog.io-impl", "org.apache.iceberg.aws.s3.S3FileIO")
.config("spark.sql.catalog.glue_catalog.glue.region", "us-east-2")
  1. Modify your IAM role permissions:

Ensure that your IAM role has permissions to access the Glue Data Catalog in us-east-2. Update your policy to explicitly allow actions on resources in us-east-2:

{
  "Effect": "Allow",
  "Action": [
    "glue:GetDatabase",
    "glue:GetTable",
    "glue:CreateTable",
    "glue:UpdateTable"
  ],
  "Resource": [
    "arn:aws:glue:us-east-2:${account_id}:catalog",
    "arn:aws:glue:us-east-2:${account_id}:database/*",
    "arn:aws:glue:us-east-2:${account_id}:table/*"
  ]
}
  1. In your Glue job configuration:

Set the default arguments to include the Glue region:

default_arguments = {
  "--additional-python-modules" = "boto3==1.24.70"
  "--conf"                      = "spark.sql.catalog.glue_catalog.glue.region=us-east-2"
  "--enable-glue-datacatalog"   = "true"
  // ... other arguments ...
}
  1. Ensure your Glue job is created in the correct region:

Make sure your aws_glue_job resource in Terraform is created in us-west-1, where the job will run:

resource "aws_glue_job" "parquet_to_iceberg" {
  provider = aws.west
  // ... other configurations ...
}
  1. Cross-region S3 access:

Ensure your IAM role has permissions to read from the source bucket in us-west-1 and write to the destination bucket in us-east-2.

By implementing these changes, your Glue job should be able to run in us-west-1, process the data there, and correctly interact with the Glue Data Catalog in us-east-2 for Iceberg table operations. This setup directs all Glue catalog operations to us-east-2 while allowing the job to run and access data in us-west-1.
Sources
Glue ETL: cross region write to Iceberg failing | AWS re:Post
Running ETL jobs on Amazon S3 tables with AWS Glue - Amazon Simple Storage Service
Working with Apache Iceberg in AWS Glue - AWS Prescriptive Guidance
Cross-account cross-Region access to DynamoDB tables - AWS Glue

answered 2 years ago

  • I have already tried it, This doesn't work. I have also tried giving the updating the role to have below policy, just to check what glue job is upto -

    { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "glue:GetTable", "glue:GetDatabase" ], "Resource": [ "arn:aws:glue:us-west-1:111:catalog", "arn:aws:glue:us-west-1:111:database/iceberg_database", "arn:aws:glue:us-west-1:111:table/iceberg_database/my_iceberg_table" ] } ] }

    got this Error:

    An error occurred while calling o113.createOrReplace. Database iceberg_database not found. (Service: Glue, Status Code: 400, Request ID: 111)

    My observation: Glue (and Apache Iceberg) will try to use the local region’s hence causing all such issues

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.