Repository: ansible-collections/amazon.aws

Issue:
aws_s3 can't `put` when bucket has ACLs disabled with `object_ownership` = `BucketOwnerEnforced`
### Summary

When I try to use `aws_s3 ` to upload a file to a bucket where ACLs are disabled, it fails with:

> An exception occurred during task execution. To see the full traceback, use -vvv. The error was: botocore.exceptions.ClientError: An error occurred (AccessControlListNotSupported) when calling the PutObjectAcl operation: The bucket does not allow ACLs

I suspect that the module is simply attempting to specify ACLs for uploaded files unconditionally, and would either need to check whether the bucket has ACLs disabled before hand, or accept object ACLs as configuration items.

### Issue Type

Bug Report

### Component Name

aws_s3

### Ansible Version

```console (paste below)
$ ansible --version`

ansible [core 2.12.4]
  config file = /home/tyler/Repos/px-partners/ansible.cfg
  configured module search path = ['/home/tyler/Repos/px-partners/programmatic-roles/library']
  ansible python module location = /home/tyler/.local/lib/python3.10/site-packages/ansible
  ansible collection location = /home/tyler/.ansible/collections:/usr/share/ansible/collections
  executable location = /home/tyler/.local/bin/ansible
  python version = 3.10.4 (main, Mar 24 2022, 13:07:27) [GCC 11.2.0]
  jinja version = 3.1.1
  libyaml = True


```


### Collection Versions

```console (paste below)
$ ansible-galaxy collection list

# /home/tyler/.local/lib/python3.10/site-packages/ansible_collections
Collection                    Version
----------------------------- -------
amazon.aws                    2.2.0  
ansible.netcommon             2.6.1  
ansible.posix                 1.3.0  
ansible.utils                 2.5.2  
ansible.windows               1.9.0  
arista.eos                    3.1.0  
awx.awx                       19.4.0 
azure.azcollection            1.12.0 
check_point.mgmt              2.3.0  
chocolatey.chocolatey         1.2.0  
cisco.aci                     2.2.0  
cisco.asa                     2.1.0  
cisco.intersight              1.0.18 
cisco.ios                     2.8.1  
cisco.iosxr                   2.9.0  
cisco.ise                     1.2.1  
cisco.meraki                  2.6.1  
cisco.mso                     1.4.0  
cisco.nso                     1.0.3  
cisco.nxos                    2.9.1  
cisco.ucs                     1.8.0  
cloud.common                  2.1.0  
cloudscale_ch.cloud           2.2.1  
community.aws                 2.4.0  
community.azure               1.1.0  
community.ciscosmb            1.0.4  
community.crypto              2.2.4  
community.digitalocean        1.16.0 
community.dns                 2.0.9  
community.docker              2.3.0  
community.fortios             1.0.0  
community.general             4.7.0  
community.google              1.0.0  
community.grafana             1.3.3  
community.hashi_vault         2.4.0  
community.hrobot              1.2.3  
community.kubernetes          2.0.1  
community.kubevirt            1.0.0  
community.libvirt             1.0.2  
community.mongodb             1.3.3  
community.mysql               2.3.5  
community.network             3.1.0  
community.okd                 2.1.0  
community.postgresql          1.7.1  
community.proxysql            1.3.1  
community.rabbitmq            1.1.0  
community.routeros            2.0.0  
community.sap                 1.0.0  
community.skydive             1.0.0  
community.sops                1.2.1  
community.vmware              1.18.0 
community.windows             1.9.0  
community.zabbix              1.5.1  
containers.podman             1.9.3  
cyberark.conjur               1.1.0  
cyberark.pas                  1.0.13 
dellemc.enterprise_sonic      1.1.0  
dellemc.openmanage            4.4.0  
dellemc.os10                  1.1.1  
dellemc.os6                   1.0.7  
dellemc.os9                   1.0.4  
f5networks.f5_modules         1.15.0 
fortinet.fortimanager         2.1.4  
fortinet.fortios              2.1.4  
frr.frr                       1.0.3  
gluster.gluster               1.0.2  
google.cloud                  1.0.2  
hetzner.hcloud                1.6.0  
hpe.nimble                    1.1.4  
ibm.qradar                    1.0.3  
infinidat.infinibox           1.3.3  
infoblox.nios_modules         1.2.1  
inspur.sm                     1.3.0  
junipernetworks.junos         2.10.0 
kubernetes.core               2.3.0  
mellanox.onyx                 1.0.0  
netapp.aws                    21.7.0 
netapp.azure                  21.10.0
netapp.cloudmanager           21.15.0
netapp.elementsw              21.7.0 
netapp.ontap                  21.17.3
netapp.storagegrid            21.10.0
netapp.um_info                21.8.0 
netapp_eseries.santricity     1.3.0  
netbox.netbox                 3.6.0  
ngine_io.cloudstack           2.2.3  
ngine_io.exoscale             1.0.0  
ngine_io.vultr                1.1.1  
openstack.cloud               1.7.2  
openvswitch.openvswitch       2.1.0  
ovirt.ovirt                   1.6.6  
purestorage.flasharray        1.12.1 
purestorage.flashblade        1.9.0  
sensu.sensu_go                1.13.0 
servicenow.servicenow         1.0.6  
splunk.es                     1.0.2  
t_systems_mms.icinga_director 1.28.0 
theforeman.foreman            2.2.0  
vyos.vyos                     2.8.0  
wti.remote                    1.0.3  

