Assured Workloads Basics
GoogleOfficialPublished by Google
- Skill
- Tool integration
- Cloud and DevOps
- Apache-2.0
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured workflow for managing Google Cloud Assured Workloads V1. It enables users to provision, inspect, update, and delete compliant folders and workloads across various regimes such as FedRAMP, DoD, and regional data boundaries. The skill emphasizes strict adherence to parameter validation, mandatory telemetry attribution, and human-in-the-loop confirmation for all mutating operations to ensure compliance and operational safety.
What it does
- Provisions new Assured Workloads V1 compliant folders and workloads.
- Lists and describes existing workloads for audit and inspection.
- Updates workload metadata and manages compliance posture updates.
- Safely deletes workloads by enforcing a strict bottom-up resource removal sequence.
- Configures service usage restrictions and CMEK settings for compliance.
- Diagnoses common API errors and LRO failures during workload operations.
When to use it
- When creating a new compliant workload environment in Google Cloud.
- When you need to audit or list existing Assured Workloads configurations.
- When updating workload labels or violation notification settings.
- When decommissioning a compliant workload and its associated resources.
- When applying available compliance posture updates to existing workloads.
Advantages
- Ensures consistent application of compliance regimes and data boundaries.
- Prevents accidental misconfiguration through mandatory parameter validation.
- Provides a safe, ordered sequence for deleting complex workload resources.
- Automates the complex process of applying compliance posture updates.
- Includes built-in diagnostic tools for troubleshooting API errors.
Usage examples
- “Create a new FedRAMP Moderate workload in the us-east1 region.”
- The agent will validate the parameters, request confirmation, and execute the gcloud command to provision the workload with the correct compliance regime.
- “List all Assured Workloads in my organization.”
- The agent will retrieve and display a JSON list of all workloads, including their current status and compliance details.
- “Delete the Assured Workload with ID 12345.”
- The agent will inspect the workload, identify child resources, and guide the user through the required bottom-up deletion process.
- “Check for and apply any pending compliance updates for my workload.”
- The agent will query the updates API, identify available changes, and apply them sequentially after user approval.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/google/assured-workloads-basics/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip assured-workloads-basics.zip -d .agents/skills
Documentation by Google
Google Cloud Assured Workloads Basics (V1 GA)
Validate prerequisites, create Assured Workloads V1 compliant folders, allowlist compliant services, and list, inspect, update, or delete existing workloads using gcloud and the Google Cloud Python Client Libraries (google-cloud-assured-workloads and google-api-python-client).
Usage Attribution
For all gcloud CLI, REST API, and MCP attribution requirements when executing commands with this skill, follow the instructions in Usage Attribution.
Mandatory Guardrails
No Fabrication: Never invent Organization IDs, Billing Account IDs, Folder IDs, Project IDs, Workload IDs, or Compliance Regime enums. Validate parameters with
scripts/validate_params.pybefore running commands.Regional / Multi-Regional Locations & CLI Surface: Assured Workloads V1 does not support
--location=global; always use a regional or multi-regional location compatible with the compliance regime (see references/regimes.md). Never usegcloud compliance-managercommands for Assured Workloads V1 workloads.Human Confirmation Before Mutation: Read-only operations (
list,describe, andscripts/manage_workload_updates.py --action=list) can run immediately after validation, but you must obtain explicit user confirmation before executing any mutating or destructive command (create,restrict_allowed_resources.py,update,manage_workload_updates.py --action=apply,delete,folders delete, orprojects delete).Telemetry Attribution & Regional Endpoints:
Prefix every
gcloudcommand you execute directly withCLOUDSDK_METRICS_REQUEST_ATTRIBUTION="agent-skills/1.0 (skill:assured-workloads-basics)"(see Usage Attribution).Configure Python GAPIC and Discovery clients with regional
ClientOptions(api_endpoint="{location}-assuredworkloads.googleapis.com").
Step 0: Prerequisites & Environment Setup
Verify these 5 prerequisites before running Assured Workloads operations:
CLI & Python Libraries: Install the Google Cloud SDK (
gcloud— see the Google Cloud SDK Installation Guide) and Python client libraries:pip install "google-cloud-assured-workloads>=1.0,<2" "google-api-python-client>=2.0,<3" "google-auth>=2.0,<3"Dual Authentication (CLI + ADC): Authenticate both
gcloudand Application Default Credentials (gcloud auth login && gcloud auth application-default login, orgcloud auth activate-service-account --key-file=/path/to/key.json+export GOOGLE_APPLICATION_CREDENTIALS="/path/to/key.json").Quota & Admin Project:
gcloud config set project {admin_project_id}andgcloud auth application-default set-quota-project {admin_project_id}.API Enablement: Enable the Assured Workloads API (
assuredworkloads.googleapis.com):gcloud services enable assuredworkloads.googleapis.com --project={admin_project_id}Organization-Level IAM Roles (
organizations/{org_id}):Caller:
roles/assuredworkloads.admin(orroles/assuredworkloads.reader/roles/assuredworkloads.editorfor read-onlylist/describe),roles/resourcemanager.organizationViewer, androles/billing.useron--billing-account=billingAccounts/{billing_id}when creating workloads (plusbilling.resourceAssociations.createon--partner-services-billing-accountfor partner workloads).Assured Workloads Service Agent (
service-{org_number}@gcp-sa-assuredworkloads.iam.gserviceaccount.com):roles/assuredworkloads.serviceAgent.
Workflow 1: Create an Assured Workloads workload (Two-Phase Provisioning)
Phase 1: Validate Parameters & Create Workload (gcloud assured workloads create)
Select Regime & Location: Check references/regimes.md for all 45 V1
--compliance-regimeenums and valid--locationvalues.Free-Tier Regional Data Boundaries (
REGIONAL_DATA_BOUNDARY): Free-tier regional data boundaries (e.g., US Data Boundary with standard commercial support and without sovereign personnel support uplift, EU Data Boundary, Australia/India/UK/Canada/Israel Data Boundary) map to--compliance-regime=REGIONAL_DATA_BOUNDARY(legacy alias:REGIONAL_CONTROLS) paired with--location={location}(except Japan, which usesJAPAN_DATA_BOUNDARY). Do not set--compliance-regimeto paid sovereign support regimes (US_REGIONS_AND_US_SUPPORT,US_DATA_BOUNDARY_AND_SUPPORT,FEDRAMP_MODERATE) when the user requests the free-tier boundary, and never invent non-existent enums likeUS_DATA_BOUNDARY.
Billing Account Discovery (If Omitted): Never guess a billing account ID. If omitted, run
gcloud billing accounts list --filter="open=true" --format="table(name,displayName)"or ask the user forbillingAccounts/{billing_id}.Validate & Create: Validate with
python3 scripts/validate_params.py --operation=create ..., surface any warnings, obtain explicit user confirmation, and executegcloud assured workloads create:gcloud assured workloads create \ --organization={org_id} --location={location} \ --display-name="{display_name}" --compliance-regime={compliance_regime} \ --billing-account=billingAccounts/{billing_id} \ [--provisioned-resources-parent=folders/{parent_folder_id}] [--enable-sovereign-controls] \ [--next-rotation-time={next_rotation_time} --rotation-period={rotation_seconds}s] \ [--resource-settings=consumer-project-id={consumer_project_id},consumer-project-name={consumer_project_name},encryption-keys-project-id={kms_project_id},encryption-keys-project-name={kms_project_name},keyring-id={keyring_id}] \ [--partner={partner_enum}] [--partner-permissions=data-logs-viewer=true|false] \ [--partner-services-billing-account=billingAccounts/{partner_billing_id}] \ [--labels=key1=val1,key2=val2] [--external-identifier={external_id}] --format=jsonCMEK & Resource Settings Rules:
--next-rotation-time(RFC 3339 timestamp) and--rotation-period(seconds, e.g.172800s) must be specified together.encryption-keys-project-id,encryption-keys-project-name, andkeyring-idin--resource-settingsrequire both rotation flags.Sovereign Controls by Partners (
--compliance-regime=ASSURED_WORKLOADS_FOR_PARTNERS):Requests for partner-managed sovereign controls (S3NS, T-Systems, SIA Minsait, PSN, CNTXT, Telefónica) map to
--compliance-regime=ASSURED_WORKLOADS_FOR_PARTNERS.Requires
--partnerand a supported partner region (see references/regimes.md for valid--partnervalues, supported regions, and partner billing account requirements).Configure partner permissions on the CLI with
--partner-permissions=data-logs-viewer=true(ordata-logs-viewer=false).
Phase 2 (Optional): Allowlist Compliant Services / Enable BigQuery (google-cloud-assured-workloads)
When Required: For
FEDRAMP_MODERATEandDATA_BOUNDARY_FOR_FEDRAMP_MODERATE, BigQuery is automatically enabled at creation (skip Phase 2). For all other regimes (such asFEDRAMP_HIGH), BigQuery is initially blocked by the folder'sconstraints/gcp.restrictServiceUsageorganization policy while a background configuration process completes (typically 5–10 minutes after creation).Never Bypass Org Policies Manually: Do not manually override or delete
constraints/gcp.restrictServiceUsageon the folder to bypass Assured Workloads compliance controls.Programmatic Allowlisting (
AssuredWorkloadsServiceClient.restrict_allowed_resources): Once background setup completes and compliant services (like BigQuery) appear incompliantButDisallowedServices, runpython3 scripts/restrict_allowed_resources.py --organization={org_id} --location={location} --workload-id={workload_id} --restriction-type=ALLOW_COMPLIANT_RESOURCES(after user confirmation), or call the officialgoogle-cloud-assured-workloadsPython client library directly:from google.api_core.client_options import ClientOptions from google.cloud import assuredworkloads_v1 client = assuredworkloads_v1.AssuredWorkloadsServiceClient( client_options=ClientOptions( api_endpoint=f"{location}-assuredworkloads.googleapis.com" ), transport="rest", ) request = assuredworkloads_v1.RestrictAllowedResourcesRequest( name=f"organizations/{org_id}/locations/{location}/workloads/{workload_id}", restriction_type=assuredworkloads_v1.RestrictAllowedResourcesRequest.RestrictionType.ALLOW_COMPLIANT_RESOURCES, ) client.restrict_allowed_resources(request=request)(Supported
RestrictionTypevalues:ALLOW_COMPLIANT_RESOURCES,APPEND_COMPLIANT_RESOURCES, orALLOW_ALL_GCP_RESOURCES).
Workflow 2: List & Describe Assured Workloads workloads (gcloud)
Read-only list and describe operations do not require user confirmation after validating with scripts/validate_params.py.
List Workloads (
ListWorkloads):gcloud assured workloads list --organization={org_id} --location={location} \ [--filter="{filter_expression}"] [--limit={limit}] [--page-size={page_size}] --format=jsonDescribe a Specific Workload (
GetWorkload):gcloud assured workloads describe {workload_id} --organization={org_id} --location={location} --format=json(Or pass the full resource name
organizations/{org_id}/locations/{location}/workloads/{workload_id}).Key
describeOutput Fields:name,displayName,complianceRegime,partner,resources(provisionedCONSUMER_FOLDERfolder ID,CONSUMER_PROJECT,ENCRYPTION_KEYS_PROJECT, andKEYRING),etag(optimistic concurrency token required forupdate/delete),compliantButDisallowedServices,kajEnrollmentState,saaEnrollmentResponse,violationNotificationsEnabled, andlabels.
Workflow 3: Delete an Assured Workloads workload (DeleteWorkload)
An Assured Workloads workload cannot be deleted while its provisioned Cloud Resource Manager folder (folders/{folder_id}) or child projects still exist (FAILED_PRECONDITION: Workload is not empty / ERROR_CODE_WORKLOAD_NOT_EMPTY). Never run gcloud assured workloads delete before deleting the provisioned folder and its child projects.
Inspect & Validate: Run
gcloud assured workloads describeto identify the provisioned folder (folders/{folder_id}) and child projects ({project_id}), and validate withpython3 scripts/validate_params.py --operation=delete --workload-id={workload_id} --organization={org_id} --location={location}.Confirm & Execute Ordered Deletion Sequence: Obtain explicit user confirmation, then run in strict bottom-up order:
# Step 1: Delete child projects inside the workload folder first gcloud projects delete {project_id} --quiet # Step 2: Delete the provisioned Assured Workloads folder gcloud resource-manager folders delete {folder_id} # Step 3: Delete the Assured Workloads workload after folder deletion propagates gcloud assured workloads delete {workload_id} --organization={org_id} --location={location} --quietPropagation Delay &
--etagNote: Folder deletion state (DELETE_REQUESTED) can take a brief propagation delay (15–60s) before Assured Workloads recognizes the folder as deleted. IfERROR_CODE_WORKLOAD_NOT_EMPTYtransiently persists after deleting the folder, wait 15–20 seconds and retrygcloud assured workloads deletewithout a stale--etag(as deleting underlying resources changes the workload's concurrency token).
Workflow 4: Update an Assured Workloads workload (UpdateWorkload & Compliance Updates)
4A. Update Workload Metadata & Notifications (gcloud assured workloads update)
Updatable Fields (at least one required):
--display-name="{new_display_name}": 4–30 characters ([a-zA-Z0-9 -], no trailing spaces).--labels=key1=val1,key2=val2: Replaces the workload's label map (include existing labels to preserve them).--violation-notifications-enabled=true|false: Enables or disables email notifications for organization policy compliance violations to Essential Contacts (Legal category).--etag={etag}: Current concurrency token fromdescribe/list.
Immutable Fields (Cannot Be Modified After Creation):
complianceRegime,location,billingAccount, andpartnerare immutable.gcloud assured workloads updatecannot change a workload'scomplianceRegimeorlocationin-place; changing regimes requires creating a new Assured Workloads workload.Validate, Confirm, & Execute: Validate with
python3 scripts/validate_params.py --operation=update ..., obtain explicit user confirmation, and run:gcloud assured workloads update {workload_id} --organization={org_id} --location={location} \ [--display-name="{new_display_name}"] [--labels=key1=val1,key2=val2] \ [--violation-notifications-enabled=true|false] [--etag={etag}] --format=json
4B. List & Apply Compliance Posture Updates (updates.list & updates.apply)
gcloud assured workloads does not have a built-in CLI subcommand to list or apply compliance posture updates from the updates sub-resource (gcloud assured workloads update only updates workload metadata). Use scripts/manage_workload_updates.py (--action=list or --action=apply after user confirmation) or googleapiclient.discovery.build against the regional Assured Workloads v1 API (https://{location}-assuredworkloads.googleapis.com):
AVAILABLEvs.WITHDRAWNUpdate States:AVAILABLE(One Update Per Constraint): EachAVAILABLEupdate record corresponds to an individual organization policy constraint update (details.orgPolicyUpdate.suggestedPolicy.constraint). If multiple updates areAVAILABLE, each corresponds to a different constraint and must be applied individually (or sequentially via--update-id=ALL)—applying the newest update does not apply olderAVAILABLEupdates for other constraints.WITHDRAWN(Superseded): When a newer policy update supersedes an older unapplied update for the same constraint, Assured Workloads marks the older updateWITHDRAWN. Filter for stateAVAILABLEand never applyWITHDRAWNupdates.
Python Discovery API Pattern (
updates.list,updates.apply, & LRO Polling):import time from google.api_core.client_options import ClientOptions from googleapiclient import discovery service = discovery.build( "assuredworkloads", "v1", client_options=ClientOptions( api_endpoint=f"https://{location}-assuredworkloads.googleapis.com" ), ) parent = f"organizations/{org_id}/locations/{location}/workloads/{workload_id}" # 1. List updates and filter for AVAILABLE state resp = ( service.organizations() .locations() .workloads() .updates() .list(parent=parent) .execute() ) available = [u for u in resp.get("updates", []) if u.get("state") == "AVAILABLE"] # 2. Apply each AVAILABLE update and poll the returned Long-Running Operation (LRO) for update in available: op = ( service.organizations() .locations() .workloads() .updates() .apply(name=update["name"], body={}) .execute() ) while not op.get("done", False): time.sleep(3) op = ( service.organizations() .locations() .operations() .get(name=op["name"]) .execute() )
Common Error Handling & Troubleshooting (scripts/diagnose_error.py)
Never blindly retry deterministic 400 INVALID_ARGUMENT, 403 PERMISSION_DENIED, 404 NOT_FOUND, or FAILED_PRECONDITION errors. Run python3 scripts/diagnose_error.py --error-message="{error_output_text}" to match any error against the full Assured Workloads ErrorInfo.reason catalog.
Troubleshooting Folder creation was unsuccessful. Please try again. (LRO Inspection)
When gcloud assured workloads create fails with the generic message Folder creation was unsuccessful. Please try again., do not blindly retry. Inspect the failed Long-Running Operation (LRO) in the same organization and location to retrieve the specific ErrorInfo.reason:
gcloud assured operations list --organization={org_id} --location={location} --format=json
gcloud assured operations describe {operation_id} --organization={org_id} --location={location} --format=json(Or run python3 scripts/diagnose_error.py --fetch-latest-lro --organization={org_id} --location={location}).
Common Folder Creation Preconditions to Check in the LRO Error:
Duplicate sibling folder display name (
ERROR_CODE_FOLDER_NAME_UNIQUENESS): A sibling folder with the same--display-namealready exists under the target parent (organizations/{org_id}orfolders/{parent_folder_id}). Choose a unique--display-nameor a different--provisioned-resources-parent.Nesting inside an existing Assured Workloads folder (
ERROR_CODE_INVALID_PROVISIONED_RESOURCES_PARENT): Assured Workloads folders cannot be nested inside another Assured Workloads folder. Specify a standard non-workload parent folder or omit--provisioned-resources-parent.Folder quota or nesting depth limit (
ERROR_CODE_FOLDER_CREATION_PRECONDITION_FAILED): The target parent reached the Cloud Resource Manager 300 direct child-folder quota or the 10-level folder nesting depth limit.
Reference Documentation
references/regimes.md: Complete table of all 45 Assured Workloads V1 compliance regimes, modern
DATA_BOUNDARY_FOR_*enums, legacy aliases, partner enums, and supported regional/multi-regional locations.Official Google Cloud Documentation: Assured Workloads Overview, Supported Locations & Data Boundaries, Assured Workloads Pricing, Apply Workload Updates, Sovereign Controls by Partners Overview, Sovereign Controls by Partners Locations.
Files
- ATTRIBUTION.mdAdded by AutoGineer866 B
- LICENSELicence file added by AutoGineer11.1 KiB
- SKILL.md19.0 KiB
- references/attribution.md1.8 KiB
- references/regimes.md13.7 KiB
- scripts/diagnose_error.pyScript16.4 KiB
- scripts/manage_workload_updates.pyScript26.1 KiB
- scripts/restrict_allowed_resources.pyScript13.8 KiB
- scripts/validate_params.pyScript56.2 KiB
scripts/manage_workload_updates.py
#!/usr/bin/env python3
r"""Helper for Assured Workloads V1 configuration updates (List & Apply Updates).
Uses the Google API Python Client Library (`google-api-python-client`) to:
1. Enable workload compliance updates (`enableComplianceUpdates`) if needed.
2. List available organization policy configuration updates (`updates.list`).
3. Apply one, multiple, or all available workload updates (`updates.apply`)
sequentially and poll each resulting Long-Running Operation
(`operations.get`) until completion.
Why Python Client Library?
`gcloud assured workloads update` only updates V1 workload metadata
(`--display-name`, `--labels`, `--violation-notifications-enabled`) and does
not expose CLI subcommands for V1 compliance posture updates
(`enableComplianceUpdates`, `updates.list`, `updates.apply`). Per the official
Google Cloud documentation and hybrid CLI + Client Library pattern, this
script uses `google-api-python-client` with regional endpoint routing
(`https://{location}-assuredworkloads.googleapis.com`) and user-agent
telemetry (`agent-skills/1.0 (skill:assured-workloads-basics)`).
Usage examples:
# List available configuration updates for a workload:
python3 scripts/manage_workload_updates.py \
--action=list \
--organization=123456789012 \
--location=us-central1 \
--workload-id=00-701ea036-7152-4781-a867-9f5
# Apply a specific configuration update by update ID:
python3 scripts/manage_workload_updates.py \
--action=apply \
--organization=123456789012 \
--location=us-central1 \
--workload-id=00-701ea036-7152-4781-a867-9f5 \
--update-id=5320de45-6c98-41af-b4a0-2ef930b124c3
# Apply all available configuration updates sequentially:
python3 scripts/manage_workload_updates.py \
--action=apply \
--organization=123456789012 \
--location=us-central1 \
--workload-id=00-701ea036-7152-4781-a867-9f5 \
--update-id=ALL
"""
import argparse
import json
import re
import sys
import time
from typing import Any
USER_AGENT = "agent-skills/1.0 (skill:assured-workloads-basics)"
_WORKLOAD_NAME_RE = re.compile(
r"^organizations/(\d+)/locations/([a-z0-9-]+)/workloads/([a-zA-Z0-9_-]+)$"
)
_UPDATE_NAME_RE = re.compile(
r"^organizations/(\d+)/locations/([a-z0-9-]+)/workloads/([a-zA-Z0-9_-]+)/updates/([a-zA-Z0-9_-]+)$"
)
_ORG_ID_RE = re.compile(r"^\d+$")
_ID_TOKEN_RE = re.compile(r"^[a-zA-Z0-9_-]+$")
def resolve_workload_and_update_names(
action: str,
workload_id: str | None = None,
organization: str | None = None,
location: str | None = None,
update_id: str | None = None,
) -> tuple[str, str, str, str, list[str] | None, list[str] | None]:
"""Resolves (workload_name, org_id, location, workload_id, update_names, update_ids)."""
clean_action = action.strip().lower()
if clean_action not in ("list", "enable", "apply"):
raise ValueError(
f"Invalid '--action' value '{action}'. Valid values: ['list', 'enable',"
" 'apply']."
)
raw_tokens: list[str] = []
if update_id and update_id.strip():
raw_tokens = [t.strip() for t in update_id.split(",") if t.strip()]
# First check if a single or multiple full update resource names were passed
inferred_org: str | None = None
inferred_loc: str | None = None
inferred_wid: str | None = None
for tok in raw_tokens:
u_match = _UPDATE_NAME_RE.fullmatch(tok)
if u_match:
u_org, u_loc, u_wid = (
u_match.group(1),
u_match.group(2),
u_match.group(3),
)
if u_loc == "global":
raise ValueError(
"Invalid location 'global' in update resource name. Assured"
" Workloads V1 requires a regional or multi-regional location."
)
if inferred_org and inferred_org != u_org:
raise ValueError(
"All update resource names in '--update-id' must belong to the"
" same organization."
)
if inferred_loc and inferred_loc != u_loc:
raise ValueError(
"All update resource names in '--update-id' must belong to the"
" same location."
)
if inferred_wid and inferred_wid != u_wid:
raise ValueError(
"All update resource names in '--update-id' must belong to the"
" same workload."
)
inferred_org, inferred_loc, inferred_wid = u_org, u_loc, u_wid
if inferred_org and inferred_loc and inferred_wid:
if organization and organization.strip():
norm_org = organization.strip()
if norm_org.startswith("organizations/"):
norm_org = norm_org[len("organizations/") :].strip()
if norm_org != inferred_org:
raise ValueError(
f"Organization ID mismatch: '--organization={norm_org}' does not"
f" match '{inferred_org}' in '--update-id'."
)
if location and location.strip():
norm_loc = location.strip().lower()
if norm_loc != inferred_loc:
raise ValueError(
f"Location mismatch: '--location={norm_loc}' does not match"
f" '{inferred_loc}' in '--update-id'."
)
clean_org = inferred_org
clean_loc = inferred_loc
clean_wid = inferred_wid
workload_name = (
f"organizations/{clean_org}/locations/{clean_loc}/workloads/{clean_wid}"
)
else:
if not workload_id or not workload_id.strip():
raise ValueError(
"Missing required '--workload-id' (or full resource name"
" 'organizations/<ORG>/locations/<LOC>/workloads/<ID>')."
)
cand_workload = workload_id.strip()
w_match = _WORKLOAD_NAME_RE.fullmatch(cand_workload)
if w_match:
clean_org, clean_loc, clean_wid = (
w_match.group(1),
w_match.group(2),
w_match.group(3),
)
if clean_loc == "global":
raise ValueError(
"Invalid location 'global' in workload resource name. Assured"
" Workloads V1 requires a regional or multi-regional location."
)
if organization and organization.strip():
norm_org = organization.strip()
if norm_org.startswith("organizations/"):
norm_org = norm_org[len("organizations/") :].strip()
if norm_org != clean_org:
raise ValueError(
f"Organization ID mismatch: '--organization={norm_org}' does not"
f" match '{clean_org}' in '--workload-id'."
)
if location and location.strip():
norm_loc = location.strip().lower()
if norm_loc != clean_loc:
raise ValueError(
f"Location mismatch: '--location={norm_loc}' does not match"
f" '{clean_loc}' in '--workload-id'."
)
workload_name = cand_workload
else:
if not organization or not organization.strip():
raise ValueError(
"Missing '--organization' when '--workload-id' is not a full"
" resource path."
)
if not location or not location.strip():
raise ValueError(
"Missing '--location' when '--workload-id' is not a full resource"
" path."
)
clean_org = organization.strip()
if clean_org.startswith("organizations/"):
clean_org = clean_org[len("organizations/") :].strip()
if not _ORG_ID_RE.fullmatch(clean_org):
raise ValueError(
f"Invalid organization ID '{organization}'. Expected numeric ID."
)
clean_loc = location.strip().lower()
if clean_loc == "global":
raise ValueError(
"Invalid location 'global'. Assured Workloads V1 requires a"
" regional or multi-regional location (e.g., 'us-central1', 'us',"
" 'eu')."
)
if not _ID_TOKEN_RE.fullmatch(cand_workload):
raise ValueError(
f"Invalid workload ID '{workload_id}'. Expected workload ID or full"
" resource name"
" 'organizations/<ORG>/locations/<LOC>/workloads/<ID>'."
)
clean_wid = cand_workload
workload_name = (
f"organizations/{clean_org}/locations/{clean_loc}/workloads/{clean_wid}"
)
if clean_action == "apply":
if not raw_tokens:
raise ValueError(
"Missing required '--update-id' when '--action=apply'. Pass a"
" specific update ID, comma-separated update IDs, or"
" '--update-id=ALL'."
)
if len(raw_tokens) == 1 and raw_tokens[0].upper() == "ALL":
return workload_name, clean_org, clean_loc, clean_wid, None, ["ALL"]
update_names: list[str] = []
update_ids: list[str] = []
for tok in raw_tokens:
if tok.upper() == "ALL":
raise ValueError(
"'ALL' cannot be combined with individual update IDs in"
" '--update-id'."
)
u_match = _UPDATE_NAME_RE.fullmatch(tok)
if u_match:
u_id = u_match.group(4)
u_name = f"{workload_name}/updates/{u_id}"
else:
if not _ID_TOKEN_RE.fullmatch(tok):
raise ValueError(
f"Invalid '--update-id' value '{tok}'. Expected update ID,"
" comma-separated update IDs, 'ALL', or full update resource"
" name"
" 'organizations/<ORG>/locations/<LOC>/workloads/<WID>/updates/<UID>'."
)
u_id = tok
u_name = f"{workload_name}/updates/{u_id}"
if u_id not in update_ids:
update_ids.append(u_id)
update_names.append(u_name)
return (
workload_name,
clean_org,
clean_loc,
clean_wid,
update_names,
update_ids,
)
return workload_name, clean_org, clean_loc, clean_wid, None, None
def _create_discovery_service(location: str) -> Any:
"""Creates a regional Assured Workloads v1 Discovery service with telemetry."""
try:
import google.auth # pylint: disable=g-import-not-at-top
from google.api_core.client_options import ClientOptions # pylint: disable=g-import-not-at-top
from googleapiclient import discovery # pylint: disable=g-import-not-at-top
import googleapiclient.http # pylint: disable=g-import-not-at-top
except ImportError as exc:
raise RuntimeError(
"Missing required Python packages 'google-api-python-client' and"
" 'google-auth'. Install via: pip install"
' "google-api-python-client>=2.0,<3" "google-auth>=2.0,<3"'
) from exc
credentials, _ = google.auth.default()
endpoint = f"https://{location}-assuredworkloads.googleapis.com"
service = discovery.build(
"assuredworkloads",
"v1",
credentials=credentials,
client_options=ClientOptions(api_endpoint=endpoint),
)
if hasattr(service, "_http") and hasattr(
googleapiclient.http, "set_user_agent"
):
googleapiclient.http.set_user_agent(service._http, USER_AGENT)
return service
def _fetch_all_updates(
updates_resource: Any, workload_name: str, page_size: int
) -> tuple[list[dict[str, Any]], list[dict[str, Any]], dict[str, int]]:
"""Fetches all updates for a workload and computes state counts."""
all_updates: list[dict[str, Any]] = []
page_token: str | None = None
while True:
req_kwargs: dict[str, Any] = {
"parent": workload_name,
"pageSize": page_size,
}
if page_token:
req_kwargs["pageToken"] = page_token
response = updates_resource.list(**req_kwargs).execute() or {}
batch = response.get("workloadUpdates", [])
if batch:
all_updates.extend(batch)
page_token = response.get("nextPageToken")
if not page_token:
break
available_updates = [
u for u in all_updates if u.get("state") == "AVAILABLE"
]
state_counts = {
"AVAILABLE": len(available_updates),
"APPLIED": sum(1 for u in all_updates if u.get("state") == "APPLIED"),
"WITHDRAWN": sum(
1 for u in all_updates if u.get("state") == "WITHDRAWN"
),
"TOTAL": len(all_updates),
}
return all_updates, available_updates, state_counts
def _apply_single_update_and_poll(
updates_resource: Any,
operations_resource: Any,
workload_name: str,
update_name: str,
update_id: str,
poll_timeout_seconds: int,
poll_interval_seconds: int,
) -> dict[str, Any]:
"""Applies a single update and polls its LRO until completion or timeout."""
operation = updates_resource.apply(
name=update_name, body={"action": "APPLY"}
).execute()
op_name = operation.get("name")
if not op_name or operation.get("done") is True:
if "error" in operation:
raise RuntimeError(
f"ApplyWorkloadUpdate operation failed for '{update_name}':"
f" {json.dumps(operation['error'])}"
)
res: dict[str, Any] = {
"status": "APPLIED_WORKLOAD_UPDATE",
"action": "apply",
"workloadName": workload_name,
"updateName": update_name,
"updateId": update_id,
"operationName": op_name,
"operationDone": True,
}
applied_update = operation.get("response", {}).get("appliedUpdate")
if applied_update is not None:
res["appliedUpdate"] = applied_update
if operation.get("metadata") is not None:
res["operationMetadata"] = operation.get("metadata")
return res
deadline = time.monotonic() + poll_timeout_seconds
latest_op = operation
while time.monotonic() < deadline:
time.sleep(poll_interval_seconds)
latest_op = operations_resource.get(name=op_name).execute() or {}
if latest_op.get("done") is True:
if "error" in latest_op:
raise RuntimeError(
f"ApplyWorkloadUpdate operation '{op_name}' failed for"
f" '{update_name}': {json.dumps(latest_op['error'])}"
)
res = {
"status": "APPLIED_WORKLOAD_UPDATE",
"action": "apply",
"workloadName": workload_name,
"updateName": update_name,
"updateId": update_id,
"operationName": op_name,
"operationDone": True,
}
applied_update = latest_op.get("response", {}).get("appliedUpdate")
if applied_update is not None:
res["appliedUpdate"] = applied_update
if latest_op.get("metadata") is not None:
res["operationMetadata"] = latest_op.get("metadata")
return res
return {
"status": "APPLY_OPERATION_IN_PROGRESS",
"action": "apply",
"workloadName": workload_name,
"updateName": update_name,
"updateId": update_id,
"operationName": op_name,
"operationDone": False,
"message": (
f"Operation '{op_name}' is still running after"
f" {poll_timeout_seconds}s. Check status via 'gcloud assured"
f" operations describe {op_name}'."
),
}
def execute_manage_workload_updates(
action: str,
workload_id: str | None = None,
organization: str | None = None,
location: str | None = None,
update_id: str | None = None,
enable_compliance_updates: bool = False,
include_all_states: bool = False,
page_size: int = 50,
poll_timeout_seconds: int = 180,
poll_interval_seconds: int = 3,
dry_run: bool = False,
service: Any = None,
) -> dict[str, Any]:
"""Executes list, enable, or apply for Assured Workloads V1 configuration updates."""
clean_action = action.strip().lower()
(
workload_name,
clean_org,
clean_loc,
clean_wid,
update_names,
update_ids,
) = resolve_workload_and_update_names(
action=clean_action,
workload_id=workload_id,
organization=organization,
location=location,
update_id=update_id,
)
if page_size < 1 or page_size > 100:
raise ValueError(
f"Invalid '--page-size' value '{page_size}'. Must be between 1 and 100."
)
if poll_timeout_seconds < 0:
raise ValueError("--poll-timeout-seconds must be >= 0.")
if poll_interval_seconds < 1:
raise ValueError("--poll-interval-seconds must be >= 1.")
regional_endpoint = f"https://{clean_loc}-assuredworkloads.googleapis.com"
if dry_run:
if clean_action == "list":
return {
"status": "DRY_RUN",
"action": "list",
"workloadName": workload_name,
"organization": clean_org,
"location": clean_loc,
"workloadId": clean_wid,
"regionalEndpoint": regional_endpoint,
"userAgent": USER_AGENT,
"enableComplianceUpdatesFirst": enable_compliance_updates,
"request": {
"method": (
"assuredworkloads.organizations.locations.workloads.updates.list"
),
"parent": workload_name,
"pageSize": page_size,
},
}
if clean_action == "enable":
return {
"status": "DRY_RUN",
"action": "enable",
"workloadName": workload_name,
"organization": clean_org,
"location": clean_loc,
"workloadId": clean_wid,
"regionalEndpoint": regional_endpoint,
"userAgent": USER_AGENT,
"request": {
"method": (
"assuredworkloads.organizations.locations.workloads.enableComplianceUpdates"
),
"name": workload_name,
},
}
if update_ids == ["ALL"]:
return {
"status": "DRY_RUN",
"action": "apply",
"workloadName": workload_name,
"updateId": "ALL",
"updateIds": ["ALL"],
"organization": clean_org,
"location": clean_loc,
"workloadId": clean_wid,
"regionalEndpoint": regional_endpoint,
"userAgent": USER_AGENT,
"request": {
"method": (
"assuredworkloads.organizations.locations.workloads.updates.apply"
),
"mode": "APPLY_ALL_AVAILABLE_SEQUENTIALLY",
"parent": workload_name,
"body": {"action": "APPLY"},
},
}
assert update_names is not None and update_ids is not None
if len(update_names) == 1:
return {
"status": "DRY_RUN",
"action": "apply",
"workloadName": workload_name,
"updateName": update_names[0],
"updateId": update_ids[0],
"organization": clean_org,
"location": clean_loc,
"workloadId": clean_wid,
"regionalEndpoint": regional_endpoint,
"userAgent": USER_AGENT,
"request": {
"method": (
"assuredworkloads.organizations.locations.workloads.updates.apply"
),
"name": update_names[0],
"body": {"action": "APPLY"},
},
}
return {
"status": "DRY_RUN",
"action": "apply",
"workloadName": workload_name,
"updateNames": update_names,
"updateIds": update_ids,
"organization": clean_org,
"location": clean_loc,
"workloadId": clean_wid,
"regionalEndpoint": regional_endpoint,
"userAgent": USER_AGENT,
"request": {
"method": (
"assuredworkloads.organizations.locations.workloads.updates.apply"
),
"names": update_names,
"body": {"action": "APPLY"},
},
}
if service is None:
service = _create_discovery_service(clean_loc)
workloads_resource = service.organizations().locations().workloads()
if clean_action == "enable":
workloads_resource.enableComplianceUpdates(name=workload_name).execute()
return {
"status": "ENABLED_COMPLIANCE_UPDATES",
"action": "enable",
"workloadName": workload_name,
"regionalEndpoint": regional_endpoint,
}
updates_resource = workloads_resource.updates()
if clean_action == "list":
if enable_compliance_updates:
workloads_resource.enableComplianceUpdates(name=workload_name).execute()
all_updates, available_updates, state_counts = _fetch_all_updates(
updates_resource, workload_name, page_size
)
result: dict[str, Any] = {
"status": "LISTED_WORKLOAD_UPDATES",
"action": "list",
"workloadName": workload_name,
"regionalEndpoint": regional_endpoint,
"complianceUpdatesEnabledInRun": enable_compliance_updates,
"stateCounts": state_counts,
"availableUpdates": available_updates,
}
if include_all_states:
result["allUpdates"] = all_updates
return result
# clean_action == "apply"
operations_resource = service.organizations().locations().operations()
if update_ids == ["ALL"]:
_, available_updates, initial_counts = _fetch_all_updates(
updates_resource, workload_name, page_size
)
if not available_updates:
return {
"status": "NO_AVAILABLE_UPDATES_TO_APPLY",
"action": "apply",
"workloadName": workload_name,
"appliedCount": 0,
"appliedUpdates": [],
"stateCounts": initial_counts,
}
targets: list[tuple[str, str]] = []
for u in available_updates:
u_name = u.get("name", "")
if u_name:
targets.append((u_name, u_name.split("/")[-1]))
else:
assert update_names is not None and update_ids is not None
targets = list(zip(update_names, update_ids))
# Single update ID (preserve exact single-update output structure)
if len(targets) == 1 and update_ids != ["ALL"]:
u_name, u_id = targets[0]
return _apply_single_update_and_poll(
updates_resource=updates_resource,
operations_resource=operations_resource,
workload_name=workload_name,
update_name=u_name,
update_id=u_id,
poll_timeout_seconds=poll_timeout_seconds,
poll_interval_seconds=poll_interval_seconds,
)
# Multiple updates or --update-id=ALL: apply sequentially, then verify via list
applied_results: list[dict[str, Any]] = []
for u_name, u_id in targets:
single_res = _apply_single_update_and_poll(
updates_resource=updates_resource,
operations_resource=operations_resource,
workload_name=workload_name,
update_name=u_name,
update_id=u_id,
poll_timeout_seconds=poll_timeout_seconds,
poll_interval_seconds=poll_interval_seconds,
)
applied_results.append(single_res)
_, remaining_available, final_state_counts = _fetch_all_updates(
updates_resource, workload_name, page_size
)
all_done = all(r.get("operationDone") is True for r in applied_results)
return {
"status": (
"APPLIED_WORKLOAD_UPDATES"
if all_done
else "APPLY_OPERATIONS_IN_PROGRESS"
),
"action": "apply",
"workloadName": workload_name,
"appliedCount": sum(
1 for r in applied_results if r.get("operationDone") is True
),
"totalRequested": len(targets),
"allOperationsDone": all_done,
"appliedUpdates": applied_results,
"postApplyStateCounts": final_state_counts,
"remainingAvailableUpdates": remaining_available,
}
def build_arg_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(
description=(
"List, enable, or apply Assured Workloads V1 configuration updates"
" via google-api-python-client."
)
)
parser.add_argument(
"--action",
default="list",
choices=["list", "enable", "apply"],
help=(
"Update action: 'list' available updates (default), 'enable'"
" compliance updates, or 'apply' update(s)."
),
)
parser.add_argument(
"--workload-id",
default=None,
help=(
"Workload ID or full resource name"
" (organizations/<ORG>/locations/<LOC>/workloads/<ID>)."
),
)
parser.add_argument(
"--organization",
default=None,
help="Numeric Organization ID (optional if full resource name is passed).",
)
parser.add_argument(
"--location",
default=None,
help=(
"Regional or multi-regional location, e.g., us-central1, us, eu"
" (optional if full resource name is passed)."
),
)
parser.add_argument(
"--update-id",
default=None,
help=(
"Update ID, comma-separated Update IDs (<ID1>,<ID2>), 'ALL' (to apply"
" all AVAILABLE updates sequentially), or full update resource name"
" (organizations/<ORG>/locations/<LOC>/workloads/<WID>/updates/<UID>)"
" required when --action=apply."
),
)
parser.add_argument(
"--enable-compliance-updates",
action="store_true",
help=(
"Call enableComplianceUpdates on the workload before listing updates"
" (useful for existing folders where compliance updates are not yet"
" enabled)."
),
)
parser.add_argument(
"--include-all-states",
action="store_true",
help=(
"Include APPLIED and WITHDRAWN updates in addition to AVAILABLE"
" updates when --action=list."
),
)
parser.add_argument(
"--page-size",
type=int,
default=50,
help="Page size for updates.list (1-100, default: 50).",
)
parser.add_argument(
"--poll-timeout-seconds",
type=int,
default=180,
help=(
"Maximum seconds to poll each ApplyWorkloadUpdate LRO until"
" completion (default: 180)."
),
)
parser.add_argument(
"--poll-interval-seconds",
type=int,
default=3,
help="Seconds between LRO status checks when --action=apply (default: 3).",
)
parser.add_argument(
"--dry-run",
action="store_true",
help=(
"Validate arguments and print the request details without calling the"
" API."
),
)
return parser
def main(argv: list[str] | None = None) -> int:
parser = build_arg_parser()
args = parser.parse_args(argv)
try:
result = execute_manage_workload_updates(
action=args.action,
workload_id=args.workload_id,
organization=args.organization,
location=args.location,
update_id=args.update_id,
enable_compliance_updates=args.enable_compliance_updates,
include_all_states=args.include_all_states,
page_size=args.page_size,
poll_timeout_seconds=args.poll_timeout_seconds,
poll_interval_seconds=args.poll_interval_seconds,
dry_run=args.dry_run,
)
print(json.dumps(result, indent=2))
return 0
except (ValueError, RuntimeError) as err:
sys.stderr.write(f"MANAGE_WORKLOAD_UPDATES_ERROR: {err}\n")
return 1
except Exception as err: # pylint: disable=broad-exception-caught
sys.stderr.write(f"MANAGE_WORKLOAD_UPDATES_ERROR: {err}\n")
return 1
if __name__ == "__main__":
sys.exit(main())
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs a command-line tool, Needs Python
Security
What our scanner noticed· Low risk
- Instructions that use the network
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Publisher
- Google (Official)
- Source
- google/skills · skills/cloud/assured-workloads-basics · commit 2194d3b
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Google or Google.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download assured-workloads-basics.zip