Enable creation of organization- and folder-level service agents (#3877)
* Enable creation of organization- and folder-level service agents * formatting * Add folder test * Add org tests * linting * more linting * Fix tests
This commit is contained in:
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@@ -7,6 +7,7 @@
|
||||
"url": "http://json-schema.org/draft-07/schema#"
|
||||
}
|
||||
],
|
||||
"files.insertFinalNewline": true,
|
||||
"yaml.schemas": {
|
||||
"modules/organization/schemas/custom-role.schema.json": [
|
||||
"data/**/custom-roles/**/*yaml"
|
||||
|
||||
@@ -54,4 +54,4 @@ module "recipe_apigee_swp" {
|
||||
subnet_proxy_only_ip_cidr_range = "10.16.2.0/24"
|
||||
}
|
||||
}
|
||||
# tftest modules=10 resources=43
|
||||
# tftest modules=10 resources=44
|
||||
|
||||
@@ -9,6 +9,7 @@ This module allows the creation and management of folders, including support for
|
||||
- [Assured Workload Folder](#assured-workload-folder)
|
||||
- [Privileged Access Manager (PAM) Entitlements](#privileged-access-manager-pam-entitlements)
|
||||
- [Privileged Access Manager (PAM) Entitlements Factory](#privileged-access-manager-pam-entitlements-factory)
|
||||
- [Service Agents](#service-agents)
|
||||
- [Organization policies](#organization-policies)
|
||||
- [Organization Policy Factory](#organization-policy-factory)
|
||||
- [Hierarchical Firewall Policy Attachments](#hierarchical-firewall-policy-attachments)
|
||||
@@ -135,7 +136,7 @@ module "folder" {
|
||||
|
||||
Note that using PAM entitlements requires specific roles to be granted to the users and groups that will be using them. For more information, see the [official documentation](https://cloud.google.com/iam/docs/pam-permissions-and-setup#before-you-begin).
|
||||
|
||||
Additionally, the Privileged Access Manager Service Agent must be created and granted the `roles/privilegedaccessmanager.folderServiceAgent` role. The service agent is not created automatically, and you can find the `gcloud` command to create it in the `service_agents` output of this module. For more information on service agents, see the [official documentation](https://cloud.google.com/iam/docs/service-agents). Refer to the [organization module's documentation](../organization/README.md#privileged-access-manager-pam-entitlements) for an example on how to grant the required role.
|
||||
Additionally, the Privileged Access Manager Service Agent must be created and granted the `roles/privilegedaccessmanager.folderServiceAgent` role. The service agent can be created automatically by adding `privilegedaccessmanager.googleapis.com` to the `services` list in the `service_agents_config` variable. Refer to the [organization module's documentation](../organization/README.md#privileged-access-manager-pam-entitlements) for an example on how to grant the required role.
|
||||
|
||||
```hcl
|
||||
module "folder" {
|
||||
@@ -179,6 +180,26 @@ module "folder" {
|
||||
}
|
||||
```
|
||||
|
||||
## Service Agents
|
||||
|
||||
The module allows managing service agents at the folder level. Service agent creation is triggered by adding them to the `service_agents_config.services` variable.
|
||||
|
||||
```hcl
|
||||
module "folder" {
|
||||
source = "./fabric/modules/folder"
|
||||
parent = var.folder_id
|
||||
name = "Folder name"
|
||||
service_agents_config = {
|
||||
services = [
|
||||
"osconfig.googleapis.com",
|
||||
"privilegedaccessmanager.googleapis.com",
|
||||
"progressiverollout.googleapis.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
# tftest inventory=agents.yaml
|
||||
```
|
||||
|
||||
## Organization policies
|
||||
|
||||
To manage organization policies, the `orgpolicy.googleapis.com` service should be enabled in the quota project.
|
||||
@@ -720,7 +741,7 @@ module "folder" {
|
||||
| [pam.tf](./pam.tf) | None | <code>google_privileged_access_manager_entitlement</code> |
|
||||
| [scc-mute-configs.tf](./scc-mute-configs.tf) | Folder-level SCC mute configurations. | <code>google_scc_v2_folder_mute_config</code> |
|
||||
| [scc-sha-custom-modules.tf](./scc-sha-custom-modules.tf) | Folder-level Custom modules with Security Health Analytics. | <code>google_scc_management_folder_security_health_analytics_custom_module</code> |
|
||||
| [service-agents.tf](./service-agents.tf) | Service agents supporting resources. | |
|
||||
| [service-agents.tf](./service-agents.tf) | Service agents supporting resources. | <code>google_folder_service_identity</code> |
|
||||
| [tags.tf](./tags.tf) | None | <code>google_tags_tag_binding</code> |
|
||||
| [variables-iam.tf](./variables-iam.tf) | None | |
|
||||
| [variables-logging.tf](./variables-logging.tf) | None | |
|
||||
@@ -760,7 +781,8 @@ module "folder" {
|
||||
| [parent](variables.tf#L272) | Parent in folders/folder_id or organizations/org_id format. | <code>string</code> | | <code>null</code> |
|
||||
| [scc_mute_configs](variables-scc.tf#L17) | SCC mute configurations keyed by name. | <code>map(object({…}))</code> | | <code>{}</code> |
|
||||
| [scc_sha_custom_modules](variables-scc.tf#L27) | SCC custom modules keyed by module name. | <code>map(object({…}))</code> | | <code>{}</code> |
|
||||
| [tag_bindings](variables.tf#L286) | Tag bindings for this folder, in key => tag value id format. | <code>map(string)</code> | | <code>null</code> |
|
||||
| [service_agents_config](variables.tf#L286) | Service agents configuration. | <code>object({…})</code> | | <code>{}</code> |
|
||||
| [tag_bindings](variables.tf#L296) | Tag bindings for this folder, in key => tag value id format. | <code>map(string)</code> | | <code>null</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
@@ -774,5 +796,5 @@ module "folder" {
|
||||
| [organization_policies_ids](outputs.tf#L54) | Map of ORGANIZATION_POLICIES => ID in the folder. | |
|
||||
| [scc_custom_sha_modules_ids](outputs.tf#L59) | Map of SCC CUSTOM SHA MODULES => ID in the folder. | |
|
||||
| [service_agents](outputs.tf#L64) | Identities of all folder-level service agents. | |
|
||||
| [sink_writer_identities](outputs.tf#L69) | Writer identities created for each sink. | |
|
||||
| [sink_writer_identities](outputs.tf#L72) | Writer identities created for each sink. | |
|
||||
<!-- END TFDOC -->
|
||||
|
||||
@@ -64,6 +64,9 @@ output "scc_custom_sha_modules_ids" {
|
||||
output "service_agents" {
|
||||
description = "Identities of all folder-level service agents."
|
||||
value = local.service_agents
|
||||
depends_on = [
|
||||
google_folder_service_identity.default
|
||||
]
|
||||
}
|
||||
|
||||
output "sink_writer_identities" {
|
||||
|
||||
@@ -18,22 +18,21 @@
|
||||
|
||||
locals {
|
||||
_sa_raw = yamldecode(file("${path.module}/service-agents.yaml"))
|
||||
_sa0 = {
|
||||
service_agents = {
|
||||
for agent in local._sa_raw :
|
||||
agent.name => {
|
||||
create_command = (
|
||||
"gcloud beta services identity create --service=${agent.api} --folder=${local.folder_number}"
|
||||
)
|
||||
name = agent.name
|
||||
api = agent.api
|
||||
display_name = agent.display_name
|
||||
email = templatestring(agent.identity, {
|
||||
folder_number = local.folder_number
|
||||
})
|
||||
}
|
||||
}
|
||||
service_agents = {
|
||||
for k, v in local._sa0 :
|
||||
k => merge(v, {
|
||||
iam_email = "serviceAccount:${v.email}"
|
||||
})
|
||||
email = templatestring(agent.identity, { folder_number = local.folder_number })
|
||||
iam_email = "serviceAccount:${templatestring(agent.identity, { folder_number = local.folder_number })}"
|
||||
} if contains(var.service_agents_config.services, agent.api)
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_folder_service_identity" "default" {
|
||||
provider = google-beta
|
||||
for_each = var.service_agents_config.create_agents ? local.service_agents : {}
|
||||
folder = local.folder_number
|
||||
service = each.value.api
|
||||
}
|
||||
|
||||
@@ -16,70 +16,40 @@
|
||||
display_name: Access Approval Service Agent
|
||||
api: accessapproval.googleapis.com
|
||||
identity: service-f${folder_number}@gcp-sa-accessapproval.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
- name: assuredworkloads
|
||||
display_name: Assured Workloads Service Agent
|
||||
api: assuredworkloads.googleapis.com
|
||||
identity: service-folder-${folder_number}@gcp-sa-assuredworkloads.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
- name: audit-manager
|
||||
display_name: Audit Manager Service Agent
|
||||
api: auditmanager.googleapis.com
|
||||
identity: service-folder-${folder_number}@gcp-sa-audit-manager.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
- name: cloudcontrolspartner
|
||||
display_name: Cloud Controls Partner Service Agent
|
||||
api: cloudcontrolspartner.googleapis.com
|
||||
identity: service-folder-${folder_number}@gcp-sa-cloudcontrolspartner.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
- name: logging
|
||||
display_name: Cloud Logging Service Agent
|
||||
api: logging.googleapis.com
|
||||
identity: service-folder-${folder_number}@gcp-sa-logging.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
- name: observability
|
||||
display_name: Cloud Observability Service Account
|
||||
api: observability.googleapis.com
|
||||
identity: service-folder-${folder_number}@gcp-sa-observability.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
- name: osconfig-rollout
|
||||
display_name: Google Cloud OS Config Rollout Service Agent
|
||||
api: osconfig.googleapis.com
|
||||
identity: service-folder-${folder_number}@gcp-sa-osconfig-rollout.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
- name: osconfig
|
||||
display_name: Google Cloud OS Config Service Agent
|
||||
api: osconfig.googleapis.com
|
||||
identity: service-folder-${folder_number}@gcp-sa-osconfig.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
- name: pam
|
||||
display_name: Privileged Access Manager Service Agent
|
||||
api: privilegedaccessmanager.googleapis.com
|
||||
identity: service-folder-${folder_number}@gcp-sa-pam.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
- name: progrollout
|
||||
display_name: Progressive Rollout Service Agent
|
||||
api: progressiverollout.googleapis.com
|
||||
identity: service-folder-${folder_number}@gcp-sa-progrollout.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
|
||||
|
||||
@@ -283,6 +283,16 @@ variable "parent" {
|
||||
}
|
||||
}
|
||||
|
||||
variable "service_agents_config" {
|
||||
description = "Service agents configuration."
|
||||
type = object({
|
||||
services = optional(list(string), [])
|
||||
create_agents = optional(bool, true)
|
||||
})
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
variable "tag_bindings" {
|
||||
description = "Tag bindings for this folder, in key => tag value id format."
|
||||
type = map(string)
|
||||
|
||||
@@ -18,6 +18,7 @@ To manage organization policies, the `orgpolicy.googleapis.com` service should b
|
||||
- [Example](#example)
|
||||
- [IAM](#iam)
|
||||
- [Conditional IAM by Principals](#conditional-iam-by-principals)
|
||||
- [Service Agents](#service-agents)
|
||||
- [Organization Policies](#organization-policies)
|
||||
- [Organization Policy Factory](#organization-policy-factory)
|
||||
- [Organization Policy Custom Constraints](#organization-policy-custom-constraints)
|
||||
@@ -166,6 +167,25 @@ module "org" {
|
||||
# tftest modules=1 resources=2 inventory=iam-bpc.yaml
|
||||
```
|
||||
|
||||
## Service Agents
|
||||
|
||||
The module allows managing service agents at the organization level. Service agent creation is triggered by adding them to the `service_agents_config.services` variable.
|
||||
|
||||
```hcl
|
||||
module "org" {
|
||||
source = "./fabric/modules/organization"
|
||||
organization_id = var.organization_id
|
||||
service_agents_config = {
|
||||
services = [
|
||||
"osconfig.googleapis.com",
|
||||
"privilegedaccessmanager.googleapis.com",
|
||||
"progressiverollout.googleapis.com"
|
||||
]
|
||||
}
|
||||
}
|
||||
# tftest inventory=agents.yaml
|
||||
```
|
||||
|
||||
## Organization Policies
|
||||
|
||||
### Organization Policy Factory
|
||||
@@ -269,19 +289,22 @@ custom.dataprocNoMoreThan10Workers:
|
||||
|
||||
Note that using PAM entitlements requires specific roles to be granted to the users and groups that will be using them. For more information, see the [official documentation](https://cloud.google.com/iam/docs/pam-permissions-and-setup#before-you-begin).
|
||||
|
||||
Additionally, the Privileged Access Manager Service Agent must be created and granted the `roles/privilegedaccessmanager.organizationServiceAgent` role. The service agent is not created automatically, and you can find the `gcloud` command to create it in the `service_agents` output of this module. For more information on service agents, see the [official documentation](https://cloud.google.com/iam/docs/service-agents).
|
||||
Additionally, the Privileged Access Manager Service Agent must be created and granted the `roles/privilegedaccessmanager.organizationServiceAgent` role. The service agent can be created automatically by adding `privilegedaccessmanager.googleapis.com` to the `services` list in the `service_agents_config` variable.
|
||||
|
||||
The following example shows how to grant the required role to the PAM service agent:
|
||||
The following example shows how to create the service agent and grant the required role:
|
||||
|
||||
```hcl
|
||||
module "organization" {
|
||||
source = "./fabric/modules/organization"
|
||||
organization_id = var.org_id
|
||||
organization_id = var.organization_id
|
||||
factories_config = {
|
||||
pam_entitlements = "factory/"
|
||||
}
|
||||
service_agents_config = {
|
||||
services = ["privilegedaccessmanager.googleapis.com"]
|
||||
}
|
||||
iam = {
|
||||
"roles/privilegedaccessmanager.serviceAgent" = [
|
||||
"roles/privilegedaccessmanager.organizationServiceAgent" = [
|
||||
module.organization.service_agents.pam.iam_email
|
||||
]
|
||||
}
|
||||
@@ -991,7 +1014,7 @@ module "org" {
|
||||
| [pam.tf](./pam.tf) | None | <code>google_privileged_access_manager_entitlement</code> |
|
||||
| [scc-mute-configs.tf](./scc-mute-configs.tf) | Organization-level SCC mute configurations. | <code>google_scc_v2_organization_mute_config</code> |
|
||||
| [scc-sha-custom-modules.tf](./scc-sha-custom-modules.tf) | Organization-level Custom modules with Security Health Analytics. | <code>google_scc_management_organization_security_health_analytics_custom_module</code> |
|
||||
| [service-agents.tf](./service-agents.tf) | Service agents supporting resources. | |
|
||||
| [service-agents.tf](./service-agents.tf) | Service agents supporting resources. | <code>google_organization_service_identity</code> |
|
||||
| [tags.tf](./tags.tf) | Manages GCP Secure Tags, keys, values, and IAM. | <code>google_tags_tag_binding</code> · <code>google_tags_tag_key</code> · <code>google_tags_tag_key_iam_binding</code> · <code>google_tags_tag_key_iam_member</code> · <code>google_tags_tag_value</code> · <code>google_tags_tag_value_iam_binding</code> · <code>google_tags_tag_value_iam_member</code> |
|
||||
| [variables-iam.tf](./variables-iam.tf) | None | |
|
||||
| [variables-identity-providers.tf](./variables-identity-providers.tf) | None | |
|
||||
@@ -1030,6 +1053,7 @@ module "org" {
|
||||
| [pam_entitlements](variables-pam.tf#L17) | Privileged Access Manager entitlements for this resource, keyed by entitlement ID. | <code>map(object({…}))</code> | | <code>{}</code> |
|
||||
| [scc_mute_configs](variables-scc.tf#L17) | SCC mute configurations keyed by name. | <code>map(object({…}))</code> | | <code>{}</code> |
|
||||
| [scc_sha_custom_modules](variables-scc.tf#L28) | SCC custom modules keyed by module name. | <code>map(object({…}))</code> | | <code>{}</code> |
|
||||
| [service_agents_config](variables.tf#L182) | Service agents configuration. | <code>object({…})</code> | | <code>{}</code> |
|
||||
| [tag_bindings](variables-tags.tf#L89) | Tag bindings for this organization, in key => tag value id format. | <code>map(string)</code> | | <code>{}</code> |
|
||||
| [tags](variables-tags.tf#L96) | Tags by key name. If `id` is provided, key or value creation is skipped. The `iam` attribute behaves like the similarly named one at module level. | <code>map(object({…}))</code> | | <code>{}</code> |
|
||||
| [tags_config](variables-tags.tf#L161) | Fine-grained control on tag resource and IAM creation. | <code>object({…})</code> | | <code>{}</code> |
|
||||
@@ -1053,10 +1077,10 @@ module "org" {
|
||||
| [scc_custom_sha_modules_ids](outputs.tf#L118) | Map of SCC CUSTOM SHA MODULES => ID in the organization. | |
|
||||
| [scc_mute_configs](outputs.tf#L123) | SCC mute configurations. | |
|
||||
| [service_agents](outputs.tf#L128) | Identities of all organization-level service agents. | |
|
||||
| [sink_writer_identities](outputs.tf#L133) | Writer identities created for each sink. | |
|
||||
| [tag_keys](outputs.tf#L141) | Tag key resources. | |
|
||||
| [tag_values](outputs.tf#L150) | Tag value resources. | |
|
||||
| [workforce_identity_pool_ids](outputs.tf#L158) | Workforce identity pool ids. | |
|
||||
| [workforce_identity_provider_names](outputs.tf#L165) | Workforce Identity provider names. | |
|
||||
| [workforce_identity_providers](outputs.tf#L172) | Workforce Identity provider attributes. | |
|
||||
| [sink_writer_identities](outputs.tf#L136) | Writer identities created for each sink. | |
|
||||
| [tag_keys](outputs.tf#L144) | Tag key resources. | |
|
||||
| [tag_values](outputs.tf#L153) | Tag value resources. | |
|
||||
| [workforce_identity_pool_ids](outputs.tf#L161) | Workforce identity pool ids. | |
|
||||
| [workforce_identity_provider_names](outputs.tf#L168) | Workforce Identity provider names. | |
|
||||
| [workforce_identity_providers](outputs.tf#L175) | Workforce Identity provider attributes. | |
|
||||
<!-- END TFDOC -->
|
||||
|
||||
@@ -128,6 +128,9 @@ output "scc_mute_configs" {
|
||||
output "service_agents" {
|
||||
description = "Identities of all organization-level service agents."
|
||||
value = local.service_agents
|
||||
depends_on = [
|
||||
google_organization_service_identity.default
|
||||
]
|
||||
}
|
||||
|
||||
output "sink_writer_identities" {
|
||||
|
||||
@@ -18,27 +18,25 @@
|
||||
|
||||
locals {
|
||||
_sa_raw = yamldecode(file("${path.module}/service-agents.yaml"))
|
||||
_sa0 = {
|
||||
service_agents = {
|
||||
for agent in local._sa_raw :
|
||||
agent.name => {
|
||||
create_command = (
|
||||
"gcloud beta services identity create --service=${agent.api} --organization=${local.organization_id_numeric}"
|
||||
)
|
||||
name = agent.name
|
||||
api = agent.api
|
||||
display_name = agent.display_name
|
||||
email = templatestring(agent.identity, {
|
||||
organization_number = local.organization_id_numeric
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
service_agents = {
|
||||
for k, v in local._sa0 :
|
||||
k => merge(v, {
|
||||
iam_email = "serviceAccount:${v.email}"
|
||||
})
|
||||
email = templatestring(agent.identity, { organization_number = local.organization_id_numeric })
|
||||
iam_email = "serviceAccount:${templatestring(agent.identity, { organization_number = local.organization_id_numeric })}"
|
||||
} if contains(var.service_agents_config.services, agent.api)
|
||||
}
|
||||
service_agents_ctx = {
|
||||
for k, v in local.service_agents :
|
||||
"$service_agents:${k}" => v.iam_email
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_organization_service_identity" "default" {
|
||||
provider = google-beta
|
||||
for_each = var.service_agents_config.create_agents ? local.service_agents : {}
|
||||
organization = local.organization_id_numeric
|
||||
service = each.value.api
|
||||
}
|
||||
|
||||
@@ -16,208 +16,104 @@
|
||||
display_name: Access Approval Service Agent
|
||||
api: accessapproval.googleapis.com
|
||||
identity: service-o${organization_number}@gcp-sa-accessapproval.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: assuredoss
|
||||
display_name: Assured OSS Service Agent
|
||||
api: assuredoss.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-assuredoss.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: asm-hpsa
|
||||
display_name: Attack Surface Management Service Agent
|
||||
api: securitycenter.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-asm-hpsa.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: audit-manager
|
||||
display_name: Audit Manager Service Agent
|
||||
api: auditmanager.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-audit-manager.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: chronicle-soar
|
||||
display_name: Chronicle Soar Service Agent
|
||||
api: chronicle.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-chronicle-soar.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: effectivepolicy
|
||||
display_name: Cloud Asset Effective Policy Service Agent
|
||||
api: cloudasset.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-effectivepolicy.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: othercloudcfg
|
||||
display_name: Cloud Asset Other Cloud Config Service Agent
|
||||
api: cloudasset.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-othercloudcfg.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: cloudkms
|
||||
display_name: Cloud KMS Organization Service Agent
|
||||
api: cloudkms.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-cloudkms.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: logging
|
||||
display_name: Cloud Logging Service Agent
|
||||
api: logging.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-logging.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: nss-hpsa
|
||||
display_name: Cloud Notebook Security Scanner Service Agent
|
||||
api: notebooksecurityscanner.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-nss-hpsa.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: observability
|
||||
display_name: Cloud Observability Service Account
|
||||
api: observability.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-observability.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: cloudresourcemanager
|
||||
display_name: Cloud Resource Manager Service Agent
|
||||
api: cloudresourcemanager.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-cloudresourcemanager.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: riskmanager
|
||||
display_name: Cloud Risk Manager Service Agent
|
||||
api: dlp.googleapis.com
|
||||
identity: organizations-${organization_number}@gcp-sa-riskmanager.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: scc-bulk-export
|
||||
display_name: Cloud Security Command Center Bulk Export Service Account
|
||||
api: securitycenter.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-scc-bulk-export.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: scc-notification
|
||||
display_name: Cloud Security Command Center Notification Service Account
|
||||
api: securitycenter.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-scc-notification.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: security-center-api
|
||||
display_name: Cloud Security Command Center Service Agent
|
||||
api: securitycenter.googleapis.com
|
||||
identity: service-org-${organization_number}@security-center-api.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: csc-hpsa
|
||||
display_name: Cloud Security Compliance Service Agent
|
||||
api: cloudsecuritycompliance.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-csc-hpsa.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: ktd-hpsa
|
||||
display_name: Container Threat Detection Service Agent
|
||||
api: containerthreatdetection.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-ktd-hpsa.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: dataplex-cmek
|
||||
display_name: Dataplex Cmek Service Agent
|
||||
api: dataplex.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-dataplex-cmek.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: dataplex
|
||||
display_name: Dataplex Service Agent
|
||||
api: dataplex.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-dataplex.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: osconfig-rollout
|
||||
display_name: Google Cloud OS Config Rollout Service Agent
|
||||
api: osconfig.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-osconfig-rollout.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: osconfig
|
||||
display_name: Google Cloud OS Config Service Agent
|
||||
api: osconfig.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-osconfig.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: v1-remediator
|
||||
display_name: Policy Remediator Service Agent (prod)
|
||||
api: policyremediator.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-v1-remediator.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: pam
|
||||
display_name: Privileged Access Manager Service Agent
|
||||
api: privilegedaccessmanager.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-pam.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: progrollout
|
||||
display_name: Progressive Rollout Service Agent
|
||||
api: progressiverollout.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-progrollout.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: sccspanner
|
||||
display_name: SCC CMEK Spanner Service Agent (PROD)
|
||||
api: securitycenter.googleapis.com
|
||||
identity: service-org-${organization_number}@gcp-sa-sccspanner.iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
|
||||
|
||||
@@ -178,3 +178,13 @@ variable "organization_id" {
|
||||
error_message = "The organization_id must in the form organizations/nnn."
|
||||
}
|
||||
}
|
||||
|
||||
variable "service_agents_config" {
|
||||
description = "Service agents configuration."
|
||||
type = object({
|
||||
services = optional(list(string), [])
|
||||
create_agents = optional(bool, true)
|
||||
})
|
||||
default = {}
|
||||
nullable = false
|
||||
}
|
||||
|
||||
@@ -44,6 +44,14 @@
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: aiplatform-pie
|
||||
display_name: AI Platform Private Instance (PIE) Service Agent
|
||||
api: aiplatform.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-aiplatform-pie.${universe_domain}iam.gserviceaccount.com
|
||||
role: roles/aiplatform.serviceAgent
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: vertex-eval
|
||||
display_name: AI Platform Rapid Eval Service Agent
|
||||
api: aiplatform.googleapis.com
|
||||
@@ -132,6 +140,14 @@
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: agentgateway
|
||||
display_name: Agent Gateway Service Account
|
||||
api: networkservices.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-agentgateway.${universe_domain}iam.gserviceaccount.com
|
||||
role: roles/agentgateway.serviceAgent
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: alloydb
|
||||
display_name: AlloyDB Service Account
|
||||
api: alloydb.googleapis.com
|
||||
@@ -277,6 +293,14 @@
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: appoptimize
|
||||
display_name: App Optimize Service Agent
|
||||
api: appoptimize.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-appoptimize.${universe_domain}iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: integrations
|
||||
display_name: Application Integration Service Agent
|
||||
api: integrations.googleapis.com
|
||||
@@ -309,6 +333,14 @@
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: autoannotate
|
||||
display_name: Auto Annotate Service Account
|
||||
api: storage.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-autoannotate.${universe_domain}iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: recommendationengine
|
||||
display_name: AutoML Recommendations Service Account
|
||||
api: recommendationengine.googleapis.com
|
||||
@@ -406,6 +438,14 @@
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: bqms
|
||||
display_name: BigQuery Migration Service Agent
|
||||
api: bigquerymigration.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-bqms.${universe_domain}iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: prod-bigqueryomni
|
||||
display_name: BigQuery Omni Service Agent
|
||||
api: bigquery.googleapis.com
|
||||
@@ -446,14 +486,6 @@
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: chronicle-sv
|
||||
display_name: Chronicle Security Validation Service Account
|
||||
api: chronicle.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-chronicle-sv.${universe_domain}iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: chronicle
|
||||
display_name: Chronicle Service Account
|
||||
api: chronicle.googleapis.com
|
||||
@@ -648,14 +680,6 @@
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: lifesciences
|
||||
display_name: Cloud Life Sciences Service Agent
|
||||
api: lifesciences.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-lifesciences.${universe_domain}iam.gserviceaccount.com
|
||||
role: roles/lifesciences.serviceAgent
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: logging
|
||||
display_name: Cloud Logging Service Account
|
||||
api: logging.googleapis.com
|
||||
@@ -864,6 +888,14 @@
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: hypercomputecluster
|
||||
display_name: Cluster Director Service Agent
|
||||
api: hypercomputecluster.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-hypercomputecluster.${universe_domain}iam.gserviceaccount.com
|
||||
role: roles/hypercomputecluster.serviceAgent
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: compute-system
|
||||
display_name: Compute Engine Service Agent
|
||||
api: compute.googleapis.com
|
||||
@@ -985,6 +1017,14 @@
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: ces
|
||||
display_name: Customer Engagement Suite Service Account
|
||||
api: ces.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-ces.${universe_domain}iam.gserviceaccount.com
|
||||
role: roles/ces.serviceAgent
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: dataconnectors
|
||||
display_name: Data Connectors Service Account
|
||||
api: dataconnectors.googleapis.com
|
||||
@@ -1009,6 +1049,14 @@
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: datastudio-cmek
|
||||
display_name: Data Studio CMEK Service Account
|
||||
api: datastudio.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-datastudio-cmek.${universe_domain}iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: datastudio
|
||||
display_name: Data Studio Service Account
|
||||
api: datastudio.googleapis.com
|
||||
@@ -1241,6 +1289,14 @@
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: storage-search
|
||||
display_name: GCS Search Service Account
|
||||
api: storage.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-storage-search.${universe_domain}iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: gkedataplanev2
|
||||
display_name: GKE Dataplane V2 Service Account
|
||||
api: gkedataplanev2.googleapis.com
|
||||
@@ -1282,6 +1338,14 @@
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: generativelanguage
|
||||
display_name: Generative Language Service Agent
|
||||
api: generativelanguage.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-generativelanguage.${universe_domain}iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: gkeonprem
|
||||
display_name: Gke On-Prem Service Account
|
||||
api: gkeonprem.googleapis.com
|
||||
@@ -1401,6 +1465,14 @@
|
||||
aliases:
|
||||
- storage
|
||||
skip_iam: false
|
||||
- name: diagon
|
||||
display_name: Hypercompute Diagon Service Account
|
||||
api: hypercomputecluster.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-diagon.${universe_domain}iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: iap
|
||||
display_name: IAP Service Account
|
||||
api: iap.googleapis.com
|
||||
@@ -1417,6 +1489,14 @@
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: global-spanner
|
||||
display_name: Infra Spanner Production Service Account
|
||||
api: spanner.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-global-spanner.${universe_domain}iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: config
|
||||
display_name: Infrastructure Manager Service Account
|
||||
api: config.googleapis.com
|
||||
@@ -1860,6 +1940,14 @@
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: vs-cmek
|
||||
display_name: Vector Search Cmek Service Account
|
||||
api: vectorsearch.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-vs-cmek.${universe_domain}iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: vectorsearch
|
||||
display_name: Vector Search Service Account
|
||||
api: vectorsearch.googleapis.com
|
||||
@@ -1868,6 +1956,14 @@
|
||||
is_primary: true
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: vertex-sandbox
|
||||
display_name: Vertex AI Agent Sandbox Service Agent
|
||||
api: aiplatform.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-vertex-sandbox.${universe_domain}iam.gserviceaccount.com
|
||||
role: roles/aiplatform.agentSandboxServiceAgent
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: vertex-shtune
|
||||
display_name: Vertex AI Ancillary Secure Fine Tuning Service Agent
|
||||
api: aiplatform.googleapis.com
|
||||
@@ -1964,6 +2060,14 @@
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: vertex-vtc
|
||||
display_name: Vertex AI Training Cluster Service Agent
|
||||
api: aiplatform.googleapis.com
|
||||
identity: service-${project_number}@gcp-sa-vertex-vtc.${universe_domain}iam.gserviceaccount.com
|
||||
role: null
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
- name: vertex-agent
|
||||
display_name: Vertex Agent Service Agent
|
||||
api: aiplatform.googleapis.com
|
||||
@@ -2012,3 +2116,4 @@
|
||||
is_primary: false
|
||||
aliases: []
|
||||
skip_iam: false
|
||||
|
||||
|
||||
39
tests/modules/folder/examples/agents.yaml
Normal file
39
tests/modules/folder/examples/agents.yaml
Normal file
@@ -0,0 +1,39 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# 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.
|
||||
|
||||
values:
|
||||
module.folder.google_folder.folder[0]:
|
||||
deletion_protection: false
|
||||
display_name: Folder name
|
||||
parent: folders/1122334455
|
||||
tags: null
|
||||
timeouts: null
|
||||
module.folder.google_folder_service_identity.default["osconfig"]:
|
||||
service: osconfig.googleapis.com
|
||||
timeouts: null
|
||||
module.folder.google_folder_service_identity.default["osconfig-rollout"]:
|
||||
service: osconfig.googleapis.com
|
||||
timeouts: null
|
||||
module.folder.google_folder_service_identity.default["pam"]:
|
||||
service: privilegedaccessmanager.googleapis.com
|
||||
timeouts: null
|
||||
module.folder.google_folder_service_identity.default["progrollout"]:
|
||||
service: progressiverollout.googleapis.com
|
||||
timeouts: null
|
||||
|
||||
counts:
|
||||
google_folder: 1
|
||||
google_folder_service_identity: 4
|
||||
modules: 1
|
||||
resources: 5
|
||||
36
tests/modules/organization/examples/agents.yaml
Normal file
36
tests/modules/organization/examples/agents.yaml
Normal file
@@ -0,0 +1,36 @@
|
||||
# Copyright 2026 Google LLC
|
||||
#
|
||||
# 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.
|
||||
|
||||
values:
|
||||
module.org.google_organization_service_identity.default["osconfig"]:
|
||||
organization: '1122334455'
|
||||
service: osconfig.googleapis.com
|
||||
timeouts: null
|
||||
module.org.google_organization_service_identity.default["osconfig-rollout"]:
|
||||
organization: '1122334455'
|
||||
service: osconfig.googleapis.com
|
||||
timeouts: null
|
||||
module.org.google_organization_service_identity.default["pam"]:
|
||||
organization: '1122334455'
|
||||
service: privilegedaccessmanager.googleapis.com
|
||||
timeouts: null
|
||||
module.org.google_organization_service_identity.default["progrollout"]:
|
||||
organization: '1122334455'
|
||||
service: progressiverollout.googleapis.com
|
||||
timeouts: null
|
||||
|
||||
counts:
|
||||
google_organization_service_identity: 4
|
||||
modules: 1
|
||||
resources: 4
|
||||
@@ -24,6 +24,7 @@
|
||||
# ]
|
||||
# ///
|
||||
|
||||
from collections import Counter
|
||||
from dataclasses import asdict, dataclass
|
||||
from itertools import chain
|
||||
|
||||
@@ -133,6 +134,17 @@ class Agent:
|
||||
is_primary: bool
|
||||
aliases: list[str]
|
||||
skip_iam: bool
|
||||
node_type: str
|
||||
|
||||
def to_dict(self):
|
||||
d = asdict(self)
|
||||
d.pop('node_type', None)
|
||||
if self.node_type in ['organization', 'folder']:
|
||||
d.pop('is_primary', None)
|
||||
d.pop('role', None)
|
||||
d.pop('skip_iam', None)
|
||||
d.pop('aliases', None)
|
||||
return d
|
||||
|
||||
|
||||
@click.command()
|
||||
@@ -180,6 +192,12 @@ def main(mode, e2e=False):
|
||||
if identity in IGNORED_AGENTS or '-IDENTIFIER' in identity:
|
||||
continue
|
||||
|
||||
role = col2.code.get_text() if 'roles/' in agent_text else None
|
||||
|
||||
# Ignore Apigee Core service agent as it shares email with primary agent
|
||||
if identity == 'service-PROJECT_NUMBER@gcp-sa-apigee.iam.gserviceaccount.com' and role == 'roles/apigee.coreServiceAgent':
|
||||
continue
|
||||
|
||||
if identity in AGENT_NAME_OVERRIDE:
|
||||
name = AGENT_NAME_OVERRIDE[identity]
|
||||
else:
|
||||
@@ -221,6 +239,7 @@ def main(mode, e2e=False):
|
||||
is_primary=PRIMARY_OVERRIDE.get(name, is_primary),
|
||||
aliases=ALIASES.get(name, []),
|
||||
skip_iam=skip_iam,
|
||||
node_type=mode,
|
||||
)
|
||||
|
||||
if mode == 'project' and agent.name == 'cloudservices':
|
||||
@@ -231,7 +250,11 @@ def main(mode, e2e=False):
|
||||
|
||||
# make sure all names and aliases are different:
|
||||
names = set(agent.name for agent in agents)
|
||||
assert len(names) == len(agents)
|
||||
duplicate_names = [
|
||||
name for name, count in Counter(agent.name for agent in agents).items()
|
||||
if count > 1
|
||||
]
|
||||
assert len(names) == len(agents), f"duplicate names found: {duplicate_names}"
|
||||
aliases = set(chain.from_iterable(agent.aliases for agent in agents))
|
||||
assert aliases.isdisjoint(names)
|
||||
|
||||
@@ -244,7 +267,7 @@ def main(mode, e2e=False):
|
||||
header = open(__file__).readlines()[2:15]
|
||||
print("".join(header))
|
||||
# and print all the agents
|
||||
print(yaml.safe_dump([asdict(a) for a in agents], sort_keys=False))
|
||||
print(yaml.safe_dump([a.to_dict() for a in agents], sort_keys=False))
|
||||
else:
|
||||
jit_services = {}
|
||||
result = {"locals": {"jit_services": jit_services}}
|
||||
|
||||
Reference in New Issue
Block a user