# /home/tyler/.ansible/collections/ansible_collections
Collection            Version
--------------------- -------
amazon.aws            3.3.0  


```


### AWS SDK versions

```console (paste below)
$ pip show boto boto3 botocore

WARNING: Package(s) not found: boto
Name: boto3
Version: 1.21.45
Summary: The AWS SDK for Python
Home-page: https://github.com/boto/boto3
Author: Amazon Web Services
Author-email: 
License: Apache License 2.0
Location: /home/tyler/.local/lib/python3.10/site-packages
Requires: botocore, jmespath, s3transfer
Required-by: 
---
Name: botocore
Version: 1.24.45
Summary: Low-level, data-driven core of boto 3.
Home-page: https://github.com/boto/botocore
Author: Amazon Web Services
Author-email: 
License: Apache License 2.0
Location: /home/tyler/.local/lib/python3.10/site-packages
Requires: jmespath, python-dateutil, urllib3
Required-by: boto3, s3transfer


```


### Configuration

```console (paste below)
$ ansible-config dump --only-changed

```


### OS / Environment

_No response_

### Steps to Reproduce

<!--- Paste example playbooks or commands between quotes below -->
```yaml (paste below)
- name: Upload a file to the bucket
  aws_s3:
    bucket: "{{ s3_bucket_name }}"
    object: "/out/test.txt"
    src: "test.txt"
    mode: put
  changed_when: false

```


### Expected Results

I expect the file to upload without issue.

### Actual Results

```console (paste below)
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: botocore.exceptions.ClientError: An error occurred (AccessControlListNotSupported) when calling the PutObjectAcl operation: The bucket does not allow ACLs
fatal: [localhost]: FAILED! => {"boto3_version": "1.21.45", "botocore_version": "1.24.45", "changed": false, "error": {"code": "AccessControlListNotSupported", "message": "The bucket does not allow ACLs"}, "msg": "Unable to set object ACL: An error occurred (AccessControlListNotSupported) when calling the PutObjectAcl operation: The bucket does not allow ACLs", "response_metadata": {"host_id": "u0/PfRLv4ecD7ZNQnx4VDI7OXbsLSNoeL6icRX1HxoAQ1kbha7eX9YyhmC50b/k/AORoReWhEQ4=", "http_headers": {"connection": "close", "content-type": "application/xml", "date": "Mon, 06 Jun 2022 17:19:35 GMT", "server": "AmazonS3", "transfer-encoding": "chunked", "x-amz-id-2": "u0/PfRLv4ecD7ZNQnx4VDI7OXbsLSNoeL6icRX1HxoAQ1kbha7eX9YyhmC50b/k/AORoReWhEQ4=", "x-amz-request-id": "8PWXR597C2TTN6CX"}, "http_status_code": 400, "request_id": "8PWXR597C2TTN6CX", "retry_attempts": 0}}


