Merge pull request #693 from thekad/feature/bucket_policy
Some checks are pending
Run integration tests for the cloud.google collection / integration (stable-2.16) (push) Waiting to run
Run integration tests for the cloud.google collection / integration (stable-2.17) (push) Waiting to run
Run integration tests for the cloud.google collection / integration (stable-2.18) (push) Waiting to run

Add iamConfiguration support to gcp_storage_bucket
This commit is contained in:
Chris Hawk 2025-07-29 15:25:49 -07:00 committed by GitHub
commit 2ed1936ad8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 180 additions and 2 deletions

View file

@ -186,6 +186,33 @@ options:
- 'Some valid choices include: "OWNER", "READER"' - 'Some valid choices include: "OWNER", "READER"'
required: true required: true
type: str type: str
iam_configuration:
description:
- IAM configuration for the storage bucket.
required: false
type: dict
suboptions:
public_access_prevention:
description:
- The bucket's public access prevention status.
required: false
type: str
default: inherited
choices:
- inherited
- enforced
uniform_bucket_level_access:
description:
- The bucket's uniform bucket-level access configuration.
required: false
type: dict
suboptions:
enabled:
description:
- Whether or not the bucket uses uniform bucket-level access.
- If set, access checks only use bucket-level IAM policies or above.
required: false
type: bool
lifecycle: lifecycle:
description: description:
- The bucket's lifecycle configuration. - The bucket's lifecycle configuration.
@ -209,7 +236,7 @@ options:
suboptions: suboptions:
storage_class: storage_class:
description: description:
- Target storage class. Required iff the type of the action is SetStorageClass. - Target storage class. Required if the type of the action is SetStorageClass.
required: false required: false
type: str type: str
type: type:
@ -627,6 +654,29 @@ defaultObjectAcl:
- The access permission for the entity. - The access permission for the entity.
returned: success returned: success
type: str type: str
iamConfiguration:
description:
- IAM configuration for the storage bucket.
returned: success
type: complex
contains:
publicAccessPrevention:
description:
- The bucket's public access prevention status.
returned: success
type: str
uniformBucketLevelAccess:
description:
- The bucket's uniform bucket-level access configuration.
returned: success
type: complex
contains:
enabled:
description:
- Whether or not the bucket uses uniform bucket-level access.
- If set, access checks only use bucket-level IAM policies or above.
returned: success
type: bool
id: id:
description: description:
- The ID of the bucket. For buckets, the id and name properities are the same. - The ID of the bucket. For buckets, the id and name properities are the same.
@ -922,6 +972,21 @@ def main():
role=dict(required=True, type='str'), role=dict(required=True, type='str'),
), ),
), ),
iam_configuration=dict(
type='dict',
options=dict(
public_access_prevention=dict(
type='str', default='inherited',
choices=['inherited', 'enforced'],
),
uniform_bucket_level_access=dict(
type='dict',
options=dict(
enabled=dict(type='bool'),
),
),
),
),
lifecycle=dict( lifecycle=dict(
type='dict', type='dict',
options=dict( options=dict(
@ -1017,6 +1082,7 @@ def resource_to_request(module):
u'cors': BucketCorsArray(module.params.get('cors', []), module).to_request(), u'cors': BucketCorsArray(module.params.get('cors', []), module).to_request(),
u'defaultEventBasedHold': module.params.get('default_event_based_hold'), u'defaultEventBasedHold': module.params.get('default_event_based_hold'),
u'defaultObjectAcl': BucketDefaultobjectaclArray(module.params.get('default_object_acl', []), module).to_request(), u'defaultObjectAcl': BucketDefaultobjectaclArray(module.params.get('default_object_acl', []), module).to_request(),
u'iamConfiguration': BucketIamconfiguration(module.params.get('iam_configuration', {}), module).to_request(),
u'lifecycle': BucketLifecycle(module.params.get('lifecycle', {}), module).to_request(), u'lifecycle': BucketLifecycle(module.params.get('lifecycle', {}), module).to_request(),
u'location': module.params.get('location'), u'location': module.params.get('location'),
u'logging': BucketLogging(module.params.get('logging', {}), module).to_request(), u'logging': BucketLogging(module.params.get('logging', {}), module).to_request(),
@ -1095,8 +1161,9 @@ def response_to_hash(module, response):
u'acl': BucketAclArray(response.get(u'acl', []), module).from_response(), u'acl': BucketAclArray(response.get(u'acl', []), module).from_response(),
u'cors': BucketCorsArray(response.get(u'cors', []), module).from_response(), u'cors': BucketCorsArray(response.get(u'cors', []), module).from_response(),
u'defaultEventBasedHold': response.get(u'defaultEventBasedHold'), u'defaultEventBasedHold': response.get(u'defaultEventBasedHold'),
u'defaultObjectAcl': BucketDefaultobjectaclArray(module.params.get('default_object_acl', []), module).to_request(), u'defaultObjectAcl': BucketDefaultobjectaclArray(module.params.get('default_object_acl', []), module).from_response(),
u'id': response.get(u'id'), u'id': response.get(u'id'),
u'iamConfiguration': BucketIamconfiguration(response.get('iamConfiguration', {}), module).from_response(),
u'lifecycle': BucketLifecycle(response.get(u'lifecycle', {}), module).from_response(), u'lifecycle': BucketLifecycle(response.get(u'lifecycle', {}), module).from_response(),
u'location': response.get(u'location'), u'location': response.get(u'location'),
u'logging': BucketLogging(response.get(u'logging', {}), module).from_response(), u'logging': BucketLogging(response.get(u'logging', {}), module).from_response(),
@ -1429,5 +1496,27 @@ class BucketWebsite(object):
return remove_nones_from_dict({u'mainPageSuffix': self.request.get(u'mainPageSuffix'), u'notFoundPage': self.request.get(u'notFoundPage')}) return remove_nones_from_dict({u'mainPageSuffix': self.request.get(u'mainPageSuffix'), u'notFoundPage': self.request.get(u'notFoundPage')})
class BucketIamconfiguration(object):
def __init__(self, transport, module):
self.module = module
# transport can be either the request or response objects
if transport:
self.transport = transport
else:
self.transport = {}
def to_request(self):
return remove_nones_from_dict({
u'publicAccessPrevention': self.transport.get('public_access_prevention'),
u'uniformBucketLevelAccess': self.transport.get('uniform_bucket_level_access'),
})
def from_response(self):
return remove_nones_from_dict({
u'publicAccessPrevention': self.transport.get('publicAccessPrevention'),
u'uniformBucketLevelAccess': self.transport.get('uniformBucketLevelAccess'),
})
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View file

@ -0,0 +1,86 @@
---
- name: Run test cases
block:
# --------------------------------------------------------------------------
- name: Create default bucket
google.cloud.gcp_storage_bucket:
name: "{{ resource_name }}-default"
project: "{{ gcp_project }}"
auth_kind: "{{ gcp_cred_kind }}"
service_account_file: "{{ gcp_cred_file | default(omit) }}"
state: present
register: result
- name: Assert changed is true and default values are returned
ansible.builtin.assert:
that:
- result.changed == true
- result.iamConfiguration.publicAccessPrevention == 'inherited'
- result.iamConfiguration.uniformBucketLevelAccess.enabled == false
# --------------------------------------------------------------------------
- name: Create bucket with enforced PAP
google.cloud.gcp_storage_bucket:
name: "{{ resource_name }}-pap"
project: "{{ gcp_project }}"
auth_kind: "{{ gcp_cred_kind }}"
service_account_file: "{{ gcp_cred_file | default(omit) }}"
state: present
iam_configuration:
public_access_prevention: enforced
register: result
- name: Assert changed is true and IAM PAP is 'enforced'
ansible.builtin.assert:
that:
- result.changed == true
- result.iamConfiguration.publicAccessPrevention == 'enforced'
# --------------------------------------------------------------------------
- name: Create bucket with UBLA enabled
google.cloud.gcp_storage_bucket:
name: "{{ resource_name }}-ublae"
project: "{{ gcp_project }}"
auth_kind: "{{ gcp_cred_kind }}"
service_account_file: "{{ gcp_cred_file | default(omit) }}"
state: present
iam_configuration:
uniform_bucket_level_access:
enabled: true
register: result
- name: Assert changed is true and IAM UBLA is enabled
ansible.builtin.assert:
that:
- result.changed == true
- result.iamConfiguration.uniformBucketLevelAccess.enabled == true
# --------------------------------------------------------------------------
- name: Create bucket with UBLA disabled
google.cloud.gcp_storage_bucket:
name: "{{ resource_name }}-ublad"
project: "{{ gcp_project }}"
auth_kind: "{{ gcp_cred_kind }}"
service_account_file: "{{ gcp_cred_file | default(omit) }}"
state: present
iam_configuration:
uniform_bucket_level_access:
enabled: false
register: result
- name: Assert changed is true and IAM UBLA is disabled
ansible.builtin.assert:
that:
- result.changed == true
- result.iamConfiguration.uniformBucketLevelAccess.enabled == false
# --------------------------------------------------------------------------
always:
- name: Clean up buckets
google.cloud.gcp_storage_bucket:
name: "{{ resource_name }}-{{ item }}"
project: "{{ gcp_project }}"
auth_kind: "{{ gcp_cred_kind }}"
service_account_file: "{{ gcp_cred_file | default(omit) }}"
state: absent
loop:
- default
- pap
- ublae
- ublad

View file

@ -1,3 +1,6 @@
--- ---
- name: Generated tests - name: Generated tests
ansible.builtin.include_tasks: autogen.yml ansible.builtin.include_tasks: autogen.yml
- name: Tests for IAM Configuration support
ansible.builtin.include_tasks: iam_configuration.yml