Skip to content

Commit

Permalink
terraform Support for Managing Entitlements. (GoogleCloudPlatform#10334)
Browse files Browse the repository at this point in the history
Co-authored-by: Nick Elliot <[email protected]>
  • Loading branch information
2 people authored and balanaguharsha committed May 2, 2024
1 parent db91a3c commit b14e690
Show file tree
Hide file tree
Showing 8 changed files with 526 additions and 0 deletions.
264 changes: 264 additions & 0 deletions mmv1/products/privilegedaccessmanager/Entitlement.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
# Copyright 2023 Google Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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.

--- !ruby/object:Api::Resource
base_url: '{{parent}}/locations/{{location}}/entitlements'
create_url: '{{parent}}/locations/{{location}}/entitlements?entitlementId={{entitlement_id}}'
delete_url: '{{parent}}/locations/{{location}}/entitlements/{{entitlement_id}}?force=true'
self_link: '{{parent}}/locations/{{location}}/entitlements/{{entitlement_id}}'
id_format: '{{parent}}/locations/{{location}}/entitlements/{{entitlement_id}}'
import_format: ['{{%parent}}/locations/{{location}}/entitlements/{{entitlement_id}}']
name: Entitlement
description: |
An Entitlement defines the eligibility of a set of users to obtain a predefined access for some time possibly after going through an approval workflow.
update_verb: :PATCH
update_mask: true
min_version: beta
autogen_async: true
examples:
- !ruby/object:Provider::Terraform::Examples
name: "privileged_access_manager_entitlement_basic"
min_version: beta
primary_resource_id: "tfentitlement"
vars:
entitlement_id: "example-entitlement"
test_env_vars:
project: :PROJECT_NAME
properties:
- !ruby/object:Api::Type::String
name: name
description: |
Output Only. The entitlement's name follows a hierarchical structure, comprising the organization, folder, or project, alongside the region and a unique entitlement ID.
Formats: organizations/{organization-number}/locations/{region}/entitlements/{entitlement-id}, folders/{folder-number}/locations/{region}/entitlements/{entitlement-id}, and projects/{project-id|project-number}/locations/{region}/entitlements/{entitlement-id}.
output: true
- !ruby/object:Api::Type::String
name: createTime
description: |
Output only. Create time stamp. A timestamp in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional digits.
Examples: "2014-10-02T15:01:23Z" and "2014-10-02T15:01:23.045123456Z"
output: true
- !ruby/object:Api::Type::String
name: updateTime
description: |
Output only. Update time stamp. A timestamp in RFC3339 UTC "Zulu" format, with nanosecond resolution and up to nine fractional digits.
Examples: "2014-10-02T15:01:23Z" and "2014-10-02T15:01:23.045123456Z".
output: true
- !ruby/object:Api::Type::Array
name: eligibleUsers
description: |
Who can create Grants using Entitlement. This list should contain at most one entry
required: true
item_type: !ruby/object:Api::Type::NestedObject
properties:
- !ruby/object:Api::Type::Array
name: principals
is_set: true
required: true
description: |
Users who are being allowed for the operation. Each entry should be a valid v1 IAM Principal Identifier. Format for these is documented at "https://cloud.google.com/iam/docs/principal-identifiers#v1"
item_type: Api::Type::String
item_validation: !ruby/object:Provider::Terraform::Validation
function: 'validateDeletedPrincipals'
- !ruby/object:Api::Type::NestedObject
name: approvalWorkflow
immutable: true
description: |
The approvals needed before access will be granted to a requester.
No approvals will be needed if this field is null. Different types of approval workflows that can be used to gate privileged access granting.
properties:
- !ruby/object:Api::Type::NestedObject
name: manualApprovals
required: true
description: |
A manual approval workflow where users who are designated as approvers need to call the ApproveGrant/DenyGrant APIs for an Grant.
The workflow can consist of multiple serial steps where each step defines who can act as Approver in that step and how many of those users should approve before the workflow moves to the next step.
This can be used to create approval workflows such as
* Require an approval from any user in a group G.
* Require an approval from any k number of users from a Group G.
* Require an approval from any user in a group G and then from a user U. etc.
A single user might be part of `approvers` ACL for multiple steps in this workflow but they can only approve once and that approval will only be considered to satisfy the approval step at which it was granted.
properties:
- !ruby/object:Api::Type::Boolean
name: requireApproverJustification
description: |
Optional. Do the approvers need to provide a justification for their actions?
- !ruby/object:Api::Type::Array
name: steps
required: true
description: |
List of approval steps in this workflow. These steps would be followed in the specified order sequentially. 1 step is supported for now.
item_type: !ruby/object:Api::Type::NestedObject
properties:
- !ruby/object:Api::Type::Array
name: approvers
required: true
min_size: 1
max_size: 1
description: |
The potential set of approvers in this step. This list should contain at only one entry.
item_type: !ruby/object:Api::Type::NestedObject
properties:
- !ruby/object:Api::Type::Array
name: principals
required: true
min_size: 1
is_set: true
item_type: Api::Type::String
item_validation: !ruby/object:Provider::Terraform::Validation
function: 'validateDeletedPrincipals'
description: |
Users who are being allowed for the operation. Each entry should be a valid v1 IAM Principal Identifier. Format for these is documented at: https://cloud.google.com/iam/docs/principal-identifiers#v1
- !ruby/object:Api::Type::Integer
name: approvalsNeeded
description: |
How many users from the above list need to approve.
If there are not enough distinct users in the list above then the workflow
will indefinitely block. Should always be greater than 0. Currently 1 is the only
supported value.
- !ruby/object:Api::Type::Array
name: approverEmailRecipients
item_type: Api::Type::String
is_set: true
description: |
Optional. Additional email addresses to be notified when a grant is pending approval.
- !ruby/object:Api::Type::NestedObject
name: privilegedAccess
description: |
Privileged access that this service can be used to gate.
required: true
properties:
- !ruby/object:Api::Type::NestedObject
name: gcpIamAccess
description: |
GcpIamAccess represents IAM based access control on a GCP resource. Refer to https://cloud.google.com/iam/docs to understand more about IAM.
required: true
properties:
- !ruby/object:Api::Type::String
name: resourceType
description: |
The type of this resource.
required: true
- !ruby/object:Api::Type::String
name: resource
description: |
Name of the resource.
required: true
- !ruby/object:Api::Type::Array
name: roleBindings
description: |
Role bindings to be created on successful grant.
required: true
item_type: !ruby/object:Api::Type::NestedObject
properties:
- !ruby/object:Api::Type::String
name: role
description: |
IAM role to be granted. https://cloud.google.com/iam/docs/roles-overview.
required: true
- !ruby/object:Api::Type::String
name: conditionExpression
description: |
The expression field of the IAM condition to be associated with the role. If specified, a user with an active grant for this entitlement would be able to access the resource only if this condition evaluates to true for their request.
https://cloud.google.com/iam/docs/conditions-overview#attributes.
- !ruby/object:Api::Type::String
name: maxRequestDuration
description: |
The maximum amount of time for which access would be granted for a request.
A requester can choose to ask for access for less than this duration but never more.
Format: calculate the time in seconds and concatenate it with 's' i.e. 2 hours = "7200s", 45 minutes = "2700s"
required: true
- !ruby/object:Api::Type::String
name: state
description: Output only. The current state of the Entitlement.
output: true
- !ruby/object:Api::Type::Fingerprint
name: etag
description: |
For Resource freshness validation (https://google.aip.dev/154)
output: true
- !ruby/object:Api::Type::NestedObject
name: requesterJustificationConfig
description: |
Defines the ways in which a requester should provide the justification while requesting for access.
required: true
allow_empty_object: true
send_empty_value: true
properties:
- !ruby/object:Api::Type::NestedObject
name: notMandatory
description: |
The justification is not mandatory but can be provided in any of the supported formats.
properties: []
allow_empty_object: true
send_empty_value: true
conflicts:
- unstructured
- !ruby/object:Api::Type::NestedObject
name: unstructured
description: |
The requester has to provide a justification in the form of free flowing text.
properties: []
allow_empty_object: true
send_empty_value: true
conflicts:
- not_mandatory
- !ruby/object:Api::Type::NestedObject
name: additionalNotificationTargets
allow_empty_object: true
send_empty_value: true
description: |
AdditionalNotificationTargets includes email addresses to be notified.
properties:
- !ruby/object:Api::Type::Array
name: adminEmailRecipients
is_set: true
description: |
Optional. Additional email addresses to be notified when a principal(requester) is granted access.
item_type: Api::Type::String
allow_empty_object: true
- !ruby/object:Api::Type::Array
name: requesterEmailRecipients
item_type: Api::Type::String
is_set: true
description: |
Optional. Additional email address to be notified about an eligible entitlement.
allow_empty_object: true
parameters:
- !ruby/object:Api::Type::String
name: location
description: |
The region of the Entitlement resource.
url_param_only: true
required: true
immutable: true
- !ruby/object:Api::Type::String
name: entitlementId
description: |
The ID to use for this Entitlement. This will become the last part of the resource name.
This value should be 4-63 characters, and valid characters are "[a-z]", "[0-9]", and "-". The first character should be from [a-z].
This value should be unique among all other Entitlements under the specified `parent`.
url_param_only: true
required: true
immutable: true
validation: !ruby/object:Provider::Terraform::Validation
function: validateEntitlementId
- !ruby/object:Api::Type::String
name: parent
immutable: true
required: true
url_param_only: true
description: |
Format: project/{project_id} or organization/{organization_number} or folder/{folder_number}
custom_code: !ruby/object:Provider::Terraform::CustomCode
pre_update: templates/terraform/pre_update/privileged_access_manager_entitlement.go.erb
constants: templates/terraform/constants/privileged_access_manager_entitlement.go.erb
40 changes: 40 additions & 0 deletions mmv1/products/privilegedaccessmanager/product.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright 2020 google Inc.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# 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.

--- !ruby/object:Api::Product
versions:
- !ruby/object:Api::Product::Version
base_url: https://privilegedaccessmanager.googleapis.com/v1beta/
name: beta
name: PrivilegedAccessManager
display_name: Privileged Access Manager
scopes:
- https://www.googleapis.com/auth/cloud-platform
async: !ruby/object:Api::OpAsync
operation: !ruby/object:Api::OpAsync::Operation
path: name
base_url: "{{op_id}}"
wait_ms: 1000
timeouts:
result: !ruby/object:Api::OpAsync::Result
path: response
resource_inside_response: true
status: !ruby/object:Api::OpAsync::Status
path: done
complete: true
allowed:
- true
- false
error: !ruby/object:Api::OpAsync::Error
path: error
message: message
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const deletedRegexp = `^deleted:`

func validateDeletedPrincipals(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if regexp.MustCompile(deletedRegexp).MatchString(value) {
errors = append(errors, fmt.Errorf(
"Terraform does not support IAM policies for deleted principals: %s", k))
}

return
}

const entitlementIdRegexp = `^[a-z][a-z0-9-]{3,62}$`

func validateEntitlementId(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if !regexp.MustCompile(entitlementIdRegexp).MatchString(value) {
errors = append(errors, fmt.Errorf(
"Entitlement Id should be 4-63 characters, and valid characters are '[a-z]', '[0-9]', and '-'. The first character should be from [a-z]. : %s", k))
}

return
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
resource "google_privileged_access_manager_entitlement" "<%= ctx[:primary_resource_id] %>" {
provider = google-beta
entitlement_id = "<%= ctx[:vars]['entitlement_id'] %>"
location = "global"
max_request_duration = "43200s"
parent = "projects/<%= ctx[:test_env_vars]['project'] %>"
requester_justification_config {
unstructured{}
}
eligible_users {
principals = ["group:[email protected]"]
}
privileged_access{
gcp_iam_access{
role_bindings{
role = "roles/storage.admin"
condition_expression = "request.time < timestamp(\"2024-04-23T18:30:00.000Z\")"
}
resource = "//cloudresourcemanager.googleapis.com/projects/<%= ctx[:test_env_vars]['project'] %>"
resource_type = "cloudresourcemanager.googleapis.com/Project"
}
}
additional_notification_targets {
admin_email_recipients = ["[email protected]"]
requester_email_recipients = ["[email protected]"]
}
approval_workflow {
manual_approvals {
require_approver_justification = true
steps {
approvals_needed = 1
approver_email_recipients = ["[email protected]"]
approvers {
principals = ["group:[email protected]"]
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
approvalWorkflowProp, err := expandPrivilegedAccessManagerEntitlementApprovalWorkflow(d.Get("approval_workflow"), d, config)
if err != nil {
return err
} else if v, ok := d.GetOkExists("approval_workflow"); !tpgresource.IsEmptyValue(reflect.ValueOf(v)) && (ok || !reflect.DeepEqual(v, approvalWorkflowProp)) {
obj["approvalWorkflow"] = approvalWorkflowProp
}
if d.HasChange("approval_workflow") {
updateMask = append(updateMask, "approvalWorkflow")
}
url, err = transport_tpg.AddQueryParams(url, map[string]string{"updateMask": strings.Join(updateMask, ",")})
if err != nil {
return err
}
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,11 @@ var ServicesListBeta = mapOf(
"displayName" to "Privateca",
"path" to "./google-beta/services/privateca"
),
"privilegedaccessmanager" to mapOf(
"name" to "privilegedaccessmanager",
"displayName" to "Privilegedaccessmanager",
"path" to "./google-beta/services/privilegedaccessmanager"
),
"publicca" to mapOf(
"name" to "publicca",
"displayName" to "Publicca",
Expand Down
Loading

0 comments on commit b14e690

Please sign in to comment.