```


### Code of Conduct

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


Discussion:
Files identified in the description:
* [`plugins/modules/aws_s3.py`](https://github.com/['ansible-collections/amazon.aws', 'ansible-collections/community.aws', 'ansible-collections/community.vmware']/blob/main/plugins/modules/aws_s3.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @jillr @linabuzachis @lwade @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/s3_object.py b/plugins/modules/s3_object.py
--- a/plugins/modules/s3_object.py
+++ b/plugins/modules/s3_object.py
@@ -634,7 +634,7 @@ def option_in_extra_args(option):
         return allowed_extra_args[temp_option]
 
 
-def upload_s3file(module, s3, bucket, obj, expiry, metadata, encrypt, headers, src=None, content=None):
+def upload_s3file(module, s3, bucket, obj, expiry, metadata, encrypt, headers, src=None, content=None, acl_disabled=False):
     if module.check_mode:
         module.exit_json(msg="PUT operation skipped - running in check mode", changed=True)
     try:
@@ -677,13 +677,14 @@ def upload_s3file(module, s3, bucket, obj, expiry, metadata, encrypt, headers, s
             s3.upload_fileobj(Fileobj=f, Bucket=bucket, Key=obj, ExtraArgs=extra)
     except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:
         module.fail_json_aws(e, msg="Unable to complete PUT operation.")
-    try:
-        for acl in module.params.get('permission'):
-            s3.put_object_acl(ACL=acl, Bucket=bucket, Key=obj)
-    except is_boto3_error_code(IGNORE_S3_DROP_IN_EXCEPTIONS):
-        module.warn("PutObjectAcl is not implemented by your storage provider. Set the permission parameters to the empty list to avoid this warning")
-    except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:  # pylint: disable=duplicate-except
-        module.fail_json_aws(e, msg="Unable to set object ACL")
+    if not acl_disabled:
+        try:
+            for acl in module.params.get('permission'):
+                s3.put_object_acl(ACL=acl, Bucket=bucket, Key=obj)
+        except is_boto3_error_code(IGNORE_S3_DROP_IN_EXCEPTIONS):
+            module.warn("PutObjectAcl is not implemented by your storage provider. Set the permission parameters to the empty list to avoid this warning")
+        except (botocore.exceptions.BotoCoreError, botocore.exceptions.ClientError) as e:  # pylint: disable=duplicate-except
+            module.fail_json_aws(e, msg="Unable to set object ACL")
 
     # Tags
     tags, changed = ensure_tags(s3, module, bucket, obj)
@@ -1056,12 +1057,25 @@ def main():
 
     validate = not ignore_nonexistent_bucket
 
+    # check if bucket exists, if yes, check if ACL is disabled
+    acl_disabled = False
+    exists = bucket_check(module, s3, bucket)
+    if exists:
+        try:
+            object_ownership = s3.get_bucket_ownership_controls(Bucket=bucket)['OwnershipControls']['Rules'][0]['ObjectOwnership']
+            if object_ownership == 'BucketOwnerEnforced':
+                acl_disabled = True
+        # if bucket ownership controls are not found
+        except botocore.exceptions.ClientError as e:
+            pass
+
     # separate types of ACLs
-    bucket_acl = [acl for acl in module.params.get('permission') if acl in bucket_canned_acl]
-    object_acl = [acl for acl in module.params.get('permission') if acl in object_canned_acl]
-    error_acl = [acl for acl in module.params.get('permission') if acl not in bucket_canned_acl and acl not in object_canned_acl]
-    if error_acl:
-        module.fail_json(msg='Unknown permission specified: %s' % error_acl)
+    if not acl_disabled:
+        bucket_acl = [acl for acl in module.params.get('permission') if acl in bucket_canned_acl]
+        object_acl = [acl for acl in module.params.get('permission') if acl in object_canned_acl]
+        error_acl = [acl for acl in module.params.get('permission') if acl not in bucket_canned_acl and acl not in object_canned_acl]
+        if error_acl:
+            module.fail_json(msg='Unknown permission specified: %s' % error_acl)
 
     # First, we check to see if the bucket exists, we get "bucket" returned.
     bucketrtn = bucket_check(module, s3, bucket, validate=validate)
@@ -1124,8 +1138,9 @@ def main():
                 get_download_url(module, s3, bucket, obj, expiry, tags, changed=tags_update)
 
         # only use valid object acls for the upload_s3file function
-        module.params['permission'] = object_acl
-        upload_s3file(module, s3, bucket, obj, expiry, metadata, encrypt, headers, src=src, content=bincontent)
+        if not acl_disabled:
+            module.params['permission'] = object_acl
+        upload_s3file(module, s3, bucket, obj, expiry, metadata, encrypt, headers, src=src, content=bincontent, acl_disabled=acl_disabled)
 
     # Delete an object from a bucket, not the entire bucket
     if mode == 'delobj':


Test patch:
diff --git a/tests/integration/targets/s3_object/meta/main.yml b/tests/integration/targets/s3_object/meta/main.yml
--- a/tests/integration/targets/s3_object/meta/main.yml
+++ b/tests/integration/targets/s3_object/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+  - setup_remote_tmp_dir
diff --git a/tests/integration/targets/s3_object/tasks/copy_object_acl_disabled_bucket.yml b/tests/integration/targets/s3_object/tasks/copy_object_acl_disabled_bucket.yml
new file mode 100644
--- /dev/null
+++ b/tests/integration/targets/s3_object/tasks/copy_object_acl_disabled_bucket.yml
@@ -0,0 +1,111 @@
+- name: test copying objects to bucket with ACL disabled
+  block:
+    - name: Create a bucket with ACL disabled for the test
+      s3_bucket:
+        name: "{{ bucket_name }}-acl-disabled"
+        object_ownership: BucketOwnerEnforced
+        state: present
+      register: create_result
+
+    - name: Ensure bucket creation
+      assert:
+        that:
+          - create_result is changed
+          - create_result is not failed
+          - create_result.object_ownership == "BucketOwnerEnforced"
+
+    - name: Create content
+      set_fact:
+          content: "{{ lookup('password', '/dev/null chars=ascii_letters,digits,hexdigits,punctuation') }}"
+
+    - name: Create local acl_disabled_upload_test.txt
+      copy:
+        content: "{{ content }}"
+        dest: "{{ remote_tmp_dir }}/acl_disabled_upload_test.txt"
+
+    - name: Upload a file to the bucket (check_mode)
+      amazon.aws.s3_object:
+        bucket: "{{ bucket_name }}-acl-disabled"
+        src: "{{ remote_tmp_dir }}/acl_disabled_upload_test.txt"
+        object: "acl_disabled_upload_test.txt"
+        mode: put
+      check_mode: true
+      register: upload_file_result
+
+    - assert:
+        that:
+          - upload_file_result is changed
+          - upload_file_result is not failed
+          - upload_file_result.msg == "PUT operation skipped - running in check mode"
+          - '"s3:PutObject" not in upload_file_result.resource_actions'
+
+    - name: Upload a file to the bucket
+      amazon.aws.s3_object:
+        bucket: "{{ bucket_name }}-acl-disabled"
+        src: "{{ remote_tmp_dir }}/acl_disabled_upload_test.txt"
+        object: "acl_disabled_upload_test.txt"
+        mode: put
+      register: upload_file_result
+
+    - assert:
+        that:
+          - upload_file_result is changed
+          - upload_file_result is not failed
+          - upload_file_result.msg == "PUT operation complete"
+          - '"s3:PutObject" in upload_file_result.resource_actions'
+
+    - name: Upload a file to the bucket (check_mode - idempotency)
+      amazon.aws.s3_object:
+        bucket: "{{ bucket_name }}-acl-disabled"
+        src: "{{ remote_tmp_dir }}/acl_disabled_upload_test.txt"
+        object: "acl_disabled_upload_test.txt"
+        mode: put
+      check_mode: true
+      register: upload_file_result
+
+    - assert:
+        that:
+          - upload_file_result is not changed
+          - upload_file_result is not failed
+          - upload_file_result.msg != "PUT operation complete"
+          - '"s3:PutObject" not in upload_file_result.resource_actions'
+
+    - name: Upload a file to the bucket (idempotency)
+      amazon.aws.s3_object:
+        bucket: "{{ bucket_name }}-acl-disabled"
+        src: "{{ remote_tmp_dir }}/acl_disabled_upload_test.txt"
+        object: "acl_disabled_upload_test.txt"
+        mode: put
+      register: upload_file_result
+
+    - assert:
+        that:
+          - upload_file_result is not changed
+          - upload_file_result is not failed
+          - upload_file_result.msg != "PUT operation complete"
+          - '"s3:PutObject" not in upload_file_result.resource_actions'
+
+  always:
+
+    - name: Delete the file in the bucket
+      amazon.aws.s3_object:
+        bucket: "{{ bucket_name }}-acl-disabled"
+        src: "{{ remote_tmp_dir }}/acl_disabled_upload_test.txt"
+        object: "acl_disabled_upload_test.txt"
+        mode: delobj
+      retries: 3
+      delay: 3
+      ignore_errors: true
+
+    - name: Delete bucket created in this test
+      s3_bucket:
+        name: "{{ bucket_name }}-acl-disabled"
+        object_ownership: BucketOwnerEnforced
+        state: absent
+      register: delete_result
+
+    - name: Ensure bucket deletion
+      assert:
+        that:
+          - delete_result is changed
+          - delete_result is not failed
diff --git a/tests/integration/targets/s3_object/tasks/main.yml b/tests/integration/targets/s3_object/tasks/main.yml
--- a/tests/integration/targets/s3_object/tasks/main.yml
+++ b/tests/integration/targets/s3_object/tasks/main.yml
@@ -16,11 +16,10 @@
       set_fact:
         aws_account: "{{ aws_caller_info.account }}"
 
-    - name: Create temporary directory
-      tempfile:
-        state: directory
-        path: /var/tmp
-      register: tmpdir
+    - name: check that temp directory was made
+      assert:
+        that:
+        - remote_tmp_dir is defined
 
     - name: Create content
       set_fact:
@@ -73,11 +72,11 @@
     - name: Create local upload.txt
       copy:
         content: "{{ content }}"
-        dest: "{{ tmpdir.path }}/upload.txt"
+        dest: "{{ remote_tmp_dir }}/upload.txt"
 
     - name: stat the file
       stat:
-        path: "{{ tmpdir.path }}/upload.txt"
+        path: "{{ remote_tmp_dir }}/upload.txt"
         get_checksum: yes
       register: upload_file
 
@@ -85,7 +84,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: put
-        src: "{{ tmpdir.path }}/upload.txt"
+        src: "{{ remote_tmp_dir }}/upload.txt"
         object: delete.txt
       retries: 3
       delay: 3
@@ -100,7 +99,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: put
-        src: "{{ tmpdir.path }}/upload.txt"
+        src: "{{ remote_tmp_dir }}/upload.txt"
         object: delete.txt
       register: test_async
       async: 30
@@ -117,7 +116,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: put
-        src: "{{ tmpdir.path }}/upload.txt"
+        src: "{{ remote_tmp_dir }}/upload.txt"
         object: delete.txt
       retries: 3
       delay: 3
@@ -165,7 +164,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: put
-        src: "{{ tmpdir.path }}/upload.txt"
+        src: "{{ remote_tmp_dir }}/upload.txt"
         object: delete.txt
         overwrite: never
       retries: 3
@@ -180,7 +179,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: put
-        src: "{{ tmpdir.path }}/upload.txt"
+        src: "{{ remote_tmp_dir }}/upload.txt"
         object: delete.txt
         overwrite: different
       retries: 3
@@ -195,7 +194,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: put
-        src: "{{ tmpdir.path }}/upload.txt"
+        src: "{{ remote_tmp_dir }}/upload.txt"
         object: delete.txt
         overwrite: always
       retries: 3
@@ -210,7 +209,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: get
-        dest: "{{ tmpdir.path }}/download.txt"
+        dest: "{{ remote_tmp_dir }}/download.txt"
         object: delete.txt
       retries: 3
       delay: 3
@@ -219,7 +218,7 @@
 
     - name: stat the file so we can compare the checksums
       stat:
-        path: "{{ tmpdir.path }}/download.txt"
+        path: "{{ remote_tmp_dir }}/download.txt"
         get_checksum: yes
       register: download_file
 
@@ -231,7 +230,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: get
-        dest: "{{ tmpdir.path }}/download.txt"
+        dest: "{{ remote_tmp_dir }}/download.txt"
         object: delete.txt
       retries: 3
       delay: 3
@@ -243,14 +242,14 @@
 
     - name: modify destination
       copy:
-        dest: "{{ tmpdir.path }}/download.txt"
+        dest: "{{ remote_tmp_dir }}/download.txt"
         src: hello.txt
 
     - name: test get with overwrite=never
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: get
-        dest: "{{ tmpdir.path }}/download.txt"
+        dest: "{{ remote_tmp_dir }}/download.txt"
         object: delete.txt
         overwrite: never
       retries: 3
@@ -265,7 +264,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: get
-        dest: "{{ tmpdir.path }}/download.txt"
+        dest: "{{ remote_tmp_dir }}/download.txt"
         object: delete.txt
       retries: 3
       delay: 3
@@ -279,7 +278,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: get
-        dest: "{{ tmpdir.path }}/download.txt"
+        dest: "{{ remote_tmp_dir }}/download.txt"
         object: delete.txt
         overwrite: always
       retries: 3
@@ -294,7 +293,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: get
-        dest: "{{ tmpdir.path }}/download.txt"
+        dest: "{{ remote_tmp_dir }}/download.txt"
         object: delete.txt
         overwrite: latest
       retries: 3
@@ -306,13 +305,13 @@
           - result is not changed
 
     - name: modify mtime for local file to past
-      shell: touch -mt 197001010900.00 "{{ tmpdir.path }}/download.txt"
+      shell: touch -mt 197001010900.00 "{{ remote_tmp_dir }}/download.txt"
 
     - name: test get with overwrite=latest and files that mtimes are different
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: get
-        dest: "{{ tmpdir.path }}/download.txt"
+        dest: "{{ remote_tmp_dir }}/download.txt"
         object: delete.txt
         overwrite: latest
       retries: 3
@@ -383,7 +382,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: put
-        src: "{{ tmpdir.path }}/upload.txt"
+        src: "{{ remote_tmp_dir }}/upload.txt"
         encrypt: yes
         object: delete_encrypt.txt
       retries: 3
@@ -399,7 +398,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: get
-        dest: "{{ tmpdir.path }}/download_encrypted.txt"
+        dest: "{{ remote_tmp_dir }}/download_encrypted.txt"
         object: delete_encrypt.txt
       retries: 3
       delay: 3
@@ -408,7 +407,7 @@
 
     - name: stat the file so we can compare the checksums
       stat:
-        path: "{{ tmpdir.path }}/download_encrypted.txt"
+        path: "{{ remote_tmp_dir }}/download_encrypted.txt"
         get_checksum: yes
       register: download_file
 
@@ -428,7 +427,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: put
-        src: "{{ tmpdir.path }}/upload.txt"
+        src: "{{ remote_tmp_dir }}/upload.txt"
         encrypt: yes
         encryption_mode: aws:kms
         object: delete_encrypt_kms.txt
@@ -445,7 +444,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: get
-        dest: "{{ tmpdir.path }}/download_kms.txt"
+        dest: "{{ remote_tmp_dir }}/download_kms.txt"
         object: delete_encrypt_kms.txt
       retries: 3
       delay: 3
@@ -454,7 +453,7 @@
 
     - name: get the stat of the file so we can compare the checksums
       stat:
-        path: "{{ tmpdir.path }}/download_kms.txt"
+        path: "{{ remote_tmp_dir }}/download_kms.txt"
         get_checksum: yes
       register: download_file
 
@@ -543,12 +542,12 @@
 
     - name: make tempfile 4 GB for OSX
       command:
-        _raw_params: "dd if=/dev/zero of={{ tmpdir.path }}/largefile bs=1m count=4096"
+        _raw_params: "dd if=/dev/zero of={{ remote_tmp_dir }}/largefile bs=1m count=4096"
       when: ansible_distribution == 'MacOSX'
 
     - name: make tempfile 4 GB for linux
       command:
-        _raw_params: "dd if=/dev/zero of={{ tmpdir.path }}/largefile bs=1M count=4096"
+        _raw_params: "dd if=/dev/zero of={{ remote_tmp_dir }}/largefile bs=1M count=4096"
       when: ansible_system == 'Linux'
 
     - name: test multipart download - platform specific
@@ -562,14 +561,14 @@
           s3_object:
             bucket: "{{ bucket_name }}"
             mode: put
-            src: "{{ tmpdir.path }}/largefile"
+            src: "{{ remote_tmp_dir }}/largefile"
             object: multipart.txt
 
         - name: download file once
           s3_object:
             bucket: "{{ bucket_name }}"
             mode: get
-            dest: "{{ tmpdir.path }}/download.txt"
+            dest: "{{ remote_tmp_dir }}/download.txt"
             object: multipart.txt
             overwrite: different
           retries: 3
@@ -585,7 +584,7 @@
           s3_object:
             bucket: "{{ bucket_name }}"
             mode: get
-            dest: "{{ tmpdir.path }}/download.txt"
+            dest: "{{ remote_tmp_dir }}/download.txt"
             object: multipart.txt
             overwrite: different
           register: result
@@ -626,7 +625,7 @@
       s3_object:
         bucket: "{{ bucket_name_acl }}"
         mode: put
-        src: "{{ tmpdir.path }}/upload.txt"
+        src: "{{ remote_tmp_dir }}/upload.txt"
         object: file-with-permissions.txt
         permission: bucket-owner-full-control
         ignore_nonexistent_bucket: True
@@ -744,7 +743,7 @@
       s3_object:
         bucket: "{{ bucket_name }}"
         mode: get
-        dest: "{{ tmpdir.path }}/download_binary.bin"
+        dest: "{{ remote_tmp_dir }}/download_binary.bin"
         object: put-binary.bin
       register: result
 
@@ -754,7 +753,7 @@
         get_checksum: yes
       loop:
       - "{{ role_path }}/files/test.png"
-      - "{{ tmpdir.path }}/download_binary.bin"
+      - "{{ remote_tmp_dir }}/download_binary.bin"
       register: binary_files
 
     - assert:
@@ -763,6 +762,8 @@
 
     - include_tasks: copy_object.yml
 
+    - include_tasks: copy_object_acl_disabled_bucket.yml
+
     # ============================================================
     - name: 'Run tagging tests'
       block:
@@ -1003,7 +1004,7 @@
     - name: delete temporary files
       file:
         state: absent
-        path: "{{ tmpdir.path }}"
+        path: "{{ remote_tmp_dir }}"
       ignore_errors: true
 
     - include_tasks: delete_bucket.yml