Repository: getmoto/moto
Pull request #8524 (2025-01-26 06:10:56)

chore: update EC2 Instance Offerings
Automated changes by [create-pull-request](https://github.com/peter-evans/create-pull-request) GitHub action


Patch:
diff --git a/docs/docs/services/s3tables.rst b/docs/docs/services/s3tables.rst
new file mode 100644
index 000000000..4ad3d48ca
--- /dev/null
+++ b/docs/docs/services/s3tables.rst
@@ -0,0 +1,45 @@
+.. _implementedservice_s3tables:
+
+.. |start-h3| raw:: html
+
+    <h3>
+
+.. |end-h3| raw:: html
+
+    </h3>
+
+========
+s3tables
+========
+
+.. autoclass:: moto.s3tables.models.S3TablesBackend
+
+|start-h3| Implemented features for this service |end-h3|
+
+- [X] create_namespace
+- [X] create_table
+- [X] create_table_bucket
+- [X] delete_namespace
+- [X] delete_table
+- [X] delete_table_bucket
+- [ ] delete_table_bucket_policy
+- [ ] delete_table_policy
+- [X] get_namespace
+- [X] get_table
+- [X] get_table_bucket
+- [ ] get_table_bucket_maintenance_configuration
+- [ ] get_table_bucket_policy
+- [ ] get_table_maintenance_configuration
+- [ ] get_table_maintenance_job_status
+- [ ] get_table_metadata_location
+- [ ] get_table_policy
+- [X] list_namespaces
+- [X] list_table_buckets
+- [X] list_tables
+- [ ] put_table_bucket_maintenance_configuration
+- [ ] put_table_bucket_policy
+- [ ] put_table_maintenance_configuration
+- [ ] put_table_policy
+- [X] rename_table
+- [X] update_table_metadata_location
+
diff --git a/moto/backend_index.py b/moto/backend_index.py
index 609fe0dae..9d8abc829 100644
--- a/moto/backend_index.py
+++ b/moto/backend_index.py
@@ -160,17 +160,18 @@ backend_url_patterns = [
         "route53resolver",
         re.compile("https?://route53resolver\\.(.+)\\.amazonaws\\.com"),
     ),
-    ("s3", re.compile("https?://s3(?!-control)(.*)\\.amazonaws.com")),
+    ("s3", re.compile("https?://s3(?!(-control|tables))(.*)\\.amazonaws.com")),
     (
         "s3",
         re.compile(
-            "https?://(?P<bucket_name>[a-zA-Z0-9\\-_.]*)\\.?s3(?!-control)(.*)\\.amazonaws.com"
+            "https?://(?P<bucket_name>[a-zA-Z0-9\\-_.]*)\\.?s3(?!(-control|tables))(.*)\\.amazonaws.com"
         ),
     ),
     (
         "s3control",
         re.compile("https?://([0-9]+)\\.s3-control\\.(.+)\\.amazonaws\\.com"),
     ),
+    ("s3tables", re.compile("https?://s3tables\\.(.+)\\.amazonaws\\.com")),
     ("sagemaker", re.compile("https?://api\\.sagemaker\\.(.+)\\.amazonaws.com")),
     (
         "sagemakermetrics",
diff --git a/moto/backends.py b/moto/backends.py
index 9b2454f77..aa0fe4c1b 100644
--- a/moto/backends.py
+++ b/moto/backends.py
@@ -124,6 +124,7 @@ if TYPE_CHECKING:
     from moto.route53resolver.models import Route53ResolverBackend
     from moto.s3.models import S3Backend
     from moto.s3control.models import S3ControlBackend
+    from moto.s3tables.models import S3TablesBackend
     from moto.sagemaker.models import SageMakerModelBackend
     from moto.sagemakermetrics.models import SageMakerMetricsBackend
     from moto.sagemakerruntime.models import SageMakerRuntimeBackend
@@ -307,6 +308,7 @@ SERVICE_NAMES = Union[
     "Literal['s3']",
     "Literal['s3bucket_path']",
     "Literal['s3control']",
+    "Literal['s3tables']",
     "Literal['sagemaker']",
     "Literal['sagemaker-metrics']",
     "Literal['sagemaker-runtime']",
@@ -753,6 +755,8 @@ def get_backend(
 ) -> "BackendDict[WorkSpacesWebBackend]": ...
 @overload
 def get_backend(name: "Literal['xray']") -> "BackendDict[XRayBackend]": ...
+@overload
+def get_backend(name: "Literal['s3tables']") -> "BackendDict[S3TablesBackend]": ...
 
 
 def get_backend(name: SERVICE_NAMES) -> "BackendDict[SERVICE_BACKEND]":
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone-id/ap-northeast-1.json b/moto/ec2/resources/instance_type_offerings/availability-zone-id/ap-northeast-1.json
index ecd50738d..fe0e4edb2 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone-id/ap-northeast-1.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone-id/ap-northeast-1.json
@@ -1023,6 +1023,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "apne1-az1"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "apne1-az1"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "apne1-az1"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "apne1-az1"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "apne1-az1"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "apne1-az1"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "apne1-az1"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "apne1-az1"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "apne1-az1"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "apne1-az1"
+ },
  {
   "InstanceType": "im4gn.16xlarge",
   "Location": "apne1-az1"
@@ -3723,6 +3759,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "apne1-az2"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "apne1-az2"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "apne1-az2"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "apne1-az2"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "apne1-az2"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "apne1-az2"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "apne1-az2"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "apne1-az2"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "apne1-az2"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "apne1-az2"
+ },
  {
   "InstanceType": "im4gn.16xlarge",
   "Location": "apne1-az2"
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-central-1.json b/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-central-1.json
index 9de62ccd6..eba6cb655 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-central-1.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-central-1.json
@@ -975,6 +975,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "euc1-az1"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "euc1-az1"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "euc1-az1"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "euc1-az1"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "euc1-az1"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "euc1-az1"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "euc1-az1"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "euc1-az1"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "euc1-az1"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "euc1-az1"
+ },
  {
   "InstanceType": "i8g.12xlarge",
   "Location": "euc1-az1"
@@ -3879,6 +3915,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "euc1-az2"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "euc1-az2"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "euc1-az2"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "euc1-az2"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "euc1-az2"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "euc1-az2"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "euc1-az2"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "euc1-az2"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "euc1-az2"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "euc1-az2"
+ },
  {
   "InstanceType": "i8g.12xlarge",
   "Location": "euc1-az2"
@@ -6799,6 +6871,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "euc1-az3"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "euc1-az3"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "euc1-az3"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "euc1-az3"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "euc1-az3"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "euc1-az3"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "euc1-az3"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "euc1-az3"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "euc1-az3"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "euc1-az3"
+ },
  {
   "InstanceType": "i8g.12xlarge",
   "Location": "euc1-az3"
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-west-2.json b/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-west-2.json
index befadd4ed..1f80108a6 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-west-2.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone-id/eu-west-2.json
@@ -723,6 +723,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "euw2-az1"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "euw2-az1"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "euw2-az1"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "euw2-az1"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "euw2-az1"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "euw2-az1"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "euw2-az1"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "euw2-az1"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "euw2-az1"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "euw2-az1"
+ },
  {
   "InstanceType": "im4gn.16xlarge",
   "Location": "euw2-az1"
@@ -2679,6 +2715,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "euw2-az2"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "euw2-az2"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "euw2-az2"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "euw2-az2"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "euw2-az2"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "euw2-az2"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "euw2-az2"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "euw2-az2"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "euw2-az2"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "euw2-az2"
+ },
  {
   "InstanceType": "im4gn.16xlarge",
   "Location": "euw2-az2"
@@ -4591,6 +4663,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "euw2-az3"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "euw2-az3"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "euw2-az3"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "euw2-az3"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "euw2-az3"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "euw2-az3"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "euw2-az3"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "euw2-az3"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "euw2-az3"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "euw2-az3"
+ },
  {
   "InstanceType": "im4gn.16xlarge",
   "Location": "euw2-az3"
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone/ap-northeast-1.json b/moto/ec2/resources/instance_type_offerings/availability-zone/ap-northeast-1.json
index 9786d9bd0..9ccc1f22f 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone/ap-northeast-1.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone/ap-northeast-1.json
@@ -4043,6 +4043,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "ap-northeast-1c"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "ap-northeast-1c"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "ap-northeast-1c"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "ap-northeast-1c"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "ap-northeast-1c"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "ap-northeast-1c"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "ap-northeast-1c"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "ap-northeast-1c"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "ap-northeast-1c"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "ap-northeast-1c"
+ },
  {
   "InstanceType": "im4gn.16xlarge",
   "Location": "ap-northeast-1c"
@@ -6743,6 +6779,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "ap-northeast-1d"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "ap-northeast-1d"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "ap-northeast-1d"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "ap-northeast-1d"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "ap-northeast-1d"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "ap-northeast-1d"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "ap-northeast-1d"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "ap-northeast-1d"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "ap-northeast-1d"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "ap-northeast-1d"
+ },
  {
   "InstanceType": "im4gn.16xlarge",
   "Location": "ap-northeast-1d"
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone/eu-central-1.json b/moto/ec2/resources/instance_type_offerings/availability-zone/eu-central-1.json
index f886f0136..299ab866e 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone/eu-central-1.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone/eu-central-1.json
@@ -995,6 +995,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "eu-central-1a"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "eu-central-1a"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "eu-central-1a"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "eu-central-1a"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "eu-central-1a"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "eu-central-1a"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "eu-central-1a"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "eu-central-1a"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "eu-central-1a"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "eu-central-1a"
+ },
  {
   "InstanceType": "i8g.12xlarge",
   "Location": "eu-central-1a"
@@ -3915,6 +3951,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "eu-central-1b"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "eu-central-1b"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "eu-central-1b"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "eu-central-1b"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "eu-central-1b"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "eu-central-1b"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "eu-central-1b"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "eu-central-1b"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "eu-central-1b"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "eu-central-1b"
+ },
  {
   "InstanceType": "i8g.12xlarge",
   "Location": "eu-central-1b"
@@ -6931,6 +7003,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "eu-central-1c"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "eu-central-1c"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "eu-central-1c"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "eu-central-1c"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "eu-central-1c"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "eu-central-1c"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "eu-central-1c"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "eu-central-1c"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "eu-central-1c"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "eu-central-1c"
+ },
  {
   "InstanceType": "i8g.12xlarge",
   "Location": "eu-central-1c"
diff --git a/moto/ec2/resources/instance_type_offerings/availability-zone/eu-west-2.json b/moto/ec2/resources/instance_type_offerings/availability-zone/eu-west-2.json
index 38c864204..6dada539a 100644
--- a/moto/ec2/resources/instance_type_offerings/availability-zone/eu-west-2.json
+++ b/moto/ec2/resources/instance_type_offerings/availability-zone/eu-west-2.json
@@ -795,6 +795,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "eu-west-2a"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "eu-west-2a"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "eu-west-2a"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "eu-west-2a"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "eu-west-2a"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "eu-west-2a"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "eu-west-2a"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "eu-west-2a"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "eu-west-2a"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "eu-west-2a"
+ },
  {
   "InstanceType": "im4gn.16xlarge",
   "Location": "eu-west-2a"
@@ -2707,6 +2743,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "eu-west-2b"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "eu-west-2b"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "eu-west-2b"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "eu-west-2b"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "eu-west-2b"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "eu-west-2b"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "eu-west-2b"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "eu-west-2b"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "eu-west-2b"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "eu-west-2b"
+ },
  {
   "InstanceType": "im4gn.16xlarge",
   "Location": "eu-west-2b"
@@ -4603,6 +4675,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "eu-west-2c"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "eu-west-2c"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "eu-west-2c"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "eu-west-2c"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "eu-west-2c"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "eu-west-2c"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "eu-west-2c"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "eu-west-2c"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "eu-west-2c"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "eu-west-2c"
+ },
  {
   "InstanceType": "im4gn.16xlarge",
   "Location": "eu-west-2c"
diff --git a/moto/ec2/resources/instance_type_offerings/region/ap-northeast-1.json b/moto/ec2/resources/instance_type_offerings/region/ap-northeast-1.json
index cf21a1523..264da540b 100644
--- a/moto/ec2/resources/instance_type_offerings/region/ap-northeast-1.json
+++ b/moto/ec2/resources/instance_type_offerings/region/ap-northeast-1.json
@@ -1059,6 +1059,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "ap-northeast-1"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "ap-northeast-1"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "ap-northeast-1"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "ap-northeast-1"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "ap-northeast-1"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "ap-northeast-1"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "ap-northeast-1"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "ap-northeast-1"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "ap-northeast-1"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "ap-northeast-1"
+ },
  {
   "InstanceType": "im4gn.16xlarge",
   "Location": "ap-northeast-1"
diff --git a/moto/ec2/resources/instance_type_offerings/region/eu-central-1.json b/moto/ec2/resources/instance_type_offerings/region/eu-central-1.json
index 74b146b43..d2aec3723 100644
--- a/moto/ec2/resources/instance_type_offerings/region/eu-central-1.json
+++ b/moto/ec2/resources/instance_type_offerings/region/eu-central-1.json
@@ -1095,6 +1095,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "eu-central-1"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "eu-central-1"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "eu-central-1"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "eu-central-1"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "eu-central-1"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "eu-central-1"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "eu-central-1"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "eu-central-1"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "eu-central-1"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "eu-central-1"
+ },
  {
   "InstanceType": "i8g.12xlarge",
   "Location": "eu-central-1"
diff --git a/moto/ec2/resources/instance_type_offerings/region/eu-west-2.json b/moto/ec2/resources/instance_type_offerings/region/eu-west-2.json
index c79f01e1f..f10b05a19 100644
--- a/moto/ec2/resources/instance_type_offerings/region/eu-west-2.json
+++ b/moto/ec2/resources/instance_type_offerings/region/eu-west-2.json
@@ -795,6 +795,42 @@
   "InstanceType": "i4i.xlarge",
   "Location": "eu-west-2"
  },
+ {
+  "InstanceType": "i7ie.12xlarge",
+  "Location": "eu-west-2"
+ },
+ {
+  "InstanceType": "i7ie.18xlarge",
+  "Location": "eu-west-2"
+ },
+ {
+  "InstanceType": "i7ie.24xlarge",
+  "Location": "eu-west-2"
+ },
+ {
+  "InstanceType": "i7ie.2xlarge",
+  "Location": "eu-west-2"
+ },
+ {
+  "InstanceType": "i7ie.3xlarge",
+  "Location": "eu-west-2"
+ },
+ {
+  "InstanceType": "i7ie.48xlarge",
+  "Location": "eu-west-2"
+ },
+ {
+  "InstanceType": "i7ie.6xlarge",
+  "Location": "eu-west-2"
+ },
+ {
+  "InstanceType": "i7ie.large",
+  "Location": "eu-west-2"
+ },
+ {
+  "InstanceType": "i7ie.xlarge",
+  "Location": "eu-west-2"
+ },
  {
   "InstanceType": "im4gn.16xlarge",
   "Location": "eu-west-2"
diff --git a/moto/s3/exceptions.py b/moto/s3/exceptions.py
index 47c2af819..f9a63cd7b 100644
--- a/moto/s3/exceptions.py
+++ b/moto/s3/exceptions.py
@@ -635,3 +635,13 @@ class DaysMustProvidedExceptForSelectRequest(S3ClientError):
             "DaysMustProvidedExceptForSelectRequest",
             "`Days` must be provided except for select requests",
         )
+
+
+class MethodNotAllowed(S3ClientError):
+    code = 405
+
+    def __init__(self) -> None:
+        super().__init__(
+            "MethodNotAllowed",
+            "The specified method is not allowed against this resource.",
+        )
diff --git a/moto/s3/models.py b/moto/s3/models.py
index 7cdcf7a9e..d50c2b9c7 100644
--- a/moto/s3/models.py
+++ b/moto/s3/models.py
@@ -56,6 +56,7 @@ from moto.s3.exceptions import (
     InvalidTagError,
     InvalidTargetBucketForLogging,
     MalformedXML,
+    MethodNotAllowed,
     MissingBucket,
     MissingKey,
     NoSuchPublicAccessBlockConfiguration,
@@ -1646,6 +1647,9 @@ class FakeBucket(CloudFormationModel):
         return now.strftime("%Y-%m-%dT%H:%M:%SZ")
 
 
+class FakeTableStorageBucket(FakeBucket): ...
+
+
 class S3Backend(BaseBackend, CloudWatchMetricProvider):
     """
     Custom S3 endpoints are supported, if you are using a S3-compatible storage solution like Ceph.
@@ -1711,6 +1715,7 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider):
     def __init__(self, region_name: str, account_id: str):
         super().__init__(region_name, account_id)
         self.buckets: Dict[str, FakeBucket] = {}
+        self.table_buckets: Dict[str, FakeTableStorageBucket] = {}
         self.tagger = TaggingService()
         self._pagination_tokens: Dict[str, str] = {}
 
@@ -1719,7 +1724,9 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider):
         # Ensure that these TemporaryFile-objects are closed, and leave no filehandles open
         #
         # First, check all known buckets/keys
-        for bucket in self.buckets.values():
+        for bucket in itertools.chain(
+            self.buckets.values(), self.table_buckets.values()
+        ):
             for key in bucket.keys.values():  # type: ignore
                 if isinstance(key, FakeKey):
                     key.dispose()
@@ -1876,6 +1883,15 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider):
 
         return new_bucket
 
+    def create_table_storage_bucket(self, region_name: str) -> FakeTableStorageBucket:
+        # every s3 table is assigned a unique s3 bucket with a random name
+        bucket_name = f"{str(random.uuid4())}--table-s3"
+        new_bucket = FakeTableStorageBucket(
+            name=bucket_name, account_id=self.account_id, region_name=region_name
+        )
+        self.table_buckets[bucket_name] = new_bucket
+        return new_bucket
+
     def list_buckets(self) -> List[FakeBucket]:
         return list(self.buckets.values())
 
@@ -1883,6 +1899,9 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider):
         if bucket_name in self.buckets:
             return self.buckets[bucket_name]
 
+        if bucket_name in self.table_buckets:
+            return self.table_buckets[bucket_name]
+
         if bucket_name in s3_backends.bucket_accounts:
             if not s3_allow_crossdomain_access():
                 raise AccessDeniedByLock
@@ -1903,6 +1922,19 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider):
             s3_backends.bucket_accounts.pop(bucket_name, None)
             return self.buckets.pop(bucket_name)
 
+    def delete_table_storage_bucket(self, bucket_name: str) -> Optional[FakeBucket]:
+        bucket = self.get_bucket(bucket_name)
+        assert isinstance(bucket, FakeTableStorageBucket)
+        # table storage buckets can be deleted while not empty
+        if bucket.keys:
+            for key in bucket.keys.values():  # type: ignore
+                if isinstance(key, FakeKey):
+                    key.dispose()
+            for part in bucket.multiparts.values():
+                part.dispose()
+        s3_backends.bucket_accounts.pop(bucket_name, None)
+        return self.table_buckets.pop(bucket_name)
+
     def get_bucket_accelerate_configuration(self, bucket_name: str) -> Optional[str]:
         bucket = self.get_bucket(bucket_name)
         return bucket.accelerate_configuration
@@ -2649,6 +2681,8 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider):
 
         MOTO_S3_DEFAULT_MAX_KEYS=5
         """
+        if isinstance(bucket, FakeTableStorageBucket):
+            raise MethodNotAllowed()
         key_results = set()
         folder_results = set()
         if prefix:
@@ -2707,6 +2741,8 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider):
 
         MOTO_S3_DEFAULT_MAX_KEYS=5
         """
+        if isinstance(bucket, FakeTableStorageBucket):
+            raise MethodNotAllowed()
         result_keys, result_folders, _, _ = self.list_objects(
             bucket, prefix, delimiter, marker=None, max_keys=None
         )
@@ -2789,6 +2825,8 @@ class S3Backend(BaseBackend, CloudWatchMetricProvider):
         bypass: bool = False,
     ) -> Tuple[bool, Dict[str, Any]]:
         bucket = self.get_bucket(bucket_name)
+        if isinstance(bucket, FakeTableStorageBucket):
+            raise MethodNotAllowed()
 
         response_meta = {}
         delete_key = bucket.keys.get(key_name)
diff --git a/moto/s3/urls.py b/moto/s3/urls.py
index 572572020..9e81bee37 100644
--- a/moto/s3/urls.py
+++ b/moto/s3/urls.py
@@ -4,8 +4,8 @@ from .responses import S3Response
 
 # Catch s3.amazonaws.com, but not s3-control.amazonaws.com
 url_bases = [
-    r"https?://s3(?!-control)(.*)\.amazonaws.com",
-    r"https?://(?P<bucket_name>[a-zA-Z0-9\-_.]*)\.?s3(?!-control)(.*)\.amazonaws.com",
+    r"https?://s3(?!(-control|tables))(.*)\.amazonaws.com",
+    r"https?://(?P<bucket_name>[a-zA-Z0-9\-_.]*)\.?s3(?!(-control|tables))(.*)\.amazonaws.com",
 ]
 
 url_bases.extend(settings.get_s3_custom_endpoints())
diff --git a/moto/s3tables/__init__.py b/moto/s3tables/__init__.py
new file mode 100644
index 000000000..6eddc58e7
--- /dev/null
+++ b/moto/s3tables/__init__.py
@@ -0,0 +1,1 @@
+from .models import s3tables_backends  # noqa: F401
diff --git a/moto/s3tables/exceptions.py b/moto/s3tables/exceptions.py
new file mode 100644
index 000000000..b9d15d463
--- /dev/null
+++ b/moto/s3tables/exceptions.py
@@ -0,0 +1,101 @@
+"""Exceptions raised by the s3tables service."""
+
+from moto.core.exceptions import JsonRESTError
+
+
+class BadRequestException(JsonRESTError):
+    code = 400
+
+    def __init__(self, message: str) -> None:
+        super().__init__("BadRequestException", message)
+
+
+class InvalidContinuationToken(BadRequestException):
+    msg = "The continuation token is not valid."
+
+    def __init__(self) -> None:
+        super().__init__(self.msg)
+
+
+class InvalidTableBucketName(BadRequestException):
+    msg = "The specified bucket name is not valid."
+
+    def __init__(self) -> None:
+        super().__init__(self.msg)
+
+
+class InvalidTableName(BadRequestException):
+    template = "1 validation error detected: Value '%s' at 'name' failed to satisfy constraint: Member must satisfy regular expression pattern: [0-9a-z_]*"
+
+    def __init__(self, name: str) -> None:
+        super().__init__(self.template.format(name))
+
+
+class InvalidNamespaceName(BadRequestException):
+    msg = "The specified namespace name is not valid."
+
+    def __init__(self) -> None:
+        super().__init__(self.msg)
+
+
+class InvalidMetadataLocation(BadRequestException):
+    msg = "The specified metadata location is not valid."
+
+    def __init__(self) -> None:
+        super().__init__(self.msg)
+
+
+class NothingToRename(BadRequestException):
+    msg = "Neither a new namespace name nor a new table name is specified."
+
+    def __init__(self) -> None:
+        super().__init__(self.msg)
+
+
+class NotFoundException(JsonRESTError):
+    code = 404
+
+    def __init__(self, message: str) -> None:
+        super().__init__("NotFoundException", message)
+
+
+class NamespaceDoesNotExist(NotFoundException):
+    msg = "The specified namespace does not exist."
+
+    def __init__(self) -> None:
+        super().__init__(self.msg)
+
+
+class DestinationNamespaceDoesNotExist(NotFoundException):
+    msg = "The specified destination namespace does not exist."
+
+    def __init__(self) -> None:
+        super().__init__(self.msg)
+
+
+class TableDoesNotExist(NotFoundException):
+    msg = "The specified table does not exist."
+
+    def __init__(self) -> None:
+        super().__init__(self.msg)
+
+
+class ConflictException(JsonRESTError):
+    code = 409
+
+    def __init__(self, message: str) -> None:
+        super().__init__("ConflictException", message)
+
+
+class VersionTokenMismatch(ConflictException):
+    msg = "Provided version token does not match the table version token."
+
+    def __init__(self) -> None:
+        super().__init__(self.msg)
+
+
+class TableAlreadyExists(ConflictException):
+    msg = "A table with an identical name already exists in the namespace."
+
+    def __init__(self) -> None:
+        super().__init__(self.msg)
diff --git a/moto/s3tables/models.py b/moto/s3tables/models.py
new file mode 100644
index 000000000..a7e497617
--- /dev/null
+++ b/moto/s3tables/models.py
@@ -0,0 +1,423 @@
+"""S3TablesBackend class with methods for supported APIs."""
+
+import datetime
+import re
+from hashlib import md5
+from typing import Dict, List, Literal, Optional, Union
+
+from moto.core.base_backend import BackendDict, BaseBackend
+from moto.moto_api._internal import mock_random as random
+from moto.s3.models import FakeBucket
+from moto.s3tables.exceptions import (
+    ConflictException,
+    DestinationNamespaceDoesNotExist,
+    InvalidContinuationToken,
+    InvalidMetadataLocation,
+    InvalidNamespaceName,
+    InvalidTableBucketName,
+    InvalidTableName,
+    NotFoundException,
+    NothingToRename,
+    TableAlreadyExists,
+    TableDoesNotExist,
+    VersionTokenMismatch,
+)
+from moto.utilities.paginator import paginate
+from moto.utilities.utils import get_partition
+
+# https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-tables-buckets-naming.html
+TABLE_BUCKET_NAME_PATTERN = re.compile(r"[a-z0-9_-]{3,63}")
+TABLE_BUCKET_NAME_RESERVED_PREFIXES = ("xn--", "sthree-", "amzn-s3-demo")
+TABLE_BUCKET_NAME_RESERVED_SUFFIXES = ("-s3alias", "--ol-s3", "--x-s3")
+NAMESPACE_NAME_PATTERN = re.compile(r"[0-9a-z_]*")
+TABLE_NAME_PATTERN = re.compile(r"[0-9a-z_]*")
+
+
+def _validate_table_bucket_name(name: str) -> None:
+    if (
+        not TABLE_BUCKET_NAME_PATTERN.match(name)
+        or any(
+            name.startswith(prefix) for prefix in TABLE_BUCKET_NAME_RESERVED_PREFIXES
+        )
+        or any(name.endswith(suffix) for suffix in TABLE_BUCKET_NAME_RESERVED_SUFFIXES)
+    ):
+        raise InvalidTableBucketName()
+
+
+def _validate_namespace_name(name: str) -> None:
+    if not NAMESPACE_NAME_PATTERN.match(name):
+        raise InvalidNamespaceName()
+
+
+def _validate_table_name(name: str) -> None:
+    if not TABLE_NAME_PATTERN.match(name):
+        raise InvalidTableName(name)
+
+
+PAGINATION_MODEL = {
+    "list_table_buckets": {
+        "input_token": "continuation_token",
+        "limit_key": "max_buckets",
+        "limit_default": 1000,
+        "unique_attribute": ["arn"],
+        "fail_on_invalid_token": InvalidContinuationToken,
+    },
+    "list_namespaces": {
+        "input_token": "continuation_token",
+        "limit_key": "max_namespaces",
+        "limit_default": 1000,
+        "unique_attribute": ["name"],
+        "fail_on_invalid_token": InvalidContinuationToken,
+    },
+    "list_tables": {
+        "input_token": "continuation_token",
+        "limit_key": "max_tables",
+        "limit_default": 1000,
+        "unique_attribute": ["arn"],
+        "fail_on_invalid_token": InvalidContinuationToken,
+    },
+}
+
+
+class Table:
+    def __init__(
+        self,
+        name: str,
+        account_id: str,
+        created_by: str,
+        format: Literal["ICEBERG"],
+        namespace: str,
+        table_bucket_arn: str,
+        type: Union[Literal["customer"], Literal["aws"]],
+        managed_by_service: bool,
+        partition: str,
+    ):
+        _validate_table_name(name)
+        self.name = name
+        self.account_id = account_id
+        self.partition = partition
+        self.created_by = created_by
+        self.format = format
+        self.type = type
+        self.version_token = self._generate_version_token()
+        self.creation_date = datetime.datetime.now(tz=datetime.timezone.utc)
+        self.last_modified = self.creation_date
+        self.modified_by: Optional[str] = None
+        self.namespace = namespace
+        self.table_bucket_arn = table_bucket_arn
+        self.region_name = table_bucket_arn.split(":")[3]
+        self.managed_by_service = managed_by_service
+        self.metadata_location: Optional[str] = None
+        self._bucket = self._create_underlying_bucket()
+        self.warehouse_location: str = f"s3://{self._bucket.name}"
+
+    @property
+    def arn(self) -> str:
+        return f"{self.table_bucket_arn}/table/{self.name}"
+
+    def was_modified(self, by: str) -> None:
+        self.last_modified = datetime.datetime.now(tz=datetime.timezone.utc)
+        self.modified_by = by
+
+    def _generate_version_token(self) -> str:
+        return md5(random.uuid4().bytes).hexdigest()[:20]
+
+    def _create_underlying_bucket(self) -> FakeBucket:
+        from moto.s3.models import s3_backends
+
+        bucket = s3_backends[self.account_id][
+            self.partition
+        ].create_table_storage_bucket(region_name=self.region_name)
+        return bucket
+
+    def update_metadata_location(
+        self, metadata_location: str, version_token: str
+    ) -> None:
+        if not metadata_location.startswith(self.warehouse_location):
+            raise InvalidMetadataLocation()
+        if not self.version_token == version_token:
+            raise VersionTokenMismatch()
+
+        self.metadata_location = metadata_location
+        self.version_token = self._generate_version_token()
+
+    def rename(self, new_name: str, by: str) -> None:
+        _validate_table_name(new_name)
+        self.name = new_name
+        self.was_modified(by)
+
+
+class Namespace:
+    def __init__(self, name: str, account_id: str, created_by: str):
+        _validate_namespace_name(name)
+        self.name = name
+        self.account_id = account_id
+        self.created_by = created_by
+        self.creation_date = datetime.datetime.now(tz=datetime.timezone.utc)
+        self.tables: Dict[str, Table] = {}
+
+
+class FakeTableBucket:
+    def __init__(self, name: str, account_id: str, region_name: str):
+        self.name = name
+        self.account_id = account_id
+        self.region_name = region_name
+        self.partition = get_partition(region_name)
+        self.creation_date = datetime.datetime.now(tz=datetime.timezone.utc)
+        self.namespaces: Dict[str, Namespace] = {}
+
+    @property
+    def arn(self) -> str:
+        return f"arn:{self.partition}:s3tables:{self.region_name}:{self.account_id}:bucket/{self.name}"
+
+
+class S3TablesBackend(BaseBackend):
+    """Implementation of S3Tables APIs."""
+
+    def __init__(self, region_name: str, account_id: str) -> None:
+        super().__init__(region_name, account_id)
+        self.table_buckets: Dict[str, FakeTableBucket] = {}
+
+    def create_table_bucket(self, name: str) -> FakeTableBucket:
+        _validate_table_bucket_name(name)
+        new_table_bucket = FakeTableBucket(
+            name=name, account_id=self.account_id, region_name=self.region_name
+        )
+        if new_table_bucket.arn in self.table_buckets:
+            raise ConflictException(
+                "The bucket that you tried to create already exists, and you own it."
+            )
+        self.table_buckets[new_table_bucket.arn] = new_table_bucket
+
+        return new_table_bucket
+
+    @paginate(pagination_model=PAGINATION_MODEL)
+    def list_table_buckets(
+        self,
+        prefix: Optional[str] = None,
+    ) -> List[FakeTableBucket]:
+        all_buckets = list(
+            bucket
+            for bucket in self.table_buckets.values()
+            if (prefix is None or bucket.name.startswith(prefix))
+        )
+
+        return all_buckets
+
+    def get_table_bucket(self, table_bucket_arn: str) -> FakeTableBucket:
+        bucket = self.table_buckets.get(table_bucket_arn)
+        if not bucket:
+            raise NotFoundException("The specified bucket does not exist.")
+        return bucket
+
+    def delete_table_bucket(self, table_bucket_arn: str) -> None:
+        # make sure table exists first
+        self.get_table_bucket(table_bucket_arn)
+        self.table_buckets.pop(table_bucket_arn)
+
+    def create_namespace(self, table_bucket_arn: str, namespace: str) -> Namespace:
+        bucket = self.table_buckets.get(table_bucket_arn)
+
+        if not bucket:
+            raise NotFoundException(
+                "The request was rejected because the specified resource could not be found."
+            )
+
+        if namespace in bucket.namespaces:
+            raise ConflictException(
+                "A namespace with an identical name already exists in the bucket."
+            )
+
+        ns = Namespace(
+            namespace, account_id=self.account_id, created_by=self.account_id
+        )
+        bucket.namespaces[ns.name] = ns
+        return ns
+
+    @paginate(pagination_model=PAGINATION_MODEL)
+    def list_namespaces(
+        self,
+        table_bucket_arn: str,
+        prefix: Optional[str] = None,
+    ) -> List[Namespace]:
+        bucket = self.get_table_bucket(table_bucket_arn)
+
+        all_namespaces = list(
+            ns
+            for ns in bucket.namespaces.values()
+            if (prefix is None or ns.name.startswith(prefix))
+        )
+
+        return all_namespaces
+
+    def get_namespace(self, table_bucket_arn: str, namespace: str) -> Namespace:
+        bucket = self.table_buckets.get(table_bucket_arn)
+        if bucket and namespace in bucket.namespaces:
+            return bucket.namespaces[namespace]
+
+        raise NotFoundException(
+            "The request was rejected because the specified resource could not be found."
+        )
+
+    def delete_namespace(self, table_bucket_arn: str, namespace: str) -> None:
+        bucket = self.table_buckets.get(table_bucket_arn)
+        if bucket and namespace in bucket.namespaces:
+            bucket.namespaces.pop(namespace)
+            return
+
+        raise NotFoundException(
+            "The request was rejected because the specified resource could not be found."
+        )
+
+    def create_table(
+        self,
+        table_bucket_arn: str,
+        namespace: str,
+        name: str,
+        format: Literal["ICEBERG"],
+    ) -> Table:
+        bucket = self.table_buckets.get(table_bucket_arn)
+        if not bucket:
+            raise NotFoundException("The specified bucket does not exist.")
+
+        if namespace not in bucket.namespaces:
+            raise NotFoundException("The specified namespace does not exist.")
+
+        ns = bucket.namespaces[namespace]
+        if name in ns.tables:
+            TableAlreadyExists()
+        table = Table(
+            name=name,
+            account_id=self.account_id,
+            created_by=self.account_id,
+            format=format,
+            namespace=namespace,
+            table_bucket_arn=table_bucket_arn,
+            type="customer",
+            managed_by_service=False,
+            partition=self.partition,
+        )
+        ns.tables[table.name] = table
+        return table
+
+    def get_table(self, table_bucket_arn: str, namespace: str, name: str) -> Table:
+        bucket = self.table_buckets.get(table_bucket_arn)
+        if bucket and namespace in bucket.namespaces:
+            if name in bucket.namespaces[namespace].tables:
+                return bucket.namespaces[namespace].tables[name]
+        raise TableDoesNotExist()
+
+    @paginate(pagination_model=PAGINATION_MODEL)
+    def list_tables(
+        self,
+        table_bucket_arn: str,
+        namespace: Optional[str] = None,
+        prefix: Optional[str] = None,
+    ) -> List[Table]:
+        bucket = self.table_buckets.get(table_bucket_arn)
+        if not bucket or (namespace and namespace not in bucket.namespaces):
+            raise NotFoundException(
+                "The request was rejected because the specified resource could not be found."
+            )
+
+        if namespace:
+            all_tables = list(
+                table
+                for table in bucket.namespaces[namespace].tables.values()
+                if (prefix is None or table.name.startswith(prefix))
+            )
+        else:
+            all_tables = list(
+                table
+                for ns in bucket.namespaces.values()
+                for table in ns.tables.values()
+                if (prefix is None or table.name.startswith(prefix))
+            )
+
+        return all_tables
+
+    def delete_table(
+        self,
+        table_bucket_arn: str,
+        namespace: str,
+        name: str,
+        version_token: Optional[str] = None,
+    ) -> None:
+        bucket = self.table_buckets.get(table_bucket_arn)
+        if (
+            not bucket
+            or namespace not in bucket.namespaces
+            or (name not in bucket.namespaces[namespace].tables)
+        ):
+            raise TableDoesNotExist()
+
+        ns = bucket.namespaces[namespace]
+        table = ns.tables[name]
+        if version_token and not version_token == table.version_token:
+            raise VersionTokenMismatch()
+        from moto.s3.models import s3_backends
+
+        s3_backends[self.account_id][self.partition].delete_table_storage_bucket(
+            table._bucket.name
+        )
+
+        ns.tables.pop(name)
+
+    def update_table_metadata_location(
+        self,
+        table_bucket_arn: str,
+        namespace: str,
+        name: str,
+        version_token: str,
+        metadata_location: str,
+    ) -> Table:
+        bucket = self.table_buckets.get(table_bucket_arn)
+        if bucket and namespace in bucket.namespaces:
+            if name in bucket.namespaces[namespace].tables:
+                table = bucket.namespaces[namespace].tables[name]
+                table.update_metadata_location(
+                    metadata_location=metadata_location, version_token=version_token
+                )
+                return table
+
+        raise TableDoesNotExist()
+
+    def rename_table(
+        self,
+        table_bucket_arn: str,
+        namespace: str,
+        name: str,
+        new_namespace_name: Optional[str] = None,
+        new_name: Optional[str] = None,
+        version_token: Optional[str] = None,
+    ) -> None:
+        if not new_namespace_name and not new_name:
+            raise NothingToRename()
+        destination_namespace = new_namespace_name if new_namespace_name else namespace
+        destination_name = new_name if new_name else name
+        _validate_table_name(destination_name)
+
+        bucket = self.table_buckets.get(table_bucket_arn)
+        if not bucket or destination_namespace not in bucket.namespaces:
+            raise DestinationNamespaceDoesNotExist()
+        if namespace not in bucket.namespaces or (
+            name not in bucket.namespaces[namespace].tables
+        ):
+            raise TableDoesNotExist()
+        table = bucket.namespaces[namespace].tables[name]
+        if version_token and not version_token == table.version_token:
+            raise VersionTokenMismatch()
+
+        if destination_name in bucket.namespaces[destination_namespace].tables:
+            raise TableAlreadyExists()
+
+        table = bucket.namespaces[namespace].tables.pop(name)
+        table.rename(new_name=destination_name, by=self.account_id)
+        bucket.namespaces[destination_namespace].tables[destination_name] = table
+
+
+s3tables_backends = BackendDict(
+    S3TablesBackend,
+    "s3tables",
+    additional_regions=["us-east-1", "us-east-2", "us-west-2"],
+)
diff --git a/moto/s3tables/responses.py b/moto/s3tables/responses.py
new file mode 100644
index 000000000..ec8066dba
--- /dev/null
+++ b/moto/s3tables/responses.py
@@ -0,0 +1,321 @@
+"""Handles incoming s3tables requests, invokes methods, returns responses."""
+
+import json
+from typing import Any, Dict
+from urllib.parse import unquote
+
+from moto.core.common_types import TYPE_RESPONSE
+from moto.core.responses import BaseResponse
+
+from .models import S3TablesBackend, s3tables_backends
+
+
+class S3TablesResponse(BaseResponse):
+    """Handler for S3Tables requests and responses."""
+
+    def __init__(self) -> None:
+        super().__init__(service_name="s3tables")
+        self.default_response_headers = {"Content-Type": "application/json"}
+
+    @property
+    def s3tables_backend(self) -> S3TablesBackend:
+        """Return backend instance specific for this region."""
+        return s3tables_backends[self.current_account][self.region]
+
+    def create_table_bucket(self) -> TYPE_RESPONSE:
+        name = json.loads(self.body)["name"]
+        bucket = self.s3tables_backend.create_table_bucket(
+            name=name,
+        )
+        return 200, self.default_response_headers, json.dumps(dict(arn=bucket.arn))
+
+    def list_table_buckets(self) -> TYPE_RESPONSE:
+        params = self._get_params()
+        prefix = params.get("prefix")
+        continuation_token = params.get("continuationToken")
+        max_buckets = params.get("maxBuckets")
+        table_buckets, continuation_token = self.s3tables_backend.list_table_buckets(
+            prefix=prefix,
+            continuation_token=continuation_token,
+            max_buckets=int(max_buckets) if max_buckets else None,
+        )
+
+        body: Dict[str, Any] = {
+            "tableBuckets": [
+                {
+                    "arn": b.arn,
+                    "name": b.name,
+                    "ownerAccountId": b.account_id,
+                    "createdAt": b.creation_date.isoformat(),
+                }
+                for b in table_buckets
+            ]
+        }
+        if continuation_token:
+            body.update(continuationToken=continuation_token)
+
+        return 200, self.default_response_headers, json.dumps(body)
+
+    def get_table_bucket(self) -> TYPE_RESPONSE:
+        _, table_bucket_arn = self.raw_path.lstrip("/").split("/")
+        table_bucket_arn = unquote(table_bucket_arn)
+        bucket = self.s3tables_backend.get_table_bucket(
+            table_bucket_arn=table_bucket_arn,
+        )
+
+        return (
+            200,
+            self.default_response_headers,
+            json.dumps(
+                dict(
+                    arn=bucket.arn,
+                    name=bucket.name,
+                    ownerAccountId=bucket.account_id,
+                    createdAt=bucket.creation_date.isoformat(),
+                )
+            ),
+        )
+
+    def delete_table_bucket(self) -> TYPE_RESPONSE:
+        _, table_bucket_arn = self.raw_path.lstrip("/").split("/")
+        table_bucket_arn = unquote(table_bucket_arn)
+        self.s3tables_backend.delete_table_bucket(
+            table_bucket_arn=table_bucket_arn,
+        )
+
+        return 204, {}, ""
+
+    def create_namespace(self) -> TYPE_RESPONSE:
+        _, table_bucket_arn = self.raw_path.lstrip("/").split("/")
+        table_bucket_arn = unquote(table_bucket_arn)
+        name = json.loads(self.body)["namespace"][0]
+        namespace = self.s3tables_backend.create_namespace(
+            table_bucket_arn=table_bucket_arn,
+            namespace=name,
+        )
+        return (
+            200,
+            self.default_response_headers,
+            json.dumps(
+                dict(tableBucketArn=table_bucket_arn, namespace=[namespace.name])
+            ),
+        )
+
+    def list_namespaces(self) -> TYPE_RESPONSE:
+        _, table_bucket_arn = self.raw_path.lstrip("/").split("/")
+        table_bucket_arn = unquote(table_bucket_arn)
+
+        params = self._get_params()
+        continuation_token = params.get("continuationToken")
+        max_namespaces = params.get("maxNamespaces")
+        prefix = params.get("prefix")
+
+        namespaces, continuation_token = self.s3tables_backend.list_namespaces(
+            table_bucket_arn=table_bucket_arn,
+            prefix=prefix,
+            continuation_token=continuation_token,
+            max_namespaces=int(max_namespaces) if max_namespaces else None,
+        )
+
+        body: Dict[str, Any] = {
+            "namespaces": [
+                {
+                    "namespace": [ns.name],
+                    "createdAt": ns.creation_date.isoformat(),
+                    "createdBy": ns.created_by,
+                    "ownerAccountId": ns.account_id,
+                }
+                for ns in namespaces
+            ]
+        }
+        if continuation_token:
+            body.update(continuationToken=continuation_token)
+
+        return 200, self.default_response_headers, json.dumps(body)
+
+    def get_namespace(self) -> TYPE_RESPONSE:
+        _, table_bucket_arn, name = self.raw_path.lstrip("/").split("/")
+        table_bucket_arn = unquote(table_bucket_arn)
+        namespace = self.s3tables_backend.get_namespace(
+            table_bucket_arn=table_bucket_arn,
+            namespace=name,
+        )
+        return (
+            200,
+            self.default_response_headers,
+            json.dumps(
+                dict(
+                    namespace=[namespace.name],
+                    createdAt=namespace.creation_date.isoformat(),
+                    createdBy=namespace.created_by,
+                    ownerAccountId=namespace.account_id,
+                )
+            ),
+        )
+
+    def delete_namespace(self) -> TYPE_RESPONSE:
+        _, table_bucket_arn, namespace = self.raw_path.lstrip("/").split("/")
+        table_bucket_arn = unquote(table_bucket_arn)
+        self.s3tables_backend.delete_namespace(
+            table_bucket_arn=table_bucket_arn,
+            namespace=namespace,
+        )
+        return 204, self.default_response_headers, ""
+
+    def create_table(self) -> TYPE_RESPONSE:
+        _, table_bucket_arn, namespace = self.raw_path.lstrip("/").split("/")
+        table_bucket_arn = unquote(table_bucket_arn)
+        body = json.loads(self.body)
+        name = body["name"]
+        format = body["format"]
+        table = self.s3tables_backend.create_table(
+            table_bucket_arn=table_bucket_arn,
+            namespace=namespace,
+            name=name,
+            format=format,
+        )
+        return (
+            200,
+            self.default_response_headers,
+            json.dumps(dict(tableARN=table.arn, versionToken=table.version_token)),
+        )
+
+    def get_table(self) -> TYPE_RESPONSE:
+        _, table_bucket_arn, namespace, name = self.raw_path.lstrip("/").split("/")
+        table_bucket_arn = unquote(table_bucket_arn)
+        table = self.s3tables_backend.get_table(
+            table_bucket_arn=table_bucket_arn,
+            namespace=namespace,
+            name=name,
+        )
+        return (
+            200,
+            self.default_response_headers,
+            json.dumps(
+                dict(
+                    name=table.name,
+                    type=table.type,
+                    tableARN=table.arn,
+                    namespace=[namespace],
+                    versionToken=table.version_token,
+                    metadataLocation=table.metadata_location,
+                    warehouseLocation=table.warehouse_location,
+                    createdAt=table.creation_date.isoformat(),
+                    createdBy=table.account_id,
+                    managedByService=table.managed_by_service,
+                    modifiedAt=table.last_modified.isoformat(),
+                    modifiedBy=table.modified_by,
+                    ownerAccountId=table.account_id,
+                    format=table.format,
+                )
+            ),
+        )
+
+    def list_tables(self) -> TYPE_RESPONSE:
+        _, table_bucket_arn = self.raw_path.lstrip("/").split("/")
+        table_bucket_arn = unquote(table_bucket_arn)
+        params = self._get_params()
+        namespace = params.get("namespace")
+        prefix = params.get("prefix")
+        continuation_token = params.get("continuationToken")
+        max_tables = params.get("maxTables")
+        tables, continuation_token = self.s3tables_backend.list_tables(
+            table_bucket_arn=table_bucket_arn,
+            namespace=namespace,
+            prefix=prefix,
+            continuation_token=continuation_token,
+            max_tables=int(max_tables) if max_tables else None,
+        )
+        body: Dict[str, Any] = {
+            "tables": [
+                {
+                    "namespace": [table.namespace],
+                    "name": table.name,
+                    "createdAt": table.creation_date.isoformat(),
+                    "modifiedAt": table.last_modified.isoformat(),
+                }
+                for table in tables
+            ]
+        }
+
+        if continuation_token:
+            body.update(continuationToken=continuation_token)
+
+        return 200, self.default_response_headers, json.dumps(body)
+
+    def delete_table(self) -> TYPE_RESPONSE:
+        _, table_bucket_arn, namespace, name = self.raw_path.lstrip("/").split("/")
+        table_bucket_arn = unquote(table_bucket_arn)
+        params = self._get_params()
+        version_token = params.get("versionToken")
+        self.s3tables_backend.delete_table(
+            table_bucket_arn=table_bucket_arn,
+            namespace=namespace,
+            name=name,
+            version_token=version_token,
+        )
+        return 204, {}, ""
+
+    def get_table_metadata_location(self) -> TYPE_RESPONSE:
+        _, table_bucket_arn, namespace, name, _ = self.raw_path.lstrip("/").split("/")
+        table_bucket_arn = unquote(table_bucket_arn)
+        table = self.s3tables_backend.get_table(
+            table_bucket_arn=table_bucket_arn,
+            namespace=namespace,
+            name=name,
+        )
+        return (
+            200,
+            self.default_response_headers,
+            json.dumps(
+                dict(
+                    versionToken=table.version_token,
+                    metadataLocation=table.metadata_location,
+                    warehouseLocation=table.warehouse_location,
+                )
+            ),
+        )
+
+    def update_table_metadata_location(self) -> TYPE_RESPONSE:
+        _, table_bucket_arn, namespace, name, _ = self.raw_path.lstrip("/").split("/")
+        table_bucket_arn = unquote(table_bucket_arn)
+        body = json.loads(self.body)
+        metadata_location = body["metadataLocation"]
+        version_token = body["versionToken"]
+        table = self.s3tables_backend.update_table_metadata_location(
+            table_bucket_arn=table_bucket_arn,
+            namespace=namespace,
+            name=name,
+            version_token=version_token,
+            metadata_location=metadata_location,
+        )
+        return (
+            200,
+            self.default_response_headers,
+            json.dumps(
+                dict(
+                    name=table.name,
+                    tableArn=table.arn,
+                    namespace=namespace,
+                    versionToken=table.version_token,
+                    metadataLocation=table.metadata_location,
+                )
+            ),
+        )
+
+    def rename_table(self) -> TYPE_RESPONSE:
+        _, table_bucket_arn, namespace, name, _ = self.raw_path.lstrip("/").split("/")
+        table_bucket_arn = unquote(table_bucket_arn)
+        body = json.loads(self.body)
+        version_token = body.get("versionToken")
+        new_namespace_name = body.get("newNamespaceName")
+        new_name = body.get("newName")
+        self.s3tables_backend.rename_table(
+            table_bucket_arn=table_bucket_arn,
+            namespace=namespace,
+            name=name,
+            new_namespace_name=new_namespace_name,
+            new_name=new_name,
+            version_token=version_token,
+        )
+        return 200, {}, ""
diff --git a/moto/s3tables/urls.py b/moto/s3tables/urls.py
new file mode 100644
index 000000000..ba6d475ab
--- /dev/null
+++ b/moto/s3tables/urls.py
@@ -0,0 +1,22 @@
+"""s3tables base URL and path."""
+
+from .responses import S3TablesResponse
+
+url_bases = [
+    r"https?://s3tables\.(.+)\.amazonaws\.com",
+]
+
+url_paths = {
+    "{0}/buckets$": S3TablesResponse.dispatch,
+    "{0}/buckets/(?P<tableBucketARN>.+)$": S3TablesResponse.dispatch,
+    "{0}/buckets/(?P<tableBucketARN_pt_1>[^/]+)/(?P<tableBucketARN_pt_2>[^/]+)$": S3TablesResponse.dispatch,
+    "{0}/namespaces/(?P<tableBucketARN>.+)$": S3TablesResponse.dispatch,
+    "{0}/tables/(?P<tableBucketARN>[^/]+)/(?P<namespace>[^/]+)$": S3TablesResponse.dispatch,
+    "{0}/tables/(?P<tableBucketARN>[^/]+)/(?P<namespace>[^/]+)/(?P<name>[^/]+)$": S3TablesResponse.dispatch,
+    "{0}/tables/(?P<tableBucketARN>[^/]+)$": S3TablesResponse.dispatch,
+    "{0}/tables/(?P<tableBucketARN_pt_1>[^/]+)/(?P<tableBucketARN_pt_2>[^/]+)/(?P<namespace>[^/]+)/(?P<name>[^/]+)$": S3TablesResponse.dispatch,
+    "{0}/tables/(?P<tableBucketARN>[^/]+)/(?P<namespace>[^/]+)/(?P<name>[^/]+)/metadata-location$": S3TablesResponse.dispatch,
+    "{0}/tables/(?P<tableBucketARN_pt_1>[^/]+)/(?P<tableBucketARN_pt_2>[^/]+)/(?P<namespace>[^/]+)/(?P<name>[^/]+)/metadata-location$": S3TablesResponse.dispatch,
+    "{0}/tables/(?P<tableBucketARN>[^/]+)/(?P<namespace>[^/]+)/(?P<name>[^/]+)/rename$": S3TablesResponse.dispatch,
+    "{0}/tables/(?P<tableBucketARN_pt_1>[^/]+)/(?P<tableBucketARN_pt_2>[^/]+)/(?P<namespace>[^/]+)/(?P<name>[^/]+)/rename$": S3TablesResponse.dispatch,
+}


Test patch:
diff --git a/tests/test_s3tables/__init__.py b/tests/test_s3tables/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/test_s3tables/test_s3tables.py b/tests/test_s3tables/test_s3tables.py
new file mode 100644
index 000000000..c21a204f1
--- /dev/null
+++ b/tests/test_s3tables/test_s3tables.py
@@ -0,0 +1,450 @@
+"""Unit tests for s3tables-supported APIs."""
+
+import boto3
+import pytest
+
+from moto import mock_aws
+
+
+@mock_aws
+def test_create_table_bucket():
+    client = boto3.client("s3tables", region_name="us-east-1")
+    response = client.create_table_bucket(name="foo")
+    assert "arn" in response
+    assert response["arn"].endswith("foo")
+
+
+@mock_aws
+def test_create_table_bucket_validates_name():
+    client = boto3.client("s3tables", region_name="us-east-1")
+    with pytest.raises(client.exceptions.BadRequestException) as exc:
+        client.create_table_bucket(name="sthree-invalid-name")
+    assert exc.match("bucket name is not valid")
+
+
+@mock_aws
+def test_list_table_buckets():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    client.create_table_bucket(name="foo")
+    response = client.list_table_buckets(prefix="foo")
+    assert "tableBuckets" in response
+    assert len(response["tableBuckets"]) == 1
+    assert response["tableBuckets"][0]["name"] == "foo"
+
+
+@mock_aws
+def test_list_table_buckets_pagination():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    client.create_table_bucket(name="foo")
+    client.create_table_bucket(name="bar")
+    response = client.list_table_buckets(maxBuckets=1)
+
+    assert len(response["tableBuckets"]) == 1
+    assert "continuationToken" in response
+
+    response = client.list_table_buckets(
+        maxBuckets=1, continuationToken=response["continuationToken"]
+    )
+
+    assert len(response["tableBuckets"]) == 1
+    assert "continuationToken" not in response
+    assert response["tableBuckets"][0]["name"] == "bar"
+
+
+@mock_aws
+def test_get_table_bucket():
+    client = boto3.client("s3tables", region_name="us-east-1")
+    response = client.create_table_bucket(name="foo")
+
+    assert client.get_table_bucket(tableBucketARN=response["arn"])["name"] == "foo"
+
+
+@mock_aws
+def test_delete_table_bucket():
+    client = boto3.client("s3tables", region_name="us-east-1")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    response = client.list_table_buckets()
+    assert response["tableBuckets"]
+
+    client.delete_table_bucket(tableBucketARN=arn)
+    response = client.list_table_buckets()
+    assert not response["tableBuckets"]
+
+
+@mock_aws
+def test_create_namespace():
+    client = boto3.client("s3tables", region_name="us-east-1")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    resp = client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+
+    assert resp["namespace"] == ["bar"]
+
+
+@mock_aws
+def test_list_namespaces():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["foo"])
+    resp = client.list_namespaces(tableBucketARN=arn)
+    assert resp["namespaces"]
+    assert resp["namespaces"][0]["namespace"] == ["foo"]
+
+
+@mock_aws
+def test_list_namespaces_pagination():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["foo"])
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    response = client.list_namespaces(tableBucketARN=arn, maxNamespaces=1)
+
+    assert len(response["namespaces"]) == 1
+    assert "continuationToken" in response
+
+    response = client.list_namespaces(
+        tableBucketARN=arn,
+        maxNamespaces=1,
+        continuationToken=response["continuationToken"],
+    )
+
+    assert len(response["namespaces"]) == 1
+    assert "continuationToken" not in response
+    assert response["namespaces"][0]["namespace"] == ["bar"]
+
+
+@mock_aws
+def test_get_namespace():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    resp = client.get_namespace(tableBucketARN=arn, namespace="bar")
+    assert resp["namespace"] == ["bar"]
+
+
+@mock_aws
+def test_delete_namespace():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    assert client.list_namespaces(tableBucketARN=arn)["namespaces"]
+    client.delete_namespace(tableBucketARN=arn, namespace="bar")
+    assert not client.list_namespaces(tableBucketARN=arn)["namespaces"]
+
+
+@mock_aws
+def test_create_table():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    resp = client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )
+    assert "tableARN" in resp
+    assert "versionToken" in resp
+
+
+@mock_aws
+def test_get_table():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    table_arn = client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )["tableARN"]
+
+    assert (
+        client.get_table(tableBucketARN=arn, namespace="bar", name="baz")["tableARN"]
+        == table_arn
+    )
+
+
+@mock_aws
+def test_list_tables():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )
+
+    resp = client.list_tables(tableBucketARN=arn, namespace="bar")
+    assert resp["tables"]
+    assert resp["tables"][0]["name"] == "baz"
+
+
+@mock_aws
+def test_list_tables_pagination():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )
+    client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz2", format="ICEBERG"
+    )
+
+    resp = client.list_tables(tableBucketARN=arn, maxTables=1)
+    assert len(resp["tables"]) == 1
+    assert "continuationToken" in resp
+
+    resp = client.list_tables(
+        tableBucketARN=arn, maxTables=1, continuationToken=resp["continuationToken"]
+    )
+
+    assert len(resp["tables"]) == 1
+    assert "continuationToken" not in resp
+    assert resp["tables"][0]["name"] == "baz2"
+
+
+@mock_aws
+def test_delete_table():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )
+
+    resp = client.list_tables(tableBucketARN=arn)
+    assert len(resp["tables"]) == 1
+
+    client.delete_table(tableBucketARN=arn, namespace="bar", name="baz")
+
+    resp = client.list_tables(tableBucketARN=arn)
+    assert len(resp["tables"]) == 0
+
+
+@mock_aws
+def test_delete_table_deletes_underlying_table_storage():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    s3 = boto3.client("s3", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )
+    warehouse = client.get_table(tableBucketARN=arn, namespace="bar", name="baz")[
+        "warehouseLocation"
+    ]
+
+    bucket_name = warehouse.replace("s3://", "")
+    s3.head_bucket(Bucket=bucket_name)
+
+    client.delete_table(tableBucketARN=arn, namespace="bar", name="baz")
+    with pytest.raises(s3.exceptions.ClientError) as exc:
+        s3.head_bucket(Bucket=bucket_name)
+    exc.match("Not Found")
+
+
+@mock_aws
+def test_delete_table_fails_on_version_token_mismatch():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    resp = client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )
+    token = resp["versionToken"]
+
+    with pytest.raises(client.exceptions.ConflictException) as exc:
+        client.delete_table(
+            tableBucketARN=arn, namespace="bar", name="baz", versionToken=f"{token}-foo"
+        )
+    exc.match("Provided version token does not match the table version token.")
+
+
+@mock_aws
+def test_get_table_metadata_location():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )
+    resp = client.get_table_metadata_location(
+        tableBucketARN=arn, namespace="bar", name="baz"
+    )
+    assert "metadataLocation" not in resp
+    assert "warehouseLocation" in resp
+
+
+@mock_aws
+def test_update_table_metadata_location():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    resp = client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )
+    resp = client.get_table(tableBucketARN=arn, namespace="bar", name="baz")
+    warehouse_location = resp["warehouseLocation"]
+    client.update_table_metadata_location(
+        tableBucketARN=arn,
+        namespace="bar",
+        name="baz",
+        metadataLocation=f"{warehouse_location}/abc",
+        versionToken=resp["versionToken"],
+    )
+    resp = client.get_table_metadata_location(
+        tableBucketARN=arn, namespace="bar", name="baz"
+    )
+    assert "metadataLocation" in resp
+    assert resp["metadataLocation"] == f"{warehouse_location}/abc"
+
+
+@mock_aws
+def test_update_table_metadata_location_raises_exception_on_invalid_path():
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    resp = client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )
+    with pytest.raises(client.exceptions.BadRequestException) as exc:
+        client.update_table_metadata_location(
+            tableBucketARN=arn,
+            namespace="bar",
+            name="baz",
+            metadataLocation="s3://abc",
+            versionToken=resp["versionToken"],
+        )
+    exc.match("The specified metadata location is not valid.")
+
+
+@mock_aws
+def test_write_metadata_to_table() -> None:
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    resp = client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )
+
+    resp = client.get_table(tableBucketARN=arn, namespace="bar", name="baz")
+    s3 = boto3.client("s3", region_name="us-east-2")
+    metadata = b'{"foo":"bar"}'
+
+    bucket_name = resp["warehouseLocation"].replace("s3://", "")
+    key = "metadata/00001-foo.metadata.json"
+    s3.put_object(Bucket=bucket_name, Key=key, Body=metadata)
+
+    client.update_table_metadata_location(
+        tableBucketARN=arn,
+        namespace="bar",
+        name="baz",
+        metadataLocation=f"s3://{bucket_name}/{key}",
+        versionToken=resp["versionToken"],
+    )
+    resp = client.get_table_metadata_location(
+        tableBucketARN=arn, namespace="bar", name="baz"
+    )
+    bucket, key = resp["metadataLocation"].replace("s3://", "").split("/", 1)
+    resp = s3.get_object(Bucket=bucket, Key=key)
+    assert resp["Body"].read() == metadata
+
+
+@mock_aws
+def test_underlying_table_storage_does_not_support_list_objects() -> None:
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    resp = client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )
+
+    resp = client.get_table(tableBucketARN=arn, namespace="bar", name="baz")
+    s3 = boto3.client("s3", region_name="us-east-2")
+
+    bucket_name = resp["warehouseLocation"].replace("s3://", "")
+    with pytest.raises(s3.exceptions.ClientError) as exc:
+        s3.list_objects_v2(Bucket=bucket_name)
+    assert exc.match("The specified method is not allowed against this resource.")
+
+
+@mock_aws
+def test_underlying_table_storage_does_not_support_delete_object() -> None:
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    resp = client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )
+
+    resp = client.get_table(tableBucketARN=arn, namespace="bar", name="baz")
+    s3 = boto3.client("s3", region_name="us-east-2")
+
+    bucket_name = resp["warehouseLocation"].replace("s3://", "")
+    s3.put_object(Bucket=bucket_name, Key="test", Body=b"{}")
+    with pytest.raises(s3.exceptions.ClientError) as exc:
+        s3.delete_object(Bucket=bucket_name, Key="test")
+    assert exc.match("The specified method is not allowed against this resource.")
+
+
+@mock_aws
+def test_rename_table() -> None:
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    resp = client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )
+
+    client.create_namespace(tableBucketARN=arn, namespace=["bar-two"])
+    client.rename_table(
+        tableBucketARN=arn,
+        namespace="bar",
+        name="baz",
+        newNamespaceName="bar-two",
+        newName="baz-two",
+        versionToken=resp["versionToken"],
+    )
+    assert (
+        client.get_table(tableBucketARN=arn, namespace="bar-two", name="baz-two")[
+            "name"
+        ]
+        == "baz-two"
+    )
+    assert client.get_table(tableBucketARN=arn, namespace="bar-two", name="baz-two")[
+        "namespace"
+    ] == ["bar-two"]
+
+
+@mock_aws
+def test_rename_table_fails_when_destination_namespace_does_not_exist() -> None:
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    resp = client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )
+
+    with pytest.raises(client.exceptions.NotFoundException) as ctx:
+        client.rename_table(
+            tableBucketARN=arn,
+            namespace="bar",
+            name="baz",
+            newNamespaceName="bar-two",
+            newName="baz-two",
+            versionToken=resp["versionToken"],
+        )
+    assert ctx.match("The specified destination namespace does not exist.")
+
+
+@mock_aws
+def test_rename_table_fails_when_no_updates_are_specified() -> None:
+    client = boto3.client("s3tables", region_name="us-east-2")
+    arn = client.create_table_bucket(name="foo")["arn"]
+    client.create_namespace(tableBucketARN=arn, namespace=["bar"])
+    resp = client.create_table(
+        tableBucketARN=arn, namespace="bar", name="baz", format="ICEBERG"
+    )
+
+    with pytest.raises(client.exceptions.BadRequestException) as ctx:
+        client.rename_table(
+            tableBucketARN=arn,
+            namespace="bar",
+            name="baz",
+            versionToken=resp["versionToken"],
+        )
+    assert ctx.match("Neither a new namespace name nor a new table name is specified.")
diff --git a/tests/test_s3tables/test_server.py b/tests/test_s3tables/test_server.py
new file mode 100644
index 000000000..5d18bcd9a
--- /dev/null
+++ b/tests/test_s3tables/test_server.py
@@ -0,0 +1,144 @@
+"""Test different server responses."""
+
+import string
+from random import choice
+from urllib.parse import quote
+
+import pytest
+
+import moto.server as server
+
+
+@pytest.fixture(name="bucket_name")
+def table_bucket_name() -> str:
+    prefix = "table-bucket"
+    random_tag = "".join(choice(string.ascii_letters) for _ in range(10))
+    return (prefix + random_tag).lower()
+
+
+def test_s3tables_list():
+    backend = server.create_backend_app("s3tables")
+    test_client = backend.test_client()
+
+    resp = test_client.get("/buckets")
+
+    assert resp.status_code == 200
+    assert "tableBuckets" in resp.get_json()
+
+
+def test_s3tables_create_bucket(bucket_name: str):
+    backend = server.create_backend_app("s3tables")
+    test_client = backend.test_client()
+
+    resp = test_client.put("/buckets", json={"name": bucket_name})
+    assert "arn" in resp.get_json()
+    assert resp.get_json()["arn"].endswith(bucket_name)
+
+
+def test_s3tables_get_bucket(bucket_name: str):
+    backend = server.create_backend_app("s3tables")
+    test_client = backend.test_client()
+
+    resp = test_client.put("/buckets", json={"name": bucket_name})
+    arn = resp.get_json()["arn"]
+
+    quoted_arn = quote(arn, safe="")
+
+    resp = test_client.get(f"/buckets/{quoted_arn}")
+    assert resp.status_code == 200
+
+
+def test_s3tables_delete_bucket(bucket_name: str):
+    backend = server.create_backend_app("s3tables")
+    test_client = backend.test_client()
+
+    resp = test_client.put("/buckets", json={"name": bucket_name})
+    arn = resp.get_json()["arn"]
+
+    quoted_arn = quote(arn, safe="")
+
+    resp = test_client.delete(f"/buckets/{quoted_arn}")
+    assert resp.status_code == 200
+
+
+def test_s3tables_create_namespace(bucket_name: str):
+    backend = server.create_backend_app("s3tables")
+    test_client = backend.test_client()
+
+    resp = test_client.put("/buckets", json={"name": bucket_name})
+    arn = resp.get_json()["arn"]
+
+    quoted_arn = quote(arn, safe="")
+    resp = test_client.put(f"/namespaces/{quoted_arn}", json={"namespace": ["bar"]})
+
+    assert resp.status_code == 200
+
+
+def test_s3tables_create_table(bucket_name: str):
+    backend = server.create_backend_app("s3tables")
+    test_client = backend.test_client()
+
+    resp = test_client.put("/buckets", json={"name": bucket_name})
+    arn = resp.get_json()["arn"]
+
+    quoted_arn = quote(arn, safe="")
+    resp = test_client.put(f"/namespaces/{quoted_arn}", json={"namespace": ["bar"]})
+
+    resp = test_client.put(
+        f"/tables/{quoted_arn}/bar", json={"name": "baz", "format": "ICEBERG"}
+    )
+    assert resp.status_code == 200
+
+
+def test_s3tables_list_tables(bucket_name: str):
+    backend = server.create_backend_app("s3tables")
+    test_client = backend.test_client()
+
+    resp = test_client.put("/buckets", json={"name": bucket_name})
+    arn = resp.get_json()["arn"]
+
+    quoted_arn = quote(arn, safe="")
+    resp = test_client.put(f"/namespaces/{quoted_arn}", json={"namespace": ["bar"]})
+
+    resp = test_client.put(
+        f"/tables/{quoted_arn}/bar", json={"name": "baz", "format": "ICEBERG"}
+    )
+
+    resp = test_client.get(f"/tables/{quoted_arn}")
+    assert resp.get_json()["tables"]
+
+
+def test_s3tables_get_table(bucket_name: str):
+    backend = server.create_backend_app("s3tables")
+    test_client = backend.test_client()
+
+    resp = test_client.put("/buckets", json={"name": bucket_name})
+    arn = resp.get_json()["arn"]
+
+    quoted_arn = quote(arn, safe="")
+    resp = test_client.put(f"/namespaces/{quoted_arn}", json={"namespace": ["bar"]})
+
+    resp = test_client.put(
+        f"/tables/{quoted_arn}/bar", json={"name": "baz", "format": "ICEBERG"}
+    )
+
+    resp = test_client.get(f"/tables/{quoted_arn}/bar/baz")
+    assert resp.status_code == 200
+
+
+def test_s3tables_delete_table(bucket_name: str):
+    backend = server.create_backend_app("s3tables")
+    test_client = backend.test_client()
+
+    resp = test_client.put("/buckets", json={"name": bucket_name})
+    arn = resp.get_json()["arn"]
+
+    quoted_arn = quote(arn, safe="")
+    resp = test_client.put(f"/namespaces/{quoted_arn}", json={"namespace": ["bar"]})
+
+    resp = test_client.put(
+        f"/tables/{quoted_arn}/bar", json={"name": "baz", "format": "ICEBERG"}
+    )
+
+    resp = test_client.delete(f"/tables/{quoted_arn}/bar/baz")
+    assert resp.status_code == 200