My previous article RBAC with Fine Grained Access - Apigee X was a quick solution at the project level to show the necessary configuration. This article provides a more robust solution using organizational groups, plus it shows how to configure using Google APIs and Terraform.
As a security stakeholder, I want to restrict access to Apigee resources so that I can assign them to separate business units or projects.
Apigee supports adding resource conditions in IAM policies to these supported resources types using conditional role bindings during role assignment. Conditions can be based on time (e.g. schedule, expiry) or a named resource (e.g. type and name starts with “bu1-”). Roles can be assigned to individual users or to a group, the later being a better practice. Assigning conditional access to a group simplifies management as once the conditions have been specified for the group, individual users are simply added to the group. This avoids updating the conditions on every user assignment.
Environments do not support conditional role assignment directly. Rather, access to an environment and its resources is granted via “Access” in the UI or via the Apigee API Environments Set IAM Policy API. Assigning a user as an “Apigee Environment Admin” to an environment (e.g. bu1-test) controls access to environment specific resources such as KVMs, Resource files, TargetServers, References, and KeyStores. It also controls access to deployments and debug sessions for the environment.
These resource types do not support “conditional” role based assignment, access to these resources can be managed via built in roles or custom roles.
Enable API
Apigee supports adding resource conditions in IAM policies on specific resource types (proxies, shared flows) used in the Apigee builtin rules through GCP IAM. Not all resource types are “conditional”, in particular Environments. Access to environments is done via “Access” in the UI or via the Apigee API Environments Set IAM Policy API. Assigning a user or group as an “Apigee Environment Admin” to an environment (e.g. bu1-test, bu2-test) controls access to environment specific resources such as KVMs, Resource files, TargetServers, References, and KeyStores.
In IAM and Admin for the organization, select Groups and create the context specific groups (e.g. bu1, bu2, bu3).
NOTE: this assumes individual users are already created in the organization.
The resulting details:
Repeat for other groups.
NOTES:
See: Service Accounts and Google Groups and Avoid using groups for granting service accounts access to resources.
This is required to allow the user to manage deployments and debug sessions in the Apigee UI. It also enables configuring Flow Hooks, for the assigned environment.
Create a custom role (e.g. Custom Role Apigee Deploy and Debug) that allows get and list permissions on deployments and environments.
apigee.deployments.get
apigee.deployments.list
apigee.entitlements.get
apigee.envgroupattachments.get
apigee.envgroupattachments.list
apigee.envgroups.get
apigee.envgroups.list
apigee.environments.get
apigee.environments.getStats
apigee.environments.list
apigee.operations.get
apigee.operations.list
apigee.projectorganizations.get
apigee.setupcontexts.get
The Apigee built in roles for Apigee API Admin and Apigee Developer Admin are required for an “API proxy developer” to create and test.
Apigee API Admin role is required to create API proxies, shared flows and related artifacts such as API Products.
Apigee Developer Admin role is required to create Developers and Apps for testing.
The custom role Custom Role Apigee Deploy and Debug avoids errors in the UI, it is assigned without conditions.
Add the following conditions to both the Apigee API Admin and Apigee Developer Admin role assignments.
NOTE: the resource.name.startsWith() condition uses the name of the Apigee X project (e.g. apigeex-exp), replace with your value.
resource.name.startsWith('organizations/apigeex-exp/apis/bu1-') ||
resource.name.startsWith('organizations/apigeex-exp/sharedflows/bu1-') ||
resource.name.startsWith('organizations/apigeex-exp/apiproducts/bu1-') ||
(resource.type == 'apigee.googleapis.com/Developer') ||
(resource.type == 'apigee.googleapis.com/DeveloperApp' && resource.name.extract('/apps/{name}').startsWith('bu1-')) ||
resource.type == 'cloudresourcemanager.googleapis.com/Project'
Steps:
Select “CONDITION EDITOR” and paste the condition from above, adjusting “bu1-” to be your resource prefix.
Repeat for the Apigee Developer Admin role.
Then repeat for other groups, changing the prefix value accordingly.
Access for users to specific environments is done in Apigee UI or via APIs on the environment.
Access to environments is required to allow management of environment specific resources such as Target Servers, KVMs and PropertySets, since these are not named resources and cannot be used in conditional role assignments.
Access to environments is also required to manage deployments for the specific environment.
Role assignment in the Apigee Management UI
TIP: Create an alias to simplify curl commands to GCP and Apigee APIs.
alias curlx='curl -s -H "Authorization: Bearer $(gcloud auth print-access-token)"'
Enable API
You’ll need to use the customer ID (e.g. customers/C03rq1yqp) which you can find in the Admin console under Account → Account Settings at: https://admin.google.com/ac/accountsettings/profile.
curlx -X POST 'https://cloudidentity.googleapis.com/v1/groups?initialGroupConfig=WITH_INITIAL_OWNER' \
--header 'X-Goog-User-Project: apigeex-exp' \
--header 'Content-Type: application/json' \
--data-raw '{
"groupKey": {
"id": "bu1@kurtkanaskie.altostrat.com"
},
"parent": "customers/C03rq1yqp",
"displayName": "Business Unit 1",
"description": "Business Unit 1 Group",
"labels": {
"cloudidentity.googleapis.com/groups.discussion_forum": ""
}
}'
# response
{
"done": true,
"response": {
"@type": "type.googleapis.com/google.apps.cloudidentity.groups.v1.Group",
"name": "groups/02ce457m220civc",
"groupKey": {
"id": "bu3@kurtkanaskie.altostrat.com"
},
"parent": "customers/C03rq1yqp",
"displayName": "Business Unit 1",
"description": "Business Unit 1 Group",
"createTime": "2022-09-13T14:12:40.425818Z",
"updateTime": "2022-09-13T14:12:40.425818Z",
"labels": {
"cloudidentity.googleapis.com/groups.discussion_forum": ""
}
}
}
NOTE: This assumes users are already onboarded in the organization.
curlx -X POST 'https://cloudidentity.googleapis.com/v1/groups/02ce457m220civc/memberships' \
--header 'X-Goog-User-Project: apigeex-exp' \
--header 'Content-Type: application/json' \
--data-raw '{
"preferredMemberKey": {
"id": "dev1.bu1@kurtkanaskie.altostrat.com"
},
"roles": [
{
"name": "MEMBER"
}
]
}'
# response
{
"done": true,
"response": {
"@type": "type.googleapis.com/google.apps.cloudidentity.groups.v1.Membership",
"name": "groups/02ce457m220civc/memberships/109834938926743824778",
"preferredMemberKey": {
"id": "dev1.bu1@kurtkanaskie.altostrat.com"
},
"roles": [
{
"name": "MEMBER"
}
]
}
}
See: Creating and managing custom roles
NOTES:
curlx -X POST https://iam.googleapis.com/v1/organization/$ORG_ID/roles \
--header 'Content-Type: application/json' \
--data-raw '{
"roleId": "CustomRoleApigeeDeployDebug",
"role": {
"title": "Custom Role Apigee Deploy and Debug",
"description": "Custom role for fine grained access with deployment and debug",
"includedPermissions": [
"apigee.deployments.get",
"apigee.deployments.list",
"apigee.entitlements.get",
"apigee.envgroupattachments.get",
"apigee.envgroupattachments.list",
"apigee.envgroups.get",
"apigee.envgroups.list",
"apigee.environments.get",
"apigee.environments.getStats",
"apigee.environments.list",
"apigee.operations.get",
"apigee.operations.list",
"apigee.projectorganizations.get"
],
"stage": "ALPHA"
}
}'
# response
{
"name": "organizations/785287799950/roles/CustomRoleApigeeDeployDebug",
"title": "Custom Role Apigee Deploy and Debug",
"description": "Custom role for fine grained access with deployment and debug",
"includedPermissions": [
"apigee.deployments.get",
"apigee.deployments.list",
"apigee.entitlements.get",
"apigee.envgroupattachments.get",
"apigee.envgroupattachments.list",
"apigee.envgroups.get",
"apigee.envgroups.list",
"apigee.environments.get",
"apigee.environments.getStats",
"apigee.environments.list",
"apigee.operations.get",
"apigee.operations.list",
"apigee.projectorganizations.get"
],
"etag": "BwXoB+k8g6E="
}
See: Manage conditional role bindings and Grant or revoke multiple roles.
NOTE: Updating the policy for users requires a complete update of all bindings.
Get the current policy and save it in a file.
curlx -X POST https://cloudresourcemanager.googleapis.com/v1/organizations/$ORG_ID:getIamPolicy --header 'Content-Type: application/json' --data-raw '{
"options": {
"requestedPolicyVersion": 3
}
}' > current_allow_policy_org.json
cat current_allow_policy_org.json
{
"version": 3,
"etag": "BwXoG4rcZMQ=",
"bindings": [
... content clipped ...
]
}
Copy the policy to a new file (e.g. update_allow_policy_org.json) and edit to add the outer “policy” element
cp current_allow_policy_org.json update_allow_policy_org.json
{
"policy":
content from current_allow_policy_org.json
}
Add in the new role assignments
curlx -X POST -H "Content-Type: application/json; charset=utf-8" -d @update_allow_policy.json "https://cloudresourcemanager.googleapis.com/v1/organizations/$ORG_ID:setIamPolicy"
cat update_allow_policy.json
{
"policy": {
"version": 3,
"etag": "BwXofUXVZlY=",
"bindings": [{
"role": "organizations/785287799950/roles/CustomRoleApigeeDeployDebug",
"members": [
"group:business-unit-1@kurtkanaskie.altostrat.com",
"group:business-unit-2@kurtkanaskie.altostrat.com"
]
},
{
"role": "roles/apigee.apiAdminV2",
"members": [
"group:business-unit-1@kurtkanaskie.altostrat.com"
],
"condition": {
"expression": "resource.name.startsWith('organizations/apigeex-exp/apis/bu1-') ||\nresource.name.startsWith('organizations/apigeex-exp/sharedflows/bu1-') ||\nresource.name.startsWith('organizations/apigeex-exp/apiproducts/bu1-') ||\n(resource.type == 'apigee.googleapis.com/Developer') ||\n(resource.type == 'apigee.googleapis.com/DeveloperApp' && resource.name.extract('/apps/{name}').startsWith('bu1-')) ||\nresource.type == 'cloudresourcemanager.googleapis.com/Project'\n",
"title": "business-unit-1"
}
},
{
"role": "roles/apigee.developerAdmin",
"members": [
"group:business-unit-1@kurtkanaskie.altostrat.com"
],
"condition": {
"expression": "resource.name.startsWith('organizations/apigeex-exp/apis/bu1-') ||\nresource.name.startsWith('organizations/apigeex-exp/sharedflows/bu1-') ||\nresource.name.startsWith('organizations/apigeex-exp/apiproducts/bu1-') ||\n(resource.type == 'apigee.googleapis.com/Developer') ||\n(resource.type == 'apigee.googleapis.com/DeveloperApp' && resource.name.extract('/apps/{name}').startsWith('bu1-')) ||\nresource.type == 'cloudresourcemanager.googleapis.com/Project'",
"title": "business-unit-1"
}
},
{
"role": "roles/apigee.apiAdminV2",
"members": [
"group:business-unit-2@kurtkanaskie.altostrat.com"
],
"condition": {
"expression": "resource.name.startsWith('organizations/apigeex-exp/apis/bu2-') ||\nresource.name.startsWith('organizations/apigeex-exp/sharedflows/bu2-') ||\nresource.name.startsWith('organizations/apigeex-exp/apiproducts/bu2-') ||\n(resource.type == 'apigee.googleapis.com/Developer') ||\n(resource.type == 'apigee.googleapis.com/DeveloperApp' && resource.name.extract('/apps/{name}').startsWith('bu2-')) ||\nresource.type == 'cloudresourcemanager.googleapis.com/Project'",
"title": "business-unit-2"
}
},
{
"role": "roles/apigee.developerAdmin",
"members": [
"group:business-unit-2@kurtkanaskie.altostrat.com"
],
"condition": {
"expression": "resource.name.startsWith('organizations/apigeex-exp/apis/bu2-') ||\nresource.name.startsWith('organizations/apigeex-exp/sharedflows/bu2-') ||\nresource.name.startsWith('organizations/apigeex-exp/apiproducts/bu2-') ||\n(resource.type == 'apigee.googleapis.com/Developer') ||\n(resource.type == 'apigee.googleapis.com/DeveloperApp' && resource.name.extract('/apps/{name}').startsWith('bu2-')) ||\nresource.type == 'cloudresourcemanager.googleapis.com/Project'",
"title": "business-unit-2"
}
}
... content clipped ...
]
}
This is a two step process:
Get and set policy for bu1-test environment.
Get the current policy
curlx 'https://apigee.googleapis.com/v1/organizations/apigeex-exp/environments/bu1-test:getIamPolicy?options.requestedPolicyVersion=3'
# response
{
"etag": "ACAB"
}
Update policy for group business-unit-1
curlx -X POST 'https://apigee.googleapis.com/v1/organizations/apigeex-exp/environments/bu1-test:setIamPolicy' \
--header 'Content-Type: application/json' \
--data-raw '{
"policy": {
"bindings": [
{
"role": "roles/apigee.environmentAdmin",
"members": [
"group:business-unit-1@kurtkanaskie.altostrat.com"
]
}
],
"etag": "ACAB",
"version": 1
}
}'
# response
{
"version": 1,
"etag": "BwXoflPFvoc=",
"bindings": [
{
"role": "roles/apigee.environmentAdmin",
"members": [
"group:business-unit-1@kurtkanaskie.altostrat.com"
]
}
]
}
Get and set policy for bu2-test environment.
Get the current policy
curlx 'https://apigee.googleapis.com/v1/organizations/apigeex-exp/environments/bu2-test:getIamPolicy?options.requestedPolicyVersion=3'
# response
{
"etag": "ACAB"
}
Update policy for group business-unit-2
curlx -X POST 'https://apigee.googleapis.com/v1/organizations/apigeex-exp/environments/bu2-test:setIamPolicy' \
--header 'Content-Type: application/json' \
--data-raw '{
"policy": {
"bindings": [
{
"role": "roles/apigee.environmentAdmin",
"members": [
"group:business-unit-2@kurtkanaskie.altostrat.com"
]
}
],
"etag": "ACAB",
"version": 1
}
}'
# response
{
"version": 1,
"etag": "BwXoflPFvoc=",
"bindings": [
{
"role": "roles/apigee.environmentAdmin",
"members": [
"group:business-unit-2@kurtkanaskie.altostrat.com"
]
}
]
}
Overview
NOTE: Expects users in organization and Service Accounts in project to exist.
terraform {
required_providers {
google = {
source = "hashicorp/google"
}
}
}
variable "domain" {
type = string
default = "kurtkanaskie.altostrat.com"
}
variable "customer_id" {
type = string
default = "C03rq1yqp"
}
variable "org_id" {
type = string
default = "785287799950"
}
variable "project_id" {
type = string
default = "apigeex-exp"
}
provider "google" {
user_project_override = true
# How do I set permissions at org level? Organization Admin role - nope
# credentials = file("sa-apigeex-exp-terraform.json")
billing_project = var.project_id
project = var.project_id
}
#######################################################################
### Custom Apigee X Deploy and Debug Role
#######################################################################
resource "google_organization_iam_custom_role" "CustomRoleApigeeXDeployDebug" {
org_id = var.org_id
role_id = "CustomRoleApigeeXDeployDebug"
title = "Custom Role Apigee X Deploy and Debug"
description = "Custom role for Apigee X fine grained access with deployment and debug"
permissions = [
"apigee.deployments.get",
"apigee.deployments.list",
"apigee.entitlements.get",
"apigee.envgroupattachments.get",
"apigee.envgroupattachments.list",
"apigee.envgroups.get",
"apigee.envgroups.list",
"apigee.environments.get",
"apigee.environments.getStats",
"apigee.environments.list",
"apigee.operations.get",
"apigee.operations.list",
"apigee.projectorganizations.get"
]
}
#######################################################################
### BU 1
### Groups and memberships BU 1
#######################################################################
resource "google_cloud_identity_group" "business_unit_1" {
display_name = "Business Unit One"
description = "Business Unit One Group"
initial_group_config = "WITH_INITIAL_OWNER"
parent = "customers/${var.customer_id}"
group_key {
id = "business-unit-1@${var.domain}"
}
labels = {
"cloudidentity.googleapis.com/groups.discussion_forum" = ""
}
}
resource "google_cloud_identity_group_membership" "business_unit_1_membership_1" {
group = google_cloud_identity_group.business_unit_1.id
preferred_member_key {
id = "dev1.bu1@${var.domain}"
}
roles {
name = "MEMBER"
}
}
resource "google_cloud_identity_group_membership" "business_unit_1_membership_2" {
group = google_cloud_identity_group.business_unit_1.id
preferred_member_key {
id = "dev2.bu1@${var.domain}"
}
roles {
name = "MEMBER"
}
}
resource "google_cloud_identity_group_membership" "business_unit_1_membership_3" {
group = google_cloud_identity_group.business_unit_1.id
preferred_member_key {
id = "cicd-test-bu1@${var.project_id}.iam.gserviceaccount.com"
}
roles {
name = "MEMBER"
}
}
#######################################################################
### Role assignment to groups with conditions BU 1
#######################################################################
resource "google_organization_iam_member" "business_unit_1_custom" {
org_id = var.org_id
role = "organizations/${var.org_id}/roles/CustomRoleApigeeXDeployDebug"
member = "group:business-unit-1@${var.domain}"
}
resource "google_organization_iam_member" "business_unit_1_api_admin" {
org_id = var.org_id
role = "roles/apigee.apiAdminV2"
member = "group:business-unit-1@${var.domain}"
condition {
title = "business-unit-1"
description = "Conditions for business unit to edit bu1- prefixed proxies, shared flows and test"
expression = "resource.name.startsWith('organizations/apigeex-exp/apis/bu1-') ||\nresource.name.startsWith('organizations/apigeex-exp/sharedflows/bu1-') ||\nresource.name.startsWith('organizations/apigeex-exp/apiproducts/bu1-') ||\n(resource.type == 'apigee.googleapis.com/Developer') ||\n(resource.type == 'apigee.googleapis.com/DeveloperApp' && resource.name.extract('/apps/{name}').startsWith('bu1-')) ||\nresource.type == 'cloudresourcemanager.googleapis.com/Project'"
}
}
resource "google_organization_iam_member" "business_unit_1_developer_admin" {
org_id = var.org_id
role = "roles/apigee.developerAdmin"
member = "group:business-unit-1@${var.domain}"
condition {
title = "business-unit-1"
description = "Conditions for business unit to edit bu1- prefixed proxies, shared flows and test"
expression = "resource.name.startsWith('organizations/apigeex-exp/apis/bu1-') ||\nresource.name.startsWith('organizations/apigeex-exp/sharedflows/bu1-') ||\nresource.name.startsWith('organizations/apigeex-exp/apiproducts/bu1-') ||\n(resource.type == 'apigee.googleapis.com/Developer') ||\n(resource.type == 'apigee.googleapis.com/DeveloperApp' && resource.name.extract('/apps/{name}').startsWith('bu1-')) ||\nresource.type == 'cloudresourcemanager.googleapis.com/Project'"
}
}
#######################################################################
### Environment Access for BU 1
#######################################################################
resource "google_apigee_environment_iam_member" "member-bu1-test" {
org_id = "organizations/${var.project_id}"
env_id = "bu1-test"
role = "roles/apigee.environmentAdmin"
member = "group:business-unit-1@${var.domain}"
}
#######################################################################
### BU 2
### Groups and memberships BU 2
#######################################################################
resource "google_cloud_identity_group" "business_unit_2" {
display_name = "Business Unit Two"
description = "Business Unit Two Group"
initial_group_config = "WITH_INITIAL_OWNER"
parent = "customers/${var.customer_id}"
group_key {
id = "business-unit-2@${var.domain}"
}
labels = {
"cloudidentity.googleapis.com/groups.discussion_forum" = ""
}
}
resource "google_cloud_identity_group_membership" "business_unit_2_membership_1" {
group = google_cloud_identity_group.business_unit_2.id
preferred_member_key {
id = "dev1.bu2@${var.domain}"
}
roles {
name = "MEMBER"
}
}
resource "google_cloud_identity_group_membership" "business_unit_2_membership_2" {
group = google_cloud_identity_group.business_unit_2.id
preferred_member_key {
id = "dev2.bu2@${var.domain}"
}
roles {
name = "MEMBER"
}
}
resource "google_cloud_identity_group_membership" "business_unit_2_membership_3" {
group = google_cloud_identity_group.business_unit_2.id
preferred_member_key {
id = "cicd-test-bu2@${var.project_id}.iam.gserviceaccount.com"
}
roles {
name = "MEMBER"
}
}
#######################################################################
### Role assignment to groups with conditions BU 2
#######################################################################
resource "google_organization_iam_member" "business_unit_2_custom" {
org_id = var.org_id
role = "organizations/${var.org_id}/roles/CustomRoleApigeeXDeployDebug"
member = "group:business-unit-2@${var.domain}"
}
resource "google_organization_iam_member" "business_unit_2_api_admin" {
org_id = var.org_id
role = "roles/apigee.apiAdminV2"
member = "group:business-unit-2@${var.domain}"
condition {
title = "business-unit-2"
description = "Conditions for business unit to edit bu2- prefixed proxies, shared flows and test"
expression = "resource.name.startsWith('organizations/apigeex-exp/apis/bu2-') ||\nresource.name.startsWith('organizations/apigeex-exp/sharedflows/bu2-') ||\nresource.name.startsWith('organizations/apigeex-exp/apiproducts/bu2-') ||\n(resource.type == 'apigee.googleapis.com/Developer') ||\n(resource.type == 'apigee.googleapis.com/DeveloperApp' && resource.name.extract('/apps/{name}').startsWith('bu2-')) ||\nresource.type == 'cloudresourcemanager.googleapis.com/Project'"
}
}
resource "google_organization_iam_member" "business_unit_2_developer_admin" {
org_id = var.org_id
role = "roles/apigee.developerAdmin"
member = "group:business-unit-2@${var.domain}"
condition {
title = "business-unit-2"
description = "Conditions for business unit to edit bu2- prefixed proxies, shared flows and test"
expression = "resource.name.startsWith('organizations/apigeex-exp/apis/bu2-') ||\nresource.name.startsWith('organizations/apigeex-exp/sharedflows/bu2-') ||\nresource.name.startsWith('organizations/apigeex-exp/apiproducts/bu2-') ||\n(resource.type == 'apigee.googleapis.com/Developer') ||\n(resource.type == 'apigee.googleapis.com/DeveloperApp' && resource.name.extract('/apps/{name}').startsWith('bu2-')) ||\nresource.type == 'cloudresourcemanager.googleapis.com/Project'"
}
}
#######################################################################
### Environment Access for BU 2
#######################################################################
resource "google_apigee_environment_iam_member" "member-bu2-test" {
org_id = "organizations/${var.project_id}"
env_id = "bu2-test"
role = "roles/apigee.environmentAdmin"
member = "group:business-unit-2@${var.domain}"
}
Users and service accounts created for role assignment:
dev1-bu1@kurtkanaskie.altostrat.com
dev2-bu1@kurtkanaskie.altostrat.com
dev1-bu2@kurtkanaskie.altostrat.com
dev2-bu2@kurtkanaskie.altostrat.com
cicd-test-bu1@apigeex-exp.iam.gserviceaccount.com
cicd-test-bu2@apigeex-exp.iam.gserviceaccount.com
Set up gcloud configuration (optional)
gcloud config configurations create $YOUR_PROJECT
gcloud config set project $YOUR_PROJECT
gcloud config set account dev1.bu1@kurtkanaskie.altostrat.com
Activate a Service Account
gcloud config set account cicd-test-bu1@apigeex-exp.iam.gserviceaccount.com
Sample API calls
# KVMs, Target Servers, Key Stores, References and Properties (OK)
200 - curlx https://apigee.googleapis.com/v1/organizations/apigeex-exp/environments/bu1-test/targetservers
403 - curlx https://apigee.googleapis.com/v1/organizations/apigeex-exp/environments/bu2-test/targetservers
# Environments and deployments (OK)
200 - curlx https://apigee.googleapis.com/v1/organizations/apigeex-exp/deployments
200 - curlx https://apigee.googleapis.com/v1/organizations/apigeex-exp/environments/bu1-test/deployments
200 - curlx https://apigee.googleapis.com/v1/organizations/apigeex-exp/environments/bu2-test/deployments (expected)
# Conditional proxies, revisions, deployments (OK)
200 - curlx https://apigee.googleapis.com/v1/organizations/apigeex-exp/apis/bu1-proxy-1/revisions
200 - curlx https://apigee.googleapis.com/v1/organizations/apigeex-exp/apis/bu1-proxy-1/deployments
403 - curlx https://apigee.googleapis.com/v1/organizations/apigeex-exp/apis/bu2-proxy-1/revisions (expected)
403 - curlx https://apigee.googleapis.com/v1/organizations/apigeex-exp/apis/bu2-proxy-1/deployments (expected)
200 - curlx https://apigee.googleapis.com/v1/organizations/apigeex-exp/apis/bu1-proxy-1/revisions/7
200 - curlx -X DELETE https://apigee.googleapis.com/v1/organizations/apigeex-exp/apis/bu1-proxy-1/revisions/7
200 - curlx https://apigee.googleapis.com/v1/organizations/apigeex-exp/apis/bu1-proxy-1/revisions/7/deployments
200 - curlx https://apigee.googleapis.com/v1/organizations/apigeex-exp/environments/bu1-test/apis/bu1-proxy-1/revisions/7/deployments
200 - curlx -X DELETE https://apigee.googleapis.com/v1/organizations/apigeex-exp/environments/bu1-test/apis/bu1-proxy-1/revisions/7/deployments
200 - curlx -X POST https://apigee.googleapis.com/v1/organizations/apigeex-exp/environments/bu1-test/apis/bu1-proxy-1/revisions/6/deployments
GCP IAM and Roles
Apigee Users and Roles
Terraform
I just realized that using this prevented me from seeing Environments and Environment Groups.
I was able to "fix" this by changing the custom role to be:
apigee.deployments.get
apigee.deployments.list
apigee.envgroupattachments.get
apigee.envgroupattachments.list
apigee.envgroups.get
apigee.envgroups.list
apigee.environments.get
apigee.environments.list
apigee.operations.get
apigee.operations.list
Public Preview of Apigee UI in the Cloud Console stating Apigee requires setup
If you have seen the Public preview release of the Apigee UI in the Google Cloud console and are using a custom role as described above, you may have seen the Console ask for Apigee to be set up again. This behavior is the result of a new permission added as part of the Preview which may be missing from the custom roles being used.
To correct this behavior, simply add apigee.projectorganizations.get to the custom role Kurt describes above.
Thanks Paul,
I've updated the article to reflect the permissions for the custom role when using the Apigee UI in the Cloud Console. Here are the permissions:
apigee.deployments.get
apigee.deployments.list
apigee.entitlements.get
apigee.envgroupattachments.get
apigee.envgroupattachments.list
apigee.envgroups.get
apigee.envgroups.list
apigee.environments.get
apigee.environments.getStats
apigee.environments.list
apigee.operations.get
apigee.operations.list
apigee.projectorganizations.get
thank you for this article, it's great!
when filtering on the resources (e.g.
resource.name.startsWith('organizations/apigeex-exp/apis/bu1-')
is it possible to filter on other fields, such as the creator of the proxy or the description?
@ghagemans wrote:
is it possible to filter on other fields, such as the creator of the proxy or the description?
Generally you can using the pattern and extract function:
(resource.type == 'apigee.googleapis.com/DeveloperApp' && resource.name.extract('/apps/{name}').startsWith('bu1-'))
However, there is limited metadata on the API itself, e.g.
curlx https://apigee.googleapis.com/v1/organizations/$ORG/apis/catch-all-v1
{
"metaData": {
"createdAt": "1619614207213",
"lastModifiedAt": "1628261685569",
"subType": "Proxy"
},
"name": "catch-all-v1",
"revision": [
"1",
"2",
"3",
"4"
],
"apiProxyType": "PROGRAMMABLE"
}
Kurt, can you confirm... I believe any values evaluated with the .startsWith() function need to be on the management API's URL path. An Entity's metadata cannot be used.
Hey Paul,
The use of extracts function work for Apps, because they use a UUID in the path, so extracting the name and using starts with is valid. For example my "roletester" cannot edit/save the App with name bu2-app-1. This works in the Classic UI, but sadly, not in the new UI.
Thanks you so much for the reply!
I've posted a question about something relationated with this topic, is this one: https://www.googlecloudcommunity.com/gc/Apigee/Apigee-hide-resources-using-IAM-Condition-Policies/td...
Can you answer about how to hide or block environments in Apigee for some group of people using IAM policies and IAM conditions?
I need this option because we are sharing the same Apigee project and for security aspect we must hide some options for some group of people.
Thanks.
Hi @igallardo
An Environment is not a conditional resource type as per docs: https://cloud.google.com/apigee/docs/api-platform/system-administration/add-iam-conditions#adding-re...
Access to environments is done via “Access” in the UI or via the Apigee API Environments Set IAM Policy API. Assigning a user or group as an “Apigee Environment Admin” to an environment (e.g. bu1-test, bu2-test) controls access to environment specific resources such as KVMs, Resource files, TargetServers, References, KeyStores and Deployments.
For example, in the environment "bu1-test":
This is detailed in the article.
And these custom roles again can it tied back to our (i.e. company) specific IAM/AD's ?
Thanks!
Salla ( Pradeep Salla)
Another question, can the data that goes into UI using CEL be exposed as an API like management API for SRE/admin's to automate ?
Thanks!
Salla ( Pradeep Salla)
Thanks for this great article @kurtkanaskie ! Very helpful!
I'm having an issue with Apps. I'm not able to see the Apps started by "bu1-".
I'm getting the following error:
Error messagePermission 'apigee.apps.get' denied on resource 'organizations/<project_id>/apps/cedce655-ac5f-4c17-9b47-22b7ba14b079' (or it may not exist)
Can you help me here?
Regarding "I'm having an issue with Apps. I'm not able to see the Apps started by "bu1-"."
Apps can only be restricted by name within the context of:
GET /v1/organizations/$ORG/developers/$DEV_EMAIL/apps/$APP_NAME
That's because apps as the org level use UUIDs:
GET /v1/organizations/$ORG/apps/$UUID
Bottom line, you will have limited ability to restrict access to Apps. There are internal tickets for improvements in this area.
Hello I have implemented this solution and when a team member in a group deploys a proxy they get a popup. They can select "The continue to deploy" to deploy the proxy yet I would like to understand why this popup is getting displayed. For one team I did grant the
Hi @BillFleming
Thanks for that feedback, apparently there's another check now in the UI to see if an environment is attached to an instance. I take it you do have the environment attached to a runtime.
I'll look into it.
Hi @BillFleming
I was able to reproduce and fix the situation.
There is indeed another check now in the UI to see if an environment is attached to an instance when you deploy. However, you will see this error in a legitimate situation when you do not have an environment attached to a runtime.
The solution is to update the "Custom Role Apigee Deploy and Debug" to include 2 additional permissions:
apigee.instanceattachments.list
apigee.instances.list
The complete custom role then looks like:
apigee.deployments.get
apigee.deployments.list
apigee.entitlements.get
apigee.envgroupattachments.get
apigee.envgroupattachments.list
apigee.envgroups.get
apigee.envgroups.list
apigee.environments.get
apigee.environments.getStats
apigee.environments.list
apigee.instanceattachments.list
apigee.instances.list
apigee.operations.get
apigee.operations.list
apigee.projectorganizations.get
Give it a try, it worked for me.
Hi @kurtkanaskie ,
Thank you very much for your help
Hi @kurtkanaskie ,
I have applied the changes and it worked! We are now longer getting the messages in the Environments or when deploying the proxy.
Appreciate your help on providing a solution!
Bill