Repository: ansible-collections/amazon.aws

Issue:
Bucket name validation is too eager and should be skippable
### Summary

With the implementation of https://github.com/ansible-collections/amazon.aws/pull/341 all bucket names must adhere to Amazon S3's naming rules. While those rules are generally followed by alternative S3 implementations, they are not a given and can sometimes be disabled (at least in the case of Ceph):

> The above constraints are relaxed if the option ‘rgw_relaxed_s3_bucket_names’ is set to true except that the bucket names must still be unique, cannot be formatted as IP address and can contain letters, numbers, periods, dashes and underscores for up to 255 characters long.

https://docs.ceph.com/en/latest/radosgw/s3/bucketops/

Furthermore, the way this works is that the bucket name is checked with every `s3_bucket` operation. So those rules don't just apply to creating new buckets, but they apply to a simple `--check --diff` run as well. At the very least, this is too eager in my estimation. If at all those rules should be applied, it should happen only when necessary.

I would like to change this in one of two ways:

1. Add an option to skip validation that is off by default (e.g. `validate_bucket_names: no (default yes)`).
2. A more fine-grained implementation that only checks the bucket name before creating a bucket, not when updating/reading it.

Those approaches could of course be combined. I'm happy to provide a pull request myself, if there are no objections to my proposal.

### Issue Type

Feature Idea

### Component Name

s3_bucket

### Additional Information

_No response_

### Code of Conduct

- [X] I agree to follow the Ansible Code of Conduct


Discussion:
Hi @href, thanks for reporting this.  If you would like to propose a PR that adds the necessary conditional handling we would definitely consider it.  We do not have a Ceph cluster (or any other non-AWS S3 implementations) available to us though for development and testing.
I implemented it by simply adding the ability to skip the check. I decided against anything smarter, because this approach has the advantage that errors bubble up early and independent of state.

That means people affected by this validation being too strict can just disable it in their playbooks, and a `--check` run alone should show if that fixes their issues.

I tested this locally with the following playbook:

```yaml
#!/usr/bin/env ansible-playbook
- name: Test bucket name validation
  hosts: localhost

  module_defaults:
    amazon.aws.s3_bucket:
      validate_bucket_name: no
      s3_url: https://objects.lpg.cloudscale.ch
    amazon.aws.aws_s3:
      validate_bucket_name: no
      s3_url: https://objects.lpg.cloudscale.ch

  tasks:
    - name: "Create bucket"
      amazon.aws.s3_bucket:
        name: foo

    - name: "Store file"
      amazon.aws.aws_s3:
        bucket: foo
        object: /bar
        content: "baz"
        mode: put
```

My employer supports bucket names shorter than 4 characters, which Amazon does not allow.
I've hit this eager bucket validation myself now using Minio.  However, I also think the existing validation is possibly not correct?  The S3 naming rules s3.py itself links to state "Bucket names must be between 3 and 63 characters long".  Does this not mean names can be a minimum of 3 chars and a max of 63?  This is certainly the rule that Minio seems to follow.  If s3.py is being pedantic and interpreting "between" to not be inclusive of 3 and 63, then the validation it does is not consistent, since it will not allow 3 char buckets, but will in fact allow 63 char buckets.  I have no way to actually test a 3 char bucket on s3, since they seem to have to be globally unique.  However, I am able to create a 63 char bucket, so those limits do appear to be inclusive.  Much thanks to all those looking into this issue!

edit - also, trying to create a bucket in S3 named "abc" actually causes an error due to the bucket name already existing, not that the name is too short

I tend to agree with @jakegatsby. If the maintaners agree I'm happy to fix this in the PR as well (a rather trivial fix). Relevant section from the AWS docs:

> Bucket names must be between 3 and 63 characters long.

https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html
cc @abikouo @s-hertel @tremble
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/plugins/modules/aws_s3.py b/plugins/modules/aws_s3.py
--- a/plugins/modules/aws_s3.py
+++ b/plugins/modules/aws_s3.py
@@ -197,6 +197,14 @@
         type: str
         description:
         - version ID of the source object.
+  validate_bucket_name:
+    description:
+      - Whether the bucket name should be validated to conform to AWS S3 naming rules.
+      - On by default, this may be disabled for S3 backends that do not enforce these rules.
+      - See https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html
+    type: bool
+    version_added: 3.1.0
+    default: True
 author:
     - "Lester Wade (@lwade)"
     - "Sloane Hertel (@s-hertel)"
@@ -928,6 +936,7 @@ def main():
         tags=dict(type='dict'),
         purge_tags=dict(type='bool', default=True),
         copy_src=dict(type='dict', options=dict(bucket=dict(required=True), object=dict(required=True), version_id=dict())),
+        validate_bucket_name=dict(type='bool', default=True),
     )
     module = AnsibleAWSModule(
         argument_spec=argument_spec,
@@ -965,7 +974,8 @@ def main():
     object_canned_acl = ["private", "public-read", "public-read-write", "aws-exec-read", "authenticated-read", "bucket-owner-read", "bucket-owner-full-control"]
     bucket_canned_acl = ["private", "public-read", "public-read-write", "authenticated-read"]
 
-    validate_bucket_name(module, bucket)
+    if module.params.get('validate_bucket_name'):
+        validate_bucket_name(module, bucket)
 
     if overwrite not in ['always', 'never', 'different', 'latest']:
         if module.boolean(overwrite):
diff --git a/plugins/modules/s3_bucket.py b/plugins/modules/s3_bucket.py
--- a/plugins/modules/s3_bucket.py
+++ b/plugins/modules/s3_bucket.py
@@ -147,6 +147,14 @@
     choices: [ 'private', 'public-read', 'public-read-write', 'authenticated-read' ]
     type: str
     version_added: 3.1.0
+  validate_bucket_name:
+    description:
+      - Whether the bucket name should be validated to conform to AWS S3 naming rules.
+      - On by default, this may be disabled for S3 backends that do not enforce these rules.
+      - See https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html
+    type: bool
+    version_added: 3.1.0
+    default: True
 
 extends_documentation_fragment:
 - amazon.aws.aws
@@ -1011,6 +1019,7 @@ def main():
         object_ownership=dict(type='str', choices=['BucketOwnerPreferred', 'ObjectWriter']),
         delete_object_ownership=dict(type='bool', default=False),
         acl=dict(type='str', choices=['private', 'public-read', 'public-read-write', 'authenticated-read']),
+        validate_bucket_name=dict(type='bool', default=True),
     )
 
     required_by = dict(
@@ -1027,7 +1036,9 @@ def main():
     )
 
     region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module, boto3=True)
-    validate_bucket_name(module, module.params["name"])
+
+    if module.params.get('validate_bucket_name'):
+        validate_bucket_name(module, module.params["name"])
 
     if region in ('us-east-1', '', None):
         # default to US Standard region


Test patch: