This is an automated email from the ASF dual-hosted git repository.

CalvinKirs pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new ceeb9bc09a9 [feat](Iceberg)Rest & S3Table Support Iam-role (#60498)
ceeb9bc09a9 is described below

commit ceeb9bc09a90ef5d0c6bf4851a7cc51090b4d1d7
Author: Calvin Kirs <[email protected]>
AuthorDate: Thu May 7 11:02:49 2026 +0800

    [feat](Iceberg)Rest & S3Table Support Iam-role (#60498)
    
    FYI https://github.com/apache/doris/pull/59893
    
    The initial implementation was contributed by https://github.com/Sbaia .
    Many thanks for his contributions.
---
 .../s3tables/CustomAwsCredentialsProvider.java     |  43 ---
 .../common/AwsCredentialsProviderFactory.java      |  28 ++
 .../common/IcebergAwsAssumeRoleProperties.java     |  52 ++++
 .../IcebergAwsClientCredentialsProperties.java     | 144 ++++++++++
 .../metastore/AbstractIcebergProperties.java       |   5 +
 .../property/metastore/IcebergRestProperties.java  |  72 ++++-
 .../IcebergS3TablesMetaStoreProperties.java        |  33 ++-
 .../datasource/property/storage/S3Properties.java  |  29 +-
 .../metastore/IcebergRestPropertiesTest.java       | 303 +++++++++++++++++++++
 .../IcebergS3TablesMetaStorePropertiesTest.java    | 272 ++++++++++++++++++
 .../property/storage/S3PropertiesTest.java         |  13 +
 .../doris/datasource/s3tables/S3TablesTest.java    |   9 -
 ...rg_s3tables_catalog_credentials_provider.groovy | 105 +++++++
 13 files changed, 1028 insertions(+), 80 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/s3tables/CustomAwsCredentialsProvider.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/s3tables/CustomAwsCredentialsProvider.java
deleted file mode 100644
index f76c8c3cc9b..00000000000
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/s3tables/CustomAwsCredentialsProvider.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-package org.apache.doris.datasource.iceberg.s3tables;
-
-import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
-import software.amazon.awssdk.auth.credentials.AwsCredentials;
-import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
-
-import java.util.Map;
-
-public class CustomAwsCredentialsProvider implements AwsCredentialsProvider {
-    private final String accessKeyId;
-    private final String secretAccessKey;
-
-    public CustomAwsCredentialsProvider(String accessKeyId, String 
secretAccessKey) {
-        this.accessKeyId = accessKeyId;
-        this.secretAccessKey = secretAccessKey;
-    }
-
-    @Override
-    public AwsCredentials resolveCredentials() {
-        return AwsBasicCredentials.create(accessKeyId, secretAccessKey);
-    }
-
-    public static CustomAwsCredentialsProvider create(Map<String, String> 
props) {
-        return new CustomAwsCredentialsProvider(props.get("s3.access-key-id"), 
props.get("s3.secret-access-key"));
-    }
-}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/AwsCredentialsProviderFactory.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/AwsCredentialsProviderFactory.java
index d2fd4a80d9e..8669488cf1a 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/AwsCredentialsProviderFactory.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/AwsCredentialsProviderFactory.java
@@ -177,4 +177,32 @@ public final class AwsCredentialsProviderFactory {
                         "AWS SDK V2 does not support credentials provider 
mode: " + mode);
         }
     }
+
+    /**
+     * Get the AWS credentials provider class name.
+     * For DEFAULT mode, returns AWS SDK native DefaultCredentialsProvider.
+     * For other modes, returns the specific provider class name.
+     */
+    public static String getV2ClassName(AwsCredentialsProviderMode mode) {
+        switch (mode) {
+            case ENV:
+                return EnvironmentVariableCredentialsProvider.class.getName();
+            case SYSTEM_PROPERTIES:
+                return SystemPropertyCredentialsProvider.class.getName();
+            case WEB_IDENTITY:
+                return WebIdentityTokenFileCredentialsProvider.class.getName();
+            case CONTAINER:
+                return ContainerCredentialsProvider.class.getName();
+            case INSTANCE_PROFILE:
+                return InstanceProfileCredentialsProvider.class.getName();
+            case ANONYMOUS:
+                return AnonymousCredentialsProvider.class.getName();
+            case DEFAULT:
+                // For Iceberg REST, use AWS SDK native 
DefaultCredentialsProvider
+                return 
"software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider";
+            default:
+                throw new UnsupportedOperationException(
+                        "AWS SDK V2 does not support credentials provider 
mode: " + mode);
+        }
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/IcebergAwsAssumeRoleProperties.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/IcebergAwsAssumeRoleProperties.java
new file mode 100644
index 00000000000..afa224511bc
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/IcebergAwsAssumeRoleProperties.java
@@ -0,0 +1,52 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.datasource.property.common;
+
+import org.apache.doris.datasource.property.storage.S3Properties;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.iceberg.aws.AssumeRoleAwsClientFactory;
+import org.apache.iceberg.aws.AwsProperties;
+
+import java.util.Map;
+
+/**
+ * Shared util for putting Iceberg AWS assume-role properties into a map.
+ * Used by Iceberg REST (glue/s3tables signing), S3 Tables catalog, and S3 
FileIO.
+ */
+public final class IcebergAwsAssumeRoleProperties {
+
+    private IcebergAwsAssumeRoleProperties() {}
+
+    /**
+     * Puts assume-role related Iceberg client properties into the target map 
when roleArn is present.
+     * No-op if roleArn is blank.
+     */
+    public static void putAssumeRoleProperties(Map<String, String> target, 
S3Properties s3Properties) {
+        if (StringUtils.isBlank(s3Properties.getS3IAMRole())) {
+            return;
+        }
+        target.put(AwsProperties.CLIENT_FACTORY, 
AssumeRoleAwsClientFactory.class.getName());
+        target.put("aws.region", s3Properties.getRegion());
+        target.put(AwsProperties.CLIENT_ASSUME_ROLE_REGION, 
s3Properties.getRegion());
+        target.put(AwsProperties.CLIENT_ASSUME_ROLE_ARN, 
s3Properties.getS3IAMRole());
+        if (StringUtils.isNotBlank(s3Properties.getS3ExternalId())) {
+            target.put(AwsProperties.CLIENT_ASSUME_ROLE_EXTERNAL_ID, 
s3Properties.getS3ExternalId());
+        }
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/IcebergAwsClientCredentialsProperties.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/IcebergAwsClientCredentialsProperties.java
new file mode 100644
index 00000000000..82228e70037
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/common/IcebergAwsClientCredentialsProperties.java
@@ -0,0 +1,144 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.datasource.property.common;
+
+import org.apache.doris.datasource.property.storage.S3Properties;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.iceberg.aws.AwsClientProperties;
+import org.apache.iceberg.aws.AwsProperties;
+import org.apache.iceberg.aws.s3.S3FileIOProperties;
+import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
+
+import java.util.Map;
+
+public final class IcebergAwsClientCredentialsProperties {
+
+    private IcebergAwsClientCredentialsProperties() {}
+
+    public static void putCredentialProviderProperties(Map<String, String> 
target, S3Properties s3Properties) {
+        switch (getCredentialType(s3Properties)) {
+            case EXPLICIT:
+                putExplicitRestCredentials(target, 
s3Properties.getAccessKey(), s3Properties.getSecretKey(),
+                        s3Properties.getSessionToken());
+                return;
+            case ASSUME_ROLE:
+                IcebergAwsAssumeRoleProperties.putAssumeRoleProperties(target, 
s3Properties);
+                return;
+            case PROVIDER_CHAIN:
+                putCredentialsProvider(target, 
s3Properties.getAwsCredentialsProviderMode());
+                return;
+            default:
+                throw new IllegalStateException("Unsupported Iceberg AWS 
credential type");
+        }
+    }
+
+    public static void putCredentialProviderProperties(Map<String, String> 
target,
+            String accessKey, String secretKey, String sessionToken, 
AwsCredentialsProviderMode providerMode) {
+        if (StringUtils.isNotBlank(accessKey) && 
StringUtils.isNotBlank(secretKey)) {
+            putExplicitRestCredentials(target, accessKey, secretKey, 
sessionToken);
+            return;
+        }
+        putCredentialsProvider(target, providerMode);
+    }
+
+    public static void putS3FileIOCredentialProperties(Map<String, String> 
target,
+            S3Properties s3Properties) {
+        putS3FileIOProperties(target, s3Properties);
+        switch (getCredentialType(s3Properties)) {
+            case EXPLICIT:
+                return;
+            case ASSUME_ROLE:
+                IcebergAwsAssumeRoleProperties.putAssumeRoleProperties(target, 
s3Properties);
+                return;
+            case PROVIDER_CHAIN:
+                putCredentialsProvider(target, 
s3Properties.getAwsCredentialsProviderMode());
+                return;
+            default:
+                throw new IllegalStateException("Unsupported Iceberg AWS 
credential type");
+        }
+    }
+
+    public static AwsCredentialsProvider 
createAwsCredentialsProvider(S3Properties s3Properties,
+            boolean includeAnonymousInDefault) {
+        switch (getCredentialType(s3Properties)) {
+            case EXPLICIT:
+            case ASSUME_ROLE:
+                return s3Properties.getAwsCredentialsProvider();
+            case PROVIDER_CHAIN:
+                return AwsCredentialsProviderFactory.createV2(
+                        s3Properties.getAwsCredentialsProviderMode(), 
includeAnonymousInDefault);
+            default:
+                throw new IllegalStateException("Unsupported Iceberg AWS 
credential type");
+        }
+    }
+
+    private static CredentialType getCredentialType(S3Properties s3Properties) 
{
+        if (StringUtils.isNotBlank(s3Properties.getAccessKey())
+                && StringUtils.isNotBlank(s3Properties.getSecretKey())) {
+            return CredentialType.EXPLICIT;
+        }
+        if (StringUtils.isNotBlank(s3Properties.getS3IAMRole())) {
+            return CredentialType.ASSUME_ROLE;
+        }
+        return CredentialType.PROVIDER_CHAIN;
+    }
+
+    private static void putExplicitRestCredentials(Map<String, String> target,
+            String accessKey, String secretKey, String sessionToken) {
+        target.put(AwsProperties.REST_ACCESS_KEY_ID, accessKey);
+        target.put(AwsProperties.REST_SECRET_ACCESS_KEY, secretKey);
+        if (StringUtils.isNotBlank(sessionToken)) {
+            target.put(AwsProperties.REST_SESSION_TOKEN, sessionToken);
+        }
+    }
+
+    private static void putS3FileIOProperties(Map<String, String> target,
+            S3Properties s3Properties) {
+        if (StringUtils.isNotBlank(s3Properties.getEndpoint())) {
+            target.put(S3FileIOProperties.ENDPOINT, 
s3Properties.getEndpoint());
+        }
+        if (StringUtils.isNotBlank(s3Properties.getUsePathStyle())) {
+            target.put(S3FileIOProperties.PATH_STYLE_ACCESS, 
s3Properties.getUsePathStyle());
+        }
+        if (StringUtils.isNotBlank(s3Properties.getAccessKey())) {
+            target.put(S3FileIOProperties.ACCESS_KEY_ID, 
s3Properties.getAccessKey());
+        }
+        if (StringUtils.isNotBlank(s3Properties.getSecretKey())) {
+            target.put(S3FileIOProperties.SECRET_ACCESS_KEY, 
s3Properties.getSecretKey());
+        }
+        if (StringUtils.isNotBlank(s3Properties.getSessionToken())) {
+            target.put(S3FileIOProperties.SESSION_TOKEN, 
s3Properties.getSessionToken());
+        }
+    }
+
+    private static void putCredentialsProvider(Map<String, String> target,
+            AwsCredentialsProviderMode providerMode) {
+        if (providerMode == null || providerMode == 
AwsCredentialsProviderMode.DEFAULT) {
+            return;
+        }
+        target.put(AwsClientProperties.CLIENT_CREDENTIALS_PROVIDER,
+                AwsCredentialsProviderFactory.getV2ClassName(providerMode));
+    }
+
+    private enum CredentialType {
+        EXPLICIT,
+        ASSUME_ROLE,
+        PROVIDER_CHAIN
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/AbstractIcebergProperties.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/AbstractIcebergProperties.java
index 9b23e61eb4d..9a3a5ef5d2a 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/AbstractIcebergProperties.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/AbstractIcebergProperties.java
@@ -20,6 +20,7 @@ package org.apache.doris.datasource.property.metastore;
 import org.apache.doris.common.security.authentication.ExecutionAuthenticator;
 import org.apache.doris.datasource.iceberg.IcebergExternalCatalog;
 import org.apache.doris.datasource.metacache.CacheSpec;
+import 
org.apache.doris.datasource.property.common.IcebergAwsAssumeRoleProperties;
 import 
org.apache.doris.datasource.property.storage.AbstractS3CompatibleProperties;
 import org.apache.doris.datasource.property.storage.S3Properties;
 import org.apache.doris.datasource.property.storage.StorageProperties;
@@ -266,6 +267,10 @@ public abstract class AbstractIcebergProperties extends 
MetastoreProperties {
         if (StringUtils.isNotBlank(s3Properties.getSessionToken())) {
             options.put(S3FileIOProperties.SESSION_TOKEN, 
s3Properties.getSessionToken());
         }
+        if (s3Properties instanceof S3Properties) {
+            S3Properties awsProperties = (S3Properties) s3Properties;
+            IcebergAwsAssumeRoleProperties.putAssumeRoleProperties(options, 
awsProperties);
+        }
     }
 
     protected Catalog buildIcebergCatalog(String catalogName, Map<String, 
String> options, Configuration conf) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergRestProperties.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergRestProperties.java
index 887e6d49c5e..407c5c58b3e 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergRestProperties.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergRestProperties.java
@@ -18,6 +18,9 @@
 package org.apache.doris.datasource.property.metastore;
 
 import org.apache.doris.datasource.iceberg.IcebergExternalCatalog;
+import org.apache.doris.datasource.property.common.AwsCredentialsProviderMode;
+import 
org.apache.doris.datasource.property.common.IcebergAwsClientCredentialsProperties;
+import org.apache.doris.datasource.property.storage.S3Properties;
 import org.apache.doris.datasource.property.storage.StorageProperties;
 import org.apache.doris.foundation.property.ConnectorProperty;
 import org.apache.doris.foundation.property.ParamRules;
@@ -41,8 +44,11 @@ public class IcebergRestProperties extends 
AbstractIcebergProperties {
     private static final String PREFIX_PROPERTY = "prefix";
     private static final String VENDED_CREDENTIALS_HEADER = 
"header.X-Iceberg-Access-Delegation";
     private static final String VENDED_CREDENTIALS_VALUE = 
"vended-credentials";
+    private static final String ICEBERG_REST_ROLE_ARN = 
"iceberg.rest.role_arn";
+    private static final String ICEBERG_REST_EXTERNAL_ID = 
"iceberg.rest.external-id";
 
     private Map<String, String> icebergRestCatalogProperties;
+    private S3Properties s3Properties;
 
     @Getter
     @ConnectorProperty(names = {"iceberg.rest.uri", "uri"},
@@ -149,6 +155,22 @@ public class IcebergRestProperties extends 
AbstractIcebergProperties {
             description = "The secret access key for the iceberg rest catalog 
service.")
     private String icebergRestSecretAccessKey = "";
 
+    @ConnectorProperty(names = {"iceberg.rest.session-token"},
+            required = false,
+            sensitive = true,
+            description = "The session-token for the iceberg rest catalog 
service.")
+    private String icebergRestSessionToken = "";
+
+    @ConnectorProperty(names = {"iceberg.rest.credentials_provider_type"},
+            required = false,
+            description = "The credentials provider type for AWS 
authentication. "
+                    + "Options are: DEFAULT, INSTANCE_PROFILE, ENV, 
SYSTEM_PROPERTIES, "
+                    + "WEB_IDENTITY, CONTAINER. "
+                    + "If not set, defaults to DEFAULT (provider chain).")
+    private String icebergRestCredentialsProviderType = 
AwsCredentialsProviderMode.DEFAULT.name();
+
+    private AwsCredentialsProviderMode icebergRestCredentialsProviderMode;
+
     @ConnectorProperty(names = {"iceberg.rest.connection-timeout-ms"},
             required = false,
             description = "Connection timeout in milliseconds for the REST 
catalog HTTP client. Default: 10000 (10s).")
@@ -182,7 +204,12 @@ public class IcebergRestProperties extends 
AbstractIcebergProperties {
     public void initNormalizeAndCheckProps() {
         super.initNormalizeAndCheckProps();
         validateSecurityType();
+        icebergRestCredentialsProviderMode =
+                
AwsCredentialsProviderMode.fromString(icebergRestCredentialsProviderType);
         buildRules().validate();
+        if (shouldUseS3PropertiesForRestCredentials()) {
+            s3Properties = S3Properties.of(origProps);
+        }
         initIcebergRestCatalogProperties();
     }
 
@@ -218,17 +245,32 @@ public class IcebergRestProperties extends 
AbstractIcebergProperties {
             }
         }
 
-        // Check for glue rest catalog specific properties
+        // When signing-name is glue or s3tables: require signing-region and 
sigv4-enabled
         rules.requireIf(icebergRestSigningName, "glue",
-                new String[] {icebergRestSigningRegion,
-                        icebergRestAccessKeyId,
-                        icebergRestSecretAccessKey,
-                        icebergRestSigV4Enabled},
-                "Rest Catalog requires signing-region, access-key-id, 
secret-access-key "
-                        + "and sigv4-enabled set to true when signing-name is 
glue");
+                new String[] {icebergRestSigningRegion, 
icebergRestSigV4Enabled},
+                "Rest Catalog requires signing-region and sigv4-enabled set to 
true when signing-name is glue");
+        rules.requireIf(icebergRestSigningName, "s3tables",
+                new String[] {icebergRestSigningRegion, 
icebergRestSigV4Enabled},
+                "Rest Catalog requires signing-region and sigv4-enabled set to 
true when signing-name is s3tables");
+
+        rejectUnsupportedAwsAssumeRoleProperty(ICEBERG_REST_ROLE_ARN);
+        rejectUnsupportedAwsAssumeRoleProperty(ICEBERG_REST_EXTERNAL_ID);
+
+        // access-key-id and secret-access-key must be set together when 
either is set
+        rules.requireTogether(new String[] {icebergRestAccessKeyId, 
icebergRestSecretAccessKey},
+                "iceberg.rest.access-key-id and iceberg.rest.secret-access-key 
must be set together");
+
         return rules;
     }
 
+    private void rejectUnsupportedAwsAssumeRoleProperty(String propertyName) {
+        if (Strings.isNotBlank(origProps.get(propertyName))) {
+            throw new IllegalArgumentException(propertyName + " is not 
supported for Iceberg REST catalog. "
+                    + "Use iceberg.rest.access-key-id and 
iceberg.rest.secret-access-key, "
+                    + "or iceberg.rest.credentials_provider_type instead");
+        }
+    }
+
     private void initIcebergRestCatalogProperties() {
         icebergRestCatalogProperties = new HashMap<>();
         // Core catalog properties
@@ -299,12 +341,24 @@ public class IcebergRestProperties extends 
AbstractIcebergProperties {
             // signing-name is case sensible, do not use lowercase()
             icebergRestCatalogProperties.put("rest.signing-name", 
icebergRestSigningName);
             icebergRestCatalogProperties.put("rest.sigv4-enabled", 
icebergRestSigV4Enabled);
-            icebergRestCatalogProperties.put("rest.access-key-id", 
icebergRestAccessKeyId);
-            icebergRestCatalogProperties.put("rest.secret-access-key", 
icebergRestSecretAccessKey);
             icebergRestCatalogProperties.put("rest.signing-region", 
icebergRestSigningRegion);
+
+            if (shouldUseS3PropertiesForRestCredentials()) {
+                
IcebergAwsClientCredentialsProperties.putCredentialProviderProperties(
+                        icebergRestCatalogProperties, s3Properties);
+            } else {
+                
IcebergAwsClientCredentialsProperties.putCredentialProviderProperties(
+                        icebergRestCatalogProperties, icebergRestAccessKeyId,
+                        icebergRestSecretAccessKey, icebergRestSessionToken, 
icebergRestCredentialsProviderMode);
+            }
         }
     }
 
+    private boolean shouldUseS3PropertiesForRestCredentials() {
+        return "glue".equals(icebergRestSigningName)
+                || "s3tables".equals(icebergRestSigningName);
+    }
+
     public Map<String, String> getIcebergRestCatalogProperties() {
         return Collections.unmodifiableMap(icebergRestCatalogProperties);
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergS3TablesMetaStoreProperties.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergS3TablesMetaStoreProperties.java
index 58edd445f23..33147cf4da8 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergS3TablesMetaStoreProperties.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/IcebergS3TablesMetaStoreProperties.java
@@ -18,14 +18,22 @@
 package org.apache.doris.datasource.property.metastore;
 
 import org.apache.doris.datasource.iceberg.IcebergExternalCatalog;
-import 
org.apache.doris.datasource.iceberg.s3tables.CustomAwsCredentialsProvider;
+import 
org.apache.doris.datasource.property.common.IcebergAwsClientCredentialsProperties;
 import org.apache.doris.datasource.property.storage.S3Properties;
 import org.apache.doris.datasource.property.storage.StorageProperties;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.exception.ExceptionUtils;
+import org.apache.iceberg.aws.AwsClientProperties;
 import org.apache.iceberg.catalog.Catalog;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.s3tables.S3TablesClient;
+import software.amazon.awssdk.services.s3tables.S3TablesClientBuilder;
 import software.amazon.s3tables.iceberg.S3TablesCatalog;
+import software.amazon.s3tables.iceberg.S3TablesProperties;
+import software.amazon.s3tables.iceberg.imports.HttpClientProperties;
 
+import java.net.URI;
 import java.util.List;
 import java.util.Map;
 
@@ -52,9 +60,10 @@ public class IcebergS3TablesMetaStoreProperties extends 
AbstractIcebergPropertie
     public Catalog initCatalog(String catalogName, Map<String, String> 
catalogProps,
                                List<StorageProperties> storagePropertiesList) {
         buildS3CatalogProperties(catalogProps);
+        S3TablesClient client = buildS3TablesClient(catalogProps);
         S3TablesCatalog catalog = new S3TablesCatalog();
         try {
-            catalog.initialize(catalogName, catalogProps);
+            catalog.initialize(catalogName, catalogProps, client);
             return catalog;
         } catch (Exception e) {
             throw new RuntimeException("Failed to initialize S3TablesCatalog 
for Iceberg. "
@@ -64,10 +73,20 @@ public class IcebergS3TablesMetaStoreProperties extends 
AbstractIcebergPropertie
     }
 
     private void buildS3CatalogProperties(Map<String, String> props) {
-        props.put("client.credentials-provider", 
CustomAwsCredentialsProvider.class.getName());
-        props.put("client.credentials-provider.s3.access-key-id", 
s3Properties.getAccessKey());
-        props.put("client.credentials-provider.s3.secret-access-key", 
s3Properties.getSecretKey());
-        props.put("client.credentials-provider.s3.session-token", 
s3Properties.getSessionToken());
-        props.put("client.region", s3Properties.getRegion());
+        props.put(AwsClientProperties.CLIENT_REGION, s3Properties.getRegion());
+        
IcebergAwsClientCredentialsProperties.putS3FileIOCredentialProperties(props, 
s3Properties);
+    }
+
+    private S3TablesClient buildS3TablesClient(Map<String, String> props) {
+        S3TablesClientBuilder builder = S3TablesClient.builder()
+                .region(Region.of(s3Properties.getRegion()))
+                
.credentialsProvider(IcebergAwsClientCredentialsProperties.createAwsCredentialsProvider(
+                        s3Properties, false));
+        String s3TablesEndpoint = 
props.get(S3TablesProperties.S3TABLES_ENDPOINT);
+        if (StringUtils.isNotBlank(s3TablesEndpoint)) {
+            builder.endpointOverride(URI.create(s3TablesEndpoint));
+        }
+        new HttpClientProperties(props).applyHttpClientConfigurations(builder);
+        return builder.build();
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
index 9b2aa2a8c11..db4608be2d0 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
@@ -58,6 +58,11 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
     private static final Logger LOG = LogManager.getLogger(S3Properties.class);
 
     public static final String USE_PATH_STYLE = "use_path_style";
+    public static final String ENDPOINT = "s3.endpoint";
+    public static final String REGION = "s3.region";
+    public static final String ROLE_ARN = "s3.role_arn";
+    public static final String EXTERNAL_ID = "s3.external_id";
+    public static final String CREDENTIALS_PROVIDER_TYPE = 
"s3.credentials_provider_type";
 
     private static final String[] ENDPOINT_NAMES_FOR_GUESSING = {
             "s3.endpoint", "AWS_ENDPOINT", "endpoint", "ENDPOINT", 
"aws.endpoint", "glue.endpoint",
@@ -65,12 +70,13 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
     };
 
     private static final String[] REGION_NAMES_FOR_GUESSING = {
-            "s3.region", "glue.region", "aws.glue.region", 
"iceberg.rest.signing-region", "client.region"
+            "s3.region", "glue.region", "aws.glue.region", 
"iceberg.rest.signing-region",
+            "rest.signing-region", "client.region"
     };
 
     @Setter
     @Getter
-    @ConnectorProperty(names = {"s3.endpoint", "AWS_ENDPOINT", "endpoint", 
"ENDPOINT", "aws.endpoint", "glue.endpoint",
+    @ConnectorProperty(names = {ENDPOINT, "AWS_ENDPOINT", "endpoint", 
"ENDPOINT", "aws.endpoint", "glue.endpoint",
             "aws.glue.endpoint"},
             required = false,
             description = "The endpoint of S3.")
@@ -78,8 +84,8 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
 
     @Setter
     @Getter
-    @ConnectorProperty(names = {"s3.region", "AWS_REGION", "region", "REGION", 
"aws.region", "glue.region",
-            "aws.glue.region", "iceberg.rest.signing-region", "client.region"},
+    @ConnectorProperty(names = {REGION, "AWS_REGION", "region", "REGION", 
"aws.region", "glue.region",
+            "aws.glue.region", "iceberg.rest.signing-region", 
"rest.signing-region", "client.region"},
             required = false,
             isRegionField = true,
             description = "The region of S3.")
@@ -104,7 +110,7 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
     protected String secretKey = "";
 
     @Getter
-    @ConnectorProperty(names = {"s3.session_token", "session_token", 
"s3.session-token"},
+    @ConnectorProperty(names = {"s3.session_token", "session_token", 
"s3.session-token", "iceberg.rest.session-token"},
             required = false,
             description = "The session token of S3.")
     protected String sessionToken = "";
@@ -161,17 +167,19 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
     protected String s3StsRegion = "";
 
     @Getter
-    @ConnectorProperty(names = {"s3.role_arn", "AWS_ROLE_ARN", 
"glue.role_arn"},
+    @ConnectorProperty(names = {ROLE_ARN, "AWS_ROLE_ARN", "glue.role_arn"},
             required = false,
             description = "The iam role of S3.")
     protected String s3IAMRole = "";
 
-    @ConnectorProperty(names = {"s3.external_id", "AWS_EXTERNAL_ID", 
"glue.external_id"},
+    @Getter
+    @ConnectorProperty(names = {EXTERNAL_ID, "AWS_EXTERNAL_ID", 
"glue.external_id"},
             required = false,
             description = "The external id of S3.")
     protected String s3ExternalId = "";
 
-    @ConnectorProperty(names = {"s3.credentials_provider_type", 
"glue.credentials_provider_type"},
+    @ConnectorProperty(names = {CREDENTIALS_PROVIDER_TYPE, 
"glue.credentials_provider_type",
+            "iceberg.rest.credentials_provider_type"},
             required = false,
             description = "The credentials provider type of S3. "
                     + "Options are: DEFAULT, ASSUME_ROLE, ENVIRONMENT, 
SYSTEM_PROPERTIES, "
@@ -179,6 +187,7 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
                     + "If not set, it will use the default provider chain of 
AWS SDK.")
     protected String awsCredentialsProviderType = 
AwsCredentialsProviderMode.DEFAULT.name();
 
+    @Getter
     private AwsCredentialsProviderMode awsCredentialsProviderMode;
 
     public static S3Properties of(Map<String, String> properties) {
@@ -429,9 +438,7 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
 
     public static final String S3_PREFIX = "s3.";
 
-    public static final String ENDPOINT = "s3.endpoint";
     public static final String EXTERNAL_ENDPOINT = "s3.external_endpoint";
-    public static final String REGION = "s3.region";
     public static final String ACCESS_KEY = "s3.access_key";
     public static final String SECRET_KEY = "s3.secret_key";
     public static final String SESSION_TOKEN = "s3.session_token";
@@ -439,8 +446,6 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
     public static final String REQUEST_TIMEOUT_MS = 
"s3.connection.request.timeout";
     public static final String CONNECTION_TIMEOUT_MS = "s3.connection.timeout";
 
-    public static final String ROLE_ARN = "s3.role_arn";
-    public static final String EXTERNAL_ID = "s3.external_id";
     public static final String ROOT_PATH = "s3.root.path";
     public static final String BUCKET = "s3.bucket";
     public static final String VALIDITY_CHECK = "s3_validity_check";
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/metastore/IcebergRestPropertiesTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/metastore/IcebergRestPropertiesTest.java
index faf6f95d3b1..47ce0669a6c 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/metastore/IcebergRestPropertiesTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/metastore/IcebergRestPropertiesTest.java
@@ -357,6 +357,8 @@ public class IcebergRestPropertiesTest {
         
Assertions.assertFalse(catalogProps.containsKey("rest.secret-access-key"));
         Assertions.assertFalse(catalogProps.containsKey("rest.sigv4-enabled"));
         props.put("iceberg.rest.signing-name", "custom-service");
+        props.put("iceberg.rest.access-key-id", "AKIAIOSFODNN7EXAMPLE");
+        props.put("iceberg.rest.secret-access-key", 
"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
         restProps = new IcebergRestProperties(props);
         restProps.initNormalizeAndCheckProps(); // Should not throw
         catalogProps = restProps.getIcebergRestCatalogProperties();
@@ -437,6 +439,307 @@ public class IcebergRestPropertiesTest {
                 || errorMessage.contains("sigv4-enabled"));
     }
 
+    @Test
+    public void testS3TablesSigningNameValidWithAccessKeyAndSecretKey() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "s3tables");
+        props.put("iceberg.rest.signing-region", "us-east-1");
+        props.put("iceberg.rest.access-key-id", "AKIAIOSFODNN7EXAMPLE");
+        props.put("iceberg.rest.secret-access-key", 
"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        Assertions.assertDoesNotThrow(restProps::initNormalizeAndCheckProps);
+
+        Map<String, String> catalogProps = 
restProps.getIcebergRestCatalogProperties();
+        Assertions.assertEquals("s3tables", 
catalogProps.get("rest.signing-name"));
+        Assertions.assertEquals("us-east-1", 
catalogProps.get("rest.signing-region"));
+        Assertions.assertEquals("AKIAIOSFODNN7EXAMPLE", 
catalogProps.get("rest.access-key-id"));
+        Assertions.assertEquals("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
+                catalogProps.get("rest.secret-access-key"));
+    }
+
+    @Test
+    public void testS3TablesSigningNameCaseInsensitive() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "S3TABLES");
+        props.put("iceberg.rest.signing-region", "us-west-2");
+        props.put("iceberg.rest.access-key-id", "AKIAIOSFODNN7EXAMPLE");
+        props.put("iceberg.rest.secret-access-key", 
"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        Assertions.assertDoesNotThrow(restProps::initNormalizeAndCheckProps);
+        Assertions.assertEquals("S3TABLES", 
restProps.getIcebergRestCatalogProperties().get("rest.signing-name"));
+    }
+
+    @Test
+    public void testGlueSigningNameWithIamRoleFails() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "glue");
+        props.put("iceberg.rest.signing-region", "us-east-1");
+        props.put("iceberg.rest.role_arn", 
"arn:aws:iam::123456789012:role/MyGlueRole");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        IllegalArgumentException e = 
Assertions.assertThrows(IllegalArgumentException.class,
+                restProps::initNormalizeAndCheckProps);
+        
Assertions.assertTrue(e.getMessage().contains("iceberg.rest.role_arn"));
+    }
+
+    @Test
+    public void testS3TablesSigningNameWithIamRoleFails() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "s3tables");
+        props.put("iceberg.rest.signing-region", "us-west-2");
+        props.put("iceberg.rest.role_arn", 
"arn:aws:iam::999999999999:role/S3TablesRole");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        IllegalArgumentException e = 
Assertions.assertThrows(IllegalArgumentException.class,
+                restProps::initNormalizeAndCheckProps);
+        
Assertions.assertTrue(e.getMessage().contains("iceberg.rest.role_arn"));
+    }
+
+    @Test
+    public void testGlueSigningNameWithDefaultCredentialsProvider() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "glue");
+        props.put("iceberg.rest.signing-region", "us-east-1");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        Assertions.assertDoesNotThrow(restProps::initNormalizeAndCheckProps);
+
+        Map<String, String> catalogProps = 
restProps.getIcebergRestCatalogProperties();
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider"));
+    }
+
+    @Test
+    public void testS3TablesSigningNameWithDefaultCredentialsProvider() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "s3tables");
+        props.put("iceberg.rest.signing-region", "us-east-1");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+        // No credentials, should use DEFAULT provider
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        Assertions.assertDoesNotThrow(restProps::initNormalizeAndCheckProps);
+
+        Map<String, String> catalogProps = 
restProps.getIcebergRestCatalogProperties();
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider"));
+    }
+
+    @Test
+    public void testS3TablesSigningNameMissingSigningRegionFails() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "s3tables");
+        props.put("iceberg.rest.access-key-id", "AKIAIOSFODNN7EXAMPLE");
+        props.put("iceberg.rest.secret-access-key", 
"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        IllegalArgumentException e = 
Assertions.assertThrows(IllegalArgumentException.class,
+                restProps::initNormalizeAndCheckProps);
+        Assertions.assertTrue(e.getMessage().contains("signing-region") && 
e.getMessage().contains("s3tables"));
+    }
+
+    @Test
+    public void testAccessKeyAndSecretKeyMustBeSetTogether() {
+        Map<String, String> props1 = new HashMap<>();
+        props1.put("iceberg.rest.uri", "http://localhost:8080";);
+        props1.put("iceberg.rest.signing-name", "glue");
+        props1.put("iceberg.rest.signing-region", "us-east-1");
+        props1.put("iceberg.rest.access-key-id", "AKIAIOSFODNN7EXAMPLE");
+        props1.put("iceberg.rest.sigv4-enabled", "true");
+
+        IcebergRestProperties restProps1 = new IcebergRestProperties(props1);
+        IllegalArgumentException e1 = 
Assertions.assertThrows(IllegalArgumentException.class,
+                restProps1::initNormalizeAndCheckProps);
+        Assertions.assertTrue(e1.getMessage().contains("access-key-id")
+                && e1.getMessage().contains("secret-access-key"));
+
+        Map<String, String> props2 = new HashMap<>();
+        props2.put("iceberg.rest.uri", "http://localhost:8080";);
+        props2.put("iceberg.rest.signing-name", "glue");
+        props2.put("iceberg.rest.signing-region", "us-east-1");
+        props2.put("iceberg.rest.secret-access-key", 
"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
+        props2.put("iceberg.rest.sigv4-enabled", "true");
+
+        IcebergRestProperties restProps2 = new IcebergRestProperties(props2);
+        Assertions.assertThrows(IllegalArgumentException.class, 
restProps2::initNormalizeAndCheckProps);
+    }
+
+    @Test
+    public void testGlueWithIamRoleAndExternalIdFails() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "glue");
+        props.put("iceberg.rest.signing-region", "us-east-1");
+        props.put("iceberg.rest.role_arn", 
"arn:aws:iam::123456789012:role/MyGlueRole");
+        props.put("iceberg.rest.external-id", "external-123");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        IllegalArgumentException e = 
Assertions.assertThrows(IllegalArgumentException.class,
+                restProps::initNormalizeAndCheckProps);
+        
Assertions.assertTrue(e.getMessage().contains("iceberg.rest.role_arn"));
+    }
+
+    @Test
+    public void testGlueWithExternalIdFails() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "glue");
+        props.put("iceberg.rest.signing-region", "us-east-1");
+        props.put("iceberg.rest.external-id", "external-123");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        IllegalArgumentException e = 
Assertions.assertThrows(IllegalArgumentException.class,
+                restProps::initNormalizeAndCheckProps);
+        
Assertions.assertTrue(e.getMessage().contains("iceberg.rest.external-id"));
+    }
+
+    @Test
+    public void testGlueWithCredentialsProviderTypeDefault() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "glue");
+        props.put("iceberg.rest.signing-region", "us-east-1");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+        props.put("iceberg.rest.credentials_provider_type", "DEFAULT");
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        Assertions.assertDoesNotThrow(restProps::initNormalizeAndCheckProps);
+
+        Map<String, String> catalogProps = 
restProps.getIcebergRestCatalogProperties();
+        Assertions.assertEquals("glue", catalogProps.get("rest.signing-name"));
+        Assertions.assertEquals("us-east-1", 
catalogProps.get("rest.signing-region"));
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider"));
+    }
+
+    @Test
+    public void testS3TablesWithCredentialsProviderTypeInstanceProfile() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "s3tables");
+        props.put("iceberg.rest.signing-region", "us-west-2");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+        props.put("iceberg.rest.credentials_provider_type", 
"INSTANCE_PROFILE");
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        Assertions.assertDoesNotThrow(restProps::initNormalizeAndCheckProps);
+
+        Map<String, String> catalogProps = 
restProps.getIcebergRestCatalogProperties();
+        Assertions.assertEquals("s3tables", 
catalogProps.get("rest.signing-name"));
+        Assertions.assertEquals(
+                
"software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider",
+                catalogProps.get("client.credentials-provider"));
+    }
+
+    @Test
+    public void testS3TablesWithCredentialsProviderTypeEnvWithoutAccessKey() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "s3tables");
+        props.put("iceberg.rest.signing-region", "us-west-2");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+        props.put("iceberg.rest.credentials_provider_type", "ENV");
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        Assertions.assertDoesNotThrow(restProps::initNormalizeAndCheckProps);
+
+        Map<String, String> catalogProps = 
restProps.getIcebergRestCatalogProperties();
+        Assertions.assertEquals("s3tables", 
catalogProps.get("rest.signing-name"));
+        Assertions.assertEquals("us-west-2", 
catalogProps.get("rest.signing-region"));
+        Assertions.assertEquals(
+                
"software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider",
+                catalogProps.get("client.credentials-provider"));
+        Assertions.assertFalse(catalogProps.containsKey("rest.access-key-id"));
+        
Assertions.assertFalse(catalogProps.containsKey("rest.secret-access-key"));
+    }
+
+    @Test
+    public void testGlueWithCredentialsProviderTypeEnv() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "glue");
+        props.put("iceberg.rest.signing-region", "ap-east-1");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+        props.put("iceberg.rest.credentials_provider_type", "ENV");
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        Assertions.assertDoesNotThrow(restProps::initNormalizeAndCheckProps);
+
+        Map<String, String> catalogProps = 
restProps.getIcebergRestCatalogProperties();
+        Assertions.assertEquals(
+                
"software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider",
+                catalogProps.get("client.credentials-provider"));
+    }
+
+    @Test
+    public void testAccessKeyPriorityOverCredentialsProviderType() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "glue");
+        props.put("iceberg.rest.signing-region", "us-east-1");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+        props.put("iceberg.rest.access-key-id", "AKIAIOSFODNN7EXAMPLE");
+        props.put("iceberg.rest.secret-access-key", 
"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
+        props.put("iceberg.rest.credentials_provider_type", "DEFAULT");
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        Assertions.assertDoesNotThrow(restProps::initNormalizeAndCheckProps);
+
+        Map<String, String> catalogProps = 
restProps.getIcebergRestCatalogProperties();
+        Assertions.assertEquals("AKIAIOSFODNN7EXAMPLE",
+                catalogProps.get("rest.access-key-id"));
+        Assertions.assertEquals("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
+                catalogProps.get("rest.secret-access-key"));
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider"));
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider.s3.access-key-id"));
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider.s3.secret-access-key"));
+    }
+
+    @Test
+    public void testIamRoleWithCredentialsProviderTypeFails() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "s3tables");
+        props.put("iceberg.rest.signing-region", "us-west-2");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+        props.put("iceberg.rest.role_arn", 
"arn:aws:iam::123456789012:role/MyRole");
+        props.put("iceberg.rest.credentials_provider_type", 
"INSTANCE_PROFILE");
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        IllegalArgumentException e = 
Assertions.assertThrows(IllegalArgumentException.class,
+                restProps::initNormalizeAndCheckProps);
+        
Assertions.assertTrue(e.getMessage().contains("iceberg.rest.role_arn"));
+    }
+
+    @Test
+    public void testNonGlueSigningNameWithoutCredentialsAllowed() {
+        Map<String, String> props = new HashMap<>();
+        props.put("iceberg.rest.uri", "http://localhost:8080";);
+        props.put("iceberg.rest.signing-name", "custom-service");
+        props.put("iceberg.rest.signing-region", "us-east-1");
+        props.put("iceberg.rest.sigv4-enabled", "true");
+
+        IcebergRestProperties restProps = new IcebergRestProperties(props);
+        Assertions.assertDoesNotThrow(restProps::initNormalizeAndCheckProps);
+
+        Map<String, String> catalogProps = 
restProps.getIcebergRestCatalogProperties();
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider"));
+    }
+
     @Test
     public void testToFileIOPropertiesPrefersNonS3Properties() {
         // When both S3Properties and OSSProperties exist, OSSProperties 
should be chosen
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/metastore/IcebergS3TablesMetaStorePropertiesTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/metastore/IcebergS3TablesMetaStorePropertiesTest.java
index 423f74856c7..3043ecc02c5 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/metastore/IcebergS3TablesMetaStorePropertiesTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/metastore/IcebergS3TablesMetaStorePropertiesTest.java
@@ -18,18 +18,61 @@
 package org.apache.doris.datasource.property.metastore;
 
 import org.apache.doris.common.UserException;
+import org.apache.doris.datasource.iceberg.IcebergExternalCatalog;
+import 
org.apache.doris.datasource.property.common.IcebergAwsClientCredentialsProperties;
+import org.apache.doris.datasource.property.storage.S3Properties;
 import org.apache.doris.datasource.property.storage.StorageProperties;
 
 import com.google.common.collect.ImmutableMap;
+import org.apache.iceberg.aws.AssumeRoleAwsClientFactory;
+import org.apache.iceberg.aws.AwsClientProperties;
+import org.apache.iceberg.aws.AwsProperties;
+import org.apache.iceberg.aws.s3.S3FileIOProperties;
 import org.apache.iceberg.catalog.Catalog;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import software.amazon.s3tables.iceberg.S3TablesCatalog;
 
+import java.lang.reflect.Method;
+import java.util.HashMap;
 import java.util.Map;
 
 public class IcebergS3TablesMetaStorePropertiesTest {
 
+    /**
+     * Call private buildS3CatalogProperties to fill catalogProps without 
initializing S3TablesCatalog
+     * (which requires warehouse/table bucket ARN and would throw 
ValidationException).
+     */
+    private static void 
buildS3CatalogProperties(IcebergS3TablesMetaStoreProperties metaProps,
+            Map<String, String> catalogProps) throws Exception {
+        Method m = 
IcebergS3TablesMetaStoreProperties.class.getDeclaredMethod("buildS3CatalogProperties",
 Map.class);
+        m.setAccessible(true);
+        m.invoke(metaProps, catalogProps);
+    }
+
+    @Test
+    public void s3FileIOCredentialPropertiesUseSharedS3Properties() {
+        Map<String, String> props = new HashMap<>();
+        props.put("s3.region", "us-east-1");
+        props.put("s3.endpoint", "https://s3.us-east-1.amazonaws.com";);
+        props.put("s3.access_key", "AKID");
+        props.put("s3.secret_key", "SECRET");
+        props.put("s3.session_token", "TOKEN");
+        props.put("s3.credentials_provider_type", "INSTANCE_PROFILE");
+
+        Map<String, String> catalogProps = new HashMap<>();
+        IcebergAwsClientCredentialsProperties.putS3FileIOCredentialProperties(
+                catalogProps, S3Properties.of(props));
+
+        Assertions.assertEquals("https://s3.us-east-1.amazonaws.com";,
+                catalogProps.get(S3FileIOProperties.ENDPOINT));
+        Assertions.assertEquals("AKID", 
catalogProps.get(S3FileIOProperties.ACCESS_KEY_ID));
+        Assertions.assertEquals("SECRET", 
catalogProps.get(S3FileIOProperties.SECRET_ACCESS_KEY));
+        Assertions.assertEquals("TOKEN", 
catalogProps.get(S3FileIOProperties.SESSION_TOKEN));
+        
Assertions.assertFalse(catalogProps.containsKey(AwsClientProperties.CLIENT_CREDENTIALS_PROVIDER));
+        
Assertions.assertFalse(catalogProps.containsKey(AwsProperties.CLIENT_ASSUME_ROLE_ARN));
+    }
+
     @Test
     public void s3TablesTest() throws UserException {
         Map<String, String> baseProps = ImmutableMap.of(
@@ -52,4 +95,233 @@ public class IcebergS3TablesMetaStorePropertiesTest {
         Assertions.assertEquals(S3TablesCatalog.class, catalog.getClass());
     }
 
+    @Test
+    public void s3TablesWithIamRole() throws Exception {
+        Map<String, String> props = new HashMap<>();
+        props.put("type", "iceberg");
+        props.put("iceberg.catalog.type", "s3tables");
+        props.put("warehouse", "s3://my-bucket/warehouse");
+        props.put("s3.region", "us-east-1");
+        props.put("s3.role_arn", 
"arn:aws:iam::123456789012:role/S3TablesRole");
+        props.put("s3.endpoint", "https://s3.us-east-1.amazonaws.com";);
+
+        IcebergS3TablesMetaStoreProperties metaProps = new 
IcebergS3TablesMetaStoreProperties(props);
+        metaProps.initNormalizeAndCheckProps();
+
+        Assertions.assertEquals(IcebergExternalCatalog.ICEBERG_S3_TABLES, 
metaProps.getIcebergCatalogType());
+
+        Map<String, String> catalogProps = new HashMap<>();
+        buildS3CatalogProperties(metaProps, catalogProps);
+
+        Assertions.assertEquals(AssumeRoleAwsClientFactory.class.getName(),
+                catalogProps.get(AwsProperties.CLIENT_FACTORY));
+        
Assertions.assertFalse(catalogProps.containsKey(S3FileIOProperties.CLIENT_FACTORY));
+        Assertions.assertEquals("arn:aws:iam::123456789012:role/S3TablesRole", 
catalogProps.get("client.assume-role.arn"));
+        Assertions.assertEquals("us-east-1", 
catalogProps.get("client.assume-role.region"));
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider"));
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider.s3.role_arn"));
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider.s3.region"));
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider.s3.credentials_provider_type"));
+        Assertions.assertFalse(catalogProps.containsKey(
+                "client.credentials-provider.assume-role.arn"));
+        Assertions.assertFalse(catalogProps.containsKey(
+                
"client.credentials-provider.assume-role.source-credentials-provider"));
+        Assertions.assertFalse(catalogProps.containsKey(
+                
"client.credentials-provider.assume-role.source-provider-type"));
+        Assertions.assertFalse(catalogProps.containsKey(
+                "client.credentials-provider.client.assume-role.arn"));
+    }
+
+    @Test
+    public void s3TablesWithIamRoleAndExternalId() throws Exception {
+        Map<String, String> props = new HashMap<>();
+        props.put("type", "iceberg");
+        props.put("iceberg.catalog.type", "s3tables");
+        props.put("warehouse", "s3://my-bucket/warehouse");
+        props.put("s3.region", "us-west-2");
+        props.put("s3.role_arn", "arn:aws:iam::999999999999:role/MyRole");
+        props.put("s3.external_id", "external-id-123");
+        props.put("s3.endpoint", "https://s3.us-west-2.amazonaws.com";);
+
+        IcebergS3TablesMetaStoreProperties metaProps = new 
IcebergS3TablesMetaStoreProperties(props);
+        metaProps.initNormalizeAndCheckProps();
+
+        Map<String, String> catalogProps = new HashMap<>();
+        buildS3CatalogProperties(metaProps, catalogProps);
+
+        Assertions.assertEquals("arn:aws:iam::999999999999:role/MyRole", 
catalogProps.get("client.assume-role.arn"));
+        Assertions.assertEquals("external-id-123", 
catalogProps.get("client.assume-role.external-id"));
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider.s3.external_id"));
+        Assertions.assertFalse(catalogProps.containsKey(
+                "client.credentials-provider.assume-role.external-id"));
+    }
+
+    @Test
+    public void s3TablesWithAccessKeyPreferOverIamRole() throws Exception {
+        Map<String, String> props = new HashMap<>();
+        props.put("type", "iceberg");
+        props.put("iceberg.catalog.type", "s3tables");
+        props.put("warehouse", "s3://my-bucket/warehouse");
+        props.put("s3.region", "us-east-1");
+        props.put("s3.access_key", "AKID");
+        props.put("s3.secret_key", "SECRET");
+        props.put("s3.role_arn", "arn:aws:iam::123456789012:role/Role");
+        props.put("s3.endpoint", "https://s3.us-east-1.amazonaws.com";);
+
+        IcebergS3TablesMetaStoreProperties metaProps = new 
IcebergS3TablesMetaStoreProperties(props);
+        metaProps.initNormalizeAndCheckProps();
+
+        Map<String, String> catalogProps = new HashMap<>();
+        buildS3CatalogProperties(metaProps, catalogProps);
+
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider"));
+        Assertions.assertEquals("AKID", 
catalogProps.get(S3FileIOProperties.ACCESS_KEY_ID));
+        Assertions.assertEquals("SECRET", 
catalogProps.get(S3FileIOProperties.SECRET_ACCESS_KEY));
+        Assertions.assertNull(catalogProps.get("client.assume-role.arn"));
+    }
+
+    // --- UT for credentials_provider_type support ---
+
+    @Test
+    public void s3TablesWithCredentialsProviderTypeDefault() throws Exception {
+        Map<String, String> props = new HashMap<>();
+        props.put("type", "iceberg");
+        props.put("iceberg.catalog.type", "s3tables");
+        props.put("warehouse", "s3://my-bucket/warehouse");
+        props.put("s3.region", "us-west-2");
+        props.put("s3.endpoint", "https://s3.us-west-2.amazonaws.com";);
+        props.put("s3.credentials_provider_type", "DEFAULT");
+
+        IcebergS3TablesMetaStoreProperties metaProps = new 
IcebergS3TablesMetaStoreProperties(props);
+        metaProps.initNormalizeAndCheckProps();
+
+        Map<String, String> catalogProps = new HashMap<>();
+        buildS3CatalogProperties(metaProps, catalogProps);
+
+        Assertions.assertEquals("us-west-2", 
catalogProps.get("client.region"));
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider"));
+    }
+
+    @Test
+    public void s3TablesWithCredentialsProviderTypeInstanceProfile() throws 
Exception {
+        Map<String, String> props = new HashMap<>();
+        props.put("type", "iceberg");
+        props.put("iceberg.catalog.type", "s3tables");
+        props.put("warehouse", "s3://my-bucket/warehouse");
+        props.put("s3.region", "ap-east-1");
+        props.put("s3.endpoint", "https://s3.ap-east-1.amazonaws.com";);
+        props.put("s3.credentials_provider_type", "INSTANCE_PROFILE");
+
+        IcebergS3TablesMetaStoreProperties metaProps = new 
IcebergS3TablesMetaStoreProperties(props);
+        metaProps.initNormalizeAndCheckProps();
+
+        Map<String, String> catalogProps = new HashMap<>();
+        buildS3CatalogProperties(metaProps, catalogProps);
+
+        
Assertions.assertEquals("software.amazon.awssdk.auth.credentials.InstanceProfileCredentialsProvider",
+                catalogProps.get("client.credentials-provider"));
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider.s3.credentials_provider_type"));
+    }
+
+    @Test
+    public void s3TablesWithCredentialsProviderTypeEnv() throws Exception {
+        Map<String, String> props = new HashMap<>();
+        props.put("type", "iceberg");
+        props.put("iceberg.catalog.type", "s3tables");
+        props.put("warehouse", "s3://my-bucket/warehouse");
+        props.put("s3.region", "us-east-1");
+        props.put("s3.endpoint", "https://s3.us-east-1.amazonaws.com";);
+        props.put("s3.credentials_provider_type", "ENV");
+
+        IcebergS3TablesMetaStoreProperties metaProps = new 
IcebergS3TablesMetaStoreProperties(props);
+        metaProps.initNormalizeAndCheckProps();
+
+        Map<String, String> catalogProps = new HashMap<>();
+        buildS3CatalogProperties(metaProps, catalogProps);
+
+        
Assertions.assertEquals("software.amazon.awssdk.auth.credentials.EnvironmentVariableCredentialsProvider",
+                catalogProps.get("client.credentials-provider"));
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider.s3.credentials_provider_type"));
+    }
+
+    @Test
+    public void s3TablesAccessKeyPriorityOverCredentialsProviderType() throws 
Exception {
+        Map<String, String> props = new HashMap<>();
+        props.put("type", "iceberg");
+        props.put("iceberg.catalog.type", "s3tables");
+        props.put("warehouse", "s3://my-bucket/warehouse");
+        props.put("s3.region", "us-west-2");
+        props.put("s3.access_key", "AKIAIOSFODNN7EXAMPLE");
+        props.put("s3.secret_key", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
+        props.put("s3.credentials_provider_type", "INSTANCE_PROFILE");
+        props.put("s3.endpoint", "https://s3.us-west-2.amazonaws.com";);
+
+        IcebergS3TablesMetaStoreProperties metaProps = new 
IcebergS3TablesMetaStoreProperties(props);
+        metaProps.initNormalizeAndCheckProps();
+
+        Map<String, String> catalogProps = new HashMap<>();
+        buildS3CatalogProperties(metaProps, catalogProps);
+
+        // Access key should take priority
+        Assertions.assertEquals("AKIAIOSFODNN7EXAMPLE", 
catalogProps.get(S3FileIOProperties.ACCESS_KEY_ID));
+        Assertions.assertEquals("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
+                catalogProps.get(S3FileIOProperties.SECRET_ACCESS_KEY));
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider"));
+    }
+
+    @Test
+    public void s3TablesIamRolePriorityOverCredentialsProviderType() throws 
Exception {
+        Map<String, String> props = new HashMap<>();
+        props.put("type", "iceberg");
+        props.put("iceberg.catalog.type", "s3tables");
+        props.put("warehouse", "s3://my-bucket/warehouse");
+        props.put("s3.region", "us-east-1");
+        props.put("s3.role_arn", 
"arn:aws:iam::123456789012:role/S3TablesRole");
+        props.put("s3.credentials_provider_type", "INSTANCE_PROFILE");
+        props.put("s3.endpoint", "https://s3.us-east-1.amazonaws.com";);
+
+        IcebergS3TablesMetaStoreProperties metaProps = new 
IcebergS3TablesMetaStoreProperties(props);
+        metaProps.initNormalizeAndCheckProps();
+
+        Map<String, String> catalogProps = new HashMap<>();
+        buildS3CatalogProperties(metaProps, catalogProps);
+
+        // IAM Role should take priority
+        Assertions.assertEquals("arn:aws:iam::123456789012:role/S3TablesRole",
+                catalogProps.get("client.assume-role.arn"));
+        Assertions.assertEquals(AssumeRoleAwsClientFactory.class.getName(),
+                catalogProps.get(AwsProperties.CLIENT_FACTORY));
+        
Assertions.assertFalse(catalogProps.containsKey(S3FileIOProperties.CLIENT_FACTORY));
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider"));
+        Assertions.assertFalse(catalogProps.containsKey(
+                "client.credentials-provider.s3.credentials_provider_type"));
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider.s3.role_arn"));
+        Assertions.assertFalse(catalogProps.containsKey(
+                
"client.credentials-provider.assume-role.source-credentials-provider"));
+        Assertions.assertFalse(catalogProps.containsKey(
+                
"client.credentials-provider.assume-role.source-provider-type"));
+        Assertions.assertFalse(catalogProps.containsKey(
+                "client.credentials-provider.client.credentials-provider"));
+    }
+
+    @Test
+    public void s3TablesDefaultCredentialsProviderTypeWhenNothingSet() throws 
Exception {
+        Map<String, String> props = new HashMap<>();
+        props.put("type", "iceberg");
+        props.put("iceberg.catalog.type", "s3tables");
+        props.put("warehouse", "s3://my-bucket/warehouse");
+        props.put("s3.region", "us-west-2");
+        props.put("s3.endpoint", "https://s3.us-west-2.amazonaws.com";);
+        // Not setting any credentials or credentials_provider_type
+        // S3Properties defaults to DEFAULT mode
+
+        IcebergS3TablesMetaStoreProperties metaProps = new 
IcebergS3TablesMetaStoreProperties(props);
+        metaProps.initNormalizeAndCheckProps();
+
+        Map<String, String> catalogProps = new HashMap<>();
+        buildS3CatalogProperties(metaProps, catalogProps);
+
+        // Let AWS SDK use its default provider chain when no credentials are 
provided.
+        
Assertions.assertFalse(catalogProps.containsKey("client.credentials-provider"));
+    }
 }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
index 481286f9886..abe52d64cc4 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
@@ -247,6 +247,19 @@ public class S3PropertiesTest {
         Assertions.assertEquals("arn:aws:iam::123456789012:role/MyTestRole", 
backendProperties.get("AWS_ROLE_ARN"));
     }
 
+    @Test
+    public void testS3PropertiesIgnoreIcebergRestIamRoleAliases() throws 
UserException {
+        origProps.put("s3.endpoint", "s3.us-west-2.amazonaws.com");
+        origProps.put("iceberg.rest.role_arn", 
"arn:aws:iam::123456789012:role/MyTestRole");
+        origProps.put("iceberg.rest.external-id", "external-123");
+
+        S3Properties s3Props = (S3Properties) 
StorageProperties.createPrimary(origProps);
+        Map<String, String> backendProperties = 
s3Props.getBackendConfigProperties();
+
+        Assertions.assertNull(backendProperties.get("AWS_ROLE_ARN"));
+        Assertions.assertNull(backendProperties.get("AWS_EXTERNAL_ID"));
+    }
+
 
     @Test
     public void testGetAwsCredentialsProviderWithIamRoleAndExternalId() {
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/s3tables/S3TablesTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/s3tables/S3TablesTest.java
index 576033785ed..4232de6f1c2 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/s3tables/S3TablesTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/s3tables/S3TablesTest.java
@@ -17,8 +17,6 @@
 
 package org.apache.doris.datasource.s3tables;
 
-import 
org.apache.doris.datasource.iceberg.s3tables.CustomAwsCredentialsProvider;
-
 import org.apache.iceberg.FileScanTask;
 import org.apache.iceberg.Table;
 import org.apache.iceberg.TableScan;
@@ -39,14 +37,7 @@ public class S3TablesTest {
         S3TablesCatalog s3TablesCatalog = new S3TablesCatalog();
         Map<String, String> s3Properties = new HashMap<>();
 
-        // ak, sk
-        String accessKeyId = "";
-        String secretKey = "";
-
         s3Properties.put("client.region", "us-east-1");
-        s3Properties.put("client.credentials-provider", 
CustomAwsCredentialsProvider.class.getName());
-        s3Properties.put("client.credentials-provider.s3.access-key-id", 
accessKeyId);
-        s3Properties.put("client.credentials-provider.s3.secret-access-key", 
secretKey);
 
         String warehouse = 
"arn:aws:s3tables:us-east-1:169698404049:bucket/yy-s3-table-bucket";
         s3Properties.put("warehouse", warehouse);
diff --git 
a/regression-test/suites/aws_iam_role_p0/test_iceberg_s3tables_catalog_credentials_provider.groovy
 
b/regression-test/suites/aws_iam_role_p0/test_iceberg_s3tables_catalog_credentials_provider.groovy
new file mode 100644
index 00000000000..bb0d67327d1
--- /dev/null
+++ 
b/regression-test/suites/aws_iam_role_p0/test_iceberg_s3tables_catalog_credentials_provider.groovy
@@ -0,0 +1,105 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+import com.google.common.base.Strings;
+
+suite("test_iceberg_s3tables_catalog_credentials_provider") {
+
+    List<String> requiredConfigNames = [
+            "icebergS3TablesCatalog1Props",
+            "icebergS3TablesCatalog2Props",
+            "icebergS3TablesCatalog3Props",
+            "icebergS3TablesPartitionDbTableName",
+            "icebergS3TablesNonPartitionDbTableName",
+            "icebergS3TablesExpectCounts"
+    ]
+    if (requiredConfigNames.any { 
Strings.isNullOrEmpty(context.config.otherConfigs.get(it)) }) {
+        return
+    }
+
+    sql """ ADMIN SET FRONTEND CONFIG 
("aws_credentials_provider_version"="v2"); """
+
+    String catalog1Props = 
context.config.otherConfigs.get("icebergS3TablesCatalog1Props")
+    String catalog2Props = 
context.config.otherConfigs.get("icebergS3TablesCatalog2Props")
+    String catalog3Props = 
context.config.otherConfigs.get("icebergS3TablesCatalog3Props")
+    String partitionDbTableName = 
context.config.otherConfigs.get("icebergS3TablesPartitionDbTableName")
+    String nonPartitionDbTableName = 
context.config.otherConfigs.get("icebergS3TablesNonPartitionDbTableName")
+    String expectCounts = 
context.config.otherConfigs.get("icebergS3TablesExpectCounts")
+    String expectedWebIdentityError = "Either the environment variable 
AWS_WEB_IDENTITY_TOKEN_FILE or the javaproperty aws.webIdentityTokenFile must 
be set."
+
+    def createCatalogAndQueryTables = {
+            catalogProps, catalogName, partitionQueryDbTableName, 
nonPartitionQueryDbTableName, expectedCounts ->
+        sql """drop catalog if exists ${catalogName}"""
+        try {
+            sql """
+                create catalog ${catalogName} properties (
+                    ${catalogProps}
+                );
+            """
+            def partitionResult = sql """
+                select count(1) from 
${catalogName}.${partitionQueryDbTableName};
+            """
+            def nonPartitionResult = sql """
+                select count(1) from 
${catalogName}.${nonPartitionQueryDbTableName};
+            """
+            def partitionCount = partitionResult[0][0]
+            def nonPartitionCount = nonPartitionResult[0][0]
+            assertTrue(partitionCount == expectedCounts.toInteger())
+            assertTrue(nonPartitionCount == expectedCounts.toInteger())
+            assertTrue(partitionCount == nonPartitionCount)
+        } finally {
+            sql """drop catalog if exists ${catalogName}"""
+        }
+    }
+
+    def createCatalogAndShowDatabasesException = { catalogProps, catalogName, 
expectedMessage ->
+        sql """drop catalog if exists ${catalogName}"""
+        try {
+            sql """
+                create catalog ${catalogName} properties (
+                    ${catalogProps}
+                );
+            """
+            sql """switch ${catalogName};"""
+            try {
+                sql """show databases;"""
+                assertTrue(false)
+            } catch (Exception e) {
+                assertTrue(e.getMessage().contains(expectedMessage))
+            }
+        } finally {
+            sql """drop catalog if exists ${catalogName}"""
+        }
+    }
+
+    createCatalogAndQueryTables(
+            catalog1Props,
+            "iceberg_s3tables_catalog_1",
+            partitionDbTableName,
+            nonPartitionDbTableName,
+            expectCounts)
+    createCatalogAndQueryTables(
+            catalog2Props,
+            "iceberg_s3tables_catalog_2",
+            partitionDbTableName,
+            nonPartitionDbTableName,
+            expectCounts)
+    createCatalogAndShowDatabasesException(
+            catalog3Props,
+            "iceberg_s3tables_catalog_3",
+            expectedWebIdentityError)
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to