diff --git a/dm/templates/shared_vpc_subnet_iam/README.md b/dm/templates/shared_vpc_subnet_iam/README.md index 930458aa9f8..45e08730525 100644 --- a/dm/templates/shared_vpc_subnet_iam/README.md +++ b/dm/templates/shared_vpc_subnet_iam/README.md @@ -62,5 +62,11 @@ See `properties` section in the schema file(s): ``` ## Examples - -- [Shared VPC Subnet IAM](examples/shared_vpc_subnet_iam.yaml) +- [Shared VPC Subnet IAM Bindings syntax](examples/shared_vpc_subnet_iam_bindings.yaml) +- [Shared VPC Subnet IAM Policy syntax](examples/shared_vpc_subnet_iam_policy.yaml) +- [Shared VPC Subnet IAM Legacy](examples/shared_vpc_subnet_iam_legacy.yaml) + +## Tests Cases +- [Shared VPC Subnet IAM Bindings syntax](tests/integration/bindings.bats) +- [Shared VPC Subnet IAM Policy syntax](tests/integration/policy.bats) +- [Shared VPC Subnet IAM Legacy syntax](tests/integration/legacy.bats) diff --git a/dm/templates/shared_vpc_subnet_iam/examples/shared_vpc_subnet_iam_bindings.yaml b/dm/templates/shared_vpc_subnet_iam/examples/shared_vpc_subnet_iam_bindings.yaml new file mode 100644 index 00000000000..19bb2263f75 --- /dev/null +++ b/dm/templates/shared_vpc_subnet_iam/examples/shared_vpc_subnet_iam_bindings.yaml @@ -0,0 +1,29 @@ +# Example usage of the shared VPC subnet IAM template +# +# The `members` property is a list of members to +# grant IAM roles on a shared VPC subnetwork. +# A member can be a user, service account, group, or domain. +# +# Replace `resourceId` with a valid subnet ID. + +imports: + - path: templates/shared_vpc_subnet_iam/shared_vpc_subnet_iam.py + name: shared_vpc_subnet_iam.py + +resources: + - name: test-shared-vpc-subnet-iam-policy + type: shared_vpc_subnet_iam.py + properties: + bindings: + - resourceId: test-subnet-1 + region: us-east1 + role: roles/compute.networkUser + members: + - user:name@example.com + - serviceAccount:example@myprojectname.gserviceaccount.com + - resourceId: + region: us-east1 + role: roles/compute.networkUser + members: + - group:admins@example.com + - domain:example.com diff --git a/dm/templates/shared_vpc_subnet_iam/examples/shared_vpc_subnet_iam.yaml b/dm/templates/shared_vpc_subnet_iam/examples/shared_vpc_subnet_iam_legacy.yaml similarity index 100% rename from dm/templates/shared_vpc_subnet_iam/examples/shared_vpc_subnet_iam.yaml rename to dm/templates/shared_vpc_subnet_iam/examples/shared_vpc_subnet_iam_legacy.yaml diff --git a/dm/templates/shared_vpc_subnet_iam/examples/shared_vpc_subnet_iam_policy.yaml b/dm/templates/shared_vpc_subnet_iam/examples/shared_vpc_subnet_iam_policy.yaml new file mode 100644 index 00000000000..ba71849d298 --- /dev/null +++ b/dm/templates/shared_vpc_subnet_iam/examples/shared_vpc_subnet_iam_policy.yaml @@ -0,0 +1,30 @@ +# Example usage of the shared VPC subnet IAM template +# +# The `members` property is a list of members to +# grant IAM roles on a shared VPC subnetwork. +# A member can be a user, service account, group, or domain. +# +# Replace `resourceId` with a valid subnet ID. + +imports: + - path: templates/shared_vpc_subnet_iam/shared_vpc_subnet_iam.py + name: shared_vpc_subnet_iam.py + +resources: + - name: test-shared-vpc-subnet-iam-policy + type: shared_vpc_subnet_iam.py + properties: + policy: + bindings: + - resourceId: test-subnet-1 + region: us-east1 + role: roles/compute.networkUser + members: + - user:name@example.com + - serviceAccount:example@myprojectname.gserviceaccount.com + - resourceId: + region: us-east1 + role: roles/compute.networkUser + members: + - group:admins@example.com + - domain:example.com diff --git a/dm/templates/shared_vpc_subnet_iam/shared_vpc_subnet_iam.py b/dm/templates/shared_vpc_subnet_iam/shared_vpc_subnet_iam.py index 59693cbe477..38119687b95 100644 --- a/dm/templates/shared_vpc_subnet_iam/shared_vpc_subnet_iam.py +++ b/dm/templates/shared_vpc_subnet_iam/shared_vpc_subnet_iam.py @@ -11,43 +11,57 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -""" This template grants IAM roles to a user on a shared VPC subnetwork. """ +"""This template grants IAM roles to a user on a shared VPC subnetwork.""" -def generate_config(context): - """ Entry point for the deployment resources. """ - +def _append_resource(subnets, project, name_id): + """Append subnets to resources.""" resources = [] out = {} - for subnet in context.properties['subnets']: - subnet_id = subnet['subnetId'] - policy_name = 'iam-subnet-policy-{}'.format(subnet_id) - - policies_to_add = [ - { - 'role': subnet['role'], - 'members': subnet['members'] + for subnet in subnets: + policy_name = 'iam-subnet-policy-{}'.format(subnet[name_id]) + resources.append({ + 'name': policy_name, + # https://cloud.google.com/compute/docs/reference/rest/beta/subnetworks/setIamPolicy + 'type': 'gcp-types/compute-beta:compute.subnetworks.setIamPolicy', + 'properties': { + 'name': subnet[name_id], + 'project': project, + 'region': subnet['region'], + 'bindings': [{ + 'role': subnet['role'], + 'members': subnet['members'] + }] } - ] - - resources.append( - { - 'name': policy_name, - 'type': 'gcp-types/compute-beta:compute.subnetworks.setIamPolicy', # pylint: disable=line-too-long - 'properties': - { - 'name': subnet_id, - 'project': context.env['project'], - 'region': subnet['region'], - 'bindings': policies_to_add - } - } - ) + }) out[policy_name] = { 'etag': '$(ref.' + policy_name + '.etag)' } + return resources, out + +def generate_config(context): + """Entry point for the deployment resources.""" + try: + resources, out = _append_resource( + context.properties['subnets'], # Legacy syntax + context.env['project'], + 'subnetId' + ) + except KeyError: + try: + resources, out = _append_resource( + context.properties['policy']['bindings'], # Policy syntax + context.env['project'], + 'resourceId' + ) + except KeyError: + resources, out = _append_resource( + context.properties['bindings'], # Bindings syntax + context.env['project'], + 'resourceId' + ) outputs = [{'name': 'policies', 'value': out}] return {'resources': resources, 'outputs': outputs} diff --git a/dm/templates/shared_vpc_subnet_iam/shared_vpc_subnet_iam.py.schema b/dm/templates/shared_vpc_subnet_iam/shared_vpc_subnet_iam.py.schema index 9ccccaacf6d..cd4141e784b 100644 --- a/dm/templates/shared_vpc_subnet_iam/shared_vpc_subnet_iam.py.schema +++ b/dm/templates/shared_vpc_subnet_iam/shared_vpc_subnet_iam.py.schema @@ -15,44 +15,176 @@ info: title: Shared VPC Subnet IAM author: Sourced Group Inc. - description: Grants IAM roles to a user on a shared VPC subnetwork + version: 1.0.0 + description: | + Grants IAM roles to a user on a shared VPC subnetwork + + For more information on this resource: + https://cloud.google.com/compute/docs/reference/rest/beta/subnetworks + + APIs endpoints used by this template: + - gcp-types/compute-beta:compute.subnetworks.setIamPolicy => + https://cloud.google.com/compute/docs/reference/rest/beta/subnetworks/setIamPolicy + +imports: + - path: shared_vpc_subnet_iam.py + +oneOf: +- required: + - bindings +- required: + - policy +- required: + - subnets # Legacy additionalProperties: false -required: - - subnets +project: + type: string + description: The Project ID. -properties: - subnets: - type: array - description: An array of subnetworks and members to grant IAM roles to. - items: - subnetId: - type: string - description: The subnet ID. - region: - type: string - description: The region the subnetId resides in. - role: - type: string - description: The role to grant. - members: +definitions: + policy: + type: object + description: | + REQUIRED: The complete policy to be applied to the 'resource'. + The size of the policy is limited to a few 10s of KB. An empty policy is + in general a valid policy but certain services (like Projects) might + reject them. + additionalProperties: false + properties: + version: + type: integer + description: Deprecated + bindings: type: array - description: A list of member identities. + description: | + Associates a list of members to a role. bindings with no members will + result in an error. + uniqItems: true items: - type: string - description: | - The identity of a member requesting access for a Cloud Platform - resource. Can have the following values: - - user:{emailid} - An email address that represents a specific - Google account. For example, user:name@example.com - - serviceAccount:{emailid} - An email address that represents a - service account. For example, - serviceAccount:my-other-app@appspot.gserviceaccount.com - - group:{emailid} - An email address that represents a Google group. - For example, group:admins@example.com - - domain:{domain} - A Google Apps domain name that represents all - the users of that domain. For example, google.com or example.com. + subnetId: # legacy + type: string + description: The subnet ID. + requestId: + type: string + description: Name or id of the resource for this request. + role: + type: string + description: | + Role that is assigned to members. For example, + roles/viewer, roles/editor, or roles/owner. + members: + type: array + description: A list of member identities. + uniqItems: true + items: + type: string + description: | + Specifies the identities requesting access for a Cloud Platform + resource. `members` can have the following values: + - allUsers: A special identifier that represents anyone who + is on the internet; with or without a Google account. + - allAuthenticatedUsers: A special identifier that represents + anyone who is authenticated with a Google account or a + service account. + - user:{emailid} - An email address that represents a + specific Google account. For example, user:name@example.com + - serviceAccount:{emailid} - An email address that represents + a service account. For example, + serviceAccount:my-other-app@appspot.gserviceaccount.com + - group:{emailid} - An email address that represents a Google + group. For example, group:admins@example.com + - domain:{domain} - A Google Apps domain name that represents + all the users of that domain. For example, + google.com or example.com. + condition: + type: object + description: | + The condition that is associated with this binding. NOTE: An + unsatisfied condition will not allow user access via current + binding. Different bindings, including their conditions, are + examined independently. + additionalProperties: false + properties: + expression: + type: string + description: | + Textual representation of an expression in Common Expression + Language syntax. The application context of the containing + message determines which well-known feature set of CEL is + supported. + title: + type: string + description: | + An optional title for the expression, i.e. a short string + describing its purpose. This can be used e.g. in UIs which + allow to enter the expression. + description: + type: string + description: | + An optional description of the expression. This is a longer + text which describes the expression, e.g. when hovered over + it in a UI. + location: + type: string + description: | + An optional string indicating the location of the expression + for error reporting, e.g. a file name and a position in the + file. + auditConfigs: + type: object + description: | + Specifies cloud audit logging configuration for this policy. + additionalProperties: false + properties: + service: + type: string + description: | + Specifies a service that will be enabled for audit logging. + For example, storage.googleapis.com, cloudsql.googleapis.com. + allServices is a special value that covers all services. + auditLogConfigs: + type: object + description: | + The configuration for logging of each type of permission. + additionalProperties: false + properties: + logType: + type: string + description: The log type that this config enables. + exemptedMembers: + type: array + description: | + Specifies the identities that do not cause logging for this + type of permission. Follows the same format of + Binding.members. + uniqItems: true + items: + type: string + etag: + type: string + description: | + etag is used for optimistic concurrency control as a way to help + prevent simultaneous updates of a policy from overwriting each other. + It is strongly suggested that systems make use of the etag in the + read-modify-write cycle to perform policy updates in order to avoid + race conditions: An etag is returned in the response to getIamPolicy, + and systems are expected to put that etag in the request to + setIamPolicy to ensure that their change will be applied to the same + version of the policy. If no etag is provided in the call to + setIamPolicy, then the existing policy is overwritten blindly. + A base64-encoded string. + +properties: + policy: + $ref: '#/definitions/policy' + bindings: + $ref: '#/definitions/policy/properties/bindings' + subnets: # legacy + $ref: '#/definitions/policy/properties/bindings' + etag: + $ref: '#/definitions/policy/properties/etag' outputs: properties: diff --git a/dm/templates/shared_vpc_subnet_iam/tests/integration/bindings.bats b/dm/templates/shared_vpc_subnet_iam/tests/integration/bindings.bats new file mode 120000 index 00000000000..785c07d8fb8 --- /dev/null +++ b/dm/templates/shared_vpc_subnet_iam/tests/integration/bindings.bats @@ -0,0 +1 @@ +shared_vpc_subnet_iam.bats \ No newline at end of file diff --git a/dm/templates/shared_vpc_subnet_iam/tests/integration/bindings.yaml b/dm/templates/shared_vpc_subnet_iam/tests/integration/bindings.yaml new file mode 100644 index 00000000000..5ca33a8bc4a --- /dev/null +++ b/dm/templates/shared_vpc_subnet_iam/tests/integration/bindings.yaml @@ -0,0 +1,25 @@ +# Test of the shared VPC subnet IAM template. +# +# Variables: +# RAND: A random string used by the testing suite. +# + +imports: + - path: templates/shared_vpc_subnet_iam/shared_vpc_subnet_iam.py + name: shared_vpc_subnet_iam.py + +resources: + - name: test-shared-vpc-subnet-iam-${RAND} + type: shared_vpc_subnet_iam.py + properties: + bindings: + - resourceId: subnet-${RAND}-1 + region: us-east1 + role: roles/compute.networkUser + members: + - serviceAccount:${TEST_SERVICE_ACCOUNT}@${CLOUD_FOUNDATION_PROJECT_ID}.iam.gserviceaccount.com + - resourceId: subnet-${RAND}-2 + region: us-east1 + role: roles/compute.networkUser + members: + - serviceAccount:${TEST_SERVICE_ACCOUNT}@${CLOUD_FOUNDATION_PROJECT_ID}.iam.gserviceaccount.com diff --git a/dm/templates/shared_vpc_subnet_iam/tests/integration/legacy.bats b/dm/templates/shared_vpc_subnet_iam/tests/integration/legacy.bats new file mode 120000 index 00000000000..785c07d8fb8 --- /dev/null +++ b/dm/templates/shared_vpc_subnet_iam/tests/integration/legacy.bats @@ -0,0 +1 @@ +shared_vpc_subnet_iam.bats \ No newline at end of file diff --git a/dm/templates/shared_vpc_subnet_iam/tests/integration/shared_vpc_subnet_iam.yaml b/dm/templates/shared_vpc_subnet_iam/tests/integration/legacy.yaml similarity index 100% rename from dm/templates/shared_vpc_subnet_iam/tests/integration/shared_vpc_subnet_iam.yaml rename to dm/templates/shared_vpc_subnet_iam/tests/integration/legacy.yaml diff --git a/dm/templates/shared_vpc_subnet_iam/tests/integration/policy.bats b/dm/templates/shared_vpc_subnet_iam/tests/integration/policy.bats new file mode 120000 index 00000000000..785c07d8fb8 --- /dev/null +++ b/dm/templates/shared_vpc_subnet_iam/tests/integration/policy.bats @@ -0,0 +1 @@ +shared_vpc_subnet_iam.bats \ No newline at end of file diff --git a/dm/templates/shared_vpc_subnet_iam/tests/integration/policy.yaml b/dm/templates/shared_vpc_subnet_iam/tests/integration/policy.yaml new file mode 100644 index 00000000000..b38fc74b15e --- /dev/null +++ b/dm/templates/shared_vpc_subnet_iam/tests/integration/policy.yaml @@ -0,0 +1,26 @@ +# Test of the shared VPC subnet IAM template. +# +# Variables: +# RAND: A random string used by the testing suite. +# + +imports: + - path: templates/shared_vpc_subnet_iam/shared_vpc_subnet_iam.py + name: shared_vpc_subnet_iam.py + +resources: + - name: test-shared-vpc-subnet-iam-${RAND} + type: shared_vpc_subnet_iam.py + properties: + policy: + bindings: + - resourceId: subnet-${RAND}-1 + region: us-east1 + role: roles/compute.networkUser + members: + - serviceAccount:${TEST_SERVICE_ACCOUNT}@${CLOUD_FOUNDATION_PROJECT_ID}.iam.gserviceaccount.com + - resourceId: subnet-${RAND}-2 + region: us-east1 + role: roles/compute.networkUser + members: + - serviceAccount:${TEST_SERVICE_ACCOUNT}@${CLOUD_FOUNDATION_PROJECT_ID}.iam.gserviceaccount.com diff --git a/dm/templates/shared_vpc_subnet_iam/tests/integration/shared_vpc_subnet_iam.bats b/dm/templates/shared_vpc_subnet_iam/tests/integration/shared_vpc_subnet_iam.bats old mode 100644 new mode 100755