GCP

Manage and secure Google Cloud Platform resources. On Cortex Platform, authentication is handled automatically via the cloud connector. For Cortex XSOAR and Cortex XSIAM (version < 3.0), configure a GCP Service Account private key JSON and a default GCP Project ID.

Cloud Services · GCP

Details

IDGCP
ProviderGoogle
CategoryCloud Services
From Version8.11.0
Docker Imagedemisto/googleapi-python3:1.0.0.10182333
Supported ModulesCloud Posture Security Cortex Cloud Cloud Runtime Security Agentix XSIAM

README

The GCP Integration automates management and security configurations for Compute Engine, Storage, and Container resources on GCP.
This integration was integrated and tested with version v1 (Compute, Storage, Container, SERVICE_USAGE), v3 (Resource Manager).

Configure Google Cloud Platform in Cortex

Parameter Description Required
Service Account Private Key (JSON) The full content of a GCP Service Account private key JSON file. In the Google Cloud Console, go to IAM & Admin > Service Accounts and create a service account with the required roles. Then under the Keys tab, create a JSON key. Paste the downloaded JSON contents here. True
GCP Project ID The GCP project ID to authenticate against when testing the integration (e.g. my-project-123). If left empty, the project ID from the Service Account private key JSON is used. False
Use system proxy settings   False
Trust any certificate (not secure)   False

Commands

You can execute these commands from the CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

gcp-compute-firewall-patch


Updates a specific firewall rule with the data included in the request. Required permissions: compute.firewalls.update, compute.firewalls.get, compute.firewalls.list, compute.networks.updatePolicy, compute.networks.list.

Base Command

gcp-compute-firewall-patch

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
resource_name Name of the firewall rule to update. Required
description An optional description of this resource, which you provide when you create the resource. Optional
network URL of the network resource for this firewall rule. If not specified when creating a firewall rule, the default network is used. Optional
priority Priority for this rule. This is an integer between 0 and 65535, both inclusive. When not specified, the value assumed is 1000. Relative priorities determine precedence of conflicting rules. Lower value of priority implies higher precedence (eg, a rule with priority 0 has higher precedence than a rule with priority 1). DENY rules take precedence over ALLOW rules. Optional
sourceRanges If source ranges are specified, the firewall applies only to traffic with source IP addresses in these ranges. These ranges must be expressed in CIDR format. One or both of sourceRanges and sourceTags may be set. If both properties are set, the firewall applies to traffic with a source IP address within sourceRanges OR the source IP belonging to a tag listed in the sourceTags property. The connection does not need to match both properties for the firewall to apply. Only IPv4 is supported, comma-separated. Optional
destinationRanges If destination ranges are specified, the firewall applies only to traffic with destination IP addresses in these ranges. These ranges must be expressed in CIDR format. Only IPv4 is supported. Comma-separated. Optional
sourceTags If source tags are specified, the firewall rule applies only to traffic with source IPs that match the primary network interfaces of VM instances that have the tag and are in the same VPC network. Source tags cannot be used to control traffic to an instance’s external IP address. It only applies to traffic between instances in the same virtual network, because tags are associated with instances, not IP addresses. One or both of sourceRanges and sourceTags may be set. If both properties are set, the firewall will apply to traffic with a source IP address within sourceRanges OR the source IP belonging to a tag listed in the sourceTags property. The connection does not need to match both properties for the firewall to apply. Comma-separated. Optional
targetTags A list of tags that controls which instances the firewall rule applies to. If targetTags are specified, then the firewall rule applies only to instances in the VPC network that have one of those tags. If no targetTags are specified, the firewall rule applies to all instances on the specified network. Comma-separated. Optional
sourceServiceAccounts If source service accounts are specified, the firewall applies only to traffic originating from an instance with a service account in this list. Source service accounts cannot be used to control traffic to an instance’s external IP address because service accounts are associated with an instance, not an IP address. sourceRanges can be set at the same time as sourceServiceAccounts. If both are set, the firewall will apply to traffic that has a source IP address within sourceRanges OR the source IP belongs to an instance with a service account listed in sourceServiceAccount. The connection does not need to match both properties for the firewall to apply. sourceServiceAccounts cannot be used simultaneously as sourceTags or targetTags. Comma-separated. Optional
targetServiceAccounts A list of service accounts indicating sets of instances located in the network that may make network connections as specified in allowed[]. targetServiceAccounts cannot be used at the same time as targetTags or sourceTags. If neither targetServiceAccounts nor targetTags are specified, the firewall rule applies to all instances on the specified network. Comma-separated. Optional
allowed The ALLOW rules list specified by this firewall. Each rule specifies a protocol and port-range tuple that describes a permitted connection. For example, ipprotocol=tcp,ports=22,443;ipprotocol=tcp,ports=8080,80. Optional
denied The DENY rules list specified by this firewall. Each rule specifies a protocol and port-range tuple that describes a denied connection. For example, ipprotocol=tcp,ports=22,443;ipprotocol=tcp,ports=8080,80. Optional
direction Direction of traffic to which this firewall applies; default is INGRESS. Note: For INGRESS traffic, it is NOT supported to specify destinationRanges; For EGRESS traffic, it is NOT supported to specify sourceRanges OR sourceTags. Optional
logConfigEnable This field denotes whether to enable logging for a particular firewall rule. Possible values are: true, false. Optional
disabled Denotes whether the firewall rule is disabled, i.e not applied to the network it is associated with. When set to true, the firewall rule is not enforced and the network behaves as if it did not exist. If this is unspecified, the firewall rule will be enabled. Possible values are: true, false. Optional

Context Output

Path Type Description
GCP.Compute.Operations.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.Operations.name string Name of the resource.
GCP.Compute.Operations.zone string The URL of the zone where the operation resides. Only available when performing per-zone operations. You must specify this field as part of the HTTP request URL. It is not configurable as a field in the request body.
GCP.Compute.Operations.clientOperationId string The value of requestId if you provided it in the request. Not present otherwise.
GCP.Compute.Operations.operationType string The type of operation, such as insert, update, or delete, and so on.
GCP.Compute.Operations.targetLink string The URL of the resource that the operation modifies. For operations related to creating a snapshot, this points to the persistent disk from which the snapshot was created.
GCP.Compute.Operations.targetId string The unique target ID, which identifies a specific incarnation of the target resource.
GCP.Compute.Operations.status string The status of the operation, which can be one of the following: PENDING, RUNNING or DONE.
GCP.Compute.Operations.statusMessage string An optional textual description of the current status of the operation.
GCP.Compute.Operations.user string The user who requested the operation, for example, EMAILADDRESS.
GCP.Compute.Operations.progress number An optional progress indicator that ranges from 0 to 100. There is no requirement that this be linear or support any granularity of operations. This should not be used to guess when the operation will be complete. This number should monotonically increase as the operation progresses.
GCP.Compute.Operations.insertTime string The time the operation was requested, in RFC3339 text format.
GCP.Compute.Operations.startTime string The time that this operation was started by the server. This value is in RFC3339 text format.
GCP.Compute.Operations.endTime string The time that this operation was completed. This value is in RFC3339 text format.
GCP.Compute.Operations.error string If errors are generated during processing of the operation, this field will be populated.
GCP.Compute.Operations.warnings string If warning messages are generated during processing of the operation, this field will be populated.
GCP.Compute.Operations.httpErrorStatusCode number If the operation fails, this field contains the HTTP error status code that was returned. For example, a 404 means the resource was not found.
GCP.Compute.Operations.httpErrorMessage string If the operation fails, this field contains the HTTP error message that was returned, such as NOT FOUND.
GCP.Compute.Operations.selfLink string Server-defined URL for the resource.
GCP.Compute.Operations.region string The URL of the region where the operation resides. Only available when performing regional operations. You must specify this field as part of the HTTP request URL. It is not configurable as a field in the request body.
GCP.Compute.Operations.description string A textual description of the operation, which is set when the operation is created.
GCP.Compute.Operations.kind string Type of the resource. Always compute#operation for Operation resources.

gcp-storage-bucket-policy-delete


Removes an entity from a bucket’s Access Control List. Required permissions: storage.buckets.getIamPolicy, storage.buckets.setIamPolicy.

Base Command

gcp-storage-bucket-policy-delete

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
resource_name Name of the GCS bucket. Required
entity Entity to remove from the Access Control List.
Common entity formats are:
user:<userId or email>
group:<groupId or email>
allUsers
allAuthenticatedUsers
For more options and details, see: https://cloud.google.com/storage/docs/json_api/v1/bucketAccessControls#resource . Default is allUsers.
Optional

Context Output

There is no context output for this command.

gcp-compute-subnet-update


Enables flow logs or Private Google Access on a subnet. Required permissions: compute.subnetworks.setPrivateIpGoogleAccess, compute.subnetworks.update, compute.subnetworks.get, compute.subnetworks.list.

Base Command

gcp-compute-subnet-update

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
region GCP region. Required
resource_name Subnet name. Required
enable_flow_logs Enable VPC Flow Logs. Possible values are: true, false. Optional
enable_private_ip_google_access Enable Private Google Access. Possible values are: true, false. Optional

Context Output

Path Type Description
GCP.Compute.Operations.name String The name of the updated subnet.
GCP.Compute.Operations.region String The region of the updated subnet.
GCP.Compute.Operations.subnetName String The name of the subnet that was updated.
GCP.Compute.Operations.enableFlowLogs Boolean Whether flow logs are enabled for the subnet.
GCP.Compute.Operations.ipCidrRange String The updated CIDR range for the subnet.
GCP.Compute.Operations.privateIpGoogleAccess Boolean Whether private Google access is enabled for the subnet.
GCP.Compute.Operations.privateIpv6GoogleAccess Boolean Whether private IPv6 Google access is enabled for the subnet.
GCP.Compute.Operations.stackType String The stack type of the subnet (e.g., `IPV4`, `IPV6`).
GCP.Compute.Operations.rangeName String The name of the IP range associated with the subnet.
GCP.Compute.Operations.secondaryIpRanges Unknown List of secondary IP ranges for the subnet.
GCP.Compute.Operations.description String The description of the subnet.

gcp-container-cluster-security-update


Configures security settings for GKE clusters, including access controls and visibility. Required permissions: container.clusters.update, container.clusters.get, container.clusters.list.

Base Command

gcp-container-cluster-security-update

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
region GCP region. Required
resource_name Name of the GKE cluster. Required
enable_intra_node_visibility Enable intra-node visibility. Possible values are: true, false. Optional
enable_master_authorized_networks Enable Master Authorized Networks. Possible values are: true, false. Optional
cidrs Comma-separated list of up to 50 CIDR blocks (e.g., “192.168.0.0/24,10.0.0.0/32”) that are allowed to access the Kubernetes master via HTTPS.
If enable_master_authorized_networks is true and no CIDRs are provided, all access will be blocked.
.
Optional

Context Output

Path Type Description
GCP.Container.Operations.name String The name of the GKE cluster.
GCP.Container.Operations.zone String The zone of the GKE cluster.
GCP.Container.Operations.enableStackdriverLogging Boolean Whether Stackdriver Logging is enabled for the cluster.
GCP.Container.Operations.enableStackdriverMonitoring Boolean Whether Stackdriver Monitoring is enabled for the cluster.
GCP.Container.Operations.enablePrivateNodes Boolean Whether private nodes are enabled for the GKE cluster.
GCP.Container.Operations.enablePrivateEndpoint Boolean Whether private endpoint is enabled for the GKE cluster control plane.
GCP.Container.Operations.enableHttpsOnly Boolean Whether HTTPS-only traffic is enforced for the cluster.
GCP.Container.Operations.enableNetworkPolicy Boolean Whether network policies are enabled for the cluster.
GCP.Container.Operations.enableAutoscaling Boolean Whether autoscaling is enabled for the cluster nodes.
GCP.Container.Operations.enableIstio Boolean Whether Istio is enabled for the GKE cluster.
GCP.Container.Operations.enablePodSecurityPolicy Boolean Whether PodSecurityPolicy is enabled for the GKE cluster.
GCP.Container.Operations.enableBinaryAuthorization Boolean Whether Binary Authorization is enabled for the cluster.
GCP.Container.Operations.enableLegacyABAC Boolean Whether legacy ABAC is enabled for the cluster.
GCP.Container.Operations.clusterIpv4Cidr String The cluster’s IPv4 CIDR block.
GCP.Container.Operations.masterAuthorizedNetworksConfig.cidrBlocks Unknown List of authorized CIDR blocks that can access the GKE cluster master.
GCP.Container.Operations.masterAuthorizedNetworksConfig.enabled Boolean Whether master authorized networks are enabled for the cluster.
GCP.Container.Operations.network String The network to which the GKE cluster belongs.
GCP.Container.Operations.subnetwork String The subnetwork to which the GKE cluster belongs.
GCP.Container.Operations.loggingService String The logging service used for the cluster (e.g., “logging.googleapis.com”).
GCP.Container.Operations.monitoringService String The monitoring service used for the cluster (e.g., “monitoring.googleapis.com”).
GCP.Container.Operations.nodePools Unknown A list of node pools in the cluster, with their configuration and security settings.
GCP.Container.Operations.privateClusterConfig.enablePrivateNodes Boolean Whether private nodes are enabled in the cluster.
GCP.Container.Operations.privateClusterConfig.enablePrivateEndpoint Boolean Whether private endpoint is enabled for the cluster control plane.
GCP.Container.Operations.masterVersion String The current version of the Kubernetes master in the GKE cluster.

gcp-storage-bucket-metadata-update


Updates Google Cloud Storage (GCS) bucket metadata, including settings such as versioning and Uniform Bucket-Level Access (UBLA). Required permission: storage.buckets.update.

Base Command

gcp-storage-bucket-metadata-update

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
resource_name Name of the bucket. Required
enable_versioning Enable versioning. Possible values are: true, false. Optional
enable_uniform_access Enable uniform bucket-level access. Possible values are: true, false. Optional

Context Output

Path Type Description
GCP.StorageBucket.Metadata Unknown Updated bucket metadata.
GCP.StorageBucket.Metadata.name String The name of the GCP bucket.
GCP.StorageBucket.Metadata.id String The ID of the GCP bucket.
GCP.StorageBucket.Metadata.location String The location of the bucket.
GCP.StorageBucket.Metadata.storageClass String The storage class of the bucket (e.g., STANDARD, NEARLINE).
GCP.StorageBucket.Metadata.created Date The creation timestamp of the bucket.
GCP.StorageBucket.Metadata.updated Date The last update timestamp of the bucket.
GCP.StorageBucket.Metadata.metageneration Number The metadata generation of the bucket.
GCP.StorageBucket.Metadata.labels Unknown The labels attached to the bucket.
GCP.StorageBucket.Metadata.defaultEventBasedHold Boolean Whether a default event-based hold is enabled on the bucket.
GCP.StorageBucket.Metadata.retentionPolicy.retentionPeriod Number The duration in seconds that objects in the bucket must be retained.
GCP.StorageBucket.Metadata.retentionPolicy.effectiveTime Date The time from which the retention policy is effective.
GCP.StorageBucket.Metadata.retentionPolicy.isLocked Boolean Whether the retention policy is locked.
GCP.StorageBucket.Metadata.versioning.enabled Boolean Whether object versioning is enabled.
GCP.StorageBucket.Metadata.logging.logBucket String The destination bucket where access logs are stored.
GCP.StorageBucket.Metadata.logging.logObjectPrefix String The object prefix used for logging.
GCP.StorageBucket.Metadata.lifecycle.rule Unknown A list of lifecycle management rules for the bucket.
GCP.StorageBucket.Metadata.iamConfiguration.uniformBucketLevelAccess Boolean Whether uniform bucket-level access is enabled.
GCP.StorageBucket.Metadata.cors Unknown CORS configuration for the bucket.
GCP.StorageBucket.Metadata.customPlacementConfig Unknown Custom placement configuration for multi-region buckets.
GCP.StorageBucket.Metadata.encryption.defaultKmsKeyName String The default Cloud KMS key used to encrypt objects.
GCP.StorageBucket.Metadata.billing.requesterPays Boolean Whether requester pays is enabled.
GCP.StorageBucket.Metadata.website.mainPageSuffix String Suffix appended to requests for the bucket’s website configuration.
GCP.StorageBucket.Metadata.website.notFoundPage String The path to the custom 404 page for the bucket website.

gcp-iam-project-policy-binding-remove


Removes a specified IAM role binding from a GCP project. Required permissions: resourcemanager.projects.getIamPolicy, resourcemanager.projects.setIamPolicy.

Base Command

gcp-iam-project-policy-binding-remove

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
member Member to remove (e.g., user:test@example.com). Required
role Role to remove (e.g., roles/viewer). Required

Context Output

There is no context output for this command.

gcp-compute-instance-service-account-set


Sets the service account for a GCP Compute Engine VM instance. The instance must be stopped before the service account can be changed. Required permissions: compute.instances.setServiceAccount, compute.instances.get.

Base Command

gcp-compute-instance-service-account-set

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
zone The name of the zone for this request. Required
resource_name Name of the VM instance. Required
service_account_email Email of the service account. Required
scopes OAuth scopes to assign (full URLs), e.g., https://www.googleapis.com/auth/cloud-platform. Empty list means no scopes. Required

Context Output

Path Type Description
GCP.Compute.Operations.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.Operations.name string Name of the resource.
GCP.Compute.Operations.zone string The URL of the zone where the operation resides. Only available when performing per-zone operations. You must specify this field as part of the HTTP request URL. It is not configurable as a field in the request body.
GCP.Compute.Operations.clientOperationId string The value of requestId if you provided it in the request. Not present otherwise.
GCP.Compute.Operations.operationType string The type of operation, such as insert, update, or delete, and so on.
GCP.Compute.Operations.targetLink string The URL of the resource that the operation modifies. For operations related to creating a snapshot, this points to the persistent disk from which the snapshot was created.
GCP.Compute.Operations.targetId string The unique target ID, which identifies a specific incarnation of the target resource.
GCP.Compute.Operations.status string The status of the operation, which can be one of the following: PENDING, RUNNING or DONE.
GCP.Compute.Operations.statusMessage string An optional textual description of the current status of the operation.
GCP.Compute.Operations.user string The user who requested the operation, for example, EMAILADDRESS.
GCP.Compute.Operations.progress number An optional progress indicator that ranges from 0 to 100. There is no requirement that this be linear or support any granularity of operations. This should not be used to guess when the operation will be complete. This number should monotonically increase as the operation progresses.
GCP.Compute.Operations.insertTime string The time the operation was requested, in RFC3339 text format.
GCP.Compute.Operations.startTime string The time that this operation was started by the server. This value is in RFC3339 text format.
GCP.Compute.Operations.endTime string The time that this operation was completed. This value is in RFC3339 text format.
GCP.Compute.Operations.error string If errors are generated during processing of the operation, this field will be populated.
GCP.Compute.Operations.warnings string If warning messages are generated during processing of the operation, this field will be populated.
GCP.Compute.Operations.httpErrorStatusCode number If the operation fails, this field contains the HTTP error status code that was returned. For example, a 404 means the resource was not found.
GCP.Compute.Operations.httpErrorMessage string If the operation fails, this field contains the HTTP error message that was returned, such as NOT FOUND.
GCP.Compute.Operations.selfLink string Server-defined URL for the resource.
GCP.Compute.Operations.region string The URL of the region where the operation resides. Only available when performing regional operations. You must specify this field as part of the HTTP request URL. It is not configurable as a field in the request body.
GCP.Compute.Operations.description string A textual description of the operation, which is set when the operation is created.
GCP.Compute.Operations.kind string Type of the resource. Always compute#operation for Operation resources.

gcp-compute-instance-service-account-remove


Removes the service account associated with a GCP Compute Engine VM instance. The instance must be stopped before the service account can be changed. Required permissions: compute.instances.setServiceAccount, compute.instances.get.

Base Command

gcp-compute-instance-service-account-remove

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
zone The name of the zone for this request. Required
resource_name Name of the VM instance. Required

Context Output

Path Type Description
GCP.Compute.Operations.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.Operations.name string Name of the resource.
GCP.Compute.Operations.zone string The URL of the zone where the operation resides. Only available when performing per-zone operations. You must specify this field as part of the HTTP request URL. It is not configurable as a field in the request body.
GCP.Compute.Operations.clientOperationId string The value of requestId if you provided it in the request. Not present otherwise.
GCP.Compute.Operations.operationType string The type of operation, such as insert, update, or delete, and so on.
GCP.Compute.Operations.targetLink string The URL of the resource that the operation modifies. For operations related to creating a snapshot, this points to the persistent disk from which the snapshot was created.
GCP.Compute.Operations.targetId string The unique target ID, which identifies a specific incarnation of the target resource.
GCP.Compute.Operations.status string The status of the operation, which can be one of the following: PENDING, RUNNING or DONE.
GCP.Compute.Operations.statusMessage string An optional textual description of the current status of the operation.
GCP.Compute.Operations.user string The user who requested the operation, for example, EMAILADDRESS.
GCP.Compute.Operations.progress number An optional progress indicator that ranges from 0 to 100. There is no requirement that this be linear or support any granularity of operations. This should not be used to guess when the operation will be complete. This number should monotonically increase as the operation progresses.
GCP.Compute.Operations.insertTime string The time that this operation was requested. This value is in RFC3339 text format.
GCP.Compute.Operations.startTime string The time that this operation was started by the server. This value is in RFC3339 text format.
GCP.Compute.Operations.endTime string The time that this operation was completed. This value is in RFC3339 text format.
GCP.Compute.Operations.error string If errors are generated during processing of the operation, this field will be populated.
GCP.Compute.Operations.warnings string If warning messages are generated during processing of the operation, this field will be populated.
GCP.Compute.Operations.httpErrorStatusCode number If the operation fails, this field contains the HTTP error status code that was returned. For example, a 404 means the resource was not found.
GCP.Compute.Operations.httpErrorMessage string If the operation fails, this field contains the HTTP error message that was returned, such as NOT FOUND.
GCP.Compute.Operations.selfLink string Server-defined URL for the resource.
GCP.Compute.Operations.region string The URL of the region where the operation resides. Only available when performing regional operations. You must specify this field as part of the HTTP request URL. It is not configurable as a field in the request body.
GCP.Compute.Operations.description string A textual description of the operation, which is set when the operation is created.
GCP.Compute.Operations.kind string Type of the resource. Always compute#operation for Operation resources.

gcp-compute-instance-start


Starts an instance that was stopped using the instances().stop method. Required permission: compute.instances.start.

Base Command

gcp-compute-instance-start

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
zone The name of the zone for this request. Required
resource_name Name of the instance resource to start. Required

Context Output

Path Type Description
GCP.Compute.Operations.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.Operations.name string Name of the resource.
GCP.Compute.Operations.zone string The URL of the zone where the operation resides. Only available when performing per-zone operations. You must specify this field as part of the HTTP request URL. It is not configurable as a field in the request body.
GCP.Compute.Operations.clientOperationId string The value of requestId if you provided it in the request. Not present otherwise.
GCP.Compute.Operations.operationType string The type of operation, such as insert, update, or delete, and so on.
GCP.Compute.Operations.targetLink string The URL of the resource that the operation modifies. For operations related to creating a snapshot, this points to the persistent disk from which the snapshot was created.
GCP.Compute.Operations.targetId string The unique target ID, which identifies a specific incarnation of the target resource.
GCP.Compute.Operations.status string The status of the operation, which can be one of the following: PENDING RUNNING or DONE.
GCP.Compute.Operations.statusMessage string An optional textual description of the current status of the operation.
GCP.Compute.Operations.user string The user who requested the operation, for example, EMAILADDRESS.
GCP.Compute.Operations.progress number An optional progress indicator that ranges from 0 to 100. There is no requirement that this be linear or support any granularity of operations. This should not be used to guess when the operation will be complete. This number should monotonically increase as the operation progresses.
GCP.Compute.Operations.insertTime string The time that this operation was requested. This value is in RFC3339 text format.
GCP.Compute.Operations.startTime string The time that this operation was started by the server. This value is in RFC3339 text format.
GCP.Compute.Operations.endTime string The time that this operation was completed. This value is in RFC3339 text format.
GCP.Compute.Operations.error string If errors are generated during processing of the operation, this field will be populated.
GCP.Compute.Operations.warnings string If warning messages are generated during processing of the operation, this field will be populated.
GCP.Compute.Operations.httpErrorStatusCode number If the operation fails, this field contains the HTTP error status code that was returned. For example, a 404 means the resource was not found.
GCP.Compute.Operations.httpErrorMessage string If the operation fails, this field contains the HTTP error message that was returned, such as NOT FOUND.
GCP.Compute.Operations.selfLink string Server-defined URL for the resource.
GCP.Compute.Operations.region string The URL of the region where the operation resides. Only available when performing regional operations. You must specify this field as part of the HTTP request URL. It is not configurable as a field in the request body.
GCP.Compute.Operations.description string A textual description of the operation, which is set when the operation is created.
GCP.Compute.Operations.kind string Type of the resource. Always compute#operation for Operation resources.

gcp-compute-instance-stop


Stops and cleanly shuts down a running instance, allowing you to restart the instance at a later time. Stopped instances do not incur VM usage charges while they are stopped. However, resources that the VM is using such as persistent disks and static IP addresses will continue to be charged until they are deleted. Required permission: compute.instances.stop.

Base Command

gcp-compute-instance-stop

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
zone The name of the zone for this request. Required
resource_name Name of the instance resource to stop. Required

Context Output

Path Type Description
GCP.Compute.Operations.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.Operations.name string Name of the resource.
GCP.Compute.Operations.zone string The URL of the zone where the operation resides. Only available when performing per-zone operations. You must specify this field as part of the HTTP request URL. It is not configurable as a field in the request body.
GCP.Compute.Operations.clientOperationId string The value of requestId if you provided it in the request. Not present otherwise.
GCP.Compute.Operations.operationType string The type of operation, such as insert, update, or delete, and so on.
GCP.Compute.Operations.targetLink string The URL of the resource that the operation modifies. For operations related to creating a snapshot, this points to the persistent disk from which the snapshot was created.
GCP.Compute.Operations.targetId string The unique target ID, which identifies a specific incarnation of the target resource.
GCP.Compute.Operations.status string The status of the operation, which can be one of the following: PENDING RUNNING or DONE.
GCP.Compute.Operations.statusMessage string An optional textual description of the current status of the operation.
GCP.Compute.Operations.user string The user who requested the operation, for example, EMAILADDRESS.
GCP.Compute.Operations.progress number An optional progress indicator that ranges from 0 to 100. There is no requirement that this be linear or support any granularity of operations. This should not be used to guess when the operation will be complete. This number should monotonically increase as the operation progresses.
GCP.Compute.Operations.insertTime string The time that this operation was requested. This value is in RFC3339 text format.
GCP.Compute.Operations.startTime string The time that this operation was started by the server. This value is in RFC3339 text format.
GCP.Compute.Operations.endTime string The time that this operation was completed. This value is in RFC3339 text format.
GCP.Compute.Operations.error string If errors are generated during processing of the operation, this field will be populated.
GCP.Compute.Operations.warnings string If warning messages are generated during processing of the operation, this field will be populated.
GCP.Compute.Operations.httpErrorStatusCode number If the operation fails, this field contains the HTTP error status code that was returned. For example, a 404 means the resource was not found.
GCP.Compute.Operations.httpErrorMessage string If the operation fails, this field contains the HTTP error message that was returned, such as NOT FOUND.
GCP.Compute.Operations.selfLink string Server-defined URL for the resource.
GCP.Compute.Operations.region string The URL of the region where the operation resides. Only available when performing regional operations. You must specify this field as part of the HTTP request URL. It is not configurable as a field in the request body.
GCP.Compute.Operations.description string A textual description of the operation, which is set when the operation is created.
GCP.Compute.Operations.kind string Type of the resource. Always compute#operation for Operation resources.

gcp-compute-instances-list


Retrieves the list of instances in the specified zone. Required permission: compute.instances.list.

Base Command

gcp-compute-instances-list

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
zone The name of the zone for this request. Required
filters A filter expression for resources listed in the response. The expression must specify a field name, a comparison operator (=, !=, >, or <), and a value, which can be a string, number, or boolean. For example, to exclude a Compute Engine instance named example-instance, use name != example-instance. Optional
limit The maximum number of results per page that should be returned. Acceptable values are 0 to 500, inclusive. Default is 50. Optional
order_by Sorts list results in a specific order. By default, results are returned in alphanumerical order based on the resource name. You can also sort results in descending order based on the creation timestamp using order_by=”creationTimestamp desc”. Optional
page_token Specifies a page token. Set page_token to the nextPageToken returned by a previous list request to get the next page of results. Optional

Context Output

Path Type Description
GCP.Compute.Instances.kind String Type of the resource.Always compute#instance for instances.
GCP.Compute.Instances.id String The unique identifier of the resource.
GCP.Compute.Instances.creationTimestamp String Creation timestamp in RFC3339 text format.
GCP.Compute.Instances.name String The name of the resource, provided by the client when the resource is first created.
GCP.Compute.Instances.description String An optional description for this resource.
GCP.Compute.Instances.tags String Tags to apply to this instance.
GCP.Compute.Instances.machineType String Full or partial URL of the machine type resource to use for this instance, in the format: zones/zone/machineTypes/machine-type.
GCP.Compute.Instances.status String The status of the instance.
GCP.Compute.Instances.statusMessage String An optional, human-readable explanation of the status.
GCP.Compute.Instances.zone String URL of the zone where the instance resides.
GCP.Compute.Instances.canIpForward String Allows this instance to send and receive packets with non-matching destination or source IPs.
GCP.Compute.Instances.networkInterfaces Object An array of network configurations for the instance.
GCP.Compute.Instances.disks Object Array of disks associated with the instance.
GCP.Compute.Instances.metadata Object The metadata key/value pairs assigned to the instance.
GCP.Compute.Instances.serviceAccounts Object A list of service accounts, with their specified scopes, authorized for the instance.
GCP.Compute.Instances.selfLink String Server-defined URL for the resource.
GCP.Compute.Instances.scheduling Object Sets the scheduling options for the instance.
GCP.Compute.Instances.cpuPlatform String The CPU platform used by the instance.
GCP.Compute.Instances.labels String Labels to apply to the instance.
GCP.Compute.Instances.labelFingerprint String A fingerprint for this request, which is essentially a hash of the label’s contents and used for optimistic locking.
GCP.Compute.Instances.instanceEncryptionKey Object Encrypts suspended data for an instance with a customer-managed encryption key.
GCP.Compute.Instances.minCpuPlatform String Specifies a minimum CPU platform for the VM instance.
GCP.Compute.Instances.guestAccelerators Object A list of the type and count of accelerator cards attached to the instance.
GCP.Compute.Instances.startRestricted Boolean Indicates whether a VM has been restricted from starting because Compute Engine detected suspicious activity.
GCP.Compute.Instances.deletionProtection Boolean Whether the resource should be protected against deletion.
GCP.Compute.Instances.resourcePolicies String Resource policies applied to this instance.
GCP.Compute.Instances.sourceMachineImage String Source machine image.
GCP.Compute.Instances.reservationAffinity Object Specifies the reservations that the instance can consume from.
GCP.Compute.Instances.hostname String Specifies the hostname of the instance.
GCP.Compute.Instances.displayDevice Object Enables display device for the instance.
GCP.Compute.Instances.shieldedInstanceConfig Object Shielded VM configuration for the instance.
GCP.Compute.Instances.sourceMachineImageEncryptionKey Object The source machine image encryption key used when creating an instance from a machine image.
GCP.Compute.Instances.confidentialInstanceConfig Object Confidential computing configuration for the instance.
GCP.Compute.Instances.fingerprint String Specifies a fingerprint for this resource, which is essentially a hash of the instance’s contents and used for optimistic locking.
GCP.Compute.Instances.privateIpv6GoogleAccess String The private IPv6 Google access type for the VM.
GCP.Compute.Instances.advancedMachineFeatures Object Controls for advanced machine-related behavior features.
GCP.Compute.Instances.lastStartTimestamp String Last start timestamp in RFC3339 text format.
GCP.Compute.Instances.lastStopTimestamp String Last stop timestamp in RFC3339 text format.
GCP.Compute.Instances.lastSuspendedTimestamp String Last suspended timestamp in RFC3339 text format.
GCP.Compute.Instances.satisfiesPzs String Indicates whether the instance satisfies physical zone separation requirements.
GCP.Compute.Instances.satisfiesPzi String Indicates whether the instance satisfies physical zone isolation requirements.
GCP.Compute.Instances.resourceStatus Object The resource status.
GCP.Compute.Instances.networkPerformanceConfig Object Network performance configuration.
GCP.Compute.Instances.keyRevocationActionType String KeyRevocationActionType of the instance.
GCP.Compute.InstancesNextPageToken String The token used to retrieve the next page of results for list requests.
GCP.Compute.InstancesSelfLink String Server-defined URL for the resource.
GCP.Compute.InstancesWarning Object Informational warning message.

gcp-compute-instance-labels-set


Sets labels on an instance. Required permission: compute.instances.setLabels.

Base Command

gcp-compute-instance-labels-set

Input

Argument Name Description Required
instance Name of the instance resource to return. Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
zone The name of the zone for this request. Required
labels An object containing a list of “key”: value pairs, without spaces. Example: key=abc,value=123;key=ABC,value=321. Required
label_fingerprint Fingerprint of the previous set of labels for this resource, used to prevent conflicts. Provide the latest fingerprint value when making a request to add or change labels. Required
add_labels Whether to add the new labels to the existing ones or override the previous labels with the news. True - add, False - override. Possible values are: true, false. Default is false. Optional

Context Output

Path Type Description
GCP.Compute.Operations.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.Operations.name string Name of the resource.
GCP.Compute.Operations.zone string The URL of the zone where the operation resides. Only available when performing per-zone operations. You must specify this field as part of the HTTP request URL. It is not settable as a field in the request body.
GCP.Compute.Operations.clientOperationId string The value of requestId if you provided it in the request. Not present otherwise.
GCP.Compute.Operations.operationType string The type of operation, such as insert, update, or delete.
GCP.Compute.Operations.targetLink string The URL of the resource that the operation modifies. For snapshot-creation operations, this points to the persistent disk from which the snapshot was created.
GCP.Compute.Operations.targetId string The unique target ID, which identifies a specific incarnation of the target resource.
GCP.Compute.Operations.status string The status of the operation, which can be one of the following: PENDING, RUNNING or DONE.
GCP.Compute.Operations.statusMessage string An optional textual description of the current status of the operation.
GCP.Compute.Operations.user string User who requested the operation, for example, EMAILADDRESS.
GCP.Compute.Operations.progress number An optional progress indicator ranging from 0 to 100. It does not need to be linear or support any specific granularity of operations. This indicator should not be used to estimate completion time. The value should monotonically increase as the operation progresses.
GCP.Compute.Operations.insertTime string The time the operation was requested, in RFC3339 text format.
GCP.Compute.Operations.startTime string The time that the operation was started by the server, in RFC3339 text format.
GCP.Compute.Operations.endTime string The time that the operation was completed, in RFC3339 text format.
GCP.Compute.Operations.error string If errors are generated during processing of the operation, this field will be populated.
GCP.Compute.Operations.httpErrorStatusCode number If the operation fails, this field contains the HTTP error status code that was returned. For example, a 404 means the resource was not found.
GCP.Compute.Operations.httpErrorMessage string If the operation fails, this field contains the HTTP error message that was returned, such as NOT FOUND.
GCP.Compute.Operations.selfLink string Server-defined URL for the resource.
GCP.Compute.Operations.region string The URL of the region where the operation resides. Only available when performing regional operations. You must specify this field as part of the HTTP request URL. It is not settable as a field in the request body.
GCP.Compute.Operations.description string A textual description of the operation, which is set when the operation is created.
GCP.Compute.Operations.kind string Type of the resource. Always compute#operation for Operation resources.

gcp-compute-instance-get


Returns a specific instance resource. To get a list of available instances, make a list() request. Required permission: compute.instances.get.

Base Command

gcp-compute-instance-get

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
zone The name of the zone for this request. Required
instance Name of the instance resource to return. Required

Context Output

Path Type Description
GCP.Compute.Instances.id string The unique identifier for the resource, defined by the server.
GCP.Compute.Instances.creationTimestamp string Creation timestamp in RFC3339 text format.
GCP.Compute.Instances.name string The name of the resource, provided by the client when first creating it. The name must be 1–63 characters long and comply with RFC1035. It must match the regular expression [a-z]([-a-z0-9]*[a-z0-9])?, meaning the first character must be a lowercase letter, and all following characters can be a dash, lowercase letter, or digit, except the last character, which cannot be a dash.
GCP.Compute.Instances.description string An optional description of this resource. Provide this property when you create the resource.
GCP.Compute.Instances.tags string Tags to apply to this instance. Tags identify valid sources or targets for network firewalls and are specified by the client during instance creation. Tags can later be modified using the setTags method. Each tag must comply with RFC1035. Multiple tags can be specified via the tags.items field.
GCP.Compute.Instances.tags.items string An array of tags. Each tag must be 1-63 characters long, and comply with RFC1035.
GCP.Compute.Instances.tags.fingerprint string Specifies a fingerprint for this request, which is essentially a hash of the tags contents and used for optimistic locking. The fingerprint is initially generated by Compute Engine and changes after every request to modify or update tags. You must always provide an up-to-date fingerprint hash in order to update or change tags.
GCP.Compute.Instances.machineType string Full or partial URL of the machine type resource to use for this instance, in the format: zones/zone/machineTypes/machine-type. This is provided by the client when the instance is created.
GCP.Compute.Instances.status string The status of the instance. One of the following values: PROVISIONING, STAGING, RUNNING, STOPPING, STOPPED, SUSPENDING, SUSPENDED, and TERMINATED.
GCP.Compute.Instances.statusMessage string An optional, human-readable explanation of the status.
GCP.Compute.Instances.zone string URL of the zone where the instance resides. This field must be specified in the HTTP request URL and cannot be set in the request body.
GCP.Compute.Instances.canIpForward boolean Allows this instance to send and receive packets with non-matching destination or source IPs. This is required if you plan to use this instance to forward routes.
GCP.Compute.Instances.networkInterfaces string An array of network configurations for this instance. These specify how interfaces are configured to interact with other network services, such as connecting to the internet. Multiple interfaces are supported per instance.
GCP.Compute.Instances.networkInterfaces.network string URL of the network resource for this instance. When creating an instance, if neither the network nor the subnetwork is specified, the default network global/networks/default is used; if the network is not specified but the subnetwork is specified, the network is inferred.
GCP.Compute.Instances.networkInterfaces.subnetwork string The URL of the Subnetwork resource for this instance. For legacy networks, do not provide this property. For auto subnet networks, specifying the subnetwork is optional. For custom subnet networks, this field must be specified. If provided, the subnetwork can be a full or partial URL.
GCP.Compute.Instances.networkInterfaces.networkIP string An IPv4 internal network address to assign to the instance for this network interface. If not specified by the user, an unused internal IP is assigned by the system.
GCP.Compute.Instances.networkInterfaces.name string The name of the network interface, generated by the server. For network devices, these are eth0, eth1, etc.
GCP.Compute.Instances.networkInterfaces.accessConfigs string An array of configurations for this interface. Currently, only one access config, ONE_TO_ONE_NAT, is supported. If there are no accessConfigs specified, then the instance will have no external internet access.
GCP.Compute.Instances.networkInterfaces.aliasIpRanges string An array of alias IP ranges for this network interface. Can only be specified for network interfaces on subnet-mode networks.
GCP.Compute.Instances.networkInterfaces.fingerprint string Fingerprint hash of the contents stored in this network interface. This field is ignored when inserting an instance or adding a network interface. To update the network interface, an up-to-date fingerprint must be provided; otherwise, the request fails with error 412 conditionNotMet.
GCP.Compute.Instances.networkInterfaces.kind string Type of the resource. Always compute#networkInterface for network interfaces.
GCP.Compute.Instances.disks string Array of disks associated with this instance. Persistent disks must be created before you can assign them.
GCP.Compute.Instances.disks.type string Specifies the type of the disk, either SCRATCH or PERSISTENT. If not specified, the default is PERSISTENT.
GCP.Compute.Instances.disks.mode string The mode in which to attach this disk, either READ_WRITE or READ_ONLY. If not specified, the default is to attach the disk in READ_WRITE mode.
GCP.Compute.Instances.disks.source string Specifies a valid partial or full URL to an existing Persistent Disk resource. When creating a new instance, one of initializeParams.sourceImage or disks.source is required except for local SSD.
GCP.Compute.Instances.disks.deviceName string Specifies a unique device name of your choice that is reflected into the /dev/disk/by-id/google-* tree of a Linux operating system running within the instance. This name can be used to reference the device for mounting, resizing, and other operations from within the instance.
GCP.Compute.Instances.disks.index number A zero-based index for this disk, where 0 is reserved for the boot disk. Each attached disk on an instance has a unique index number.
GCP.Compute.Instances.disks.boot boolean Indicates that this is a boot disk. The VM uses the first partition of the disk as its root filesystem.
GCP.Compute.Instances.disks.initializeParams string Specifies parameters for a new disk to be created with the instance. Use initialization parameters to create boot disks or local SSDs attached to the instance.
GCP.Compute.Instances.disks.autoDelete boolean Specifies whether the disk is auto-deleted when the instance is deleted (not when the disk is detached).
GCP.Compute.Instances.disks.licenses string Any valid publicly visible licenses.
GCP.Compute.Instances.disks.interface string Specifies the disk interface for attaching this disk, either SCSI or NVME. The default is SCSI. Persistent disks must use SCSI; attaching a persistent disk with any other interface will fail. Local SSDs can use either NVME or SCSI. For performance characteristics of SCSI versus NVMe, see Local SSD performance.
GCP.Compute.Instances.disks.guestOsFeatures string A list of features to enable on the guest operating system. Applicable only for bootable images. Read Enabling guest operating system features to see a list of available options.
GCP.Compute.Instances.disks.diskEncryptionKey Object Encrypts or decrypts a disk using a customer-supplied encryption key.
GCP.Compute.Instances.disks.kind string Type of the resource. Always compute#attachedDisk for attached disks.
GCP.Compute.Instances.metadata string The metadata key/value pairs assigned to the instance. This includes custom metadata and predefined keys.
GCP.Compute.Instances.metadata.fingerprint string Specifies a fingerprint for this request, which is a hash of the metadata contents used for optimistic locking. The fingerprint is generated by Compute Engine and changes after every metadata update. You must provide an up-to-date fingerprint to modify metadata; otherwise, the request fails with error 412 conditionNotMet.
GCP.Compute.Instances.metadata.items string Array of key/value pairs. The total size of all keys and values must be less than 512 KB.
GCP.Compute.Instances.metadata.kind string Type of the resource. Always compute#metadata for metadata.
GCP.Compute.Instances.serviceAccounts string A list of service accounts, with their specified scopes, authorized for the instance. Only one service account per VM instance is supported.
GCP.Compute.Instances.serviceAccounts.email string Email address of the service account.
GCP.Compute.Instances.serviceAccounts.scopes string The list of scopes to be made available for this service account.
GCP.Compute.Instances.selfLink string Server-defined URL for the resource.
GCP.Compute.Instances.scheduling string Sets the scheduling options for the instance.
GCP.Compute.Instances.scheduling.onHostMaintenance string Defines the maintenance behavior for this instance. For standard instances, the default behavior is MIGRATE. For preemptible instances, the default and only possible behavior is TERMINATE. For more information, see Setting Instance Scheduling Options.
GCP.Compute.Instances.scheduling.automaticRestart boolean Specifies whether the instance should be automatically restarted if it is terminated by Compute Engine (not terminated by a user). You can only set the automatic restart option for standard instances. Preemptible instances cannot be automatically restarted.
GCP.Compute.Instances.scheduling.preemptible boolean Defines whether the instance is preemptible. This can only be set during instance creation, it cannot be set or changed after the instance has been created.
GCP.Compute.Instances.scheduling.nodeAffinities string A set of node affinity and anti-affinity.
GCP.Compute.Instances.cpuPlatform string The CPU platform used by the instance.
GCP.Compute.Instances.labels string Labels to apply to the instance. These can be later modified by the setLabels method.
GCP.Compute.Instances.labels.key string The label key.
GCP.Compute.Instances.labels.value string The label value.
GCP.Compute.Instances.labelFingerprint string A fingerprint for this request, which is essentially a hash of the labels contents and used for optimistic locking. The fingerprint is initially generated by Compute Engine and changes after every request to modify or update labels. You must always provide an up-to-date fingerprint hash in order to update or change labels.
GCP.Compute.Instances.minCpuPlatform string Specifies a minimum CPU platform for the VM instance. Applicable values are the friendly names of CPU platforms, such as minCpuPlatform: “Intel Haswell” or minCpuPlatform: “Intel Sandy Bridge”.
GCP.Compute.Instances.guestAccelerators string A list of the type and count of accelerator cards attached to the instance.
GCP.Compute.Instances.guestAccelerators.acceleratorType string Full or partial URL of the accelerator type resource to attach to this instance. For example: projects/my-project/zones/us-central1-c/acceleratorTypes/nvidia-tesla-p100. When creating an instance template, specify only the accelerator name.
GCP.Compute.Instances.guestAccelerators.acceleratorCount string The number of the guest accelerator cards exposed to the instance.
GCP.Compute.Instances.startRestricted boolean Indicates whether a VM has been restricted from starting because Compute Engine detected suspicious activity.
GCP.Compute.Instances.deletionProtection boolean Whether the resource should be protected against deletion.
GCP.Compute.Instances.hostname string Hostname.
GCP.Compute.Instances.kind string Type of the resource. Always compute#instance for instances.

gcp-storage-bucket-list


Retrieves the list of buckets in the project associated with the client. Required permission: storage.buckets.list.

Base Command

gcp-storage-bucket-list

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
limit Maximum number of buckets to return. Optional
prefix Filter results to buckets whose names begin with this prefix. Optional
page_token Token for pagination. Optional

Context Output

Path Type Description
GCP.Storage.Bucket.id String The unique identifier for the bucket.
GCP.Storage.Bucket.name String The name of the bucket.
GCP.Storage.Bucket.kind String The type of resource (for example, storage#bucket).
GCP.Storage.Bucket.location String The location of the bucket.
GCP.Storage.Bucket.locationType String The type of location (for example, multi-region).
GCP.Storage.Bucket.projectNumber String The GCP project number associated with the bucket.
GCP.Storage.Bucket.storageClass String The storage class of the bucket.
GCP.Storage.Bucket.rpo String The recovery point objective setting of the bucket.
GCP.Storage.Bucket.etag String The HTTP entity tag of the bucket.
GCP.Storage.Bucket.generation String The generation number of the bucket.
GCP.Storage.Bucket.metageneration String The metageneration number of the bucket.
GCP.Storage.Bucket.timeCreated Date The time the bucket was created.
GCP.Storage.Bucket.timeUpdated Date The time the bucket was last updated.
GCP.Storage.Bucket.selfLink String The link to the bucket resource on the GCP API.

gcp-storage-bucket-get


Retrieves information about a specific bucket. Required permission: storage.buckets.get.

Base Command

gcp-storage-bucket-get

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
bucket_name Name of the bucket to retrieve. Required

Context Output

Path Type Description
GCP.Storage.Bucket.id String The unique identifier for the bucket.
GCP.Storage.Bucket.name String The name of the bucket.
GCP.Storage.Bucket.kind String The type of resource (for example, storage#bucket).
GCP.Storage.Bucket.location String The location of the bucket.
GCP.Storage.Bucket.locationType String The type of location (for example, multi-region).
GCP.Storage.Bucket.projectNumber String The GCP project number associated with the bucket.
GCP.Storage.Bucket.storageClass String The storage class of the bucket.
GCP.Storage.Bucket.rpo String The recovery point objective setting of the bucket.
GCP.Storage.Bucket.etag String The HTTP entity tag of the bucket.
GCP.Storage.Bucket.generation String The generation number of the bucket.
GCP.Storage.Bucket.metageneration String The metageneration number of the bucket.
GCP.Storage.Bucket.timeCreated Date The time the bucket was created.
GCP.Storage.Bucket.timeUpdated Date The time the bucket was last updated.
GCP.Storage.Bucket.selfLink String The link to the bucket resource on the GCP API.

gcp-storage-bucket-objects-list


Retrieves the list of objects in a bucket. Required permission: storage.objects.list.

Base Command

gcp-storage-bucket-objects-list

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
bucket_name Name of the bucket to list objects from. Required
prefix Filter results to objects whose names begin with this prefix. Optional
delimiter Delimiter to use for grouping objects. For example delimiter=”/” Returns results in a directory-like mode, with / being a common value for the delimiter. Optional
limit Maximum number of objects to return. Optional
page_token Token for pagination. Optional

Context Output

Path Type Description
GCP.Storage.BucketObject.id String The unique identifier for the object.
GCP.Storage.BucketObject.name String The name of the object.
GCP.Storage.BucketObject.kind String The type of resource (for example, storage#object).
GCP.Storage.BucketObject.bucket String The name of the bucket containing the object.
GCP.Storage.BucketObject.contentType String The MIME type of the object.
GCP.Storage.BucketObject.size Number The size of the object in bytes.
GCP.Storage.BucketObject.crc32c String The CRC32C checksum of the object.
GCP.Storage.BucketObject.md5Hash String The MD5 hash of the object.
GCP.Storage.BucketObject.etag String The HTTP entity tag of the object.
GCP.Storage.BucketObject.generation String The generation number of the object.
GCP.Storage.BucketObject.metageneration String The metageneration number of the object.
GCP.Storage.BucketObject.storageClass String The storage class of the object.
GCP.Storage.BucketObject.mediaLink String The link for downloading the object content.
GCP.Storage.BucketObject.selfLink String The link to the object resource in the GCP API.
GCP.Storage.BucketObject.timeCreated Date The time when the object was created.
GCP.Storage.BucketObject.timeFinalized Date The time when the object was finalized.
GCP.Storage.BucketObject.timeStorageClassUpdated Date The time when the object’s storage class was last updated.
GCP.Storage.BucketObject.updated Date The time when the object was last modified.

gcp-storage-bucket-policy-list


Retrieves the IAM policy for a bucket. Required permissions: storage.buckets.getIamPolicy, storage.buckets.get.

Base Command

gcp-storage-bucket-policy-list

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
bucket_name Name of the bucket to retrieve IAM policy from. Required
requested_policy_version The IAM policy version to be returned. If the optionsRequestedPolicyVersion is for an older version that doesn’t support part of the requested IAM policy, the request fails. Required to be 3 or greater for buckets with IAM Conditions. Optional

Context Output

Path Type Description
GCP.Storage.BucketPolicy.version Number IAM policy version.
GCP.Storage.BucketPolicy.etag String ETag of the IAM policy.
GCP.Storage.BucketPolicy.bindings List List of role bindings for the bucket.
GCP.Storage.BucketPolicy.resourceId String Resource ID of the updated IAM policy. e.g. projects/_/buckets/BUCKET_NAME.

Usage

  • add=false: Replaces the entire bucket IAM policy with the JSON provided in policy.
  • add=true: Reads the current bucket policy (getIamPolicy), merges the provided bindings per role (deduplicates members), and updates the bucket policy (setIamPolicy) while preserving other top-level fields.

gcp-storage-bucket-policy-set


Sets the IAM policy for a bucket. Required permission: storage.buckets.setIamPolicy.

Base Command

gcp-storage-bucket-policy-set

Usage

  • add=false: Replaces the entire bucket IAM policy with the JSON provided in policy.
  • add=true: Reads the current bucket policy (getIamPolicy), merges the provided bindings per role (deduplicates members), and updates the bucket policy (setIamPolicy) while preserving other top-level fields.

Warning: Use this command with extreme caution. Running it without explicitly merging (i.e., with add=false) will overwrite the bucket’s existing IAM policy with the provided policy. If you intend to keep current bindings and add new ones, use add=true.

Policy structure

  • bindings: Array of binding objects. Each binding:
    • role: String. For example, roles/storage.objectViewer, roles/storage.admin.
    • members: Array of strings. Allowed formats:
      • user:<email> (e.g., user:alice@example.com)
      • group:<email>
      • serviceAccount:<email>
      • domain:<domain> (e.g., domain:example.com)
      • allUsers allAuthenticatedUsers
  • version: Number. Required to be 3 or greater if any binding includes condition.
  • etag: String. Recommended for replace flow (add=false) to avoid overwriting concurrent updates.
  • Optional fields like kind, resourceId may appear in responses but are not required in requests.

Notes:

  • For add=true (merge), only a valid bindings array is required; other top-level fields are taken from the existing policy.
  • For add=false (replace), the provided object becomes the entire policy on the bucket.

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
bucket_name Name of the bucket to set IAM policy on. Required
policy JSON string representing the IAM policy to set. Required
add When true, merges the provided policy bindings into the current bucket policy (per role, deduplicating members) by first calling getIamPolicy and then setIamPolicy with the merged result. When false, replaces the entire policy with the provided JSON via setIamPolicy.
. Possible values are: true, false. Default is false.
Optional

Context Output

Path Type Description
GCP.Storage.BucketPolicy.version Number IAM policy version after update.
GCP.Storage.BucketPolicy.etag String ETag of the updated IAM policy.
GCP.Storage.BucketPolicy.bindings List List of role bindings for the bucket.

gcp-storage-bucket-object-policy-list


Retrieves the IAM policy for a specific object in a bucket. Required permission: storage.objects.getIamPolicy.

Base Command

gcp-storage-bucket-object-policy-list

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
bucket_name Name of the bucket containing the object. Required
object_name Name of the object to retrieve IAM policy from. Required
generation Generation of the object. Optional

Context Output

Path Type Description
GCP.Storage.BucketObjectPolicy.bucketName String Name of the bucket containing the object.
GCP.Storage.BucketObjectPolicy.objectName String Name of the object.
GCP.Storage.BucketObjectPolicy.bindings List List of role bindings for the object.

gcp-storage-bucket-object-policy-set


Sets the IAM policy for a specific object in a bucket. Required permission: storage.objects.setIamPolicy.

Base Command

gcp-storage-bucket-object-policy-set

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
bucket_name Name of the bucket containing the object. Required
object_name Name of the object to set IAM policy on. Required
policy JSON string representing the IAM policy to set. Required
generation Generation of the object. Optional

Context Output

Path Type Description
GCP.Storage.BucketObjectPolicy.version Number IAM policy version after update.
GCP.Storage.BucketObjectPolicy.etag String ETag of the updated IAM policy.
GCP.Storage.BucketObjectPolicy.bindings Unknown List of role bindings for the object.

gcp-compute-snapshot-get


Retrieves details for a specific snapshot. Required permission: compute.snapshots.get.

Base Command

gcp-compute-snapshot-get

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
resource_name Snapshot name. Required

Context Output

Path Type Description
GCP.Compute.Snapshot.id String Unique identifier for the snapshot resource.
GCP.Compute.Snapshot.name String Name of the snapshot resource.
GCP.Compute.Snapshot.kind String Type of the resource, for example compute#snapshot.
GCP.Compute.Snapshot.status String Current status of the snapshot, such as READY or FAILED.
GCP.Compute.Snapshot.autoCreated Boolean Indicates whether the snapshot was automatically created.
GCP.Compute.Snapshot.architecture String CPU architecture of the source disk, for example X86_64.
GCP.Compute.Snapshot.creationTimestamp Date The time when the snapshot was created.
GCP.Compute.Snapshot.creationSizeBytes Number Total size of the snapshot in bytes at creation time.
GCP.Compute.Snapshot.diskSizeGb Number Size of the snapshot in gigabytes.
GCP.Compute.Snapshot.downloadBytes Number Total bytes downloaded to create the snapshot.
GCP.Compute.Snapshot.enableConfidentialCompute Boolean Indicates if confidential compute is enabled for this snapshot.
GCP.Compute.Snapshot.labelFingerprint String Fingerprint for the labels applied to the snapshot.
GCP.Compute.Snapshot.licenseCodes Unknown List of license code identifiers attached to the snapshot.
GCP.Compute.Snapshot.licenses Unknown List of license URLs associated with the snapshot.
GCP.Compute.Snapshot.selfLink String Server-defined URL for the snapshot resource.
GCP.Compute.Snapshot.sourceDisk String URL of the source disk used to create the snapshot.
GCP.Compute.Snapshot.sourceDiskId String Unique ID of the source disk used to create the snapshot.
GCP.Compute.Snapshot.sourceSnapshotSchedulePolicy String URL of the snapshot schedule policy used to create this snapshot.
GCP.Compute.Snapshot.sourceSnapshotSchedulePolicyId String Unique ID of the snapshot schedule policy used to create this snapshot.
GCP.Compute.Snapshot.storageBytes Number Total storage size of the snapshot in bytes.
GCP.Compute.Snapshot.storageBytesStatus String Status of the storage bytes usage, for example UP_TO_DATE.
GCP.Compute.Snapshot.storageLocations Unknown List of storage locations for the snapshot.

gcp-compute-instances-aggregated-list-by-ip


Returns an aggregated list of instances across all zones that can be filtered by internal or external IP. Required permission: cloudasset.assets.searchAllResources.

Base Command

gcp-compute-instances-aggregated-list-by-ip

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
ip_address The IP address to search for. Required
match_external If true, match against external NAT IPs; otherwise internal NIC IPs. Possible values are: true, false. Optional
limit Maximum number of results to return. Acceptable values are 0 to 500, inclusive. Default is 50. Optional
page_token Token for pagination. Optional

Context Output

Path Type Description
GCP.Compute.Instance.name string Instance name.
GCP.Compute.Instance.id string Instance ID.
GCP.Compute.Instance.zone string Instance zone URL.
GCP.Compute.Instance.status string Instance status.
GCP.Compute.Instance.networkInterfaces Unknown Network interfaces of the instance.

gcp-compute-network-tag-set


Adds a network tag to a VM instance (merges with existing tags). Required permission: compute.instances.setTags.

Base Command

gcp-compute-network-tag-set

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
zone Zone of the VM (e.g., us-central1-a). Required
resource_name Instance name. Required
tag Tag to add. Required
tags_fingerprint Fingerprint of the previous set of tags for this resource, used to prevent conflicts. Provide the latest fingerprint value when making a request to add or change tags. To retrieve the fingerprint use the command gcp-compute-instance-get. Required
add_tag If true, adds the tag to the existing tags; otherwise, overrides them. The default is true. Possible values are: true, false. Default is true. Optional

Context Output

Path Type Description
GCP.Compute.Operations.id string Operation ID.
GCP.Compute.Operations.name string Operation name.
GCP.Compute.Operations.status string Current operation status (e.g., RUNNING, DONE).
GCP.Compute.Operations.operationType string Type of operation being performed.
GCP.Compute.Operations.progress number Operation progress percentage (0-100).
GCP.Compute.Operations.user string User or service account that initiated the operation.
GCP.Compute.Operations.targetLink string Full URL of the target resource for this operation.
GCP.Compute.Operations.targetId string Target resource ID.
GCP.Compute.Operations.zone string Zone where the operation is performed.
GCP.Compute.Operations.insertTime string Time when the operation was inserted.
GCP.Compute.Operations.startTime string Time when the operation started.
GCP.Compute.Operations.selfLink string URL for this operation resource.
GCP.Compute.Operations.kind string Type of the resource (e.g., compute#operation).

gcp-compute-image-get


Returns a specific image. Required permission: compute.images.get.

Base Command

gcp-compute-image-get

Input

Argument Name Description Required
project_id Project ID for this request. Required for Cortex XSIAM (version >= 3.0) and Cortex Cloud; optional for Cortex XSOAR and Cortex XSIAM (version < 3.0), where it can be retrieved from the integration configuration. Optional
image Name of the image resource to return. Required

Context Output

Path Type Description
GCP.Compute.Images.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.Images.creationTimestamp string Creation timestamp in RFC3339 text format.
GCP.Compute.Images.name string Name of the resource; provided by the client when the resource is created. The name must be 1-63 characters long, and comply with RFC1035. Specifically, the name must be 1-63 characters long and match the regular expression [a-z]([-a-z0-9]*[a-z0-9])? which means the first character must be a lowercase letter, and all following characters must be a dash, lowercase letter, or digit, except the last character, which cannot be a dash.
GCP.Compute.Images.description string An optional description of this resource.
GCP.Compute.Images.sourceType string The type of the image used to create this disk. The default and only value is RAW.
GCP.Compute.Images.rawDisk string The parameters of the raw disk image.
GCP.Compute.Images.rawDisk.source string The full Google Cloud Storage URL where the disk image is stored. You must provide either this property or the sourceDisk property but not both.
GCP.Compute.Images.rawDisk.sha1Checksum string An optional SHA1 checksum of the disk image before unpackaging provided by the client when the disk image is created.
GCP.Compute.Images.rawDisk.containerType string The format used to encode and transmit the block device, which should be TAR. This is just a container and transmission format and not a runtime format. Provided by the client when the disk image is created.
GCP.Compute.Images.deprecated string The deprecation status associated with this image.
GCP.Compute.Images.deprecated.state string The deprecation state of this resource. This can be ACTIVE DEPRECATED, OBSOLETE, or DELETED. Operations which communicate the end of life date for an image, can use ACTIVE. Operations which create a new resource using a DEPRECATED resource will return successfully, but with a warning indicating the deprecated resource and recommending its replacement. Operations which use OBSOLETE or DELETED resources will be rejected and result in an error.
GCP.Compute.Images.deprecated.replacement string The URL of the suggested replacement for a deprecated resource. The suggested replacement resource must be the same kind of resource as the deprecated resource.
GCP.Compute.Images.deprecated.deprecated string An optional RFC3339 timestamp on or after which the state of this resource is intended to change to DEPRECATED. This is only informational and the status will not change unless the client explicitly changes it.
GCP.Compute.Images.deprecated.obsolete string An optional RFC3339 timestamp on or after which the state of this resource is intended to change to OBSOLETE. This is only informational and the status will not change unless the client explicitly changes it.
GCP.Compute.Images.deprecated.deleted string An optional RFC3339 timestamp on or after which the state of this resource is intended to change to DELETED. This is only informational and the status will not change unless the client explicitly changes it.
GCP.Compute.Images.status string The status of the image. An image can be used to create other resources, such as instances, only after the image has been successfully created and the status is set to READY. Possible values are FAILED, PENDING, or READY.
GCP.Compute.Images.archiveSizeBytes string Size of the image tar.gz archive stored in Google Cloud Storage (in bytes).
GCP.Compute.Images.diskSizeGb string Size of the image when restored onto a persistent disk (in GB).
GCP.Compute.Images.sourceDisk string URL of the source disk used to create this image. This can be a full or valid partial URL. You must provide either this property or the rawDisk.source property but not both to create an image. For example, the following are valid values: https://www.googleapis.com/compute/v1/projects/project/zones/zone/disks/disk , projects/project/zones/zone/disks/disk , zones/zone/disks/disk.
GCP.Compute.Images.sourceDiskId string The ID value of the disk used to create this image. This value may be used to determine whether the image was taken from the current or a previous instance of a given disk name.
GCP.Compute.Images.licenses string Any applicable license URI.
GCP.Compute.Images.family string The name of the image family to which this image belongs. You can create disks by specifying an image family instead of a specific image name. The image family always returns its latest image that is not deprecated. The name of the image family must comply with RFC1035.
GCP.Compute.Images.imageEncryptionKey string Encrypts the image using a customer-supplied encryption key. After you encrypt an image with a customer-supplied key, you must provide the same key if you use the image later (e.g. to create a disk from the image). Customer-supplied encryption keys do not protect access to metadata of the disk. If you do not provide an encryption key when creating the image, then the disk will be encrypted using an automatically generated key and you do not need to provide a key to use the image later.
GCP.Compute.Images.imageEncryptionKey.rawKey string Specifies a 256-bit customer-supplied encryption key, encoded in RFC 4648 base64 to either encrypt or decrypt this resource.
GCP.Compute.Images.imageEncryptionKey.kmsKeyName string The name of the encryption key that is stored in Google Cloud KMS.
GCP.Compute.Images.imageEncryptionKey.sha256 string The RFC 4648 base64 encoded SHA-256 hash of the customer-supplied encryption key that protects this resource.
GCP.Compute.Images.sourceDiskEncryptionKey string The customer-supplied encryption key of the source disk. Required if the source disk is protected by a customer-supplied encryption key.
GCP.Compute.Images.sourceDiskEncryptionKey.rawKey string Specifies a 256-bit customer-supplied encryption key, encoded in RFC 4648 base64 to either encrypt or decrypt this resource.
GCP.Compute.Images.sourceDiskEncryptionKey.kmsKeyName string The name of the encryption key that is stored in Google Cloud KMS.
GCP.Compute.Images.sourceDiskEncryptionKey.sha256 string The RFC 4648 base64 encoded SHA-256 hash of the customer-supplied encryption key that protects this resource.
GCP.Compute.Images.selfLink string Server-defined URL for the resource.
GCP.Compute.Images.labels string Labels to apply to this image. These can be later modified by the setLabels method.
GCP.Compute.Images.labelFingerprint string A fingerprint for the labels being applied to this image, which is essentially a hash of the labels used for optimistic locking. The fingerprint is initially generated by Compute Engine and changes after every request to modify or update labels. You must always provide an up-to-date fingerprint hash in order to update or change labels, otherwise the request will fail with error 412 conditionNotMet.
GCP.Compute.Images.guestOsFeatures string A list of features to enable on the guest operating system. Applicable only for bootable images. Read Enabling guest operating system features to see a list of available options.
GCP.Compute.Images.guestOsFeatures.type string The ID of a supported feature. Read Enabling guest operating system features to see a list of available options.
GCP.Compute.Images.licenseCodes string Integer license codes indicating which licenses are attached to this image.
GCP.Compute.Images.sourceImage string URL of the source image used to create this image. This can be a full or valid partial URL.
GCP.Compute.Images.sourceImageId string The ID value of the image used to create this image. This value may be used to determine whether the image was taken from the current or a previous instance of a given image name.
GCP.Compute.Images.sourceImageEncryptionKey string The customer-supplied encryption key of the source image. Required if the source image is protected by a customer-supplied encryption key.
GCP.Compute.Images.sourceImageEncryptionKey.rawKey string Specifies a 256-bit customer-supplied encryption key, encoded in RFC 4648 base64 to either encrypt or decrypt this resource.
GCP.Compute.Images.sourceImageEncryptionKey.kmsKeyName string The name of the encryption key that is stored in Google Cloud KMS.
GCP.Compute.Images.sourceImageEncryptionKey.sha256 string The RFC 4648 base64 encoded SHA-256 hash of the customer-supplied encryption key that protects this resource.
GCP.Compute.Images.sourceSnapshot string URL of the source snapshot used to create this image. This can be a full or valid partial URL.
GCP.Compute.Images.sourceSnapshotId string The ID value of the snapshot used to create this image. This value may be used to determine whether the snapshot was taken from the current or a previous instance of a given snapshot name.
GCP.Compute.Images.sourceSnapshotEncryptionKey string The customer-supplied encryption key of the source snapshot. Required if the source snapshot is protected by a customer-supplied encryption key.
GCP.Compute.Images.sourceSnapshotEncryptionKey.rawKey string Specifies a 256-bit customer-supplied encryption key, encoded in RFC 4648 base64 to either encrypt or decrypt this resource.
GCP.Compute.Images.sourceSnapshotEncryptionKey.kmsKeyName string The name of the encryption key that is stored in Google Cloud KMS.
GCP.Compute.Images.sourceSnapshotEncryptionKey.sha256 string The RFC 4648 base64 encoded SHA-256 hash of the customer-supplied encryption key that protects this resource.
GCP.Compute.Images.kind string Type of the resource. Always compute#image for images.

gcp-compute-instance-group-get


Returns a specific instance group. Required permission: compute.instanceGroups.get.

Base Command

gcp-compute-instance-group-get

Input

Argument Name Description Required
instance_group The name of the instance group. Required
project_id The GCP project ID. Required for Cortex XSIAM (version >= 3.0) and Cortex Cloud; optional for Cortex XSOAR and Cortex XSIAM (version < 3.0), where it can be retrieved from the integration configuration. Optional
zone The name of the zone where the instance group is located. Required

Context Output

Path Type Description
GCP.Compute.InstanceGroups.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.InstanceGroups.creationTimestamp string The creation timestamp for this instance group in RFC3339 text format.
GCP.Compute.InstanceGroups.name string The name of the instance group. The name must be 1-63 characters long, and comply with RFC1035.
GCP.Compute.InstanceGroups.description string An optional description of this resource.
GCP.Compute.InstanceGroups.namedPorts string Assigns a name to a port number.
GCP.Compute.InstanceGroups.namedPorts.name string The name for this named port. The name must be 1-63 characters long, and comply with RFC1035.
GCP.Compute.InstanceGroups.namedPorts.port string The port number, which can be a value between 1 and 65535.
GCP.Compute.InstanceGroups.network string The URL of the network to which all instances in the instance group belong.
GCP.Compute.InstanceGroups.fingerprint string The fingerprint of the named ports. The system uses this fingerprint to detect conflicts when multiple users change the named ports concurrently.
GCP.Compute.InstanceGroups.zone string The URL of the zone where the instance group is located (for zonal resources).
GCP.Compute.InstanceGroups.selfLink string The URL for this instance group. The server generates this URL.
GCP.Compute.InstanceGroups.size string The total number of instances in the instance group.
GCP.Compute.InstanceGroups.region string The URL of the region where the instance group is located (for regional resources).
GCP.Compute.InstanceGroups.subnetwork string The URL of the subnetwork to which all instances in the instance group belong.
GCP.Compute.InstanceGroups.kind string The resource type, which is always compute#instance_group for instance groups.

gcp-compute-region-get


Returns a specific region resource. Required permission: compute.regions.get.

Base Command

gcp-compute-region-get

Input

Argument Name Description Required
region Name of the region resource to return. Required
project_id The GCP project ID. Required for Cortex XSIAM (version >= 3.0) and Cortex Cloud; optional for Cortex XSOAR and Cortex XSIAM (version < 3.0), where it can be retrieved from the integration configuration. Optional

Context Output

Path Type Description
GCP.Compute.Regions.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.Regions.creationTimestamp string Creation timestamp in RFC3339 text format.
GCP.Compute.Regions.name string Name of the resource.
GCP.Compute.Regions.description string Textual description of the resource.
GCP.Compute.Regions.status string Status of the region, either UP or DOWN.
GCP.Compute.Regions.zones string A list of zones available in this region, in the form of resource URLs.
GCP.Compute.Regions.quotas string Quotas assigned to this region.
GCP.Compute.Regions.quotas.metric string Name of the quota metric.
GCP.Compute.Regions.quotas.limit string Quota limit for this metric.
GCP.Compute.Regions.quotas.usage string Current usage of this metric.
GCP.Compute.Regions.quotas.owner string Owning resource. The resource to which this quota applies.
GCP.Compute.Regions.deprecated string The deprecation status associated with this region.
GCP.Compute.Regions.deprecated.state string The deprecation state of this resource. This can be ACTIVE DEPRECATED, OBSOLETE, or DELETED. Operations which communicate the end of life date for an image, can use ACTIVE. Operations which create a new resource using a DEPRECATED resource will return successfully, but with a warning indicating the deprecated resource and recommending its replacement. Operations which use OBSOLETE or DELETED resources will be rejected and result in an error.
GCP.Compute.Regions.deprecated.replacement string The URL of the suggested replacement for a deprecated resource. The suggested replacement resource must be the same kind of resource as the deprecated resource.
GCP.Compute.Regions.deprecated.deprecated string An optional RFC3339 timestamp on or after which the state of this resource is intended to change to DEPRECATED. This is only informational and the status will not change unless the client explicitly changes it.
GCP.Compute.Regions.deprecated.obsolete string An optional RFC3339 timestamp on or after which the state of this resource is intended to change to OBSOLETE. This is only informational and the status will not change unless the client explicitly changes it.
GCP.Compute.Regions.deprecated.deleted string An optional RFC3339 timestamp on or after which the state of this resource is intended to change to DELETED. This is only informational and the status will not change unless the client explicitly changes it.
GCP.Compute.Regions.selfLink string Server-defined URL for the resource.
GCP.Compute.Regions.kind string Type of the resource. Always compute#region for regions.

gcp-compute-zone-get


Returns a specific zone resource. Required permission: compute.zones.get.

Base Command

gcp-compute-zone-get

gcp-compute-image-get


Returns a specific image. Required permission: compute.images.get.

Base Command

gcp-compute-image-get

Input

Argument Name Description Required
project_id The project ID for this request. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
image Name of the image resource to return. Required

Context Output

Path Type Description
GCP.Compute.Images.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.Images.creationTimestamp string Creation timestamp in RFC3339 text format.
GCP.Compute.Images.name string Name of the resource; provided by the client when the resource is created. The name must be 1-63 characters long, and comply with RFC1035. Specifically, the name must be 1-63 characters long and match the regular expression [a-z]([-a-z0-9]*[a-z0-9])? which means the first character must be a lowercase letter, and all following characters must be a dash, lowercase letter, or digit, except the last character, which cannot be a dash.
GCP.Compute.Images.description string An optional description of this resource.
GCP.Compute.Images.sourceType string The type of the image used to create this disk. The default and only value is RAW.
GCP.Compute.Images.rawDisk string The parameters of the raw disk image.
GCP.Compute.Images.rawDisk.source string The full Google Cloud Storage URL where the disk image is stored. You must provide either this property or the sourceDisk property but not both.
GCP.Compute.Images.rawDisk.sha1Checksum string An optional SHA1 checksum of the disk image before unpackaging provided by the client when the disk image is created.
GCP.Compute.Images.rawDisk.containerType string The format used to encode and transmit the block device, which should be TAR. This is just a container and transmission format and not a runtime format. Provided by the client when the disk image is created.
GCP.Compute.Images.deprecated string The deprecation status associated with this image.
GCP.Compute.Images.deprecated.state string The deprecation state of this resource. This can be ACTIVE DEPRECATED, OBSOLETE, or DELETED. Operations which communicate the end of life date for an image, can use ACTIVE. Operations which create a new resource using a DEPRECATED resource will return successfully, but with a warning indicating the deprecated resource and recommending its replacement. Operations which use OBSOLETE or DELETED resources will be rejected and result in an error.
GCP.Compute.Images.deprecated.replacement string The URL of the suggested replacement for a deprecated resource. The suggested replacement resource must be the same kind of resource as the deprecated resource.
GCP.Compute.Images.deprecated.deprecated string An optional RFC3339 timestamp on or after which the state of this resource is intended to change to DEPRECATED. This is only informational and the status will not change unless the client explicitly changes it.
GCP.Compute.Images.deprecated.obsolete string An optional RFC3339 timestamp on or after which the state of this resource is intended to change to OBSOLETE. This is only informational and the status will not change unless the client explicitly changes it.
GCP.Compute.Images.deprecated.deleted string An optional RFC3339 timestamp on or after which the state of this resource is intended to change to DELETED. This is only informational and the status will not change unless the client explicitly changes it.
GCP.Compute.Images.status string The status of the image. An image can be used to create other resources, such as instances, only after the image has been successfully created and the status is set to READY. Possible values are FAILED, PENDING, or READY.
GCP.Compute.Images.archiveSizeBytes string Size of the image tar.gz archive stored in Google Cloud Storage (in bytes).
GCP.Compute.Images.diskSizeGb string Size of the image when restored onto a persistent disk (in GB).
GCP.Compute.Images.sourceDisk string URL of the source disk used to create this image. This can be a full or valid partial URL. You must provide either this property or the rawDisk.source property but not both to create an image. For example, the following are valid values: https://www.googleapis.com/compute/v1/projects/project/zones/zone/disks/disk , projects/project/zones/zone/disks/disk , zones/zone/disks/disk.
GCP.Compute.Images.sourceDiskId string The ID value of the disk used to create this image. This value may be used to determine whether the image was taken from the current or a previous instance of a given disk name.
GCP.Compute.Images.licenses string Any applicable license URI.
GCP.Compute.Images.family string The name of the image family to which this image belongs. You can create disks by specifying an image family instead of a specific image name. The image family always returns its latest image that is not deprecated. The name of the image family must comply with RFC1035.
GCP.Compute.Images.imageEncryptionKey string Encrypts the image using a customer-supplied encryption key. After you encrypt an image with a customer-supplied key, you must provide the same key if you use the image later (e.g. to create a disk from the image). Customer-supplied encryption keys do not protect access to metadata of the disk. If you do not provide an encryption key when creating the image, then the disk will be encrypted using an automatically generated key and you do not need to provide a key to use the image later.
GCP.Compute.Images.imageEncryptionKey.rawKey string Specifies a 256-bit customer-supplied encryption key, encoded in RFC 4648 base64 to either encrypt or decrypt this resource.
GCP.Compute.Images.imageEncryptionKey.kmsKeyName string The name of the encryption key that is stored in Google Cloud KMS.
GCP.Compute.Images.imageEncryptionKey.sha256 string The RFC 4648 base64 encoded SHA-256 hash of the customer-supplied encryption key that protects this resource.
GCP.Compute.Images.sourceDiskEncryptionKey string The customer-supplied encryption key of the source disk. Required if the source disk is protected by a customer-supplied encryption key.
GCP.Compute.Images.sourceDiskEncryptionKey.rawKey string Specifies a 256-bit customer-supplied encryption key, encoded in RFC 4648 base64 to either encrypt or decrypt this resource.
GCP.Compute.Images.sourceDiskEncryptionKey.kmsKeyName string The name of the encryption key that is stored in Google Cloud KMS.
GCP.Compute.Images.sourceDiskEncryptionKey.sha256 string The RFC 4648 base64 encoded SHA-256 hash of the customer-supplied encryption key that protects this resource.
GCP.Compute.Images.selfLink string Server-defined URL for the resource.
GCP.Compute.Images.labels string Labels to apply to this image. These can be later modified by the setLabels method.
GCP.Compute.Images.labelFingerprint string A fingerprint for the labels being applied to this image, which is essentially a hash of the labels used for optimistic locking. The fingerprint is initially generated by Compute Engine and changes after every request to modify or update labels. You must always provide an up-to-date fingerprint hash in order to update or change labels, otherwise the request will fail with error 412 conditionNotMet.
GCP.Compute.Images.guestOsFeatures string A list of features to enable on the guest operating system. Applicable only for bootable images. Read Enabling guest operating system features to see a list of available options.
GCP.Compute.Images.guestOsFeatures.type string The ID of a supported feature. Read Enabling guest operating system features to see a list of available options.
GCP.Compute.Images.licenseCodes string Integer license codes indicating which licenses are attached to this image.
GCP.Compute.Images.sourceImage string URL of the source image used to create this image. This can be a full or valid partial URL.
GCP.Compute.Images.sourceImageId string The ID value of the image used to create this image. This value may be used to determine whether the image was taken from the current or a previous instance of a given image name.
GCP.Compute.Images.sourceImageEncryptionKey string The customer-supplied encryption key of the source image. Required if the source image is protected by a customer-supplied encryption key.
GCP.Compute.Images.sourceImageEncryptionKey.rawKey string Specifies a 256-bit customer-supplied encryption key, encoded in RFC 4648 base64 to either encrypt or decrypt this resource.
GCP.Compute.Images.sourceImageEncryptionKey.kmsKeyName string The name of the encryption key that is stored in Google Cloud KMS.
GCP.Compute.Images.sourceImageEncryptionKey.sha256 string The RFC 4648 base64 encoded SHA-256 hash of the customer-supplied encryption key that protects this resource.
GCP.Compute.Images.sourceSnapshot string URL of the source snapshot used to create this image. This can be a full or valid partial URL.
GCP.Compute.Images.sourceSnapshotId string The ID value of the snapshot used to create this image. This value may be used to determine whether the snapshot was taken from the current or a previous instance of a given snapshot name.
GCP.Compute.Images.sourceSnapshotEncryptionKey string The customer-supplied encryption key of the source snapshot. Required if the source snapshot is protected by a customer-supplied encryption key.
GCP.Compute.Images.sourceSnapshotEncryptionKey.rawKey string Specifies a 256-bit customer-supplied encryption key, encoded in RFC 4648 base64 to either encrypt or decrypt this resource.
GCP.Compute.Images.sourceSnapshotEncryptionKey.kmsKeyName string The name of the encryption key that is stored in Google Cloud KMS.
GCP.Compute.Images.sourceSnapshotEncryptionKey.sha256 string The RFC 4648 base64 encoded SHA-256 hash of the customer-supplied encryption key that protects this resource.
GCP.Compute.Images.kind string Type of the resource. Always compute#image for images.

gcp-compute-instance-group-get


Returns a specific instance group. Required permission: compute.instanceGroups.get.

Base Command

gcp-compute-instance-group-get

Input

Argument Name Description Required
instance_group The name of the instance group. Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
zone The name of the zone where the instance group is located. Required

Context Output

Path Type Description
GCP.Compute.InstanceGroups.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.InstanceGroups.creationTimestamp string The creation timestamp for this instance group in RFC3339 text format.
GCP.Compute.InstanceGroups.name string The name of the instance group. The name must be 1-63 characters long, and comply with RFC1035.
GCP.Compute.InstanceGroups.description string An optional description of this resource.
GCP.Compute.InstanceGroups.namedPorts string Assigns a name to a port number.
GCP.Compute.InstanceGroups.namedPorts.name string The name for this named port. The name must be 1-63 characters long, and comply with RFC1035.
GCP.Compute.InstanceGroups.namedPorts.port string The port number, which can be a value between 1 and 65535.
GCP.Compute.InstanceGroups.network string The URL of the network to which all instances in the instance group belong.
GCP.Compute.InstanceGroups.fingerprint string The fingerprint of the named ports. The system uses this fingerprint to detect conflicts when multiple users change the named ports concurrently.
GCP.Compute.InstanceGroups.zone string The URL of the zone where the instance group is located (for zonal resources).
GCP.Compute.InstanceGroups.selfLink string The URL for this instance group. The server generates this URL.
GCP.Compute.InstanceGroups.size string The total number of instances in the instance group.
GCP.Compute.InstanceGroups.region string The URL of the region where the instance group is located (for regional resources).
GCP.Compute.InstanceGroups.subnetwork string The URL of the subnetwork to which all instances in the instance group belong.
GCP.Compute.InstanceGroups.kind string The resource type, which is always compute#instance_group for instance groups.

gcp-compute-region-get


Returns a specific region resource. Required permission: compute.regions.get.

Base Command

gcp-compute-region-get

Input

Argument Name Description Required
region Name of the region resource to return. Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional

Context Output

Path Type Description
GCP.Compute.Regions.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.Regions.creationTimestamp string Creation timestamp in RFC3339 text format.
GCP.Compute.Regions.name string Name of the resource.
GCP.Compute.Regions.description string Textual description of the resource.
GCP.Compute.Regions.status string Status of the region, either UP or DOWN.
GCP.Compute.Regions.zones string A list of zones available in this region, in the form of resource URLs.
GCP.Compute.Regions.quotas string Quotas assigned to this region.
GCP.Compute.Regions.quotas.metric string Name of the quota metric.
GCP.Compute.Regions.quotas.limit string Quota limit for this metric.
GCP.Compute.Regions.quotas.usage string Current usage of this metric.
GCP.Compute.Regions.quotas.owner string Owning resource. The resource to which this quota applies.
GCP.Compute.Regions.deprecated string The deprecation status associated with this region.
GCP.Compute.Regions.deprecated.state string The deprecation state of this resource. This can be ACTIVE DEPRECATED, OBSOLETE, or DELETED. Operations which communicate the end of life date for an image, can use ACTIVE. Operations which create a new resource using a DEPRECATED resource will return successfully, but with a warning indicating the deprecated resource and recommending its replacement. Operations which use OBSOLETE or DELETED resources will be rejected and result in an error.
GCP.Compute.Regions.deprecated.replacement string The URL of the suggested replacement for a deprecated resource. The suggested replacement resource must be the same kind of resource as the deprecated resource.
GCP.Compute.Regions.deprecated.deprecated string An optional RFC3339 timestamp on or after which the state of this resource is intended to change to DEPRECATED. This is only informational and the status will not change unless the client explicitly changes it.
GCP.Compute.Regions.deprecated.obsolete string An optional RFC3339 timestamp on or after which the state of this resource is intended to change to OBSOLETE. This is only informational and the status will not change unless the client explicitly changes it.
GCP.Compute.Regions.deprecated.deleted string An optional RFC3339 timestamp on or after which the state of this resource is intended to change to DELETED. This is only informational and the status will not change unless the client explicitly changes it.
GCP.Compute.Regions.selfLink string Server-defined URL for the resource.
GCP.Compute.Regions.kind string Type of the resource. Always compute#region for regions.

gcp-compute-zone-get


Returns a specific zone resource. Required permission: compute.zones.get.

Base Command

gcp-compute-zone-get

Input

Argument Name Description Required
zone Name of the zone resource to return. Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional

Context Output

Path Type Description
GCP.Compute.Zones.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.Zones.creationTimestamp string Creation timestamp in RFC3339 text format.
GCP.Compute.Zones.name string Name of the resource.
GCP.Compute.Zones.description string Textual description of the resource.
GCP.Compute.Zones.status string Status of the zone, either UP or DOWN.
GCP.Compute.Zones.deprecated string The deprecation status associated with this zone.
GCP.Compute.Zones.deprecated.state string The deprecation state of this resource. This can be ACTIVE DEPRECATED, OBSOLETE, or DELETED. Operations which communicate the end of life date for an image, can use ACTIVE. Operations which create a new resource using a DEPRECATED resource will return successfully, but with a warning indicating the deprecated resource and recommending its replacement. Operations which use OBSOLETE or DELETED resources will be rejected and result in an error.
GCP.Compute.Zones.deprecated.replacement string The URL of the suggested replacement for a deprecated resource. The suggested replacement resource must be the same kind of resource as the deprecated resource.
GCP.Compute.Zones.deprecated.deprecated string An optional RFC3339 timestamp on or after which the state of this resource is intended to change to DEPRECATED. This is only informational and the status will not change unless the client explicitly changes it.
GCP.Compute.Zones.deprecated.obsolete string An optional RFC3339 timestamp on or after which the state of this resource is intended to change to OBSOLETE. This is only informational and the status will not change unless the client explicitly changes it.
GCP.Compute.Zones.deprecated.deleted string An optional RFC3339 timestamp on or after which the state of this resource is intended to change to DELETED. This is only informational and the status will not change unless the client explicitly changes it.
GCP.Compute.Zones.region string Full URL reference to the region which hosts the zone.
GCP.Compute.Zones.selfLink string Server-defined URL for the resource.
GCP.Compute.Zones.availableCpuPlatforms string Available cpu/platform selections for the zone. Do not use field = 7 or field = 11. Next available field = 14.
GCP.Compute.Zones.kind string Type of the resource. Always compute#zone for zones.

gcp-compute-networks-list


Retrieves a list of networks available for the specified project. Required permission: compute.networks.list.

Base Command

gcp-compute-networks-list

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
limit The maximum number of results per page that should be returned. If the number of available results is larger than limit, Compute Engine returns a nextPageToken that can be used to get the next page of results in subsequent list requests. Acceptable values are 0 to 50, inclusive. Default is 50. Optional
filters A filter expression that filters resources listed in the response. The expression must specify the field name, a comparison operator, and the value that you want to use for filtering. The value must be a string, a number, or a boolean. The comparison operator must be either =, !=, >, or <. For example, if you are filtering Compute Engine instances, you can exclude instances named example-instance by specifying name != example-instance. Optional
order_by Sorts list results by a certain order. By default, results are returned in alphanumerical order based on the resource name. You can also sort results in descending order based on the creation timestamp using order_by=”creationTimestamp desc”. This sorts results based on the creationTimestamp field in reverse chronological order (newest result first). Use this to sort resources like operations so that the newest operation is returned first. Optional
page_token Specifies a page token to use. Set page_token to the nextPageToken returned by a previous list request to get the next page of results. Optional

Context Output

Path Type Description
GCP.Compute.Networks.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.Networks.creationTimestamp string Creation timestamp in RFC3339 text format.
GCP.Compute.Networks.name string Name of the resource. Provided by the client when the resource is created. The name must be 1-63 characters long, and comply with RFC1035. Specifically, the name must be 1-63 characters long and match the regular expression [a-z]([-a-z0-9]*[a-z0-9])? which means the first character must be a lowercase letter, and all following characters must be a dash, lowercase letter, or digit, except the last character, which cannot be a dash.
GCP.Compute.Networks.description string An optional description of this resource.
GCP.Compute.Networks.gatewayIPv4 string The gateway address for default routing out of the network. This value is read only and is selected by GCP.
GCP.Compute.Networks.selfLink string Server-defined URL for the resource.
GCP.Compute.Networks.autoCreateSubnetworks boolean When set to true, the VPC network is created in “auto” mode. When set to false, the VPC network is created in “custom” mode.
GCP.Compute.Networks.subnetworks string Server-defined fully-qualified URLs for all subnetworks in this VPC network.
GCP.Compute.Networks.peerings string A list of network peerings for the resource.
GCP.Compute.Networks.peerings.name string Name of this peering. Provided by the client when the peering is created. The name must comply with RFC1035. Specifically, the name must be 1-63 characters long and match regular expression [a-z]([-a-z0-9]*[a-z0-9])? which means the first character must be a lowercase letter, and all the following characters must be a dash, lowercase letter, or digit, except the last character, which cannot be a dash.
GCP.Compute.Networks.peerings.network string The URL of the peer network. It can be either full URL or partial URL. The peer network may belong to a different project. If the partial URL does not contain project, it is assumed that the peer network is in the same project as the current network.
GCP.Compute.Networks.peerings.state string State for the peering.
GCP.Compute.Networks.peerings.stateDetails string Details about the current state of the peering.
GCP.Compute.Networks.peerings.autoCreateRoutes boolean This field will be deprecated soon. Prefer using exchangeSubnetRoutes instead. Indicates whether full mesh connectivity is created and managed automatically. When set to true, Google Compute Engine automatically creates and manages the routes between two networks while the state is ACTIVE. If set to false, the user must manually create routes to direct packets to the peer network.
GCP.Compute.Networks.peerings.exchangeSubnetRoutes boolean Whether full mesh connectivity is created and managed automatically. When set to true, Google Compute Engine automatically creates and manages the routes between two networks while the peering state is ACTIVE. If set to false, the user must manually create routes to send packets to the peer network.
GCP.Compute.Networks.routingConfig string The network-level routing configuration for this network. Used by Cloud Router to determine what type of network-wide routing behavior to enforce.
GCP.Compute.Networks.routingConfig.routingMode string Specifies the network-wide routing mode. If set to REGIONAL, the network’s cloud routers advertise routes only for subnets in the same region as the router. If set to GLOBAL, cloud routers advertise routes for all subnets in the network across all regions.
GCP.Compute.Networks.kind string Type of the resource. Always compute#network for networks.

gcp-compute-network-insert


Creates a network in the specified project using the data included in the request. Required permission: compute.networks.create.

Base Command

gcp-compute-network-insert

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
name Name of the resource. Provided by the client when the resource is created. The name must be 1-63 characters long, and comply with RFC1035. Specifically, the name must be 1-63 characters long and match the regular expression a-z? which means the first character must be a lowercase letter, and all following characters must be a dash, lowercase letter, or digit, except the last character, which cannot be a dash. Required
description An optional description of this resource. Optional
auto_create_sub_networks When set to true, the VPC network is created in “auto” mode. When set to false, the VPC network is created in “custom” mode. An auto mode VPC network starts with one subnet per region. Each subnet has a predetermined range as described in Auto mode VPC network IP ranges. Possible values are: true, false. Optional
routing_config_routing_mode The network-wide routing mode to use. If set to REGIONAL, this network’s cloud routers will only advertise routes with subnets of this network in the same region as the router. If set to GLOBAL, this network’s cloud routers will advertise routes with all subnets of this network, across regions. Possible values are: REGIONAL, GLOBAL. Optional

Context Output

Path Type Description
GCP.Compute.Operations.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.Operations.name string Name of the resource.
GCP.Compute.Operations.zone string The URL of the zone where the operation resides. Only available when performing per-zone operations. You must specify this field as part of the HTTP request URL. It is not settable as a field in the request body.
GCP.Compute.Operations.clientOperationId string The value of requestId if you provided it in the request. Not present otherwise.
GCP.Compute.Operations.operationType string The type of operation, such as insert, update, or delete, and so on.
GCP.Compute.Operations.targetLink string The URL of the resource that the operation modifies. For operations related to creating a snapshot, this points to the persistent disk that the snapshot was created from.
GCP.Compute.Operations.targetId string The unique target ID, which identifies a specific incarnation of the target resource.
GCP.Compute.Operations.status string The status of the operation, which can be one of the following: PENDING RUNNING or DONE.
GCP.Compute.Operations.statusMessage string An optional textual description of the current status of the operation.
GCP.Compute.Operations.user string User who requested the operation for example EMAILADDRESS.
GCP.Compute.Operations.progress number An optional progress indicator that ranges from 0 to 100. There is no requirement that this be linear or support any granularity of operations. This should not be used to guess when the operation will be complete. This number should monotonically increase as the operation progresses.
GCP.Compute.Operations.insertTime string The time that this operation was requested. This value is in RFC3339 text format.
GCP.Compute.Operations.startTime string The time that this operation was started by the server. This value is in RFC3339 text format.
GCP.Compute.Operations.endTime string The time that this operation was completed. This value is in RFC3339 text format.
GCP.Compute.Operations.error string If errors are generated during processing of the operation, this field will be populated.
GCP.Compute.Operations.error.errors string The array of errors encountered while processing this operation.
GCP.Compute.Operations.error.errors string The error type identifier for this error.
GCP.Compute.Operations.error.errors string Indicates the field in the request that caused the error. This property is optional.
GCP.Compute.Operations.error.errors string An optional, human-readable error message.
GCP.Compute.Operations.warnings string If warning messages are generated during processing of the operation, this field will be populated.
GCP.Compute.Operations.warnings.code string A warning code, if applicable. For example, Compute Engine returns NO_RESULTS_ON_PAGE if there are no results in the response.
GCP.Compute.Operations.warnings.message string A human-readable description of the warning code.
GCP.Compute.Operations.warnings.data string Metadata about this warning in key: value format.
GCP.Compute.Operations.warnings.data string A key that provides more detail on the warning being returned. For example, for warnings where there are no results in a list request for a particular zone, this key might be scope and the key value might be the zone name. Other examples might be a key indicating a deprecated resource and a suggested replacement, or a warning about invalid network settings (for example, if an instance attempts to perform IP forwarding but is not enabled for IP forwarding).
GCP.Compute.Operations.warnings.data string A warning data value corresponding to the key.
GCP.Compute.Operations.httpErrorStatusCode number If the operation fails, this field contains the HTTP error status code that was returned. For example, a 404 means the resource was not found.
GCP.Compute.Operations.httpErrorMessage string If the operation fails, this field contains the HTTP error message that was returned, such as NOT FOUND.
GCP.Compute.Operations.selfLink string Server-defined URL for the resource.
GCP.Compute.Operations.region string The URL of the region where the operation resides. Only available when performing regional operations. You must specify this field as part of the HTTP request URL. It is not settable as a field in the request body.
GCP.Compute.Operations.description string A textual description of the operation, which is set when the operation is created.
GCP.Compute.Operations.kind string Type of the resource. Always compute#operation for Operation resources.

gcp-compute-network-get


Returns the specified network.

Base Command

gcp-compute-network-get

Input

Argument Name Description Required
network Name of the network to return. Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional

Context Output

Path Type Description
GCP.Compute.Networks.id string The unique identifier for the resource. This identifier is defined by the server.
GCP.Compute.Networks.creationTimestamp string Creation timestamp in RFC3339 text format.
GCP.Compute.Networks.name string Name of the resource. Provided by the client when the resource is created. The name must be 1-63 characters long, and comply with RFC1035. Specifically, the name must be 1-63 characters long and match the regular expression [a-z]([-a-z0-9]*[a-z0-9])? which means the first character must be a lowercase letter, and all following characters must be a dash, lowercase letter, or digit, except the last character, which cannot be a dash.
GCP.Compute.Networks.description string An optional description of this resource.
GCP.Compute.Networks.gatewayIPv4 string The gateway address for default routing out of the network. This value is read only and is selected by GCP.
GCP.Compute.Networks.selfLink string Server-defined URL for the resource.
GCP.Compute.Networks.autoCreateSubnetworks boolean When set to true, the VPC network is created in “auto” mode. When set to false, the VPC network is created in “custom” mode.
GCP.Compute.Networks.subnetworks string Server-defined fully-qualified URLs for all subnetworks in this VPC network.
GCP.Compute.Networks.peerings string A list of network peerings for the resource.
GCP.Compute.Networks.peerings.name string Name of this peering. Provided by the client when the peering is created. The name must comply with RFC1035. Specifically, the name must be 1-63 characters long and match regular expression [a-z]([-a-z0-9]*[a-z0-9])? which means the first character must be a lowercase letter, and all the following characters must be a dash, lowercase letter, or digit, except the last character, which cannot be a dash.
GCP.Compute.Networks.peerings.network string The URL of the peer network. It can be either full URL or partial URL. The peer network may belong to a different project. If the partial URL does not contain project, it is assumed that the peer network is in the same project as the current network.
GCP.Compute.Networks.peerings.state string State for the peering.
GCP.Compute.Networks.peerings.stateDetails string Details about the current state of the peering.
GCP.Compute.Networks.peerings.autoCreateRoutes boolean This field will be deprecated soon. Prefer using exchangeSubnetRoutes instead. Indicates whether full mesh connectivity is created and managed automatically. When it is set to true, Google Compute Engine will automatically create and manage the routes between two networks when the state is ACTIVE. Otherwise, user needs to create routes manually to route packets to peer network.
GCP.Compute.Networks.peerings.exchangeSubnetRoutes boolean Whether full mesh connectivity is created and managed automatically. When it is set to true, Google Compute Engine will automatically create and manage the routes between two networks when the peering state is ACTIVE. Otherwise, user needs to create routes manually to route packets to peer network.
GCP.Compute.Networks.routingConfig string The network-level routing configuration for this network. Used by Cloud Router to determine what type of network-wide routing behavior to enforce.
GCP.Compute.Networks.routingConfig.routingMode string The network-wide routing mode to use. If set to REGIONAL, this networks cloud routers will only advertise routes with subnets of this network in the same region as the router. If set to GLOBAL, this networks cloud routers will advertise routes with all subnets of this network, across regions.
GCP.Compute.Networks.kind string Type of the resource. Always compute#network for networks.

gcp-compute-firewall-insert


Creates a new firewall rule in a specific project. Required permission: compute.firewalls.create.

Base Command

gcp-compute-firewall-insert

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
resource_name Name of the firewall rule to create. Required
description An optional description for the firewall rule. Optional
network URL of the network, e.g., global/networks/default. Optional
priority Priority 0-65535. Default 1000. Optional
direction Direction of traffic to which this firewall applies. Default INGRESS. Possible values are: INGRESS, EGRESS. Default is INGRESS. Optional
allowed ALLOW rules in tuples, e.g., ipprotocol=tcp,ports=443;ipprotocol=tcp,ports=80. Optional
denied DENY rules in tuples, e.g., ipprotocol=tcp,ports=22,443. Optional
source_ranges Comma-separated CIDRs for INGRESS. Optional
destination_ranges Comma-separated CIDRs for EGRESS. Optional
source_tags Comma-separated instance tags to match as source. Optional
target_tags Comma-separated tags to apply this rule to. Optional
source_service_accounts Comma-separated service accounts for source. Optional
target_service_accounts Comma-separated service accounts to target. Optional
log_config_enable Enable firewall logging. Possible values are: true, false. Optional
disabled Whether this firewall rule is disabled. Possible values are: true, false. Optional

Context Output

Path Type Description
GCP.Compute.Operations.id String Unique identifier for the operation resource, defined by the server.
GCP.Compute.Operations.name String Name of the operation resource.
GCP.Compute.Operations.kind String Type of the resource, for example compute#operation.
GCP.Compute.Operations.operationType String Type of operation, such as insert, update, or delete.
GCP.Compute.Operations.status String Current status of the operation.
GCP.Compute.Operations.progress Number Progress of the operation as a percentage between 0 and 100.
GCP.Compute.Operations.targetId String Unique target ID of the resource affected by the operation.
GCP.Compute.Operations.targetLink String URL of the target resource modified by the operation.
GCP.Compute.Operations.selfLink String Server-defined URL for the operation resource.
GCP.Compute.Operations.insertTime Date The time when the operation resource was created.
GCP.Compute.Operations.startTime Date The time when the operation started running.
GCP.Compute.Operations.user String The user account that performed the operation.

gcp-compute-firewall-list


Lists the firewall rules in a specific project. Required permission: compute.firewalls.list.

Base Command

gcp-compute-firewall-list

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
limit Maximum number of results to return. Acceptable values are 0 to 500, inclusive. Default is 50. Optional
page_token Token for pagination. Optional
filter A filter expression for resources listed in the response. The expression must specify a field name, a comparison operator (=, !=, >, or <), and a value, which can be a string, number, or boolean. For example, to exclude a Compute Engine instance named example-instance, use name != example-instance.
For more options and details, see:
https://cloud.google.com/compute/docs/reference/rest/v1/firewalls/list#:~:text=page%20of%20results.-,filter,-string.
Optional

Context Output

Path Type Description
GCP.Compute.Firewall.id String Unique identifier for the firewall rule.
GCP.Compute.Firewall.name String Name of the firewall rule.
GCP.Compute.Firewall.kind String Type of the resource (for example, compute#firewall).
GCP.Compute.Firewall.description String Description of the firewall rule.
GCP.Compute.Firewall.direction String Direction of traffic for the rule (INGRESS or EGRESS).
GCP.Compute.Firewall.disabled Boolean Indicates whether the firewall rule is disabled.
GCP.Compute.Firewall.priority Number Priority value of the firewall rule.
GCP.Compute.Firewall.network String The network URL this firewall rule applies to.
GCP.Compute.Firewall.selfLink String Server-defined URL for the resource.
GCP.Compute.Firewall.creationTimestamp Date The creation timestamp of the firewall rule.
GCP.Compute.Firewall.logConfig.enable Boolean Indicates whether logging is enabled for the firewall rule.
GCP.Compute.Firewall.sourceRanges Unknown List of source IP ranges that the rule applies to.
GCP.Compute.Firewall.targetTags Unknown List of target instance tags to which the rule applies.
GCP.Compute.FirewallNextToken String Next page token for pagination.

gcp-compute-firewall-get


Retrieves a specific firewall rule by name. Required permission: compute.firewalls.get.

Base Command

gcp-compute-firewall-get

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
resource_name Firewall rule name. Required

Context Output

Path Type Description
GCP.Compute.Firewall.name string Firewall rule name.
GCP.Compute.Firewall.network string Network URL for the firewall rule.
GCP.Compute.Firewall.direction string Direction of traffic (INGRESS/EGRESS).
GCP.Compute.Firewall.priority number Priority of the rule.
GCP.Compute.Firewall.allowed Unknown Allowed tuples.
GCP.Compute.Firewall.denied Unknown Denied tuples.
GCP.Compute.Firewall.targetTags Unknown Target instance tags.

gcp-compute-snapshots-list


Lists snapshots in a specific project. Required permission: compute.snapshots.list.

Base Command

gcp-compute-snapshots-list

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
limit Maximum number of results to return. Acceptable values are 0 to 500, inclusive. Default is 50. Optional
page_token Token for pagination. Optional
filter A filter expression for resources listed in the response. The expression must specify a field name, a comparison operator (=, !=, >, or <), and a value, which can be a string, number, or boolean. For example, to exclude a Compute Engine instance named example-instance, use name != example-instance.
For more options and details, see:
https://cloud.google.com/compute/docs/reference/rest/v1/snapshots/list#:~:text=page%20of%20results.-,filter,-string.
Optional

Context Output

Path Type Description
GCP.Compute.Snapshot.id String Unique identifier for the snapshot resource.
GCP.Compute.Snapshot.name String Name of the snapshot resource.
GCP.Compute.Snapshot.kind String Type of the resource, for example compute#snapshot.
GCP.Compute.Snapshot.status String Current status of the snapshot, such as READY or FAILED.
GCP.Compute.Snapshot.autoCreated Boolean Indicates whether the snapshot was automatically created.
GCP.Compute.Snapshot.architecture String CPU architecture of the source disk, for example X86_64.
GCP.Compute.Snapshot.creationTimestamp Date The time when the snapshot was created.
GCP.Compute.Snapshot.creationSizeBytes Number Total size of the snapshot in bytes at creation time.
GCP.Compute.Snapshot.diskSizeGb Number Size of the snapshot in gigabytes.
GCP.Compute.Snapshot.downloadBytes Number Total bytes downloaded to create the snapshot.
GCP.Compute.Snapshot.enableConfidentialCompute Boolean Indicates if confidential compute is enabled for this snapshot.
GCP.Compute.Snapshot.labelFingerprint String Fingerprint for the labels applied to the snapshot.
GCP.Compute.Snapshot.licenseCodes Unknown List of license code identifiers attached to the snapshot.
GCP.Compute.Snapshot.licenses Unknown List of license URLs associated with the snapshot.
GCP.Compute.Snapshot.selfLink String Server-defined URL for the snapshot resource.
GCP.Compute.Snapshot.sourceDisk String URL of the source disk used to create the snapshot.
GCP.Compute.Snapshot.sourceDiskId String Unique ID of the source disk used to create the snapshot.
GCP.Compute.Snapshot.sourceSnapshotSchedulePolicy String URL of the snapshot schedule policy used to create this snapshot.
GCP.Compute.Snapshot.sourceSnapshotSchedulePolicyId String Unique ID of the snapshot schedule policy used to create this snapshot.
GCP.Compute.Snapshot.storageBytes Number Total storage size of the snapshot in bytes.
GCP.Compute.Snapshot.storageBytesStatus String Status of the storage bytes usage, for example UP_TO_DATE.
GCP.Compute.Snapshot.storageLocations Unknown List of storage locations for the snapshot.
GCP.Compute.SnapshotNextToken String Next page token for pagination.

gcp-bq-dataset-policy-remove


Removes an email from the BigQuery dataset policy. Required Permissions: bigquery.datasets.update, bigquery.datasets.get, bigquery.datasets.getIamPolicy, bigquery.datasets.setIamPolicy.

Base Command

gcp-bq-dataset-policy-remove

Input

Argument Name Description Required
project_id The GCP project ID. Required for Cortex Platform (which includes Cortex XSIAM version >=3.0 and Cortex Cloud). Optional for Cortex XSOAR and Cortex XSIAM version < 3.0, where it can be retrieved from the integration configuration. Optional
dataset_id The dataset ID of the requested dataset. Required
email The email address to remove from the dataset access list. Required

Context Output

Path Type Description
GCP.BigQuery.Datasets.kind String The resource type.
GCP.BigQuery.Datasets.etag String A hash of the resource.
GCP.BigQuery.Datasets.id String The fully-qualified unique name of the dataset in the format projectId:datasetId.
GCP.BigQuery.Datasets.selfLink String A URL that can be used to access the resource again.
GCP.BigQuery.Datasets.datasetReference Unknown A reference that identifies the dataset.
GCP.BigQuery.Datasets.friendlyName String A descriptive name for the dataset.
GCP.BigQuery.Datasets.description String A user-friendly description of the dataset.
GCP.BigQuery.Datasets.defaultTableExpirationMs String The default lifetime of all tables in the dataset, in milliseconds.
GCP.BigQuery.Datasets.defaultPartitionExpirationMs String The default partition expiration, in milliseconds.
GCP.BigQuery.Datasets.labels String The labels associated with this dataset.
GCP.BigQuery.Datasets.access.role String The role assigned to the entity.
GCP.BigQuery.Datasets.access.userByEmail String An email address of a user to grant access to.
GCP.BigQuery.Datasets.access.groupByEmail String An email address of a group to grant access to.
GCP.BigQuery.Datasets.access.domain String A domain to grant access to.
GCP.BigQuery.Datasets.access.specialGroup String A special group to grant access to.
GCP.BigQuery.Datasets.access.iamMember String A type of member that appears in the IAM Policy that isn’t a user, group, domain, or special group.
GCP.BigQuery.Datasets.access.view Unknown A view from a different dataset to grant access to.
GCP.BigQuery.Datasets.access.routine Unknown A routine from a different dataset to grant access to.
GCP.BigQuery.Datasets.access.dataset Unknown A grant authorizing access to this dataset for all resources of a particular type.
GCP.BigQuery.Datasets.access.condition Unknown The binding condition.
GCP.BigQuery.Datasets.creationTime String The time since the epoch the dataset was created, in milliseconds.
GCP.BigQuery.Datasets.lastModifiedTime String The time since the epoch the dataset was last modified, in milliseconds.
GCP.BigQuery.Datasets.location String The geographic location where the dataset resides.
GCP.BigQuery.Datasets.defaultEncryptionConfiguration String The default encryption configuration for all tables in the dataset.
GCP.BigQuery.Datasets.type String The type of the dataset.
GCP.BigQuery.Datasets.linkedDatasetSource Unknown The source dataset reference when the dataset is of type LINKED.
GCP.BigQuery.Datasets.linkedDatasetMetadata Unknown Metadata about the LinkedDataset.
GCP.BigQuery.Datasets.externalDatasetReference Unknown Reference to a read-only external dataset defined in data catalogs outside of BigQuery.
GCP.BigQuery.Datasets.externalCatalogDatasetOptions Unknown Options defining open source compatible datasets in the BigQuery catalog. Contains metadata of the open source database, schema, or namespace of the current dataset.
GCP.BigQuery.Datasets.isCaseInsensitive String True if the dataset and its table names are case-insensitive.
GCP.BigQuery.Datasets.defaultCollation String The default collation specification of future tables created in the dataset.
GCP.BigQuery.Datasets.defaultRoundingMode String The default rounding mode specification of new tables created within this dataset.
GCP.BigQuery.Datasets.maxTimeTravelHours String The time travel window in hours.
GCP.BigQuery.Datasets.resourceTags String The tags attached to this dataset.
GCP.BigQuery.Datasets.storageBillingModel String The billing model that will be applied to the dataset.
GCP.BigQuery.Datasets.catalogSource String The origin of the dataset.

Configuration parameters

  • credentials — (required)
  • project_id — GCP Project ID
  • proxy — Use system proxy settings
  • insecure — Trust any certificate (not secure)

Commands (45)

  • gcp-bq-dataset-policy-remove

    Removes an email from the BigQuery dataset policy. Required Permissions: bigquery.datasets.update, bigquery.datasets.get, bigquery.datasets.getIamPolicy, bigquery.datasets.setIamPolicy.

  • gcp-compute-enable-private-ip-access-on-subnet-quick-action

    Enables Private Google Access on a GCP subnet. Required permissions: compute.subnetworks.setPrivateIpGoogleAccess, compute.subnetworks.update, compute.subnetworks.get, compute.subnetworks.list.

  • gcp-compute-firewall-get

    Retrieves a specific firewall rule by name. Required permission: compute.firewalls.get.

  • gcp-compute-firewall-insert

    Creates a new firewall rule in a specific project. Required permission: compute.firewalls.create.

  • gcp-compute-firewall-list

    Lists the firewall rules in a specific project. Required permission: compute.firewalls.list.

  • gcp-compute-firewall-patch

    Updates a specific firewall rule with the data included in the request. Required permissions: compute.firewalls.update, compute.firewalls.get, compute.firewalls.list, compute.networks.updatePolicy, compute.networks.list.

  • gcp-compute-firewall-patch-disable-gcp-default-firewall-rule-quick-action

    Disables a default GCP firewall rule that is overly permissive (except HTTP and HTTPS). Required permissions: compute.firewalls.update, compute.firewalls.get, compute.firewalls.list, compute.networks.updatePolicy, compute.networks.list.

  • gcp-compute-image-get

    Returns a specific image. Required permission: compute.images.get.

  • gcp-compute-instance-get

    Returns a specific instance resource. To get a list of available instances, make a list() request. Required permission: compute.instances.get.

  • gcp-compute-instance-group-get

    Returns a specific instance group. Required permission: compute.instanceGroups.get.

  • gcp-compute-instance-labels-set

    Sets labels on an instance. Required permission: compute.instances.setLabels.

  • gcp-compute-instance-service-account-remove

    Removes the service account associated with a GCP Compute Engine VM instance. The instance must be stopped before the service account can be changed. Required permissions: compute.instances.setServiceAccount, compute.instances.get.

  • gcp-compute-instance-service-account-set

    Sets the service account for a GCP Compute Engine VM instance. The instance must be stopped before the service account can be changed. Required permissions: compute.instances.setServiceAccount, compute.instances.get.

  • gcp-compute-instance-start

    Starts an instance that was stopped using the instances().stop method. Required permission: compute.instances.start.

  • gcp-compute-instance-stop

    Stops and cleanly shuts down a running instance, allowing you to restart the instance at a later time. Stopped instances do not incur VM usage charges while they are stopped. However, resources that the VM is using such as persistent disks and static IP addresses will continue to be charged until they are deleted. Required permission: compute.instances.stop.

  • gcp-compute-instances-aggregated-list-by-ip

    Returns an aggregated list of instances across all zones that can be filtered by internal or external IP. Required permission: cloudasset.assets.searchAllResources.

  • gcp-compute-instances-list

    Retrieves the list of instances in the specified zone. Required permission: compute.instances.list.

  • gcp-compute-network-get

    Returns the specified network.

  • gcp-compute-network-insert

    Creates a network in the specified project using the data included in the request. Required permission: compute.networks.create.

  • gcp-compute-network-tag-set

    Adds a network tag to a VM instance (merges with existing tags). Required permission: compute.instances.setTags.

  • gcp-compute-networks-list

    Retrieves a list of networks available for the specified project. Required permission: compute.networks.list.

  • gcp-compute-region-get

    Returns a specific region resource. Required permission: compute.regions.get.

  • gcp-compute-snapshot-get

    Retrieves details for a specific snapshot. Required permission: compute.snapshots.get.

  • gcp-compute-snapshots-list

    Lists snapshots in a specific project. Required permission: compute.snapshots.list.

  • gcp-compute-subnet-update

    Enables flow logs or Private Google Access on a subnet. Required permissions: compute.subnetworks.setPrivateIpGoogleAccess, compute.subnetworks.update, compute.subnetworks.get, compute.subnetworks.list.

  • gcp-compute-subnet-update-enable-gcp-subnet-flow-logs-quick-action

    Enables VPC Flow Logs on a GCP subnet. Required permissions: compute.subnetworks.setPrivateIpGoogleAccess, compute.subnetworks.update, compute.subnetworks.get, compute.subnetworks.list.

  • gcp-compute-zone-get

    Returns a specific zone resource. Required permission: compute.zones.get.

  • gcp-container-cluster-security-update

    Configures security settings for GKE clusters, including access controls and visibility. Required permissions: container.clusters.update, container.clusters.get, container.clusters.list.

  • gcp-container-cluster-security-update-enable-gke-cluster-intra-node-visibility-quick-action

    Enables intra-node visibility on a GKE cluster for enhanced network monitoring. Required permissions: container.clusters.update, container.clusters.get, container.clusters.list.

  • gcp-container-cluster-security-update-enable-master-authorized-networks-on-gke-quick-action

    Enables Master Authorized Networks on a GKE cluster to restrict API server access. Required permissions: container.clusters.update, container.clusters.get, container.clusters.list.

  • gcp-iam-project-policy-binding-remove

    Removes a specified IAM role binding from a GCP project. Required permissions: resourcemanager.projects.getIamPolicy, resourcemanager.projects.setIamPolicy.

  • gcp-storage-bucket-get

    Retrieves information about a specific bucket. Required permission: storage.buckets.get.

  • gcp-storage-bucket-list

    Retrieves the list of buckets in the project associated with the client. Required permission: storage.buckets.list.

  • gcp-storage-bucket-metadata-update

    Updates Google Cloud Storage (GCS) bucket metadata, including settings such as versioning and Uniform Bucket-Level Access (UBLA). Required permission: storage.buckets.update.

  • gcp-storage-bucket-metadata-update-enable-GCP-bucket-versioning-quick-action

    Enables versioning on a GCP storage bucket to preserve object versions. Required permission: storage.buckets.update.

  • gcp-storage-bucket-metadata-update-set-GCP-bucket-access-to-uniform-quick-action

    Enables uniform bucket-level access on a GCP storage bucket for enhanced security. Required permission: storage.buckets.update.

  • gcp-storage-bucket-object-policy-list

    Retrieves the IAM policy for a specific object in a bucket. Required permission: storage.objects.getIamPolicy.

  • gcp-storage-bucket-object-policy-set

    Sets the IAM policy for a specific object in a bucket. Required permission: storage.objects.setIamPolicy.

  • gcp-storage-bucket-objects-list

    Retrieves the list of objects in a bucket. Required permission: storage.objects.list.

  • gcp-storage-bucket-policy-delete

    Removes an entity from a bucket's Access Control List. Required permissions: storage.buckets.getIamPolicy, storage.buckets.setIamPolicy.

  • gcp-storage-bucket-policy-delete-make-gcp-bucket-private-quick-action

    Revokes 'allUsers' and 'allAuthenticatedUsers' permissions access in a GCP storage bucket. Required permissions: storage.buckets.getIamPolicy, storage.buckets.setIamPolicy.

  • gcp-storage-bucket-policy-delete-remove-AllAuthenticatedUser-access-quick-action

    Revokes 'allAuthenticatedUsers' permission access in GCP storage buckets. Required permissions: storage.buckets.getIamPolicy, storage.buckets.setIamPolicy.

  • gcp-storage-bucket-policy-delete-remove-allusers-access-quick-action

    Revokes 'allUsers' permission access in GCP storage buckets. Required permissions: storage.buckets.getIamPolicy, storage.buckets.setIamPolicy.

  • gcp-storage-bucket-policy-list

    Retrieves the IAM policy for a bucket. Required permissions: storage.buckets.getIamPolicy, storage.buckets.get.

  • gcp-storage-bucket-policy-set

    Sets the IAM policy for a bucket. Required permission: storage.buckets.setIamPolicy.

import ast
import json
import pytest
from google.oauth2.credentials import Credentials
from unittest.mock import MagicMock
import os
import re
import yaml


def util_load_json(path):
    with open(path, encoding="utf-8") as f:
        return json.loads(f.read())


QUICK_ACTION_SUFFIX = "-quick-action"
_INTEGRATION_DIR = os.path.dirname(os.path.abspath(__file__))
_YML_PATH = os.path.join(_INTEGRATION_DIR, "GCP.yml")
_PY_PATH = os.path.join(_INTEGRATION_DIR, "GCP.py")

# The integration sources are read (and the YML parsed) once at import time, so the
# checks below do not re-read them from disk for every test.
with open(_PY_PATH, encoding="utf-8") as _py_file:
    _PY_SOURCE = _py_file.read()
with open(_YML_PATH, encoding="utf-8") as _yml_file:
    _YML = yaml.safe_load(_yml_file)

# The .py is parsed into an AST exactly once and every structural check below
# reuses this tree, so the file is never re-parsed per test.
_PY_TREE = ast.parse(_PY_SOURCE)

# Platform-standard arguments that are resolved centrally (via get_credentials /
# the integration configuration) rather than read with args.get(...) inside each
# command handler. They are exempt from the per-handler verbatim arg check.
PLATFORM_STANDARD_ARGS = {"project_id", "account_id"}


def test_parse_firewall_rule_valid_input():
    """
    Given: A valid firewall rule string with multiple rules
    When: parse_firewall_rule is called
    Then: The function returns a correctly parsed list of dictionaries
    """
    from GCP import parse_firewall_rule

    input_str = "ipprotocol=tcp,ports=80,443;ipprotocol=udp,ports=53"
    expected = [{"IPProtocol": "tcp", "ports": ["80", "443"]}, {"IPProtocol": "udp", "ports": ["53"]}]

    result = parse_firewall_rule(input_str)
    assert result == expected


def test_parse_firewall_rule_invalid_input():
    """
    Given: An invalid firewall rule string
    When: parse_firewall_rule is called
    Then: The function raises a ValueError with appropriate message
    """
    from GCP import parse_firewall_rule

    input_str = "invalid=format"
    with pytest.raises(ValueError) as e:
        parse_firewall_rule(input_str)

    assert "Could not parse field" in str(e.value)
    assert "Please make sure you provided like so" in str(e.value)


def test_parse_metadata_items_valid_input():
    """
    Given: A valid metadata items string with multiple items
    When: parse_metadata_items is called
    Then: The function returns a correctly parsed list of dictionaries
    """
    from GCP import parse_metadata_items

    input_str = "key=enable-oslogin,value=true;key=serial-port-enable,value=false"
    expected = [{"key": "enable-oslogin", "value": "true"}, {"key": "serial-port-enable", "value": "false"}]

    result = parse_metadata_items(input_str)
    assert result == expected


def test_parse_metadata_items_invalid_input():
    """
    Given: An invalid metadata items string
    When: parse_metadata_items is called
    Then: The function raises a ValueError with appropriate message
    """
    from GCP import parse_metadata_items

    input_str = "wrong=format"
    with pytest.raises(ValueError) as e:
        parse_metadata_items(input_str)

    assert "Could not parse field" in str(e.value)
    assert "Please make sure you provided like so: key=abc,value=123" in str(e.value)


def test_compute_firewall_patch_edge_cases(mocker):
    """
    Given: Valid credentials with empty and complex arguments for a firewall rule update
    When: compute_firewall_patch is called with boolean and list conversions
    Then: The function handles data transformations correctly and builds proper requests
    """
    from GCP import compute_firewall_patch

    # Mock credentials
    mock_creds = mocker.MagicMock()

    # Set up mocks
    mock_compute = mocker.MagicMock()
    mock_firewalls = mocker.MagicMock()
    mock_patch = mocker.MagicMock()

    mock_compute.firewalls.return_value = mock_firewalls
    mock_firewalls.patch.return_value = mock_patch
    mock_patch.execute.return_value = {"id": "operation-123", "status": "RUNNING"}

    # Mock the build function
    mocker.patch("GCP.build", return_value=mock_compute)
    mocker.patch("GCP.tableToMarkdown", return_value="mocked markdown")

    # Test case 1: Empty configuration
    empty_args = {"project_id": "test-project", "resource_name": "fw-rule"}

    compute_firewall_patch(mock_creds, empty_args)

    # Should call with empty config
    mock_firewalls.patch.assert_called_with(project="test-project", firewall="fw-rule", body={})

    # Reset mock for next test
    mock_firewalls.patch.reset_mock()

    # Test case 2: Boolean conversions and special fields
    bool_args = {
        "project_id": "test-project",
        "resource_name": "fw-rule",
        "disabled": "true",  # String boolean that should be converted
        "logConfigEnable": "false",  # Another string boolean
        "sourceTags": "single-tag",  # Single item that should become a list
        "allowed": "ipprotocol=all,ports=*",  # Special format for allowed
    }

    result = compute_firewall_patch(mock_creds, bool_args)

    # Get the body passed to patch
    called_with = mock_firewalls.patch.call_args[1]["body"]

    # Verify boolean conversions
    assert called_with["disabled"] is True
    assert called_with["logConfig"]["enable"] is False

    # Verify list conversions
    assert called_with["sourceTags"] == ["single-tag"]

    # Verify allowed rules parsing
    assert called_with["allowed"] == [{"IPProtocol": "all", "ports": ["*"]}]

    mock_firewalls.patch.assert_called_once()
    assert result.outputs_prefix == "GCP.Compute.Operations"


def test_compute_firewall_insert_basic(mocker):
    """
    Given: Minimal args for creating a firewall rule
    When: compute_firewall_insert is called
    Then: The request body contains the name and optional fields are omitted
    """
    from GCP import compute_firewall_insert

    args = {"project_id": "p1", "resource_name": "fw-1"}

    mock_creds = mocker.Mock(spec=Credentials)
    mock_compute = mocker.Mock()
    mock_firewalls = mocker.Mock()
    mock_compute.firewalls.return_value = mock_firewalls
    mock_firewalls.insert.return_value.execute.return_value = {"id": "op-1", "status": "PENDING"}

    mocker.patch("GCP.build", return_value=mock_compute)
    mocker.patch("GCP.tableToMarkdown", return_value="md")

    res = compute_firewall_insert(mock_creds, args)

    called_body = mock_firewalls.insert.call_args[1]["body"]
    assert called_body == {"name": "fw-1"}
    assert res.outputs_prefix == "GCP.Compute.Operations"


def test_compute_firewall_insert_full_body(mocker):
    """
    Given: All supported args for creating a firewall rule
    When: compute_firewall_insert is called
    Then: The request body reflects all conversions and parsing
    """
    from GCP import compute_firewall_insert

    args = {
        "project_id": "p1",
        "resource_name": "fw-1",
        "description": "desc",
        "network": "net-1",
        "priority": "123",
        "source_ranges": "1.1.1.1/32,2.2.2.0/24",
        "destination_ranges": "10.0.0.0/8",
        "source_tags": "tag-a",
        "target_tags": "tag-b,tag-c",
        "source_service_accounts": "sa:one",
        "target_service_accounts": "sa:two",
        "allowed": "ipprotocol=tcp,ports=80,443",
        "denied": "ipprotocol=udp,ports=53",
        "direction": "INGRESS",
        "log_config_enable": "true",
        "disabled": "false",
    }

    mock_creds = mocker.Mock(spec=Credentials)
    mock_compute = mocker.Mock()
    mock_firewalls = mocker.Mock()
    mock_compute.firewalls.return_value = mock_firewalls
    mock_firewalls.insert.return_value.execute.return_value = {"id": "op-1", "status": "PENDING"}

    mocker.patch("GCP.build", return_value=mock_compute)
    mocker.patch("GCP.tableToMarkdown", return_value="md")

    compute_firewall_insert(mock_creds, args)

    body = mock_firewalls.insert.call_args[1]["body"]
    assert body["name"] == "fw-1"
    assert body["priority"] == 123
    assert body["sourceRanges"] == ["1.1.1.1/32", "2.2.2.0/24"]
    assert body["destinationRanges"] == ["10.0.0.0/8"]
    assert body["sourceTags"] == ["tag-a"]
    assert body["targetTags"] == ["tag-b", "tag-c"]
    assert body["logConfig"]["enable"] is True
    assert body["disabled"] is False
    assert body["allowed"] == [{"IPProtocol": "tcp", "ports": ["80", "443"]}]
    assert body["denied"] == [{"IPProtocol": "udp", "ports": ["53"]}]


def test_compute_firewall_list_with_pagination_and_filter(mocker):
    """
    Given: Pagination and filter arguments
    When: compute_firewall_list is called
    Then: next token is returned in outputs and metadata is set
    """
    from GCP import compute_firewall_list

    args = {"project_id": "p1", "limit": "2", "page_token": "t0", "filter": "name eq fw-*"}

    mock_creds = mocker.Mock(spec=Credentials)
    mock_compute = mocker.Mock()
    mock_firewalls = mocker.Mock()
    mock_compute.firewalls.return_value = mock_firewalls
    mock_firewalls.list.return_value.execute.return_value = {
        "items": [{"name": "fw-1", "id": "1"}],
        "nextPageToken": "t1",
    }

    mocker.patch("GCP.build", return_value=mock_compute)
    mocker.patch("GCP.tableToMarkdown", return_value="md")

    res = compute_firewall_list(mock_creds, args)

    called_kwargs = mock_firewalls.list.call_args[1]
    assert called_kwargs["project"] == "p1"
    assert called_kwargs["maxResults"] == 2
    assert called_kwargs["pageToken"] == "t0"
    assert called_kwargs["filter"] == "name eq fw-*"

    assert res.outputs["GCP.Compute(true)"]["FirewallNextToken"] == "t1"


def test_compute_firewall_get_found_and_not_found(mocker):
    """
    Given: A firewall name
    When: compute_firewall_get is called
    Then: Returns details if found and a readable message if 404 not found
    """
    from GCP import compute_firewall_get
    from googleapiclient.errors import HttpError

    mock_creds = mocker.Mock(spec=Credentials)
    mock_compute = mocker.Mock()
    mock_firewalls = mocker.Mock()
    mock_compute.firewalls.return_value = mock_firewalls

    # Found case
    mock_firewalls.get.return_value.execute.return_value = {"name": "fw-1", "id": "1"}
    mocker.patch("GCP.build", return_value=mock_compute)
    mocker.patch("GCP.tableToMarkdown", return_value="md")
    res = compute_firewall_get(mock_creds, {"project_id": "p1", "resource_name": "fw-1"})
    assert res.outputs_prefix == "GCP.Compute.Firewall"

    # Not found case
    resp = mocker.MagicMock()
    resp.status = 404
    error = HttpError(resp, b'{"error": {"message": "The resource fw-2 was not found"}}')
    mock_firewalls.get.return_value.execute.side_effect = error
    res2 = compute_firewall_get(mock_creds, {"project_id": "p1", "resource_name": "fw-2"})
    assert "not found" in res2.readable_output


def test_compute_snapshots_list_with_pagination(mocker):
    """
    Given: Pagination args
    When: compute_snapshots_list is called
    Then: next token is returned in outputs
    """
    from GCP import compute_snapshots_list

    args = {"project_id": "p1", "limit": "5", "page_token": "a", "filter": "status = READY"}

    mock_creds = mocker.Mock(spec=Credentials)
    mock_compute = mocker.Mock()
    mock_snapshots = mocker.Mock()
    mock_compute.snapshots.return_value = mock_snapshots
    mock_snapshots.list.return_value.execute.return_value = {
        "items": [{"name": "snap-1", "id": "10"}],
        "nextPageToken": "b",
    }

    mocker.patch("GCP.build", return_value=mock_compute)
    mocker.patch("GCP.tableToMarkdown", return_value="md")

    res = compute_snapshots_list(mock_creds, args)
    assert res.outputs["GCP.Compute(true)"]["SnapshotNextToken"] == "b"


def test_compute_snapshot_get_found_and_not_found(mocker):
    """
    Given: A snapshot name
    When: compute_snapshot_get is called
    Then: Returns details if found and a readable message if 404 not found
    """
    from GCP import compute_snapshot_get
    from googleapiclient.errors import HttpError

    mock_creds = mocker.Mock(spec=Credentials)
    mock_compute = mocker.Mock()
    mock_snapshots = mocker.Mock()
    mock_compute.snapshots.return_value = mock_snapshots

    # Found
    mock_snapshots.get.return_value.execute.return_value = {"name": "snap-1", "id": "1"}
    mocker.patch("GCP.build", return_value=mock_compute)
    mocker.patch("GCP.tableToMarkdown", return_value="md")
    res = compute_snapshot_get(mock_creds, {"project_id": "p1", "resource_name": "snap-1"})
    assert res.outputs_prefix == "GCP.Compute.Snapshot"

    # Not found
    resp = mocker.MagicMock()
    resp.status = 404
    error = HttpError(resp, b'{"error": {"message": "The resource snap-2 was not found"}}')
    mock_snapshots.get.return_value.execute.side_effect = error
    res2 = compute_snapshot_get(mock_creds, {"project_id": "p1", "resource_name": "snap-2"})
    assert "not found" in res2.readable_output


def test_compute_instances_aggregated_list_by_ip_internal(mocker):
    """
    Given: An internal IP to match
    When: compute_instances_aggregated_list_by_ip is called without match_external
    Then: Only instances with matching networkIP are returned
    """
    from GCP import compute_instances_aggregated_list_by_ip

    mock_creds = mocker.Mock(spec=Credentials)
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances

    response = {
        "items": {
            "zones/us": {
                "instances": [
                    {
                        "name": "i-1",
                        "id": "1",
                        "status": "RUNNING",
                        "zone": "us",
                        "networkInterfaces": [{"networkIP": "10.0.0.5"}],
                    },
                    {
                        "name": "i-2",
                        "id": "2",
                        "status": "RUNNING",
                        "zone": "us",
                        "networkInterfaces": [{"networkIP": "10.0.0.6"}],
                    },
                ]
            }
        }
    }
    mock_instances.aggregatedList.return_value.execute.return_value = response

    mocker.patch("GCP.build", return_value=mock_compute)
    mocker.patch("GCP.tableToMarkdown", return_value="md")

    res = compute_instances_aggregated_list_by_ip(mock_creds, {"project_id": "p1", "ip_address": "10.0.0.6", "limit": "10"})

    # Expect only i-2
    assert len(res.outputs) == 1
    assert res.outputs[0]["name"] == "i-2"


def test_compute_instances_aggregated_list_by_ip_external(mocker):
    """
    Given: An external IP to match
    When: compute_instances_aggregated_list_by_ip is called with match_external=true
    Then: Only instances with matching accessConfigs.natIP are returned
    """
    from GCP import compute_instances_aggregated_list_by_ip

    mock_creds = mocker.Mock(spec=Credentials)
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances

    response = {
        "items": {
            "zones/us": {
                "instances": [
                    {
                        "name": "i-1",
                        "id": "1",
                        "status": "RUNNING",
                        "zone": "us",
                        "networkInterfaces": [{"networkIP": "10.0.0.5", "accessConfigs": [{"natIP": "34.1.1.1"}]}],
                    },
                    {
                        "name": "i-2",
                        "id": "2",
                        "status": "RUNNING",
                        "zone": "us",
                        "networkInterfaces": [{"networkIP": "10.0.0.6", "accessConfigs": [{"natIP": "34.1.1.2"}]}],
                    },
                ]
            }
        }
    }
    mock_instances.aggregatedList.return_value.execute.return_value = response

    mocker.patch("GCP.build", return_value=mock_compute)
    mocker.patch("GCP.tableToMarkdown", return_value="md")

    res = compute_instances_aggregated_list_by_ip(
        mock_creds, {"project_id": "p1", "ip_address": "34.1.1.2", "match_external": "true", "limit": "10"}
    )

    # Expect only i-2
    assert len(res.outputs) == 1
    assert res.outputs[0]["name"] == "i-2"


def test__collect_instance_ips_basic():
    """
    Given: An instance with multiple NICs and access configs
    When: _collect_instance_ips is called
    Then: It returns lists of internal and external IPs
    """
    from GCP import _collect_instance_ips

    instance = {
        "networkInterfaces": [
            {"networkIP": "10.0.0.5", "accessConfigs": [{"natIP": "34.1.1.1"}]},
            {"networkIP": "10.0.0.6", "accessConfigs": [{"natIP": "34.1.1.2"}]},
        ]
    }

    internal, external = _collect_instance_ips(instance)
    assert internal == ["10.0.0.5", "10.0.0.6"]
    assert external == ["34.1.1.1", "34.1.1.2"]


def test__match_instance_by_ip_internal_external_none():
    """
    Given: An instance with both internal and external IPs
    When: _match_instance_by_ip is called in both modes
    Then: It reports correct match flag and matchType
    """
    from GCP import _match_instance_by_ip

    instance = {"networkInterfaces": [{"networkIP": "10.0.0.5", "accessConfigs": [{"natIP": "34.1.1.1"}]}]}

    # Internal mode
    matched, t = _match_instance_by_ip(instance, "10.0.0.5", match_external=False)
    assert matched is True
    assert t == "internal"

    matched, t = _match_instance_by_ip(instance, "34.1.1.1", match_external=False)
    assert matched is False
    assert t == "none"

    # External mode
    matched, t = _match_instance_by_ip(instance, "34.1.1.1", match_external=True)
    assert matched is True
    assert t == "external"

    matched, t = _match_instance_by_ip(instance, "1.2.3.4", match_external=True)
    assert matched is False
    assert t == "none"


def test_compute_instances_aggregated_list_by_ip_hr_headers(mocker):
    """
    Given: A match is found
    When: compute_instances_aggregated_list_by_ip runs
    Then: HR headers include matchType and matchedIP
    """
    from GCP import compute_instances_aggregated_list_by_ip

    mock_creds = mocker.Mock()
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances

    response = {
        "items": {
            "zones/us": {
                "instances": [
                    {
                        "name": "i-1",
                        "id": "1",
                        "status": "RUNNING",
                        "zone": "us",
                        "networkInterfaces": [{"networkIP": "10.0.0.5", "accessConfigs": [{"natIP": "34.1.1.1"}]}],
                    }
                ]
            }
        }
    }
    mock_instances.aggregatedList.return_value.execute.return_value = response

    mocker.patch("GCP.build", return_value=mock_compute)
    ttmd = mocker.patch("GCP.tableToMarkdown", return_value="md")

    compute_instances_aggregated_list_by_ip(mock_creds, {"project_id": "p1", "ip_address": "10.0.0.5", "limit": "10"})

    # Inspect headers
    _, kwargs = ttmd.call_args
    headers = kwargs.get("headers")
    assert "matchType" in headers
    assert "matchedIP" in headers


def test_compute_network_tag_set_add_and_error(mocker):
    """
    Given: A VM with existing tags and a new tag to add
    When: compute_network_tag_set is called
    Then: It merges tags and calls setTags with fingerprint. Also, on HttpError it returns readable failure
    """
    from GCP import compute_network_tag_set

    mock_creds = mocker.Mock(spec=Credentials)
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances

    # Instance has tags
    mock_instances.get.return_value.execute.return_value = {"tags": {"fingerprint": "fp", "items": ["old"]}}
    # Successful setTags
    mock_instances.setTags.return_value.execute.return_value = {"id": "op"}

    mocker.patch("GCP.build", return_value=mock_compute)

    # Success path
    res = compute_network_tag_set(
        mock_creds,
        {
            "project_id": "p1",
            "zone": "us-central1-a",
            "resource_name": "i-1",
            "tag": "new",
            "tags_fingerprint": "fp",
            "add_tag": "true",
        },
    )
    # Body should include both tags merged
    called_body = mock_instances.setTags.call_args[1]["body"]
    assert called_body == {"items": ["new", "old"], "fingerprint": "fp"}
    assert "Added 'new' tag" in res.readable_output


def test_storage_bucket_policy_delete_multiple_entities(mocker):
    """
    Given: A bucket with IAM policy containing multiple entities to be removed
    When: storage_bucket_policy_delete is called with entity='allUsers,user:test@mail.com'
    Then: The policy is updated with both entities removed from all roles
    """
    from GCP import storage_bucket_policy_delete

    # Mock data
    args = {"resource_name": "test-bucket", "entity": "allUsers,user:test@mail.com"}

    policy = {
        "bindings": [
            {"role": "roles/storage.objectViewer", "members": ["allUsers", "user:test@mail.com", "user:other@example.com"]},
            {"role": "roles/storage.admin", "members": ["user:admin@example.com", "user:test@mail.com"]},
        ]
    }

    # Mock the GCP API calls
    mock_storage = mocker.Mock()
    mock_buckets = mocker.Mock()

    mock_storage.buckets.return_value = mock_buckets
    mock_buckets.getIamPolicy.return_value.execute.return_value = policy
    mock_buckets.setIamPolicy.return_value.execute.return_value = {}

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.build", return_value=mock_storage)

    # Run the function
    result = storage_bucket_policy_delete(mock_creds, args)

    # Verify the results
    assert "`allUsers`" in result.readable_output
    assert "`user:test@mail.com`" in result.readable_output
    mock_buckets.getIamPolicy.assert_called_once_with(bucket="test-bucket")
    mock_buckets.setIamPolicy.assert_called_once()
    # Verify that the removed entities are no longer in the policy
    call_args = mock_buckets.setIamPolicy.call_args[1]
    updated_policy = call_args["body"]
    for binding in updated_policy["bindings"]:
        assert "allUsers" not in binding.get("members", [])
        assert "user:test@mail.com" not in binding.get("members", [])


def test_compute_subnet_update_flow_logs(mocker):
    """
    Given: A GCP subnet that needs flow logs enabled
    When: compute_subnet_update is called with enable_flow_logs=true
    Then: The subnet's flow logs are enabled with proper fingerprint validation
    """
    from GCP import compute_subnet_update

    # Mock data
    args = {"project_id": "test-project", "region": "us-east1", "resource_name": "test-subnet", "enable_flow_logs": "true"}

    # Subnet response with fingerprint
    subnet_response = {"name": "test-subnet", "fingerprint": "test-fingerprint-123", "enableFlowLogs": False}

    # Expected patch operation response
    patch_response = {
        "id": "operation-123",
        "name": "operation-123",
        "kind": "compute#operation",
        "operationType": "patch",
        "progress": "100",
        "zone": "us-east1",
        "status": "RUNNING",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_subnetworks = mocker.Mock()

    mock_compute.subnetworks.return_value = mock_subnetworks
    mock_subnetworks.get.return_value.execute.return_value = subnet_response
    mock_subnetworks.patch.return_value.execute.return_value = patch_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.build", return_value=mock_compute)

    # Run the function
    result = compute_subnet_update(mock_creds, args)

    # Verify the results
    assert "Flow Logs configuration for subnet test-subnet" in result.readable_output
    assert result.outputs[0] == patch_response

    # Check that the correct API calls were made
    mock_subnetworks.get.assert_called_once_with(project="test-project", region="us-east1", subnetwork="test-subnet")

    mock_subnetworks.patch.assert_called_once_with(
        project="test-project",
        region="us-east1",
        subnetwork="test-subnet",
        body={"enableFlowLogs": True, "fingerprint": "test-fingerprint-123"},
    )


def test_compute_subnet_update_private_access(mocker):
    """
    Given: A GCP subnet that needs private IP Google access enabled
    When: compute_subnet_update is called with enable_private_ip_google_access=true
    Then: The subnet's private IP Google access is enabled
    """
    from GCP import compute_subnet_update

    # Mock data
    args = {
        "project_id": "test-project",
        "region": "us-east1",
        "resource_name": "test-subnet",
        "enable_private_ip_google_access": "true",
    }

    # Expected operation response
    set_response = {
        "id": "operation-456",
        "name": "operation-456",
        "kind": "compute#operation",
        "operationType": "setPrivateIpGoogleAccess",
        "progress": "100",
        "zone": "us-east1",
        "status": "RUNNING",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_subnetworks = mocker.Mock()

    mock_compute.subnetworks.return_value = mock_subnetworks
    mock_subnetworks.setPrivateIpGoogleAccess.return_value.execute.return_value = set_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.build", return_value=mock_compute)

    # Run the function
    result = compute_subnet_update(mock_creds, args)

    # Verify the results
    assert "Private IP Google Access configuration for subnet test-subnet" in result.readable_output
    assert result.outputs[1] == set_response

    # Check that the correct API calls were made
    mock_subnetworks.setPrivateIpGoogleAccess.assert_called_once_with(
        project="test-project", region="us-east1", subnetwork="test-subnet", body={"privateIpGoogleAccess": True}
    )


#
# def test_compute_instance_metadata_add_new_item(mocker):
#     """
#     Given: Project metadata needs to be updated with a new key-value pair
#     When: compute_instance_metadata_add is called with new metadata
#     Then: The function should add the new item to existing metadata and call setMetadata with the updated list
#     """
#     from GCP import compute_instance_metadata_add
#
#     # Mock arguments
#     args = {
#         "project_id": "test-project",
#         "zone": "us-central1-c",
#         "resource_name": "gke-test-instance",
#         "metadata": "key=enable-oslogin,value=true",
#     }
#
#     # Mock credentials
#     mock_creds = mocker.Mock(spec=Credentials)
#
#     # Setup mock instance and response
#     mock_instance = {
#         "metadata": {"fingerprint": "test-fingerprint", "items": [{"key": "existing-key", "value": "existing-value"}]}
#     }
#
#     mock_response = {"id": "operation-123", "name": "operation-name", "status": "RUNNING"}
#
#     # Use MagicMock for compute
#     mock_compute = MagicMock()
#     mock_compute.instances().get().execute.return_value = mock_instance
#     mock_compute.instances().setMetadata().execute.return_value = mock_response
#
#     mocker.patch("GCP.build", return_value=mock_compute)
#
#     # Execute the function
#     result = compute_instance_metadata_add(mock_creds, args)
#
#     # Check call to setMetadata with correct body
#     called_args, called_kwargs = mock_compute.instances().setMetadata.call_args
#
#     assert called_kwargs["project"] == "test-project"
#     assert called_kwargs["zone"] == "us-central1-c"
#     assert called_kwargs["instance"] == "gke-test-instance"
#
#     # Check body has expected items
#     body = called_kwargs["body"]
#     assert body["fingerprint"] == "test-fingerprint"
#
#     # Convert items to dict for easier comparison
#     items_dict = {item["key"]: item["value"] for item in body["items"]}
#     assert items_dict["existing-key"] == "existing-value"
#     assert items_dict["enable-oslogin"] == "true"
#
#     # Check outputs
#     assert result.outputs_prefix == "GCP.Compute.Operations"
#     assert result.outputs == mock_response
#
#
# def test_compute_instance_metadata_add_update_existing(mocker):
#     """
#     Given: Project metadata needs to be updated where a key already exists
#     When: compute_instance_metadata_add is called with metadata containing an existing key
#     Then: The function should update the value of the existing key and preserve other metadata
#     """
#     from GCP import compute_instance_metadata_add
#
#     # Mock arguments
#     args = {
#         "project_id": "test-project",
#         "zone": "us-central1-c",
#         "resource_name": "gke-test-instance",
#         "metadata": "key=enable-oslogin,value=false;key=new-key,value=new-value",
#     }
#
#     # Mock credentials
#     mock_creds = mocker.Mock(spec=Credentials)
#
#     # Setup mock instance and response
#     mock_instance = {
#         "metadata": {
#             "fingerprint": "test-fingerprint",
#             "items": [{"key": "enable-oslogin", "value": "true"}, {"key": "existing-key", "value": "existing-value"}],
#         }
#     }
#
#     mock_response = {"id": "operation-123", "name": "operation-name", "status": "RUNNING"}
#
#     # Use MagicMock for compute
#     mock_compute = MagicMock()
#     mock_compute.instances().get().execute.return_value = mock_instance
#     mock_compute.instances().setMetadata().execute.return_value = mock_response
#
#     mocker.patch("GCP.build", return_value=mock_compute)
#
#     # Execute the function
#     result = compute_instance_metadata_add(mock_creds, args)
#
#     # Check body has expected items
#     called_args, called_kwargs = mock_compute.instances().setMetadata.call_args
#     body = called_kwargs["body"]
#
#     # Convert items to dict for easier comparison
#     items_dict = {item["key"]: item["value"] for item in body["items"]}
#     assert items_dict["enable-oslogin"] == "false"  # Should be updated
#     assert items_dict["existing-key"] == "existing-value"  # Should remain unchanged
#     assert items_dict["new-key"] == "new-value"  # Should be added
#
#     assert result.outputs == mock_response


def test_container_cluster_security_update_master_auth_networks(mocker):
    """
    Given: A GKE cluster needs to update its master authorized networks
    When: container_cluster_security_update is called with enable_master_authorized_networks and CIDRs
    Then: The function should make an API call with the correct CIDR blocks configuration
    """
    from GCP import container_cluster_security_update

    # Mock arguments
    args = {
        "project_id": "test-project",
        "region": "us-central1-c",
        "resource_name": "test-cluster-1",
        "enable_master_authorized_networks": "true",
        "cidrs": "192.168.0.0/24,10.0.0.0/32",
    }

    # Mock credentials
    mock_creds = mocker.Mock(spec=Credentials)

    # Mock response
    mock_response = {"name": "operation-123", "status": "RUNNING"}

    # Use MagicMock for container
    mock_container = MagicMock()
    mock_container.projects().locations().clusters().update().execute.return_value = mock_response

    mocker.patch("GCP.build", return_value=mock_container)

    # Execute the function
    result = container_cluster_security_update(mock_creds, args)

    # Verify correct parameters were used
    called_args, called_kwargs = mock_container.projects().locations().clusters().update.call_args

    assert called_kwargs["name"] == "projects/test-project/locations/us-central1-c/clusters/test-cluster-1"

    # Check body contents
    body = called_kwargs["body"]
    auth_networks_config = body["update"]["desiredControlPlaneEndpointsConfig"]["ipEndpointsConfig"]["authorizedNetworksConfig"]
    assert auth_networks_config["enabled"] is True

    # Check CIDR blocks
    cidr_blocks = auth_networks_config["cidrBlocks"]
    assert len(cidr_blocks) == 2
    assert {"cidrBlock": "192.168.0.0/24"} in cidr_blocks
    assert {"cidrBlock": "10.0.0.0/32"} in cidr_blocks

    assert result.outputs == mock_response


def test_storage_bucket_metadata_update_enable_both_settings(mocker):
    """
    Given: A GCS bucket needs both versioning and uniform access settings updated
    When: storage_bucket_metadata_update is called with both settings
    Then: The function should call bucket.Patch with both settings configured correctly
    """
    from GCP import storage_bucket_metadata_update

    # Mock arguments
    args = {
        "project_id": "test-project",
        "resource_name": "test-bucket",
        "enable_versioning": "true",
        "enable_uniform_access": "false",
    }

    # Mock credentials
    mock_creds = mocker.Mock(spec=Credentials)

    # Mock response
    mock_response = {
        "name": "test-bucket",
        "id": "bucket-123",
        "kind": "storage#bucket",
        "selfLink": "https://storage.googleapis.com/storage/v1/b/test-bucket",
        "projectNumber": "123456",
        "updated": "2023-01-01T00:00:00Z",
        "location": "us-central1",
        "versioning": {"enabled": True},
        "iamConfiguration": {"uniformBucketLevelAccess": {"enabled": False}},
    }

    # Use MagicMock for storage
    mock_storage = MagicMock()
    mock_storage.buckets().patch().execute.return_value = mock_response

    mocker.patch("GCP.build", return_value=mock_storage)

    # Execute the function
    result = storage_bucket_metadata_update(mock_creds, args)

    # Verify correct parameters were used
    called_args, called_kwargs = mock_storage.buckets().patch.call_args

    assert called_kwargs["bucket"] == "test-bucket"

    # Check body contents
    body = called_kwargs["body"]
    assert body["versioning"]["enabled"] is True
    assert body["iamConfiguration"]["uniformBucketLevelAccess"]["enabled"] is False

    # Check outputs
    assert result.outputs_prefix == "GCP.StorageBucket.Metadata"
    assert result.outputs == mock_response
    assert result.outputs_key_field == "name"


def test_storage_bucket_policy_set_merge_add_true(mocker):
    """
    Given: A bucket with an existing IAM policy and a new set of bindings to add
    When: storage_bucket_policy_set is called with add=true
    Then: The resulting policy sent to setIamPolicy is a merge (union) of members per role
    """
    from GCP import storage_bucket_policy_set

    bucket_name = "test-bucket"
    current_policy = {
        "version": 3,
        "etag": "BwWKmjvelug=",
        "bindings": [
            {"role": "roles/storage.objectViewer", "members": ["user:viewer1@example.com"]},
            {"role": "roles/storage.admin", "members": ["user:admin1@example.com"]},
        ],
    }
    new_bindings = {
        "bindings": [
            {"role": "roles/storage.objectViewer", "members": ["allUsers"]},
            {"role": "roles/storage.admin", "members": ["user:admin2@example.com"]},
        ]
    }
    args = {
        "bucket_name": bucket_name,
        "policy": json.dumps(new_bindings),
        "add": "true",
    }

    mock_storage = mocker.Mock()
    mock_buckets = mocker.Mock()
    mock_storage.buckets.return_value = mock_buckets
    mock_buckets.getIamPolicy.return_value.execute.return_value = current_policy

    expected_merged_bindings = [
        {
            "role": "roles/storage.objectViewer",
            "members": sorted(["user:viewer1@example.com", "allUsers"]),
        },
        {
            "role": "roles/storage.admin",
            "members": sorted(["user:admin1@example.com", "user:admin2@example.com"]),
        },
    ]

    mock_response = {"version": 3, "bindings": expected_merged_bindings}
    mock_buckets.setIamPolicy.return_value.execute.return_value = mock_response

    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.build", return_value=mock_storage)

    result = storage_bucket_policy_set(mock_creds, args)

    mock_buckets.getIamPolicy.assert_called_once_with(bucket=bucket_name)
    assert mock_buckets.setIamPolicy.call_count == 1

    _, set_kwargs = mock_buckets.setIamPolicy.call_args
    assert set_kwargs["bucket"] == bucket_name
    body_sent = set_kwargs["body"]

    assert body_sent.get("version") == 3
    assert body_sent.get("etag") == current_policy["etag"]

    sent_bindings = {b["role"]: set(b.get("members", [])) for b in body_sent.get("bindings", [])}
    expected_bindings = {b["role"]: set(b.get("members", [])) for b in expected_merged_bindings}
    assert sent_bindings == expected_bindings

    # Outputs should reflect mock response
    assert result.outputs == mock_response


def test_compute_instance_service_account_set(mocker):
    """
    Given: A VM instance that needs a service account assigned
    When: compute_instance_service_account_set is called with service_account_email and scopes
    Then: The function should call setServiceAccount with the proper email and scopes
    """
    from GCP import compute_instance_service_account_set

    # Mock arguments
    args = {
        "project_id": "test-project",
        "zone": "us-central1-c",
        "resource_name": "test-instance",
        "service_account_email": "service-account@test-project.iam.gserviceaccount.com",
        "scopes": "https://www.googleapis.com/auth/compute,https://www.googleapis.com/auth/devstorage.read_only",
    }

    # Mock response
    mock_response = {
        "id": "operation-123",
        "name": "operation-123",
        "operationType": "setServiceAccount",
        "progress": "100",
        "zone": "us-central1-c",
        "status": "RUNNING",
    }

    # Use MagicMock for compute
    mock_compute = MagicMock()
    mock_compute.instances().setServiceAccount().execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.build", return_value=mock_compute)
    mocker.patch("GCP.tableToMarkdown", return_value="mocked markdown")

    # Execute the function
    result = compute_instance_service_account_set(mock_creds, args)

    # Verify correct parameters were used
    called_args, called_kwargs = mock_compute.instances().setServiceAccount.call_args

    assert called_kwargs["project"] == "test-project"
    assert called_kwargs["zone"] == "us-central1-c"
    assert called_kwargs["instance"] == "test-instance"

    # Check that email and scopes are set correctly
    body = called_kwargs["body"]
    assert body["email"] == "service-account@test-project.iam.gserviceaccount.com"
    assert body["scopes"] == ["https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/devstorage.read_only"]

    # Check outputs
    assert result.outputs_prefix == "GCP.Compute.Operations"
    assert result.outputs == mock_response


def test_compute_instance_service_account_set_empty_scopes(mocker):
    """
    Given: A VM instance that needs a service account assigned with empty scopes
    When: compute_instance_service_account_set is called with only a service_account_email
    Then: The function should call setServiceAccount with the email and empty scopes list
    """
    from GCP import compute_instance_service_account_set

    # Mock arguments
    args = {
        "project_id": "test-project",
        "zone": "us-central1-c",
        "resource_name": "test-instance",
        "service_account_email": "service-account@test-project.iam.gserviceaccount.com",
    }

    # Mock response
    mock_response = {
        "id": "operation-123",
        "name": "operation-123",
        "operationType": "setServiceAccount",
        "progress": "100",
        "zone": "us-central1-c",
        "status": "RUNNING",
    }

    # Use MagicMock for compute
    mock_compute = MagicMock()
    mock_compute.instances().setServiceAccount().execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.build", return_value=mock_compute)
    mocker.patch("GCP.tableToMarkdown", return_value="mocked markdown")

    # Execute the function
    result = compute_instance_service_account_set(mock_creds, args)

    # Verify correct parameters were used
    called_args, called_kwargs = mock_compute.instances().setServiceAccount.call_args

    # Check that email is set correctly and scopes is an empty list
    body = called_kwargs["body"]
    assert body["email"] == "service-account@test-project.iam.gserviceaccount.com"
    assert body["scopes"] == []

    # Check outputs
    assert result.outputs_prefix == "GCP.Compute.Operations"
    assert result.outputs == mock_response


def test_compute_instance_service_account_remove(mocker):
    """
    Given: A VM instance that has a service account attached
    When: compute_instance_service_account_remove is called
    Then: The function should call setServiceAccount with an empty email and scopes
    """
    from GCP import compute_instance_service_account_remove

    # Mock arguments
    args = {"project_id": "test-project", "zone": "us-central1-c", "resource_name": "test-instance"}

    # Mock response
    mock_response = {
        "id": "operation-123",
        "name": "operation-123",
        "operationType": "setServiceAccount",
        "progress": "100",
        "zone": "us-central1-c",
        "status": "RUNNING",
    }

    # Use MagicMock for compute
    mock_compute = MagicMock()
    mock_compute.instances().setServiceAccount().execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.build", return_value=mock_compute)
    mocker.patch("GCP.tableToMarkdown", return_value="mocked markdown")

    # Execute the function
    result = compute_instance_service_account_remove(mock_creds, args)

    # Verify correct parameters were used
    called_args, called_kwargs = mock_compute.instances().setServiceAccount.call_args

    assert called_kwargs["project"] == "test-project"
    assert called_kwargs["zone"] == "us-central1-c"
    assert called_kwargs["instance"] == "test-instance"

    # Check that email and scopes are empty
    body = called_kwargs["body"]
    assert body["email"] == ""
    assert body["scopes"] == []

    # Check outputs
    assert result.outputs_prefix == "GCP.Compute.Operations"
    assert result.outputs == mock_response


def test_iam_project_policy_binding_remove(mocker):
    """
    Given: A GCP project with an IAM policy that has members assigned to roles
    When: iam_project_policy_binding_remove is called to remove a member from a specific role
    Then: The function should call setIamPolicy with the updated policy
    """
    from GCP import iam_project_policy_binding_remove

    # Mock arguments
    args = {"project_id": "test-project", "member": "user:test@example.com,serviceAccount:sa@example.com", "role": "roles/editor"}

    # Mock policy response with multiple roles and members
    mock_policy = {
        "bindings": [
            {
                "role": "roles/editor",
                "members": ["user:test@example.com", "serviceAccount:sa@example.com", "user:keep@example.com"],
            },
            {"role": "roles/viewer", "members": ["user:test@example.com", "user:other@example.com"]},
        ],
        "etag": "BwWKmjvelug=",
    }

    # Use MagicMock for resource manager
    mock_resource_manager = MagicMock()
    mock_resource_manager.projects().getIamPolicy().execute.return_value = mock_policy
    mock_resource_manager.projects().setIamPolicy().execute.return_value = {}

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.build", return_value=mock_resource_manager)

    # Execute the function
    result = iam_project_policy_binding_remove(mock_creds, args)

    # Verify getIamPolicy was called correctly
    get_policy_args = mock_resource_manager.projects().getIamPolicy.call_args[1]
    assert get_policy_args["resource"] == "projects/test-project"

    # Verify setIamPolicy was called with modified policy
    set_policy_args, set_policy_kwargs = mock_resource_manager.projects().setIamPolicy.call_args
    updated_policy = set_policy_kwargs["body"]["policy"]

    # Check that the members were removed from the editor role but kept in viewer role
    editor_role = next(binding for binding in updated_policy["bindings"] if binding["role"] == "roles/editor")
    viewer_role = next(binding for binding in updated_policy["bindings"] if binding["role"] == "roles/viewer")

    assert "user:test@example.com" not in editor_role["members"]
    assert "serviceAccount:sa@example.com" not in editor_role["members"]
    assert "user:keep@example.com" in editor_role["members"]
    assert "user:test@example.com" in viewer_role["members"]  # Should still be in other roles

    # Check readable output
    assert "`user:test@example.com`" in result.readable_output
    assert "`serviceAccount:sa@example.com`" in result.readable_output
    assert "successfully removed" in result.readable_output


# The following commands are currently unsupported:
# def test_iam_project_deny_policy_create(mocker):
#     """
#     Given: A GCP project that needs a deny policy created
#     When: iam_project_deny_policy_create is called with policy details
#     Then: The function should call createPolicy with the correct configuration
#     """
#     from GCP import iam_project_deny_policy_create
#
#     # Mock arguments
#     args = {
#         "project_id": "test-project",
#         "policy_id": "test-deny-policy",
#         "display_name": "Test Deny Policy",
#         "denied_principals": "user:test@example.com,serviceAccount:sa@example.com",
#         "denied_permissions": "compute.instances.create,compute.instances.delete",
#     }
#
#     # Mock response
#     mock_response = {
#         "name": "policies/cloudresourcemanager.googleapis.com%2Fprojects%2Ftest-project/denypolicies/test-deny-policy",
#         "displayName": "Test Deny Policy",
#         "createTime": "2023-08-15T12:00:00Z",
#         "updateTime": "2023-08-15T12:00:00Z",
#         "rules": [
#             {
#                 "denyRule": {
#                     "deniedPrincipals": ["user:test@example.com", "serviceAccount:sa@example.com"],
#                     "deniedPermissions": ["compute.instances.create", "compute.instances.delete"],
#                 }
#             }
#         ],
#     }
#
#     # Use MagicMock for IAM
#     mock_iam = MagicMock()
#     mock_iam.policies().createPolicy().execute.return_value = mock_response
#
#     # Mock the build function
#     mock_creds = mocker.Mock(spec=Credentials)
#     mocker.patch("GCP.build", return_value=mock_iam)
#
#     # Execute the function
#     result = iam_project_deny_policy_create(mock_creds, args)
#
#     # Verify createPolicy was called with correct parameters
#     call_args, call_kwargs = mock_iam.policies().createPolicy.call_args
#
#     assert call_kwargs["parent"] == "policies/cloudresourcemanager.googleapis.com%2Fprojects%2Ftest-project/denypolicies"
#     assert call_kwargs["policyId"] == "test-deny-policy"
#
#     body = call_kwargs["body"]
#     assert body["displayName"] == "Test Deny Policy"
#     assert body["rules"][0]["denyRule"]["deniedPrincipals"] == ["user:test@example.com", "serviceAccount:sa@example.com"]
#     assert body["rules"][0]["denyRule"]["deniedPermissions"] == ["compute.instances.create", "compute.instances.delete"]
#
#     # Check outputs
#     assert result.outputs_prefix == "GCP.IAM.DenyPolicy"
#     assert result.outputs == mock_response
#     assert "test-deny-policy" in result.readable_output
#     assert "successfully created" in result.readable_output
#
#
# def test_iam_service_account_delete(mocker):
#     """
#     Given: A GCP service account that needs to be deleted
#     When: iam_service_account_delete is called with project_id and service_account_email
#     Then: The function should call serviceAccounts().delete with the correct resource name
#     """
#     from GCP import iam_service_account_delete
#
#     # Mock arguments
#     args = {"project_id": "test-project", "service_account_email": "test-sa@test-project.iam.gserviceaccount.com"}
#
#     # Use MagicMock for IAM
#     mock_iam = MagicMock()
#     mock_iam.projects().serviceAccounts().delete().execute.return_value = {}
#
#     # Mock the build function
#     mock_creds = mocker.Mock(spec=Credentials)
#     mocker.patch("GCP.build", return_value=mock_iam)
#
#     # Execute the function
#     result = iam_service_account_delete(mock_creds, args)
#
#     # Verify delete was called with correct name
#     call_args, call_kwargs = mock_iam.projects().serviceAccounts().delete.call_args
#
#     assert call_kwargs["name"] == "projects/test-project/serviceAccounts/test-sa@test-project.iam.gserviceaccount.com"
#
#     # Check readable output
#     assert "test-sa@test-project.iam.gserviceaccount.com" in result.readable_output
#     assert "successfully deleted" in result.readable_output
#
#
# def test_iam_group_membership_delete(mocker):
#     """
#     Given: A Google Cloud Identity group with a member that needs to be removed
#     When: iam_group_membership_delete is called with group_id and membership_id
#     Then: The function should call memberships().delete with the correct membership name
#     """
#     from GCP import iam_group_membership_delete
#
#     # Mock arguments
#     args = {"group_id": "01abc123def456", "membership_id": "member789ghi"}
#
#     # Use MagicMock for Cloud Identity
#     mock_cloud_identity = MagicMock()
#     mock_cloud_identity.groups().memberships().delete().execute.return_value = {}
#
#     # Mock the build function
#     mock_creds = mocker.Mock(spec=Credentials)
#     mocker.patch("GCP.build", return_value=mock_cloud_identity)
#
#     # Execute the function
#     result = iam_group_membership_delete(mock_creds, args)
#
#     # Verify delete was called with correct name
#     call_args, call_kwargs = mock_cloud_identity.groups().memberships().delete.call_args
#
#     assert call_kwargs["name"] == "groups/01abc123def456/memberships/member789ghi"
#
#     # Check readable output
#     assert "Membership member789ghi was deleted from group 01abc123def456" in result.readable_output


def test_health_check_successful(mocker):
    """
    Given: Valid GCP credentials and a project with accessible services
    When: health_check is called with proper shared credentials
    Then: The function should return None indicating successful connectivity
    """
    from GCP import health_check

    # Mock shared credentials
    shared_creds = {"access_token": "valid-token-123"}
    project_id = "test-project"
    connector_id = "connector-123"

    # Mock successful service tests
    mock_service_results = [
        ("compute", True, ""),
        ("storage", True, ""),
        ("container", True, ""),
    ]

    mocker.patch("GCP.GCPServices.test_all_services", return_value=mock_service_results)

    # Execute the function
    result = health_check(shared_creds, project_id, connector_id)

    # Should return None for successful health check
    assert result is None


def test_health_check_missing_token(mocker):
    """
    Given: Shared credentials that are missing the access token
    When: health_check is called with invalid credentials
    Then: The function should return a HealthCheckError with connectivity error type
    """
    from GCP import health_check

    # Mock shared credentials without token
    shared_creds = {"some_other_field": "value"}
    project_id = "test-project"
    connector_id = "connector-123"

    # Execute the function
    result = health_check(shared_creds, project_id, connector_id)

    # Should return HealthCheckError for missing token
    assert result is not None
    assert result.account_id == project_id
    assert result.connector_id == connector_id
    assert "token is missing from credentials" in result.message
    assert result.error_type == "Connectivity Error"


def test_health_check_service_connectivity_failure(mocker):
    """
    Given: Valid credentials but GCP services are not accessible
    When: health_check is called and service tests fail with non-permission errors
    Then: The function should return a HealthCheckError indicating connectivity issues
    """
    from GCP import health_check

    # Mock shared credentials
    shared_creds = {"access_token": "valid-token-123"}
    project_id = "test-project"
    connector_id = "connector-123"

    # Mock service test failure (non-permission related)
    mock_service_results = [
        ("compute", True, ""),
        ("storage", False, "Network timeout occurred"),
        ("container", True, ""),
    ]

    mocker.patch("GCP.GCPServices.test_all_services", return_value=mock_service_results)

    # Execute the function
    result = health_check(shared_creds, project_id, connector_id)

    # Should return HealthCheckError for service connectivity failure
    assert result is not None
    assert len(result) == 1
    assert result[0].account_id == project_id
    assert result[0].connector_id == connector_id
    assert "Sample check failed" in result[0].message
    assert "Network timeout occurred" in result[0].message
    assert result[0].error_type == "Connectivity Error"


def test_health_check_service_permission_failure_ignored(mocker):
    """
    Given: Valid credentials but services fail with permission-related errors
    When: health_check is called and service tests fail with permission errors
    Then: The function should return None as permission errors are expected and ignored
    """
    from GCP import health_check

    # Mock shared credentials
    shared_creds = {"access_token": "valid-token-123"}
    project_id = "test-project"
    connector_id = "connector-123"

    # Mock service test failure (permission related - should be ignored)
    mock_service_results = [
        ("compute", True, ""),
        ("storage", False, "Permission denied for storage.buckets.list"),
        ("container", False, "Insufficient Permission to access container API"),
    ]

    mocker.patch("GCP.GCPServices.test_all_services", return_value=mock_service_results)

    # Execute the function
    result = health_check(shared_creds, project_id, connector_id)

    # Should return None as permission errors are ignored in health checks
    assert result is None


def test_health_check_credentials_creation_failure(mocker):
    """
    Given: Shared credentials that cause an exception during Credentials object creation
    When: health_check is called and credential creation fails
    Then: The function should return a HealthCheckError with the exception details
    """
    from GCP import health_check

    # Mock shared credentials
    shared_creds = {"access_token": "invalid-token"}
    project_id = "test-project"
    connector_id = "connector-123"

    # Mock Credentials creation failure
    mocker.patch("GCP.Credentials", side_effect=ValueError("Invalid token format"))

    # Execute the function
    result = health_check(shared_creds, project_id, connector_id)

    # Should return HealthCheckError for credential creation failure
    assert result is not None
    assert result.account_id == project_id
    assert result.connector_id == connector_id
    assert "Invalid token format" in result.message
    assert result.error_type == "Connectivity Error"


def test_health_check_service_multiple_failures(mocker):
    """
    Given: Valid credentials but services fail with errors
    When: health_check is called and service tests fail with errors
    Then: The function should return a list of HealthCheckError
    """
    from GCP import health_check

    # Mock shared credentials
    shared_creds = {"access_token": "valid-token-123"}
    project_id = "test-project"
    connector_id = "connector-123"

    # Mock service test failure (permission related - should be ignored)
    mock_service_results = [
        ("compute", True, ""),
        ("storage", False, "Request is prohibited by organization's policy"),
        ("container", False, "Request is prohibited by organization's policy"),
    ]

    mocker.patch("GCP.GCPServices.test_all_services", return_value=mock_service_results)

    # Execute the function
    result = health_check(shared_creds, project_id, connector_id)

    # Should return a list of HealthCheckError
    assert isinstance(result, list)
    assert len(result) == 2


def test_parse_labels_valid_2_inputs():
    """
    Given: A valid labels string with multiple labels
    When: parse_labels is called
    Then: The function returns a correctly parsed dictionary
    """
    from GCP import parse_labels

    input_str = "key=label1,value=true;key=label2,value=false"
    expected = {"label1": "true", "label2": "false"}

    result = parse_labels(input_str)
    assert result == expected


def test_parse_labels_valid_1_input():
    """
    Given: A valid labels string with a single label
    When: parse_labels is called
    Then: The function returns a correctly parsed dictionary
    """
    from GCP import parse_labels

    input_str_option1 = "key=label1,value=true;"
    input_str_option2 = "key=label1,value=true"
    expected_option1 = {
        "label1": "true",
    }
    expected_option2 = {
        "label1": "true",
    }

    result_option1 = parse_labels(input_str_option1)
    result_option2 = parse_labels(input_str_option2)
    assert result_option1 == expected_option1
    assert result_option2 == expected_option2


def test_handle_permission_error_valid_json_with_matching_permission(mocker):
    """
    Given: An HttpError with JSON content containing a permission error that matches command requirements
    When: handle_permission_error is called with the error
    Then: The function should extract permission info and call return_multiple_permissions_error
    """
    from GCP import handle_permission_error
    from googleapiclient.errors import HttpError
    import json

    command_name = "gcp-compute-instance-labels-set"

    # Create mock HTTP response
    mock_resp = mocker.MagicMock()
    mock_resp.status = 403
    mock_resp.get.return_value = "application/json"

    error_content = {
        "error": {
            "errors": [{"reason": "forbidden"}],
            "message": "Required 'compute.instances.setLabels' permission for "
            "'projects/project/zones/zone/instances/instance_number'",
        }
    }

    # Create HttpError with mocked content
    http_error = HttpError(mock_resp, json.dumps(error_content).encode())

    # Mock demisto functions
    mocker.patch("GCP.demisto.debug")
    mock_return_error = mocker.patch("GCP.return_multiple_permissions_error")

    # Execute the function
    handle_permission_error(http_error, "test-project", command_name)

    # Verify return_multiple_permissions_error was called with correct parameters
    mock_return_error.assert_called_once()
    error_entries = mock_return_error.call_args[0][0]

    assert len(error_entries) == 1
    assert error_entries[0]["account_id"] == "test-project"
    assert error_entries[0]["name"] == "compute.instances.setLabels"
    assert "compute.instances.setLabels" in error_entries[0]["message"]


def test_handle_permission_error_valid_json_no_matching_permission(mocker):
    """
    Given: An HttpError with JSON content that doesn't match any command requirements
    When: handle_permission_error is called with the error
    Then: The function should use "N/A" as the permission name
    """
    from GCP import handle_permission_error
    from googleapiclient.errors import HttpError
    import json

    # Mock command requirements
    command_name = "gcp-compute-firewall-patch"

    # Create mock HTTP response
    mock_resp = mocker.MagicMock()
    mock_resp.status = 403
    mock_resp.get.return_value = "application/json"

    error_content = {"error": {"errors": [{"reason": "forbidden"}], "message": "Access denied for unknown resource"}}

    # Create HttpError with mocked content
    http_error = HttpError(mock_resp, json.dumps(error_content).encode())

    # Mock demisto functions
    mocker.patch("GCP.demisto.debug")
    mock_return_error = mocker.patch("GCP.return_multiple_permissions_error")

    # Execute the function
    handle_permission_error(http_error, "test-project", command_name)

    # Verify return_multiple_permissions_error was called with N/A permission
    error_entries = mock_return_error.call_args[0][0]

    assert len(error_entries) == 1
    assert error_entries[0]["account_id"] == "test-project"
    assert error_entries[0]["name"] == "N/A"
    assert "Access denied for unknown resource" in error_entries[0]["message"]


def test_handle_permission_error_multiple_matching_permissions(mocker):
    """
    Given: An HttpError with JSON content containing multiple permissions that match command requirements
    When: handle_permission_error is called with the error
    Then: The function should extract all matching permissions and create multiple error entries
    """
    from GCP import handle_permission_error
    from googleapiclient.errors import HttpError
    import json

    # Mock command requirements with multiple permissions
    command_name = "gcp-compute-firewall-patch"

    # Create mock HTTP response
    mock_resp = mocker.MagicMock()
    mock_resp.status = 403
    mock_resp.get.return_value = "application/json"

    error_content = {
        "error": {
            "errors": [{"reason": "forbidden"}],
            "message": "Required 'compute.firewalls.update' and compute.firewalls.get' permissions for project.",
        }
    }

    # Create HttpError with mocked content
    http_error = HttpError(mock_resp, json.dumps(error_content).encode())

    # Mock demisto functions
    mocker.patch("GCP.demisto.debug")
    mock_return_error = mocker.patch("GCP.return_multiple_permissions_error")

    # Execute the function
    handle_permission_error(http_error, "test-project", command_name)

    # Verify return_multiple_permissions_error was called with multiple permissions
    error_entries = mock_return_error.call_args[0][0]

    assert len(error_entries) == 2
    permission_names = [entry["name"] for entry in error_entries]
    assert "compute.firewalls.get" in permission_names
    assert "compute.firewalls.update" in permission_names

    for entry in error_entries:
        assert entry["account_id"] == "test-project"
        assert entry["message"] == "Required 'compute.firewalls.update' and compute.firewalls.get' permissions for project."


def test_handle_permission_error_non_json_content_type(mocker):
    """
    Given: An HttpError with non-JSON content type
    When: handle_permission_error is called with the error
    Then: The function should re-raise the original HttpError
    """
    from GCP import handle_permission_error
    from googleapiclient.errors import HttpError

    # Create mock HTTP response with non-JSON content
    mock_resp = mocker.MagicMock()
    mock_resp.status = 403
    mock_resp.get.return_value = "text/html"

    # Create HttpError
    http_error = HttpError(mock_resp, b"<html>Error page</html>")

    # Mock demisto functions
    mocker.patch("GCP.demisto.debug")

    # Execute the function and expect the same error to be raised
    with pytest.raises(SystemExit) as exc_info:
        handle_permission_error(http_error, "test-project", "some-command")

    # Verify there was a graceful exist
    assert exc_info.typename == "SystemExit"
    assert exc_info.value.code == 0


def test_handle_permission_error_missing_error_structure(mocker):
    """
    Given: An HttpError with valid JSON but missing expected error structure
    When: handle_permission_error is called with the error
    Then: The function should handle missing keys gracefully
    """
    from GCP import handle_permission_error
    from googleapiclient.errors import HttpError
    import json

    # Mock command requirements
    command_name = "gcp-compute-firewall-patch"

    # Create mock HTTP response with incomplete error structure
    mock_resp = mocker.MagicMock()
    mock_resp.status = 403
    mock_resp.get.return_value = "application/json"

    error_content = {
        "error": {
            "errors": [{}],  # Missing 'reason' key
            # Missing 'message' key
        }
    }

    # Create HttpError with mocked content
    http_error = HttpError(mock_resp, json.dumps(error_content).encode())

    # Mock demisto functions
    mocker.patch("GCP.demisto.debug")
    mock_return_error = mocker.patch("GCP.return_multiple_permissions_error")

    # Execute the function and expect a KeyError or similar handling
    # with pytest.raises(SystemExit):
    handle_permission_error(http_error, "test-project", command_name)
    error_entries = mock_return_error.call_args[0][0]

    assert len(error_entries) == 1
    assert error_entries[0]["account_id"] == "test-project"
    assert error_entries[0]["name"] == "N/A"


def test_handle_permission_error_case_insensitive_matching(mocker):
    """
    Given: An HttpError with JSON content containing permissions in different cases
    When: handle_permission_error is called with the error
    Then: The function should match permissions case-insensitively
    """
    from GCP import handle_permission_error
    from googleapiclient.errors import HttpError
    import json

    # Mock command requirements
    command_name = "gcp-compute-firewall-patch"

    # Create mock HTTP response
    mock_resp = mocker.MagicMock()
    mock_resp.status = 403
    mock_resp.get.return_value = "application/json"

    error_content = {
        "error": {"errors": [{"reason": "forbidden"}], "message": "Required 'COMPUTE.FIREWALLS.UPDATE' for the resource"}
    }

    # Create HttpError with mocked content
    http_error = HttpError(mock_resp, json.dumps(error_content).encode())

    # Mock demisto functions
    mocker.patch("GCP.demisto.debug")
    mock_return_error = mocker.patch("GCP.return_multiple_permissions_error")

    # Execute the function
    handle_permission_error(http_error, "test-project", command_name)

    # Verify return_multiple_permissions_error was called with matched permission
    error_entries = mock_return_error.call_args[0][0]

    assert len(error_entries) == 1
    assert error_entries[0]["account_id"] == "test-project"
    assert error_entries[0]["name"] == "compute.firewalls.update"


def test_gcp_compute_instances_list_command_basic_success(mocker):
    """
    Given: Valid credentials and basic arguments
    When: gcp_compute_instances_list_command is called
    Then: The function should return instances list with correct outputs
    """
    from GCP import gcp_compute_instances_list_command

    # Mock arguments
    args = {
        "project_id": "test-project",
        "zone": "us-central1-a",
    }

    # Mock API response
    mock_response = util_load_json("test_data/list_instances_response.json")

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances
    mock_instances.list.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_instances_list_command(mock_creds, args)

    # Verify API call parameters
    mock_instances.list.assert_called_once_with(
        project="test-project", zone="us-central1-a", filter=None, maxResults=50, orderBy=None, pageToken=None
    )

    # Verify outputs structure
    assert "GCP.Compute.Instances(val.id && val.id == obj.id)" in result.outputs
    assert "GCP.Compute(true)" in result.outputs
    assert len(result.outputs["GCP.Compute.Instances(val.id && val.id == obj.id)"]) == 2


def test_gcp_compute_instances_list_command_with_pagination(mocker):
    """
    Given: Valid arguments with pagination
    When: gcp_compute_instances_list_command is called with page_token
    Then: The function should handle pagination correctly and return next page token
    """
    from GCP import gcp_compute_instances_list_command

    # Mock arguments
    args = {"project_id": "test-project", "zone": "us-central1-a", "limit": 1, "page_token": "current-page-token"}

    # Mock API response with next page token
    mock_response = {
        "kind": "compute#instanceList",
        "items": [
            {
                "id": "123456789",
                "name": "test-instance-1",
                "kind": "compute#instance",
                "creationTimestamp": "2023-01-01T10:00:00.000-07:00",
                "status": "RUNNING",
                "machineType": "projects/test-project/zones/us-central1-a/machineTypes/n1-standard-1",
                "zone": "projects/test-project/zones/us-central1-a",
            }
        ],
        "nextPageToken": "next-page-token-123",
        "selfLink": "https://www.googleapis.com/compute/v1/projects/test-project/zones/us-central1-a/instances",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances
    mock_instances.list.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_instances_list_command(mock_creds, args)

    # Verify API call parameters include pagination
    mock_instances.list.assert_called_once_with(
        project="test-project", zone="us-central1-a", filter=None, maxResults=1, orderBy=None, pageToken="current-page-token"
    )

    # Verify pagination handling in outputs
    assert result.outputs["GCP.Compute(true)"]["InstancesNextPageToken"] == "next-page-token-123"
    assert "InstancesNextPageToken" in result.raw_response
    assert "nextPageToken" not in result.raw_response
    assert (
        "Run the following command to retrieve the next batch of instances:\n!gcp-compute-instances-list "
        "project_id=test-project zone=us-central1-a page_token=next-page-token-123 limit=1\n" in result.readable_output
    )


def test_gcp_compute_instances_list_command_with_filters_and_ordering(mocker):
    """
    Given: Valid arguments with filters and ordering
    When: gcp_compute_instances_list_command is called with filters and order_by
    Then: The function should pass filters and ordering to the API call
    """
    from GCP import gcp_compute_instances_list_command

    # Mock arguments
    args = {
        "project_id": "test-project",
        "zone": "us-central1-a",
        "filters": "status=RUNNING",
        "order_by": "creationTimestamp desc",
        "limit": 100,
    }

    # Mock API response
    mock_response = {
        "kind": "compute#instanceList",
        "items": [
            {
                "id": "123456789",
                "name": "test-instance-1",
                "kind": "compute#instance",
                "creationTimestamp": "2023-01-01T10:00:00.000-07:00",
                "status": "RUNNING",
                "machineType": "projects/test-project/zones/us-central1-a/machineTypes/n1-standard-1",
                "zone": "projects/test-project/zones/us-central1-a",
            }
        ],
        "selfLink": "https://www.googleapis.com/compute/v1/projects/test-project/zones/us-central1-a/instances",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances
    mock_instances.list.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_instances_list_command(mock_creds, args)

    # Verify API call parameters include filters and ordering
    mock_instances.list.assert_called_once_with(
        project="test-project",
        zone="us-central1-a",
        filter="status=RUNNING",
        maxResults=100,
        orderBy="creationTimestamp desc",
        pageToken=None,
    )

    # Verify successful execution
    assert len(result.outputs["GCP.Compute.Instances(val.id && val.id == obj.id)"]) == 1


def test_gcp_compute_instances_list_command_limit_validation_too_high(mocker):
    """
    Given: Arguments with limit greater than 500
    When: gcp_compute_instances_list_command is called
    Then: The function should raise DemistoException
    """
    from GCP import gcp_compute_instances_list_command, DemistoException

    # Mock arguments with invalid limit
    args = {"project_id": "test-project", "zone": "us-central1-a", "limit": "501"}

    mock_creds = mocker.Mock(spec=Credentials)

    # Execute the function and expect exception
    with pytest.raises(DemistoException) as exc_info:
        gcp_compute_instances_list_command(mock_creds, args)

    assert "The acceptable values of the argument limit are 1 to 500" in str(exc_info.value)
    assert "501" in str(exc_info.value)


def test_gcp_compute_instances_list_command_limit_validation_too_low(mocker):
    """
    Given: Arguments with limit less than 1
    When: gcp_compute_instances_list_command is called
    Then: The function should raise DemistoException
    """
    from GCP import gcp_compute_instances_list_command, DemistoException

    # Mock arguments with invalid limit
    args = {"project_id": "test-project", "zone": "us-central1-a", "limit": "0"}

    mock_creds = mocker.Mock(spec=Credentials)

    # Execute the function and expect exception
    with pytest.raises(DemistoException) as exc_info:
        gcp_compute_instances_list_command(mock_creds, args)

    assert "The acceptable values of the argument limit are 1 to 500" in str(exc_info.value)
    assert "0" in str(exc_info.value)


def test_gcp_compute_instances_list_command_empty_response(mocker):
    """
    Given: Valid arguments but no instances found
    When: gcp_compute_instances_list_command is called
    Then: The function should handle empty response gracefully
    """
    from GCP import gcp_compute_instances_list_command

    # Mock arguments
    args = {
        "project_id": "test-project",
        "zone": "us-central1-a",
    }

    # Mock API response with no instances
    mock_response = {
        "kind": "compute#instanceList",
        "id": "projects/test-project/zones/us-central1-a/instances",
        "items": [],
        "selfLink": "https://www.googleapis.com/compute/v1/projects/test-project/zones/us-central1-a/instances",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances
    mock_instances.list.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_instances_list_command(mock_creds, args)

    # Verify empty results are handled properly
    assert len(result.outputs["GCP.Compute.Instances(val.id && val.id == obj.id)"]) == 0


def test_gcp_compute_instance_get_command_basic_success(mocker):
    """
    Given: Valid credentials and basic arguments for getting an instance
    When: gcp_compute_instance_get_command is called
    Then: The function should return instance details with correct outputs
    """
    from GCP import gcp_compute_instance_get_command

    # Mock arguments
    args = {"project_id": "test-project", "zone": "us-central1-a", "instance": "test-instance-1"}

    # Mock API response
    mock_response = {
        "id": "123456789",
        "name": "test-instance-1",
        "kind": "compute#instance",
        "creationTimestamp": "2023-01-01T10:00:00.000-07:00",
        "description": "Test instance for unit testing",
        "status": "RUNNING",
        "machineType": "projects/test-project/zones/us-central1-a/machineTypes/n1-standard-1",
        "zone": "projects/test-project/zones/us-central1-a",
        "networkInterfaces": [
            {
                "network": "projects/test-project/global/networks/default",
                "subnetwork": "projects/test-project/regions/us-central1/subnetworks/default",
            }
        ],
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances
    mock_instances.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_instance_get_command(mock_creds, args)

    # Verify API call parameters
    mock_instances.get.assert_called_once_with(project="test-project", zone="us-central1-a", instance="test-instance-1")

    # Verify outputs structure
    assert result.outputs_prefix == "GCP.Compute.Instances"
    assert result.outputs_key_field == "id"
    assert result.outputs == mock_response
    assert result.raw_response == mock_response


def test_gcp_compute_instance_get_command_minimal_response(mocker):
    """
    Given: API response with minimal fields (some optional fields missing)
    When: gcp_compute_instance_get_command is called
    Then: The function should handle missing optional fields gracefully
    """
    from GCP import gcp_compute_instance_get_command

    # Mock arguments
    args = {"project_id": "test-project", "zone": "asia-east1-a", "instance": "minimal-instance"}

    # Mock API response with minimal fields
    mock_response = {
        "id": "555666777",
        "name": "minimal-instance",
        "kind": "compute#instance",
        "status": "PROVISIONING",
        "machineType": "projects/test-project/zones/asia-east1-a/machineTypes/f1-micro",
        # Missing: creationTimestamp, description
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances
    mock_instances.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_instance_get_command(mock_creds, args)

    # Verify API call
    mock_instances.get.assert_called_once_with(project="test-project", zone="asia-east1-a", instance="minimal-instance")

    # Verify outputs handle missing fields
    assert result.outputs == mock_response
    assert result.outputs["id"] == "555666777"
    assert result.outputs["name"] == "minimal-instance"
    assert "creationTimestamp" not in result.outputs
    assert "description" not in result.outputs


def test_gcp_compute_instance_get_command_complete_response(mocker):
    """
    Given: API response with all possible fields populated
    When: gcp_compute_instance_get_command is called
    Then: The function should handle complete response correctly
    """
    from GCP import gcp_compute_instance_get_command

    # Mock arguments
    args = {"project_id": "test-project", "zone": "us-west2-c", "instance": "full-instance"}

    # Mock API response with all fields
    mock_response = {
        "id": "111222333",
        "name": "full-instance",
        "kind": "compute#instance",
        "creationTimestamp": "2023-03-15T08:45:00.000-07:00",
        "description": "Comprehensive test instance with all fields",
        "status": "RUNNING",
        "machineType": "projects/test-project/zones/us-west2-c/machineTypes/n2-standard-4",
        "zone": "projects/test-project/zones/us-west2-c",
        "tags": {"items": ["web-server", "database"]},
        "labels": {"environment": "production", "team": "backend"},
        "metadata": {"items": [{"key": "startup-script", "value": "#!/bin/bash\necho 'Hello World'"}]},
        "disks": [
            {"type": "PERSISTENT", "mode": "READ_WRITE", "source": "projects/test-project/zones/us-west2-c/disks/full-instance"}
        ],
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances
    mock_instances.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_instance_get_command(mock_creds, args)

    # Verify complete response is returned
    assert result.outputs == mock_response
    assert result.outputs["tags"]["items"] == ["web-server", "database"]
    assert result.outputs["labels"]["environment"] == "production"
    assert result.outputs["metadata"]["items"][0]["key"] == "startup-script"


def test_gcp_compute_instance_get_command_table_generation(mocker):
    """
    Given: Valid instance data
    When: gcp_compute_instance_get_command is called
    Then: The function should generate readable output table with correct headers and data
    """
    from GCP import gcp_compute_instance_get_command

    # Mock arguments
    args = {"project_id": "test-project", "zone": "us-east1-b", "instance": "table-test-instance"}

    # Mock API response
    mock_response = {
        "id": "444555666",
        "name": "table-test-instance",
        "kind": "compute#instance",
        "creationTimestamp": "2023-04-10T12:00:00.000-07:00",
        "description": "Instance for table testing",
        "status": "TERMINATED",
        "machineType": "projects/test-project/zones/us-east1-b/machineTypes/g1-small",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances
    mock_instances.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)
    mock_table = mocker.patch("GCP.tableToMarkdown", return_value="Generated table output")
    mock_pascal_to_space = mocker.patch("GCP.pascalToSpace")

    # Execute the function
    result = gcp_compute_instance_get_command(mock_creds, args)

    # Verify tableToMarkdown was called with correct parameters
    mock_table.assert_called_once()
    table_call_args = mock_table.call_args

    # Check table title
    assert table_call_args[0][0] == "GCP Instance table-test-instance from zone us-east1-b"

    # Check table data
    table_data = table_call_args[0][1]
    assert table_data["id"] == "444555666"
    assert table_data["name"] == "table-test-instance"
    assert table_data["status"] == "TERMINATED"

    # Check headers
    expected_headers = [
        "id",
        "name",
        "kind",
        "creationTimestamp",
        "description",
        "status",
        "machineType",
        "labels",
        "labelFingerprint",
    ]
    assert table_call_args[1]["headers"] == expected_headers

    # Check other parameters
    assert table_call_args[1]["removeNull"] is True
    assert table_call_args[1]["headerTransform"] == mock_pascal_to_space

    # Verify readable output uses table result
    assert result.readable_output == "Generated table output"


def test_gcp_compute_instance_label_set_command_basic_success(mocker):
    """
    Given: Valid credentials and basic arguments for setting instance labels
    When: gcp_compute_instance_label_set_command is called
    Then: The function should set labels and return operation details with correct outputs
    """
    from GCP import gcp_compute_instance_label_set_command

    # Mock arguments
    args = {
        "project_id": "test-project",
        "zone": "us-central1-a",
        "instance": "test-instance",
        "label_fingerprint": "abc123fingerprint",
        "labels": "key=environment,value=production;key=team,value=backend",
    }

    # Mock API response
    mock_response = {
        "id": "operation-12345",
        "name": "operation-set-labels",
        "kind": "compute#operation",
        "status": "RUNNING",
        "progress": "0",
        "operationType": "setLabels",
        "zone": "projects/test-project/zones/us-central1-a",
        "targetId": "567890",
        "targetLink": "projects/test-project/zones/us-central1-a/instances/test-instance",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances
    mock_instances.setLabels.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_instance_label_set_command(mock_creds, args)

    # Verify API call parameters
    expected_body = {"labels": {"environment": "production", "team": "backend"}, "labelFingerprint": "abc123fingerprint"}
    mock_instances.setLabels.assert_called_once_with(
        project="test-project", zone="us-central1-a", instance="test-instance", body=expected_body
    )

    # Verify outputs structure
    assert result.outputs_prefix == "GCP.Compute.Operations"
    assert result.outputs_key_field == "id"
    assert result.outputs == mock_response
    assert result.raw_response == mock_response


def test_gcp_compute_instance_label_set_command_no_labels(mocker):
    """
    Given: Valid arguments with empty labels string
    When: gcp_compute_instance_label_set_command is called
    Then: An exception should be raised for empty labels.
    """
    from GCP import gcp_compute_instance_label_set_command

    # Mock arguments with empty labels
    args = {
        "project_id": "test-project",
        "zone": "asia-southeast1-a",
        "instance": "test-instance-3",
        "label_fingerprint": "xyz789fingerprint",
        "labels": "''",
    }

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)

    # Execute the function
    with pytest.raises(ValueError) as e:
        gcp_compute_instance_label_set_command(mock_creds, args)

    assert "Could not parse field" in str(e.value)


def test_gcp_compute_instance_label_set_command_multiple_labels(mocker):
    """
    Given: Valid arguments with multiple complex labels
    When: gcp_compute_instance_label_set_command is called
    Then: The function should parse and set all labels correctly
    """
    from GCP import gcp_compute_instance_label_set_command

    # Mock arguments with multiple labels
    args = {
        "project_id": "test-project",
        "zone": "us-east1-c",
        "instance": "multi-label-instance",
        "label_fingerprint": "multi123fingerprint",
        "labels": "key=env,value=staging;key=app,value=frontend;key=version,value=v2.1.0;key=owner,value=team-alpha",
    }

    # Mock API response
    mock_response = {
        "id": "operation-22222",
        "name": "operation-multi-labels",
        "kind": "compute#operation",
        "status": "RUNNING",
        "progress": "25",
        "operationType": "setLabels",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances
    mock_instances.setLabels.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_instance_label_set_command(mock_creds, args)

    # Verify API call with all labels
    expected_body = {
        "labels": {"env": "staging", "app": "frontend", "version": "v2.1.0", "owner": "team-alpha"},
        "labelFingerprint": "multi123fingerprint",
    }
    mock_instances.setLabels.assert_called_once_with(
        project="test-project", zone="us-east1-c", instance="multi-label-instance", body=expected_body
    )

    assert result.outputs == mock_response


def test_gcp_compute_instance_label_set_command_minimal_response(mocker):
    """
    Given: API response with minimal fields
    When: gcp_compute_instance_label_set_command is called
    Then: The function should handle missing optional fields gracefully in data_res
    """
    from GCP import gcp_compute_instance_label_set_command

    # Mock arguments
    args = {
        "project_id": "test-project",
        "zone": "us-central1-b",
        "instance": "minimal-response-instance",
        "label_fingerprint": "minimal123",
        "labels": "key=test,value=minimal",
    }

    # Mock API response with minimal fields
    mock_response = {
        "id": "operation-33333",
        "kind": "compute#operation",
        "status": "DONE",
        # Missing: name, progress, operationType
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances
    mock_instances.setLabels.return_value.execute.return_value = mock_response

    # Mock the build function and table generation
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)
    mock_table = mocker.patch("GCP.tableToMarkdown", return_value="Generated table")

    # Execute the function
    result = gcp_compute_instance_label_set_command(mock_creds, args)

    # Verify table generation handles missing fields
    mock_table.assert_called_once()
    table_data = mock_table.call_args[0][1]

    assert table_data["id"] == "operation-33333"
    assert table_data["status"] == "DONE"
    assert table_data["kind"] == "compute#operation"
    assert table_data["name"] is None
    assert table_data["progress"] is None
    assert table_data["operationType"] is None

    assert result.readable_output == "Generated table"
    assert result.outputs == mock_response


def test_parse_labels_empty_string():
    """
    Given: An empty labels string
    When: parse_labels is called
    Then: The function returns an empty dictionary
    """
    from GCP import parse_labels

    input_str = ""
    expected = {}

    result = parse_labels(input_str)
    assert result == expected


def test_parse_labels_whitespace_only():
    """
    Given: A labels string with only whitespace
    When: parse_labels is called
    Then: The function returns an empty dictionary
    """
    from GCP import parse_labels

    input_str = "   "
    expected = {}

    result = parse_labels(input_str)
    assert result == expected


def test_parse_labels_single_label_with_trailing_semicolon():
    """
    Given: A valid labels string with a single label and trailing semicolon
    When: parse_labels is called
    Then: The function returns a correctly parsed dictionary
    """
    from GCP import parse_labels

    input_str = "key=environment,value=PRODUCTION;"
    expected = {"environment": "production"}

    result = parse_labels(input_str)
    assert result == expected


def test_parse_labels_case_conversion():
    """
    Given: A labels string with mixed case keys and values
    When: parse_labels is called
    Then: The function converts both keys and values to lowercase
    """
    from GCP import parse_labels

    input_str = "key=Environment,value=PRODUCTION;key=TEAM,value=Backend"
    expected = {"environment": "production", "team": "backend"}

    result = parse_labels(input_str)
    assert result == expected


def test_parse_labels_special_characters_in_values():
    """
    Given: A labels string with special characters in values
    When: parse_labels is called
    Then: The function handles special characters correctly
    """
    from GCP import parse_labels

    input_str = "key=version,value=v2.1.0;key=branch,value=feature-branch_123"
    expected = {"version": "v2.1.0", "branch": "feature-branch_123"}

    result = parse_labels(input_str)
    assert result == expected


def test_parse_labels_empty_segments():
    """
    Given: A labels string with empty segments between semicolons
    When: parse_labels is called
    Then: The function skips empty segments and processes valid ones
    """
    from GCP import parse_labels

    input_str = "key=app,value=web;;key=env,value=test;"
    expected = {"app": "web", "env": "test"}

    result = parse_labels(input_str)
    assert result == expected


def test_parse_labels_malformed_missing_key():
    """
    Given: A labels string missing the key part
    When: parse_labels is called
    Then: The function raises a ValueError with appropriate message
    """
    from GCP import parse_labels

    input_str = "value=production"
    with pytest.raises(ValueError) as e:
        parse_labels(input_str)

    assert "Could not parse field" in str(e.value)
    assert "value=production" in str(e.value)
    assert "Please make sure you provided like so: key=abc,value=123" in str(e.value)


def test_parse_labels_malformed_missing_value():
    """
    Given: A labels string missing the value part
    When: parse_labels is called
    Then: The function raises a ValueError with appropriate message
    """
    from GCP import parse_labels

    input_str = "key=environment"
    with pytest.raises(ValueError) as e:
        parse_labels(input_str)

    assert "Could not parse field" in str(e.value)
    assert "key=environment" in str(e.value)
    assert "Please make sure you provided like so: key=abc,value=123" in str(e.value)


def test_parse_labels_malformed_wrong_separator():
    """
    Given: A labels string using wrong separator between key and value
    When: parse_labels is called
    Then: The function raises a ValueError with appropriate message
    """
    from GCP import parse_labels

    input_str = "key:environment,value:production"
    with pytest.raises(ValueError) as e:
        parse_labels(input_str)

    assert "Could not parse field" in str(e.value)
    assert "key:environment,value:production" in str(e.value)
    assert "Please make sure you provided like so: key=abc,value=123" in str(e.value)


def test_parse_labels_malformed_extra_equals():
    """
    Given: A labels string with extra equals signs
    When: parse_labels is called
    Then: The function raises a ValueError with appropriate message
    """
    from GCP import parse_labels

    input_str = "key=env=prod,value=test"
    with pytest.raises(ValueError) as e:
        parse_labels(input_str)

    assert "Could not parse field" in str(e.value)
    assert "key=env=prod,value=test" in str(e.value)
    assert "Please make sure you provided like so: key=abc,value=123" in str(e.value)


def test_parse_labels_numeric_values():
    """
    Given: A labels string with numeric values
    When: parse_labels is called
    Then: The function converts numeric values to lowercase strings
    """
    from GCP import parse_labels

    input_str = "key=port,value=8080;key=instances,value=3"
    expected = {"port": "8080", "instances": "3"}

    result = parse_labels(input_str)
    assert result == expected


def test_parse_labels_duplicate_keys():
    """
    Given: A labels string with duplicate keys
    When: parse_labels is called
    Then: The function updates with the last value for duplicate keys
    """
    from GCP import parse_labels

    input_str = "key=env,value=dev;key=app,value=web;key=env,value=prod"
    expected = {"env": "prod", "app": "web"}

    result = parse_labels(input_str)
    assert result == expected


def test_parse_labels_mixed_valid_invalid():
    """
    Given: A labels string with mix of valid and invalid segments
    When: parse_labels is called
    Then: The function raises a ValueError on the first invalid segment
    """
    from GCP import parse_labels

    input_str = "key=env,value=prod;invalid_format;key=app,value=web"
    with pytest.raises(ValueError) as e:
        parse_labels(input_str)

    assert "Could not parse field" in str(e.value)
    assert "invalid_format" in str(e.value)
    assert "Please make sure you provided like so: key=abc,value=123" in str(e.value)


def test_parse_labels_empty_key_value():
    """
    Given: A labels string with empty key or value
    When: parse_labels is called
    Then: An exception is raised.
    """
    from GCP import parse_labels

    input_str = "key=,value=empty_key;key=empty_value,value="

    with pytest.raises(ValueError) as e:
        parse_labels(input_str)
    assert "Could not parse field" in str(e.value)


def test_parse_labels_complex_valid_format():
    """
    Given: A complex labels string with multiple valid labels including special characters
    When: parse_labels is called
    Then: The function returns correctly parsed dictionary with all labels
    """
    from GCP import parse_labels

    input_str = (
        "key=app-name,value=my-web-app;key=version_tag,value=v1.2.3-beta;key=environment,value=STAGING;key=owner.team,"
        "value=Platform_Team"
    )
    expected = {"app-name": "my-web-app", "version_tag": "v1.2.3-beta", "environment": "staging", "owner.team": "platform_team"}

    result = parse_labels(input_str)
    assert result == expected


def test_gcp_compute_instance_label_set_command_add_labels_true(mocker):
    """
    Given: Valid arguments with add_labels=true and existing instance labels
    When: gcp_compute_instance_label_set_command is called
    Then: The function should merge new labels with existing ones
    """
    from GCP import gcp_compute_instance_label_set_command

    # Mock arguments with add_labels=true
    args = {
        "project_id": "test-project",
        "zone": "us-central1-a",
        "instance": "test-instance",
        "label_fingerprint": "abc123fingerprint",
        "labels": "key=newlabel,value=newvalue;key=app,value=updated",
        "add_labels": "true",
    }

    # Mock relevant instance data with current labels
    mock_instance_response = {
        "id": "567890",
        "name": "test-instance",
        "labels": {"environment": "production", "app": "oldvalue", "team": "backend"},
    }

    # Mock setLabels operation response
    mock_operation_response = {
        "id": "operation-12345",
        "name": "operation-set-labels",
        "kind": "compute#operation",
        "status": "RUNNING",
        "progress": "0",
        "operationType": "setLabels",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances
    mock_instances.get.return_value.execute.return_value = mock_instance_response
    mock_instances.setLabels.return_value.execute.return_value = mock_operation_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_instance_label_set_command(mock_creds, args)

    # Verify get was called to fetch existing labels
    mock_instances.get.assert_called_once_with(project="test-project", zone="us-central1-a", instance="test-instance")

    # Verify setLabels was called with merged labels
    expected_body = {
        "labels": {
            "environment": "production",
            "team": "backend",
            "newlabel": "newvalue",
            "app": "updated",  # Should override existing value
        },
        "labelFingerprint": "abc123fingerprint",
    }
    mock_instances.setLabels.assert_called_once_with(
        project="test-project", zone="us-central1-a", instance="test-instance", body=expected_body
    )

    assert result.outputs == mock_operation_response


def test_validate_bucket_policy_for_set_add_mode_valid():
    """
    Given: A minimal valid policy for add=true (merge mode)
    When: _validate_bucket_policy_for_set is called
    Then: No exception is raised
    """
    from GCP import _validate_bucket_policy_for_set

    policy = {"bindings": [{"role": "roles/storage.objectViewer", "members": ["user:alice@example.com"]}]}

    # Should not raise
    _validate_bucket_policy_for_set(policy, add_mode=True)


def test_validate_bucket_policy_for_set_add_mode_missing_role():
    """
    Given: A binding without role in add=true mode
    When: _validate_bucket_policy_for_set is called
    Then: DemistoException is raised mentioning 'role'
    """
    from GCP import _validate_bucket_policy_for_set, DemistoException

    policy = {"bindings": [{"members": ["allUsers"]}]}
    with pytest.raises(DemistoException) as e:
        _validate_bucket_policy_for_set(policy, add_mode=True)
    assert "role" in str(e.value)


def test_validate_bucket_policy_for_set_add_mode_members_not_list():
    """
    Given: A binding with members not as list in add=true mode
    When: _validate_bucket_policy_for_set is called
    Then: DemistoException is raised mentioning 'members'
    """
    from GCP import _validate_bucket_policy_for_set, DemistoException

    policy = {"bindings": [{"role": "roles/storage.objectViewer", "members": "allUsers"}]}
    with pytest.raises(DemistoException) as e:
        _validate_bucket_policy_for_set(policy, add_mode=True)
    assert "members" in str(e.value)


def test_validate_bucket_policy_for_set_condition_requires_version():
    """
    Given: A binding with condition but policy.version < 3
    When: _validate_bucket_policy_for_set is called
    Then: DemistoException is raised about version requirement
    """
    from GCP import _validate_bucket_policy_for_set, DemistoException

    policy = {
        "version": 1,
        "bindings": [
            {
                "role": "roles/storage.objectViewer",
                "members": ["group:security@example.com"],
                "condition": {"title": "t", "expression": "true"},
            }
        ],
    }

    with pytest.raises(DemistoException) as e:
        _validate_bucket_policy_for_set(policy, add_mode=False)
    assert "version" in str(e.value)


def test_validate_bucket_policy_for_set_replace_mode_invalid_bindings_type():
    """
    Given: Replace mode policy with invalid bindings type (dict instead of list)
    When: _validate_bucket_policy_for_set is called with add_mode=False
    Then: DemistoException is raised
    """
    from GCP import _validate_bucket_policy_for_set, DemistoException

    policy = {"bindings": {"role": "roles/storage.objectViewer", "members": ["allUsers"]}}
    with pytest.raises(DemistoException):
        _validate_bucket_policy_for_set(policy, add_mode=False)


def test_format_gcp_datetime():
    """
    Given: An RFC3339 timestamp string with Z suffix
    When: _format_gcp_datetime is called
    Then: It returns a formatted timestamp in '%Y-%m-%d %H:%M:%S'
    """
    from GCP import _format_gcp_datetime

    assert _format_gcp_datetime(None) is None
    assert _format_gcp_datetime("2023-01-01T12:34:56Z") == "2023-01-01 12:34:56"


def test_is_ubla_enabled_true_false_and_exception(mocker):
    """
    Given: Bucket metadata responses for UBLA enabled/disabled and an exception case
    When: _is_ubla_enabled is called
    Then: It returns True/False accordingly and False on exception
    """
    from GCP import _is_ubla_enabled

    # Mock storage client chain
    storage_client = mocker.MagicMock()
    buckets = storage_client.buckets.return_value

    # Enabled case
    buckets.get.return_value.execute.return_value = {"iamConfiguration": {"uniformBucketLevelAccess": {"enabled": True}}}
    assert _is_ubla_enabled(storage_client, "b1") is True

    # Disabled case
    buckets.get.return_value.execute.return_value = {"iamConfiguration": {"uniformBucketLevelAccess": {"enabled": False}}}
    assert _is_ubla_enabled(storage_client, "b1") is False

    # Exception case
    buckets.get.return_value.execute.side_effect = Exception("boom")
    assert _is_ubla_enabled(storage_client, "b1") is False


def test_is_ubla_error_variants():
    """
    Given: Error objects with 400 status and UBLA phrase in content (str/bytes) and non-matching cases
    When: _is_ubla_error is called
    Then: It returns True only for 400 with UBLA phrase
    """
    from GCP import _is_ubla_error
    from googleapiclient.errors import HttpError

    class Err(HttpError):
        def __init__(self, status, content):
            self.resp = type("R", (), {"status": status})()
            self.content = content

    assert _is_ubla_error(Err(400, "Uniform Bucket-Level Access must be enabled")) is True
    assert _is_ubla_error(Err(400, b"...uniform bucket-level access is required...")) is True
    assert _is_ubla_error(Err(403, "uniform bucket-level access")) is False
    assert _is_ubla_error(Err(400, "other error")) is False


def test_storage_bucket_list_basic(mocker):
    """
    Given: Project with two buckets returned
    When: storage_bucket_list is called
    Then: API called with request params and outputs are normalized
    """
    from GCP import storage_bucket_list

    mock_storage = mocker.Mock()
    mock_buckets = mocker.Mock()
    mock_storage.buckets.return_value = mock_buckets
    mock_buckets.list.return_value.execute.return_value = {
        "items": [
            {
                "name": "b1",
                "timeCreated": "2024-01-01T00:00:00Z",
                "updated": "2024-01-02T00:00:00Z",
                "owner": {"entityId": "123"},
                "location": "US",
                "storageClass": "STANDARD",
            }
        ]
    }
    mocker.patch("GCP.GCPServices.STORAGE.build", return_value=mock_storage)

    creds = mocker.Mock(spec=Credentials)
    args = {"project_id": "p1", "limit": "10", "prefix": "p", "page_token": "t"}

    result = storage_bucket_list(creds, args)

    mock_buckets.list.assert_called_with(project="p1", maxResults=10, prefix="p", pageToken="t")
    assert result.outputs_prefix == "GCP.Storage.Bucket"
    assert result.outputs[0]["name"] == "b1"


def test_storage_bucket_get_basic(mocker):
    """
    Given: A bucket exists
    When: storage_bucket_get is called
    Then: It fetches via buckets().get and returns normalized fields
    """
    from GCP import storage_bucket_get

    mock_storage = mocker.Mock()
    mock_buckets = mocker.Mock()
    mock_storage.buckets.return_value = mock_buckets
    mock_buckets.get.return_value.execute.return_value = {
        "name": "b1",
        "timeCreated": "2024-01-01T00:00:00Z",
        "updated": "2024-01-02T00:00:00Z",
        "owner": {"entityId": "123"},
        "location": "US",
        "storageClass": "STANDARD",
    }
    mocker.patch("GCP.GCPServices.STORAGE.build", return_value=mock_storage)

    creds = mocker.Mock(spec=Credentials)
    result = storage_bucket_get(creds, {"bucket_name": "b1"})

    mock_buckets.get.assert_called_with(bucket="b1")
    assert result.outputs_prefix == "GCP.Storage.Bucket"
    assert result.outputs["name"] == "b1"


def test_storage_bucket_objects_list_basic(mocker):
    """
    Given: A bucket with two objects
    When: storage_bucket_objects_list is called
    Then: objects().list is called with args and outputs normalized
    """
    from GCP import storage_bucket_objects_list

    mock_storage = mocker.Mock()
    mock_objects = mocker.Mock()
    mock_storage.objects.return_value = mock_objects
    mock_objects.list.return_value.execute.return_value = {
        "items": [
            {
                "name": "o1",
                "bucket": "b1",
                "contentType": "text/plain",
                "size": "1",
                "timeCreated": "2024-01-01T00:00:00Z",
                "updated": "2024-01-02T00:00:00Z",
                "md5Hash": "md5",
                "crc32c": "crc",
            }
        ]
    }
    mocker.patch("GCP.GCPServices.STORAGE.build", return_value=mock_storage)

    creds = mocker.Mock(spec=Credentials)
    args = {"bucket_name": "b1", "prefix": "p/", "delimiter": "/", "limit": "5", "page_token": "tok"}
    result = storage_bucket_objects_list(creds, args)

    mock_objects.list.assert_called_with(bucket="b1", prefix="p/", delimiter="/", maxResults=5, pageToken="tok")
    assert result.outputs_prefix == "GCP.Storage.BucketObject"
    assert result.outputs[0]["name"] == "o1"


def test_storage_bucket_policy_list_with_version(mocker):
    """
    Given: A bucket policy exists
    When: storage_bucket_policy_list is called with requested_policy_version
    Then: It calls getIamPolicy with optionsRequestedPolicyVersion and returns policy
    """
    from GCP import storage_bucket_policy_list

    mock_storage = mocker.Mock()
    mock_buckets = mocker.Mock()
    mock_storage.buckets.return_value = mock_buckets
    mock_buckets.getIamPolicy.return_value.execute.return_value = {"version": 3, "etag": "abc", "bindings": []}
    mocker.patch("GCP.GCPServices.STORAGE.build", return_value=mock_storage)

    creds = mocker.Mock(spec=Credentials)
    args = {"bucket_name": "b1", "requested_policy_version": "3"}
    result = storage_bucket_policy_list(creds, args)

    mock_buckets.getIamPolicy.assert_called_with(bucket="b1", optionsRequestedPolicyVersion=3)
    assert result.outputs_prefix == "GCP.Storage.BucketPolicy"
    assert result.outputs["version"] == 3


def test_storage_bucket_policy_set_basic(mocker):
    """
    Given: A policy document to apply
    When: storage_bucket_policy_set is called
    Then: It calls setIamPolicy with body and returns response
    """
    from GCP import storage_bucket_policy_set

    mock_storage = mocker.Mock()
    mock_buckets = mocker.Mock()
    mock_storage.buckets.return_value = mock_buckets
    mock_buckets.setIamPolicy.return_value.execute.return_value = {"version": 3, "etag": "etag1"}
    mocker.patch("GCP.GCPServices.STORAGE.build", return_value=mock_storage)

    creds = mocker.Mock(spec=Credentials)
    policy = {"bindings": [{"role": "roles/storage.objectViewer", "members": ["allUsers"]}]}
    args = {"bucket_name": "b1", "policy": json.dumps(policy), "add": "false"}
    result = storage_bucket_policy_set(creds, args)

    mock_buckets.setIamPolicy.assert_called_with(bucket="b1", body=policy)
    assert result.outputs_prefix == "GCP.Storage.BucketPolicy"
    assert result.outputs["etag"] == "etag1"


def test_storage_bucket_object_policy_list_normal_and_ubla(mocker):
    """
    Given: UBLA disabled and enabled scenarios
    When: storage_bucket_object_policy_list is called
    Then: It lists object ACLs when UBLA disabled, and returns bucket policy when enabled
    """
    from GCP import storage_bucket_object_policy_list

    # Case 1: UBLA disabled
    mock_storage = mocker.Mock()
    mock_oac = mocker.Mock()
    mock_storage.objectAccessControls.return_value = mock_oac
    mock_oac.list.return_value.execute.return_value = {"items": [{"entity": "allUsers", "role": "READER"}]}
    mocker.patch("GCP.GCPServices.STORAGE.build", return_value=mock_storage)
    mocker.patch("GCP._is_ubla_enabled", return_value=False)

    creds = mocker.Mock(spec=Credentials)
    result = storage_bucket_object_policy_list(creds, {"bucket_name": "b1", "object_name": "o1"})
    mock_oac.list.assert_called_with(bucket="b1", object="o1")
    assert result.outputs_prefix == "GCP.Storage.BucketObjectPolicy"
    assert result.outputs[0]["entity"] == "allUsers"

    # Case 2: UBLA enabled -> delegates to bucket policy list
    mocker.patch("GCP._is_ubla_enabled", return_value=True)
    # Patch bucket policy list to observe delegation
    mocker.patch("GCP.storage_bucket_policy_list", return_value=MagicMock(outputs_prefix="GCP.Storage.BucketObjectPolicy"))
    result2 = storage_bucket_object_policy_list(creds, {"bucket_name": "b1", "object_name": "o1"})
    assert result2.outputs_prefix == "GCP.Storage.BucketObjectPolicy"


def test_storage_bucket_object_policy_set_update_then_insert(mocker):
    """
    Given: An ACL entry to apply
    When: storage_bucket_object_policy_set is called
    Then: It tries update first; if update raises, it falls back to insert
    """
    from GCP import storage_bucket_object_policy_set

    mock_storage = mocker.Mock()
    mock_oac = mocker.Mock()
    mock_storage.objectAccessControls.return_value = mock_oac

    # Patch fails, insert succeeds
    mock_oac.patch.return_value.execute.side_effect = Exception("not found")
    mock_oac.insert.return_value.execute.return_value = {"entity": "allUsers", "role": "READER"}

    mocker.patch("GCP.GCPServices.STORAGE.build", return_value=mock_storage)
    mocker.patch("GCP._is_ubla_enabled", return_value=False)

    creds = mocker.Mock(spec=Credentials)
    args = {"bucket_name": "b1", "object_name": "o1", "policy": json.dumps({"entity": "allUsers", "role": "READER"})}
    result = storage_bucket_object_policy_set(creds, args)

    mock_oac.patch.assert_called()
    mock_oac.insert.assert_called()
    assert result.outputs_prefix == "GCP.Storage.BucketObjectPolicy"
    assert result.outputs[0]["entity"] == "allUsers"


def test_storage_bucket_object_policy_set_update_success(mocker):
    """
    Given: An ACL entry for which update succeeds
    When: storage_bucket_object_policy_set is called
    Then: It returns the update response and does not call insert
    """
    from GCP import storage_bucket_object_policy_set

    mock_storage = mocker.Mock()
    mock_oac = mocker.Mock()
    mock_storage.objectAccessControls.return_value = mock_oac
    mock_oac.patch.return_value.execute.return_value = {"entity": "user:a@example.com", "role": "WRITER"}

    mocker.patch("GCP.GCPServices.STORAGE.build", return_value=mock_storage)
    mocker.patch("GCP._is_ubla_enabled", return_value=False)

    creds = mocker.Mock(spec=Credentials)
    args = {"bucket_name": "b1", "object_name": "o1", "policy": json.dumps({"entity": "user:a@example.com", "role": "WRITER"})}
    result = storage_bucket_object_policy_set(creds, args)

    mock_oac.patch.assert_called()
    mock_oac.insert.assert_not_called()
    assert result.outputs[0]["role"] == "WRITER"


def test_storage_bucket_object_policy_set_ubla_short_circuit(mocker):
    """
    Given: UBLA is enabled on bucket
    When: storage_bucket_object_policy_set is called
    Then: It short-circuits with guidance and does not call ObjectAccessControls
    """
    from GCP import storage_bucket_object_policy_set

    mock_storage = mocker.Mock()
    mocker.patch("GCP.GCPServices.STORAGE.build", return_value=mock_storage)
    mocker.patch("GCP._is_ubla_enabled", return_value=True)

    creds = mocker.Mock(spec=Credentials)
    args = {"bucket_name": "b1", "object_name": "o1", "policy": json.dumps({"entity": "allUsers", "role": "READER"})}
    result = storage_bucket_object_policy_set(creds, args)

    # Should be a guidance readable output and no outputs list
    assert "Uniform Bucket-Level Access (UBLA) is enabled" in result.readable_output


def test_gcp_compute_instance_label_set_command_add_labels_no_existing(mocker):
    """
    Given: Valid arguments with add_labels=true but instance has no existing labels
    When: gcp_compute_instance_label_set_command is called
    Then: The function should handle missing labels field gracefully
    """
    from GCP import gcp_compute_instance_label_set_command

    # Mock arguments with add_labels=true
    args = {
        "project_id": "test-project",
        "zone": "us-west1-b",
        "instance": "new-instance",
        "label_fingerprint": "xyz789fingerprint",
        "labels": "key=firstlabel,value=firstvalue",
        "add_labels": "true",
    }

    # Mock instance response without labels field
    mock_instance_response = {
        "id": "111222333",
        "name": "new-instance",
        # No labels field
    }

    # Mock setLabels operation response
    mock_operation_response = {"id": "operation-99999", "kind": "compute#operation", "status": "DONE"}

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances
    mock_instances.get.return_value.execute.return_value = mock_instance_response
    mock_instances.setLabels.return_value.execute.return_value = mock_operation_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_instance_label_set_command(mock_creds, args)

    # Verify setLabels was called with only new labels (no existing to merge)
    expected_body = {"labels": {"firstlabel": "firstvalue"}, "labelFingerprint": "xyz789fingerprint"}
    mock_instances.setLabels.assert_called_once_with(
        project="test-project", zone="us-west1-b", instance="new-instance", body=expected_body
    )

    assert result.outputs == mock_operation_response


def test_gcp_compute_instance_label_set_command_add_labels_false(mocker):
    """
    Given: Valid arguments with add_labels=false (default behavior)
    When: gcp_compute_instance_label_set_command is called
    Then: The function should not fetch existing labels and only set new ones
    """
    from GCP import gcp_compute_instance_label_set_command

    # Mock arguments with add_labels=false (explicit)
    args = {
        "project_id": "test-project",
        "zone": "europe-west1-c",
        "instance": "replace-labels-instance",
        "label_fingerprint": "replace123fingerprint",
        "labels": "key=onlylabel,value=onlyvalue",
        "add_labels": "false",
    }

    # Mock setLabels operation response
    mock_operation_response = {
        "id": "operation-replace",
        "name": "operation-replace-labels",
        "kind": "compute#operation",
        "status": "RUNNING",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_instances = mocker.Mock()
    mock_compute.instances.return_value = mock_instances
    mock_instances.setLabels.return_value.execute.return_value = mock_operation_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_instance_label_set_command(mock_creds, args)

    # Verify get was NOT called (no existing labels fetched)
    mock_instances.get.assert_not_called()

    # Verify setLabels was called with only new labels
    expected_body = {"labels": {"onlylabel": "onlyvalue"}, "labelFingerprint": "replace123fingerprint"}
    mock_instances.setLabels.assert_called_once_with(
        project="test-project", zone="europe-west1-c", instance="replace-labels-instance", body=expected_body
    )

    assert result.outputs == mock_operation_response


def test_validate_limit_valid_input():
    """
    Given: A valid limit value (between 1 and 500 inclusive)
    When: validate_limit is called
    Then: No exception is raised
    """
    from GCP import validate_limit

    # Test with minimum valid limit
    validate_limit(1)
    # Test with maximum valid limit
    validate_limit(500)
    # Test with a value in between
    validate_limit(250)


def test_validate_limit_invalid_input_too_low():
    """
    Given: An invalid limit value (less than 1)
    When: validate_limit is called
    Then: DemistoException is raised with an appropriate message
    """
    from GCP import validate_limit, DemistoException

    with pytest.raises(DemistoException) as e:
        validate_limit(0)
    assert "The acceptable values of the argument limit are 1 to 500, inclusive. Currently the value is 0" in str(e.value)


def test_validate_limit_invalid_input_too_high():
    """
    Given: An invalid limit value (greater than 500)
    When: validate_limit is called
    Then: DemistoException is raised with an appropriate message
    """
    from GCP import validate_limit, DemistoException

    with pytest.raises(DemistoException) as e:
        validate_limit(501)
    assert "The acceptable values of the argument limit are 1 to 500, inclusive. Currently the value is 501" in str(e.value)


# gcp_compute_zone_get
def test_gcp_compute_zone_get_basic_success(mocker):
    """
    Given: Valid credentials and basic arguments for getting a zone
    When: gcp_compute_zone_get is called
    Then: The function should return zone details with correct outputs
    """
    from GCP import gcp_compute_zone_get

    # Mock arguments
    args = {"project_id": "test-project", "zone": "us-central1-a"}

    # Mock API response
    mock_response = {
        "id": "2000",
        "name": "us-central1-a",
        "description": "us-central1-a",
        "status": "UP",
        "region": "https://www.googleapis.com/compute/v1/projects/test-project/regions/us-central1",
        "selfLink": "https://www.googleapis.com/compute/v1/projects/test-project/zones/us-central1-a",
        "availableCpuPlatforms": ["Intel Skylake", "Intel Broadwell"],
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_zones = mocker.Mock()
    mock_compute.zones.return_value = mock_zones
    mock_zones.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_zone_get(mock_creds, args)

    # Verify API call parameters
    mock_zones.get.assert_called_once_with(project="test-project", zone="us-central1-a")

    # Verify outputs structure
    assert result.outputs_prefix == "GCP.Compute.Zones"
    assert result.outputs_key_field == "id"
    assert result.outputs == mock_response
    assert result.raw_response == mock_response


def test_gcp_compute_zone_get_minimal_response(mocker):
    """
    Given: API response with minimal fields (some optional fields missing)
    When: gcp_compute_zone_get is called
    Then: The function should handle missing optional fields gracefully
    """
    from GCP import gcp_compute_zone_get

    # Mock arguments
    args = {"project_id": "test-project", "zone": "asia-east1-b"}

    # Mock API response with minimal fields
    mock_response = {
        "id": "3000",
        "name": "asia-east1-b",
        "status": "DOWN",
        # Missing: description, region, selfLink, availableCpuPlatforms
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_zones = mocker.Mock()
    mock_compute.zones.return_value = mock_zones
    mock_zones.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_zone_get(mock_creds, args)

    # Verify API call
    mock_zones.get.assert_called_once_with(project="test-project", zone="asia-east1-b")

    # Verify outputs handle missing fields
    assert result.outputs == mock_response
    assert result.outputs["id"] == "3000"
    assert result.outputs["name"] == "asia-east1-b"
    assert result.outputs["status"] == "DOWN"


def test_gcp_compute_zone_get_table_generation(mocker):
    """
    Given: Valid zone data
    When: gcp_compute_zone_get is called
    Then: The function should generate readable output table with correct headers and data
    """
    from GCP import gcp_compute_zone_get

    # Mock arguments
    args = {"project_id": "test-project", "zone": "europe-west1-c"}

    # Mock API response
    mock_response = {
        "id": "4000",
        "name": "europe-west1-c",
        "status": "UP",
        "description": "europe-west1-c zone",
        "region": "https://www.googleapis.com/compute/v1/projects/test-project/regions/europe-west1",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_zones = mocker.Mock()
    mock_compute.zones.return_value = mock_zones
    mock_zones.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)
    mock_table = mocker.patch("GCP.tableToMarkdown", return_value="Generated table output")

    # Execute the function
    result = gcp_compute_zone_get(mock_creds, args)

    # Verify tableToMarkdown was called with correct parameters
    mock_table.assert_called_once()
    table_call_args = mock_table.call_args

    # Check table title
    assert table_call_args[0][0] == "GCP zone europe-west1-c"

    # Check table data
    table_data = table_call_args[0][1]
    assert table_data["status"] == "UP"
    assert table_data["name"] == "europe-west1-c"
    assert table_data["id"] == "4000"

    # Check headers
    expected_headers = ["id", "name", "status"]
    assert table_call_args[1]["headers"] == expected_headers

    # Check other parameters
    assert table_call_args[1]["removeNull"] is True

    # Verify readable output uses table result
    assert result.readable_output == "Generated table output"


def test_gcp_compute_zone_get_none_values_handling(mocker):
    """
    Given: API response with None values for some fields
    When: gcp_compute_zone_get is called
    Then: The function should handle None values correctly in data_res
    """
    from GCP import gcp_compute_zone_get

    # Mock arguments
    args = {"project_id": "test-project", "zone": "us-west2-a"}

    # Mock API response with None values
    mock_response = {
        "id": None,
        "name": "us-west2-a",
        "status": None,
        "description": "Test zone with None values",
        "region": "https://www.googleapis.com/compute/v1/projects/test-project/regions/us-west2",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_zones = mocker.Mock()
    mock_compute.zones.return_value = mock_zones
    mock_zones.get.return_value.execute.return_value = mock_response

    # Mock the build function and table generation
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)
    mock_table = mocker.patch("GCP.tableToMarkdown", return_value="Generated table")

    # Execute the function
    result = gcp_compute_zone_get(mock_creds, args)

    # Verify table generation handles None values
    mock_table.assert_called_once()
    table_data = mock_table.call_args[0][1]

    assert table_data["status"] is None
    assert table_data["name"] == "us-west2-a"
    assert table_data["id"] is None

    # Verify removeNull parameter is set to handle None values
    assert mock_table.call_args[1]["removeNull"] is True

    assert result.readable_output == "Generated table"
    assert result.outputs == mock_response


def test_gcp_compute_zone_get_complete_response(mocker):
    """
    Given: API response with all possible fields populated
    When: gcp_compute_zone_get is called
    Then: The function should handle complete response correctly
    """
    from GCP import gcp_compute_zone_get

    # Mock arguments
    args = {"project_id": "comprehensive-project", "zone": "us-east4-b"}

    # Mock API response with all fields
    mock_response = {
        "id": "5000",
        "name": "us-east4-b",
        "description": "Comprehensive zone for testing",
        "status": "UP",
        "region": "https://www.googleapis.com/compute/v1/projects/comprehensive-project/regions/us-east4",
        "selfLink": "https://www.googleapis.com/compute/v1/projects/comprehensive-project/zones/us-east4-b",
        "availableCpuPlatforms": ["Intel Skylake", "Intel Broadwell", "Intel Haswell"],
        "creationTimestamp": "2023-01-01T00:00:00.000-00:00",
        "kind": "compute#zone",
        "supportsPzs": True,
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_zones = mocker.Mock()
    mock_compute.zones.return_value = mock_zones
    mock_zones.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_zone_get(mock_creds, args)

    # Verify complete response is returned
    assert result.outputs == mock_response
    assert result.outputs["availableCpuPlatforms"] == ["Intel Skylake", "Intel Broadwell", "Intel Haswell"]
    assert result.outputs["supportsPzs"] is True
    assert result.outputs["creationTimestamp"] == "2023-01-01T00:00:00.000-00:00"


def test_gcp_compute_zone_get_different_project_zones(mocker):
    """
    Given: Different project and zone combinations
    When: gcp_compute_zone_get is called with various project/zone pairs
    Then: The function should handle different combinations correctly
    """
    from GCP import gcp_compute_zone_get

    test_cases = [
        ("project-alpha", "australia-southeast1-a"),
        ("project-beta", "southamerica-east1-b"),
        ("project-gamma", "northamerica-northeast1-c"),
    ]

    for project_id, zone_name in test_cases:
        # Mock arguments
        args = {"project_id": project_id, "zone": zone_name}

        # Mock API response
        mock_response = {
            "id": f"{hash(zone_name) % 10000}",
            "name": zone_name,
            "status": "UP",
            "description": f"Zone {zone_name} in project {project_id}",
        }

        # Mock the GCP API calls
        mock_compute = mocker.Mock()
        mock_zones = mocker.Mock()
        mock_compute.zones.return_value = mock_zones
        mock_zones.get.return_value.execute.return_value = mock_response

        # Mock the build function
        mock_creds = mocker.Mock(spec=Credentials)
        mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

        # Execute the function
        result = gcp_compute_zone_get(mock_creds, args)

        # Verify API call parameters
        mock_zones.get.assert_called_with(project=project_id, zone=zone_name)

        # Verify outputs
        assert result.outputs["name"] == zone_name
        assert zone_name in result.outputs["description"]


def test_gcp_compute_zone_get_special_zone_names(mocker):
    """
    Given: Zone names with special characters or patterns
    When: gcp_compute_zone_get is called
    Then: The function should handle special zone names correctly
    """
    from GCP import gcp_compute_zone_get

    # Mock arguments with special zone name
    args = {"project_id": "test-project-special", "zone": "us-central1-a"}

    # Mock API response
    mock_response = {
        "id": "6000",
        "name": "us-central1-a",
        "status": "UP",
        "description": "Zone with hyphens and numbers",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_zones = mocker.Mock()
    mock_compute.zones.return_value = mock_zones
    mock_zones.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)
    mock_table = mocker.patch("GCP.tableToMarkdown", return_value="Special zone table")

    # Execute the function
    result = gcp_compute_zone_get(mock_creds, args)

    # Verify zone name is handled correctly in table title
    mock_table.assert_called_once()
    table_title = mock_table.call_args[0][0]
    assert table_title == "GCP zone us-central1-a"

    assert result.outputs["name"] == "us-central1-a"


# gcp_compute_network_get_command
def test_gcp_compute_network_get_command_basic_success(mocker):
    """
    Given: Valid credentials and basic arguments for getting a network
    When: gcp_compute_network_get_command is called
    Then: The function should return network details with correct outputs
    """
    from GCP import gcp_compute_network_get_command

    # Mock arguments
    args = {"project_id": "test-project", "network": "default"}

    # Mock API response
    mock_response = {
        "id": "1234567890123456789",
        "name": "default",
        "kind": "compute#network",
        "description": "Default network for the project",
        "selfLink": "https://www.googleapis.com/compute/v1/projects/test-project/global/networks/default",
        "autoCreateSubnetworks": True,
        "creationTimestamp": "2023-01-01T10:00:00.000-07:00",
        "routingConfig": {"routingMode": "REGIONAL"},
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_networks = mocker.Mock()
    mock_compute.networks.return_value = mock_networks
    mock_networks.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_network_get_command(mock_creds, args)

    # Verify API call parameters
    mock_networks.get.assert_called_once_with(project="test-project", network="default")

    # Verify outputs structure
    assert result.outputs_prefix == "GCP.Compute.Networks"
    assert result.outputs_key_field == "id"
    assert result.outputs == mock_response
    assert result.raw_response == mock_response


def test_gcp_compute_network_get_command_minimal_response(mocker):
    """
    Given: API response with minimal fields (some optional fields missing)
    When: gcp_compute_network_get_command is called
    Then: The function should handle missing optional fields gracefully
    """
    from GCP import gcp_compute_network_get_command

    # Mock arguments
    args = {"project_id": "test-project", "network": "minimal-network"}

    # Mock API response with minimal fields
    mock_response = {
        "id": "9876543210987654321",
        "name": "minimal-network",
        "kind": "compute#network",
        # Missing: description, selfLink, autoCreateSubnetworks, creationTimestamp, routingConfig
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_networks = mocker.Mock()
    mock_compute.networks.return_value = mock_networks
    mock_networks.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_network_get_command(mock_creds, args)

    # Verify API call
    mock_networks.get.assert_called_once_with(project="test-project", network="minimal-network")

    # Verify outputs handle missing fields
    assert result.outputs == mock_response
    assert result.outputs["id"] == "9876543210987654321"
    assert result.outputs["name"] == "minimal-network"
    assert "description" not in result.outputs
    assert "selfLink" not in result.outputs


def test_gcp_compute_network_get_command_table_generation(mocker):
    """
    Given: Valid network data
    When: gcp_compute_network_get_command is called
    Then: The function should generate readable output table with correct headers and data
    """
    from GCP import gcp_compute_network_get_command

    # Mock arguments
    args = {"project_id": "test-project", "network": "custom-vpc"}

    # Mock API response
    mock_response = {
        "id": "5555666677778888999",
        "name": "custom-vpc",
        "kind": "compute#network",
        "description": "Custom VPC network",
        "autoCreateSubnetworks": False,
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_networks = mocker.Mock()
    mock_compute.networks.return_value = mock_networks
    mock_networks.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)
    mock_table = mocker.patch("GCP.tableToMarkdown", return_value="Generated table output")

    # Execute the function
    result = gcp_compute_network_get_command(mock_creds, args)

    # Verify tableToMarkdown was called with correct parameters
    mock_table.assert_called_once()
    table_call_args = mock_table.call_args

    # Check table title
    assert table_call_args[0][0] == "GCP network custom-vpc"

    # Check table data
    table_data = table_call_args[0][1]
    assert table_data["name"] == "custom-vpc"
    assert table_data["id"] == "5555666677778888999"

    # Check headers
    expected_headers = ["id", "name", "creationTimestamp", "description"]
    assert table_call_args[1]["headers"] == expected_headers

    # Check other parameters
    assert table_call_args[1]["removeNull"] is True

    # Verify readable output uses table result
    assert result.readable_output == "Generated table output"


def test_gcp_compute_network_get_command_none_values_handling(mocker):
    """
    Given: API response with None values for some fields
    When: gcp_compute_network_get_command is called
    Then: The function should handle None values correctly in data_res
    """
    from GCP import gcp_compute_network_get_command

    # Mock arguments
    args = {"project_id": "test-project", "network": "network-with-nulls"}

    # Mock API response with None values
    mock_response = {
        "id": None,
        "name": "network-with-nulls",
        "kind": "compute#network",
        "description": None,
        "autoCreateSubnetworks": True,
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_networks = mocker.Mock()
    mock_compute.networks.return_value = mock_networks
    mock_networks.get.return_value.execute.return_value = mock_response

    # Mock the build function and table generation
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)
    mock_table = mocker.patch("GCP.tableToMarkdown", return_value="Generated table")

    # Execute the function
    result = gcp_compute_network_get_command(mock_creds, args)

    # Verify table generation handles None values
    mock_table.assert_called_once()
    table_data = mock_table.call_args[0][1]

    assert table_data["name"] == "network-with-nulls"
    assert table_data["id"] is None

    # Verify removeNull parameter is set to handle None values
    assert mock_table.call_args[1]["removeNull"] is True

    assert result.readable_output == "Generated table"
    assert result.outputs == mock_response


def test_gcp_compute_network_get_command_complete_response(mocker):
    """
    Given: API response with all possible fields populated
    When: gcp_compute_network_get_command is called
    Then: The function should handle complete response correctly
    """
    from GCP import gcp_compute_network_get_command

    # Mock arguments
    args = {"project_id": "comprehensive-project", "network": "full-feature-network"}

    # Mock API response with all fields
    mock_response = {
        "id": "1111222233334444555",
        "name": "full-feature-network",
        "kind": "compute#network",
        "description": "Network with all features enabled",
        "selfLink": "https://www.googleapis.com/compute/v1/projects/comprehensive-project/global/networks/full-feature-network",
        "autoCreateSubnetworks": False,
        "creationTimestamp": "2023-05-01T15:30:00.000-07:00",
        "routingConfig": {"routingMode": "GLOBAL"},
        "subnetworks": [
            "https://www.googleapis.com/compute/v1/projects/comprehensive-project/regions/us-central1/subnetworks/subnet1"
        ],
        "IPv4Range": "10.0.0.0/8",
        "gatewayIPv4": "10.0.0.1",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_networks = mocker.Mock()
    mock_compute.networks.return_value = mock_networks
    mock_networks.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_network_get_command(mock_creds, args)

    # Verify complete response is returned
    assert result.outputs == mock_response
    assert result.outputs["autoCreateSubnetworks"] is False
    assert result.outputs["routingConfig"]["routingMode"] == "GLOBAL"
    assert result.outputs["IPv4Range"] == "10.0.0.0/8"
    assert result.outputs["gatewayIPv4"] == "10.0.0.1"


def test_gcp_compute_network_get_command_different_project_networks(mocker):
    """
    Given: Different project and network combinations
    When: gcp_compute_network_get_command is called with various project/network pairs
    Then: The function should handle different combinations correctly
    """
    from GCP import gcp_compute_network_get_command

    test_cases = [
        ("project-alpha", "vpc-alpha"),
        ("project-beta", "shared-vpc"),
        ("project-gamma", "legacy-network"),
    ]

    for project_id, network_name in test_cases:
        # Mock arguments
        args = {"project_id": project_id, "network": network_name}

        # Mock API response
        mock_response = {
            "id": f"{hash(network_name) % 10000000000000000000}",
            "name": network_name,
            "kind": "compute#network",
            "description": f"Network {network_name} in project {project_id}",
        }

        # Mock the GCP API calls
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_compute.networks.return_value = mock_networks
        mock_networks.get.return_value.execute.return_value = mock_response

        # Mock the build function
        mock_creds = mocker.Mock(spec=Credentials)
        mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

        # Execute the function
        result = gcp_compute_network_get_command(mock_creds, args)

        # Verify API call parameters
        mock_networks.get.assert_called_with(project=project_id, network=network_name)

        # Verify outputs
        assert result.outputs["name"] == network_name
        assert network_name in result.outputs["description"]


def test_gcp_compute_network_get_command_special_network_names(mocker):
    """
    Given: Network names with special characters or patterns
    When: gcp_compute_network_get_command is called
    Then: The function should handle special network names correctly
    """
    from GCP import gcp_compute_network_get_command

    # Mock arguments with special network name
    args = {"project_id": "test-project-special", "network": "vpc-with-hyphens-123"}

    # Mock API response
    mock_response = {
        "id": "7777888899990000111",
        "name": "vpc-with-hyphens-123",
        "kind": "compute#network",
        "description": "Network with hyphens and numbers",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_networks = mocker.Mock()
    mock_compute.networks.return_value = mock_networks
    mock_networks.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)
    mock_table = mocker.patch("GCP.tableToMarkdown", return_value="Special network table")

    # Execute the function
    result = gcp_compute_network_get_command(mock_creds, args)

    # Verify network name is handled correctly in table title
    mock_table.assert_called_once()
    table_title = mock_table.call_args[0][0]
    assert table_title == "GCP network vpc-with-hyphens-123"

    assert result.outputs["name"] == "vpc-with-hyphens-123"


def test_gcp_compute_network_get_command_data_res_structure(mocker):
    """
    Given: A network API response with various fields
    When: gcp_compute_network_get_command is called
    Then: The data_res structure should only contain name and id fields
    """
    from GCP import gcp_compute_network_get_command

    # Mock arguments
    args = {"project_id": "test-project", "network": "filtered-network"}

    # Mock API response with extra fields that shouldn't be in data_res
    mock_response = {
        "id": "8888999900001111222",
        "name": "filtered-network",
        "kind": "Should not be in data_res",
        "description": "Should not be in data_res",
        "selfLink": "Should not be in data_res",
        "autoCreateSubnetworks": "Should not be in data_res",
        "extraField": "Should not be in data_res",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_networks = mocker.Mock()
    mock_compute.networks.return_value = mock_networks
    mock_networks.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)
    mock_table = mocker.patch("GCP.tableToMarkdown", return_value="Filtered data table")

    # Execute the function
    gcp_compute_network_get_command(mock_creds, args)

    # Verify tableToMarkdown was called with filtered data_res
    mock_table.assert_called_once()


# gcp_compute_image_get
def test_gcp_compute_image_get_basic_success(mocker):
    """
    Given: Valid credentials and basic arguments for getting an image
    When: gcp_compute_image_get is called
    Then: The function should return image details with correct outputs
    """
    from GCP import gcp_compute_image_get

    # Mock arguments
    args = {"project_id": "test-project", "image": "ubuntu-2004-focal-v20230724"}

    # Mock API response
    mock_response = {
        "id": "1234567890123456789",
        "name": "ubuntu-2004-focal-v20230724",
        "kind": "compute#image",
        "description": "Canonical, Ubuntu, 20.04 LTS, amd64 focal image built on 2023-07-24",
        "selfLink": "https://www.googleapis.com/compute/v1/projects/test-project/global/images/ubuntu-2004-focal-v20230724",
        "family": "ubuntu-2004-lts",
        "status": "READY",
        "creationTimestamp": "2023-07-24T10:00:00.000-07:00",
        "diskSizeGb": "10",
        "archiveSizeBytes": "1073741824",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_images = mocker.Mock()
    mock_compute.images.return_value = mock_images
    mock_images.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_image_get(mock_creds, args)

    # Verify API call parameters
    mock_images.get.assert_called_once_with(project="test-project", image="ubuntu-2004-focal-v20230724")

    # Verify outputs structure
    assert result.outputs_prefix == "GCP.Compute.Images"
    assert result.outputs_key_field == "id"
    assert result.outputs == mock_response


def test_gcp_compute_image_get_minimal_response(mocker):
    """
    Given: API response with minimal fields (some optional fields missing)
    When: gcp_compute_image_get is called
    Then: The function should handle missing optional fields gracefully
    """
    from GCP import gcp_compute_image_get

    # Mock arguments
    args = {"project_id": "test-project", "image": "minimal-image"}

    # Mock API response with minimal fields
    mock_response = {
        "id": "9876543210987654321",
        "name": "minimal-image",
        "kind": "compute#image",
        # Missing: description, selfLink, family, status, creationTimestamp, diskSizeGb
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_images = mocker.Mock()
    mock_compute.images.return_value = mock_images
    mock_images.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_image_get(mock_creds, args)

    # Verify API call
    mock_images.get.assert_called_once_with(project="test-project", image="minimal-image")

    # Verify outputs handle missing fields
    assert result.outputs == mock_response
    assert result.outputs["id"] == "9876543210987654321"
    assert result.outputs["name"] == "minimal-image"
    assert "description" not in result.outputs
    assert "selfLink" not in result.outputs


def test_gcp_compute_image_get_table_generation(mocker):
    """
    Given: Valid image data
    When: gcp_compute_image_get is called
    Then: The function should generate readable output table with correct headers and data
    """
    from GCP import gcp_compute_image_get

    # Mock arguments
    args = {"project_id": "test-project", "image": "centos-7-v20230724"}

    # Mock API response
    mock_response = {
        "id": "5555666677778888999",
        "name": "centos-7-v20230724",
        "kind": "compute#image",
        "description": "CentOS 7 image",
        "family": "centos-7",
        "status": "READY",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_images = mocker.Mock()
    mock_compute.images.return_value = mock_images
    mock_images.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)
    mock_table = mocker.patch("GCP.tableToMarkdown", return_value="Generated table output")

    # Execute the function
    result = gcp_compute_image_get(mock_creds, args)

    # Verify tableToMarkdown was called with correct parameters
    mock_table.assert_called_once()
    table_call_args = mock_table.call_args

    # Check table title
    assert table_call_args[0][0] == "GCP image centos-7-v20230724"

    # Check table data
    table_data = table_call_args[0][1]
    assert table_data["name"] == "centos-7-v20230724"
    assert table_data["id"] == "5555666677778888999"

    # Check headers
    expected_headers = ["id", "name", "creationTimestamp", "description"]
    assert table_call_args[1]["headers"] == expected_headers

    # Check other parameters
    assert table_call_args[1]["removeNull"] is True

    # Verify readable output uses table result
    assert result.readable_output == "Generated table output"


def test_gcp_compute_image_get_none_values_handling(mocker):
    """
    Given: API response with None values for some fields
    When: gcp_compute_image_get is called
    Then: The function should handle None values correctly in data_res
    """
    from GCP import gcp_compute_image_get

    # Mock arguments
    args = {"project_id": "test-project", "image": "image-with-nulls"}

    # Mock API response with None values
    mock_response = {
        "id": None,
        "name": "image-with-nulls",
        "kind": "compute#image",
        "description": None,
        "status": "READY",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_images = mocker.Mock()
    mock_compute.images.return_value = mock_images
    mock_images.get.return_value.execute.return_value = mock_response

    # Mock the build function and table generation
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)
    mock_table = mocker.patch("GCP.tableToMarkdown", return_value="Generated table")

    # Execute the function
    result = gcp_compute_image_get(mock_creds, args)

    # Verify table generation handles None values
    mock_table.assert_called_once()
    table_data = mock_table.call_args[0][1]

    assert table_data["name"] == "image-with-nulls"
    assert table_data["id"] is None

    # Verify removeNull parameter is set to handle None values
    assert mock_table.call_args[1]["removeNull"] is True

    assert result.readable_output == "Generated table"
    assert result.outputs == mock_response


def test_gcp_compute_image_get_complete_response(mocker):
    """
    Given: API response with all possible fields populated
    When: gcp_compute_image_get is called
    Then: The function should handle complete response correctly
    """
    from GCP import gcp_compute_image_get

    # Mock arguments
    args = {"project_id": "comprehensive-project", "image": "full-feature-image"}

    # Mock API response with all fields
    mock_response = {
        "id": "1111222233334444555",
        "name": "full-feature-image",
        "kind": "compute#image",
        "description": "Comprehensive image with all features",
        "selfLink": "https://www.googleapis.com/compute/v1/projects/comprehensive-project/global/images/full-feature-image",
        "family": "custom-family",
        "status": "READY",
        "creationTimestamp": "2023-08-01T12:00:00.000-07:00",
        "diskSizeGb": "20",
        "archiveSizeBytes": "2147483648",
        "licenses": ["https://www.googleapis.com/compute/v1/projects/vm-options/global/licenses/enable-vmx"],
        "sourceType": "RAW",
        "deprecated": {"state": "ACTIVE"},
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_images = mocker.Mock()
    mock_compute.images.return_value = mock_images
    mock_images.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

    # Execute the function
    result = gcp_compute_image_get(mock_creds, args)

    # Verify complete response is returned
    assert result.outputs == mock_response
    assert result.outputs["family"] == "custom-family"
    assert result.outputs["diskSizeGb"] == "20"
    assert result.outputs["archiveSizeBytes"] == "2147483648"
    assert result.outputs["sourceType"] == "RAW"
    assert result.outputs["deprecated"]["state"] == "ACTIVE"


def test_gcp_compute_image_get_different_project_images(mocker):
    """
    Given: Different project and image combinations
    When: gcp_compute_image_get is called with various project/image pairs
    Then: The function should handle different combinations correctly
    """
    from GCP import gcp_compute_image_get

    test_cases = [
        ("project-alpha", "debian-11-bullseye-v20230724"),
        ("project-beta", "windows-server-2019-dc-v20230724"),
        ("project-gamma", "ubuntu-minimal-2204-jammy-v20230724"),
    ]

    for project_id, image_name in test_cases:
        # Mock arguments
        args = {"project_id": project_id, "image": image_name}

        # Mock API response
        mock_response = {
            "id": f"{hash(image_name) % 10000000000000000000}",
            "name": image_name,
            "kind": "compute#image",
            "description": f"Image {image_name} in project {project_id}",
        }

        # Mock the GCP API calls
        mock_compute = mocker.Mock()
        mock_images = mocker.Mock()
        mock_compute.images.return_value = mock_images
        mock_images.get.return_value.execute.return_value = mock_response

        # Mock the build function
        mock_creds = mocker.Mock(spec=Credentials)
        mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)

        # Execute the function
        result = gcp_compute_image_get(mock_creds, args)

        # Verify API call parameters
        mock_images.get.assert_called_with(project=project_id, image=image_name)

        # Verify outputs
        assert result.outputs["name"] == image_name
        assert image_name in result.outputs["description"]


def test_gcp_compute_image_get_special_image_names(mocker):
    """
    Given: Image names with special characters or patterns
    When: gcp_compute_image_get is called
    Then: The function should handle special image names correctly
    """
    from GCP import gcp_compute_image_get

    # Mock arguments with special image name
    args = {"project_id": "test-project-special", "image": "custom-image-with-hyphens-v1-2-3"}

    # Mock API response
    mock_response = {
        "id": "7777888899990000111",
        "name": "custom-image-with-hyphens-v1-2-3",
        "kind": "compute#image",
        "description": "Custom image with hyphens and version numbers",
    }

    # Mock the GCP API calls
    mock_compute = mocker.Mock()
    mock_images = mocker.Mock()
    mock_compute.images.return_value = mock_images
    mock_images.get.return_value.execute.return_value = mock_response

    # Mock the build function
    mock_creds = mocker.Mock(spec=Credentials)
    mocker.patch("GCP.GCPServices.COMPUTE.build", return_value=mock_compute)
    mock_table = mocker.patch("GCP.tableToMarkdown", return_value="Special image table")

    # Execute the function
    result = gcp_compute_image_get(mock_creds, args)

    # Verify image name is handled correctly in table title
    mock_table.assert_called_once()
    table_title = mock_table.call_args[0][0]
    assert table_title == "GCP image custom-image-with-hyphens-v1-2-3"

    assert result.outputs["name"] == "custom-image-with-hyphens-v1-2-3"


# gcp_compute_region_get
def test_gcp_compute_region_get_success(mocker):
    """
    Given: Valid credentials and region retrieval arguments
    When: gcp_compute_region_get is called with proper project_id and region
    Then: The function should return correct CommandResults with region data and verify API calls
    """
    from GCP import gcp_compute_region_get
    from GCP import GCPServices

    mock_creds = mocker.Mock()
    mock_compute = mocker.Mock()
    mock_regions = mocker.Mock()
    mock_get = mocker.Mock()

    mock_response = {
        "id": "12345",
        "name": "us-central1",
        "status": "UP",
        "description": "Test region",
        "zones": ["us-central1-a", "us-central1-b"],
    }

    mock_get.execute.return_value = mock_response
    mock_regions.get.return_value = mock_get
    mock_compute.regions.return_value = mock_regions

    mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

    args = {"project_id": "test-project", "region": "us-central1"}

    result = gcp_compute_region_get(mock_creds, args)

    assert result.outputs_prefix == "GCP.Compute.Regions"
    assert result.outputs_key_field == "id"
    assert result.outputs == mock_response
    assert "us-central1" in result.readable_output
    assert "12345" in result.readable_output

    mock_regions.get.assert_called_once_with(project="test-project", region="us-central1")


def test_gcp_compute_region_get_minimal_response(mocker):
    """
    Given: A mocked environment where the Google Compute API is set to return
           a minimal, successful response for a specific region.
    When: gcp_compute_region_get is called with valid project ID and region name ("europe-west1").
    Then: The result's structured output must match the mocked API response,
          and the readable output must contain both the region name and its ID.
    """
    from GCP import GCPServices
    from GCP import gcp_compute_region_get

    mock_creds = mocker.Mock()
    mock_compute = mocker.Mock()
    mock_regions = mocker.Mock()
    mock_get = mocker.Mock()

    mock_response = {"id": "67890", "name": "europe-west1"}

    mock_get.execute.return_value = mock_response
    mock_regions.get.return_value = mock_get
    mock_compute.regions.return_value = mock_regions

    mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

    args = {"project_id": "minimal-project", "region": "europe-west1"}

    result = gcp_compute_region_get(mock_creds, args)

    assert result.outputs == mock_response
    assert "europe-west1" in result.readable_output
    assert "67890" in result.readable_output


def test_gcp_compute_region_get_empty_args(mocker):
    """
    Given: A mocked API environment configured to return a response with a status of "DOWN".
    When: gcp_compute_region_get is called with explicitly set empty arguments (project_id=None, region=None).
    Then: The function should call the API's get method with None values for project and region,
          and return the mocked response data.
    """
    from GCP import GCPServices
    from GCP import gcp_compute_region_get

    mock_creds = mocker.Mock()
    mock_compute = mocker.Mock()
    mock_regions = mocker.Mock()
    mock_get = mocker.Mock()

    mock_response = {"id": "11111", "name": None, "status": "DOWN"}

    mock_get.execute.return_value = mock_response
    mock_regions.get.return_value = mock_get
    mock_compute.regions.return_value = mock_regions

    mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

    args = {"project_id": None, "region": None}

    result = gcp_compute_region_get(mock_creds, args)

    mock_regions.get.assert_called_once_with(project=None, region=None)
    assert result.outputs == mock_response


def test_gcp_compute_region_get_api_exception(mocker):
    """
    Given: A mocked API environment configured to raise a generic Exception (API Error).
    When: gcp_compute_region_get is called with arguments that would trigger the API call.
    Then: The function call should raise the expected Exception with the matching error message ("API Error").
    """
    from GCP import GCPServices
    from GCP import gcp_compute_region_get

    mock_creds = mocker.Mock()
    mock_compute = mocker.Mock()
    mock_regions = mocker.Mock()
    mock_get = mocker.Mock()

    mock_get.execute.side_effect = Exception("API Error")
    mock_regions.get.return_value = mock_get
    mock_compute.regions.return_value = mock_regions

    mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

    args = {"project_id": "error-project", "region": "invalid-region"}

    with pytest.raises(Exception, match="API Error"):
        gcp_compute_region_get(mock_creds, args)


def test_gcp_compute_region_get_all_status_values(mocker):
    """
    Given: A mocked API environment where the region API call is configured to return a region with a "DOWN" status.
    When: gcp_compute_region_get is called for the region "asia-east1".
    Then: The result's structured output must correctly show the "DOWN" status, and the readable output must also reflect the
    "DOWN" status.
    """
    from GCP import GCPServices
    from GCP import gcp_compute_region_get

    mock_creds = mocker.Mock()
    mock_compute = mocker.Mock()
    mock_regions = mocker.Mock()
    mock_get = mocker.Mock()

    mock_response = {"id": "22222", "name": "asia-east1", "status": "DOWN"}

    mock_get.execute.return_value = mock_response
    mock_regions.get.return_value = mock_get
    mock_compute.regions.return_value = mock_regions

    mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

    args = {"project_id": "status-project", "region": "asia-east1"}

    result = gcp_compute_region_get(mock_creds, args)

    assert result.outputs["status"] == "DOWN"
    assert "DOWN" in result.readable_output


def test_gcp_compute_region_get_missing_keys_args(mocker):
    """
    Given: A mocked API environment where no project_id or region arguments are provided in the args dictionary.
    When: gcp_compute_region_get is called with empty arguments.
    Then: The function should handle missing keys gracefully, call the API with None values, and return the mock response.
    """
    from GCP import GCPServices
    from GCP import gcp_compute_region_get

    mock_creds = mocker.Mock()
    mock_compute = mocker.Mock()
    mock_regions = mocker.Mock()
    mock_get = mocker.Mock()

    mock_response = {"id": "33333", "name": "us-west1", "status": "UP"}

    mock_get.execute.return_value = mock_response
    mock_regions.get.return_value = mock_get
    mock_compute.regions.return_value = mock_regions

    mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

    args = {}

    result = gcp_compute_region_get(mock_creds, args)

    mock_regions.get.assert_called_once_with(project=None, region=None)
    assert result.outputs == mock_response


# gcp_compute_instance_group_get
class TestGCPComputeInstanceGroupGet:
    def test_gcp_compute_instance_group_get_success(self, mocker):
        """
        Given: A mocked GCP Compute API environment with instance group data.
        When: gcp_compute_instance_group_get is called with valid project_id, instance_group, and zone arguments.
        Then: The function should successfully retrieve the instance group, return the correct outputs and readable output,
        and call the API with the expected parameters.
        """
        from GCP import GCPServices
        from GCP import gcp_compute_instance_group_get

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_instance_groups = mocker.Mock()
        mock_get = mocker.Mock()

        mock_response = {
            "id": "123456789",
            "name": "test-instance-group",
            "zone": "https://www.googleapis.com/compute/v1/projects/test-project/zones/us-central1-a",
            "network": "https://www.googleapis.com/compute/v1/projects/test-project/global/networks/default",
        }

        mock_get.execute.return_value = mock_response
        mock_instance_groups.get.return_value = mock_get
        mock_compute.instanceGroups.return_value = mock_instance_groups

        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)
        mocker.patch("GCP.tableToMarkdown", return_value="Mocked table output")

        args = {"project_id": "test-project", "instance_group": "test-instance-group", "zone": "us-central1-a"}

        result = gcp_compute_instance_group_get(mock_creds, args)

        assert result.outputs_prefix == "GCP.Compute.InstanceGroups"
        assert result.outputs_key_field == "id"
        assert result.outputs == mock_response
        assert result.readable_output == "Mocked table output"

        mock_instance_groups.get.assert_called_once_with(
            project="test-project", zone="us-central1-a", instanceGroup="test-instance-group"
        )

    def test_gcp_compute_instance_group_get_minimal_response(self, mocker):
        """
        Given: A mocked API environment configured to return a minimal Instance Group response.
        When: gcp_compute_instance_group_get is called with a project, instanceGroup name, and zone.
        Then: The function should return the correct minimal Instance Group data in the structured output and
        the expected readable output.
        """
        from GCP import GCPServices
        from GCP import gcp_compute_instance_group_get

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_instance_groups = mocker.Mock()
        mock_get = mocker.Mock()

        mock_response = {"id": "987654321", "name": "minimal-group"}

        mock_get.execute.return_value = mock_response
        mock_instance_groups.get.return_value = mock_get
        mock_compute.instanceGroups.return_value = mock_instance_groups

        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)
        mocker.patch("GCP.tableToMarkdown", return_value="Minimal table output")

        args = {"project_id": "test-project", "instanceGroup": "minimal-group", "zone": "us-west1-b"}

        result = gcp_compute_instance_group_get(mock_creds, args)

        assert result.outputs == mock_response
        assert result.readable_output == "Minimal table output"

    def test_gcp_compute_instance_group_get_empty_response(self, mocker):
        """
        Given: A mocked API environment configured to return an empty Instance Group response.
        When: gcp_compute_instance_group_get is called with a project, instanceGroup name, and zone.
        Then: The function should handle the empty response gracefully and return empty outputs with the expected
        readable output.
        """
        from GCP import GCPServices
        from GCP import gcp_compute_instance_group_get

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_instance_groups = mocker.Mock()
        mock_get = mocker.Mock()

        mock_response = {}

        mock_get.execute.return_value = mock_response
        mock_instance_groups.get.return_value = mock_get
        mock_compute.instanceGroups.return_value = mock_instance_groups

        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)
        mocker.patch("GCP.tableToMarkdown", return_value="Empty table output")

        args = {"project_id": "empty-project", "instanceGroup": "empty-group", "zone": "us-east1-a"}

        result = gcp_compute_instance_group_get(mock_creds, args)

        assert result.outputs == {}
        assert result.readable_output == "Empty table output"

    def test_gcp_compute_instance_group_get_missing_args(self, mocker):
        """
        Given: A mocked API environment and missing required parameters (None values for project_id,
        instanceGroup, and zone).
        When: gcp_compute_instance_group_get is called with None values for required arguments.
        Then: The function should raise an exception indicating missing required parameters.
        """
        from GCP import GCPServices
        from GCP import gcp_compute_instance_group_get

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_instance_groups = mocker.Mock()
        mock_get = mocker.Mock()

        mock_get.execute.side_effect = Exception("Missing required parameters")
        mock_instance_groups.get.return_value = mock_get
        mock_compute.instanceGroups.return_value = mock_instance_groups

        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"project_id": None, "instanceGroup": None, "zone": None}

        with pytest.raises(Exception, match="Missing required parameters"):
            gcp_compute_instance_group_get(mock_creds, args)

    def test_gcp_compute_instance_group_get_api_error(self, mocker):
        """
        Given: A mocked API environment where the API call fails with an exception.
        When: gcp_compute_instance_group_get is called with valid arguments but the API returns an error.
        Then: The function should raise an exception with the appropriate error message.
        """
        from GCP import GCPServices
        from GCP import gcp_compute_instance_group_get

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_instance_groups = mocker.Mock()
        mock_get = mocker.Mock()

        mock_get.execute.side_effect = Exception("Instance group not found")
        mock_instance_groups.get.return_value = mock_get
        mock_compute.instanceGroups.return_value = mock_instance_groups

        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"project_id": "test-project", "instanceGroup": "nonexistent-group", "zone": "us-central1-a"}

        with pytest.raises(Exception, match="Instance group not found"):
            gcp_compute_instance_group_get(mock_creds, args)


# gcp_compute_network_insert
class TestGCPComputeNetworkInsert:
    def test_gcp_compute_network_insert_minimal_required_args(self, mocker):
        """
        Given: A mocked GCP Compute service environment with minimal required arguments.
        When: gcp_compute_network_insert is called with only name and project_id.
        Then: The function should create a network with default autoCreateSubnetworks=True and return operation details.
        """
        from GCP import GCPServices
        from GCP import gcp_compute_network_insert

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_insert = mocker.Mock()

        mock_compute.networks.return_value = mock_networks
        mock_networks.insert.return_value = mock_insert
        mock_insert.execute.return_value = {
            "status": "PENDING",
            "kind": "compute#operation",
            "name": "operation-123",
            "id": "123456789",
            "progress": 0,
            "operationType": "insert",
        }

        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"name": "test-network", "project_id": "test-project"}

        result = gcp_compute_network_insert(mock_creds, args)

        expected_config = {"name": "test-network", "autoCreateSubnetworks": True}

        mock_networks.insert.assert_called_once_with(project="test-project", body=expected_config)
        assert result.outputs_prefix == "GCP.Compute.Operations"
        assert result.outputs_key_field == "id"

    def test_gcp_compute_network_insert_missing_name_raises_error(self, mocker):
        """
        Given: A mocked GCP Compute service environment with missing name argument.
        When: gcp_compute_network_insert is called without a name parameter.
        Then: The function should raise a ValueError indicating that the 'name' argument is required.
        """
        from GCP import gcp_compute_network_insert

        mock_creds = mocker.Mock()
        args = {"project_id": "test-project"}

        with pytest.raises(ValueError, match="The 'name' argument is required to create a network."):
            gcp_compute_network_insert(mock_creds, args)

    def test_gcp_compute_network_insert_empty_name_raises_error(self, mocker):
        """
        Given: A mocked GCP Compute service environment with an empty name argument.
        When: gcp_compute_network_insert is called with an empty string for the name parameter.
        Then: The function should raise a ValueError indicating that the 'name' argument is required.
        """
        from GCP import gcp_compute_network_insert

        mock_creds = mocker.Mock()
        args = {"name": "", "project_id": "test-project"}

        with pytest.raises(ValueError, match="The 'name' argument is required to create a network."):
            gcp_compute_network_insert(mock_creds, args)

    def test_gcp_compute_network_insert_with_description(self, mocker):
        """
        Given: A mocked GCP Compute service environment with name and description arguments.
        When: gcp_compute_network_insert is called with name and description parameters.
        Then: The function should create a network with the provided name and description, and verify the correct API
        call.
        """
        from GCP import GCPServices
        from GCP import gcp_compute_network_insert

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_insert = mocker.Mock()

        mock_compute.networks.return_value = mock_networks
        mock_networks.insert.return_value = mock_insert
        mock_insert.execute.return_value = {
            "status": "DONE",
            "kind": "compute#operation",
            "name": "operation-456",
            "id": "987654321",
        }

        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"name": "Network-With-Desc", "description": "Test network description", "project_id": "test-project"}

        gcp_compute_network_insert(mock_creds, args)

        expected_config = {"name": "Network-With-Desc", "description": "Test network description", "autoCreateSubnetworks": True}

        mock_networks.insert.assert_called_once_with(project="test-project", body=expected_config)

    def test_gcp_compute_network_insert_auto_create_subnets_false_string(self, mocker):
        """
        Given: A mocked GCP Compute service environment with a string 'false' value for auto_create_sub_networks.
        When: gcp_compute_network_insert is called with auto_create_sub_networks set to the string "false".
        Then: The function should create a network with auto_create_sub_networks converted to boolean False, and
        verify the correct API call.
        """
        from GCP import GCPServices
        from GCP import gcp_compute_network_insert

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_insert = mocker.Mock()

        mock_compute.networks.return_value = mock_networks
        mock_networks.insert.return_value = mock_insert
        mock_insert.execute.return_value = {"status": "PENDING"}

        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"name": "test-network", "auto_create_sub_networks": "false", "project_id": "test-project"}

        gcp_compute_network_insert(mock_creds, args)

        expected_config = {"name": "test-network", "autoCreateSubnetworks": False}

        mock_networks.insert.assert_called_once_with(project="test-project", body=expected_config)

    def test_gcp_compute_network_insert_auto_create_subnets_true_string(self, mocker):
        """
        Given: A mocked GCP Compute service environment with a string 'TRUE' value for autoCreateSubnetworks.
        When: gcp_compute_network_insert is called with autoCreateSubnetworks set to the string "TRUE".
        Then: The function should create a network with autoCreateSubnetworks converted to boolean True, and verify the
        correct API call.
        """
        from GCP import GCPServices
        from GCP import gcp_compute_network_insert

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_insert = mocker.Mock()

        mock_compute.networks.return_value = mock_networks
        mock_networks.insert.return_value = mock_insert
        mock_insert.execute.return_value = {"status": "PENDING"}

        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"name": "test-network", "autoCreateSubnetworks": "TRUE", "project_id": "test-project"}

        gcp_compute_network_insert(mock_creds, args)

        expected_config = {"name": "test-network", "autoCreateSubnetworks": True}

        mock_networks.insert.assert_called_once_with(project="test-project", body=expected_config)

    def test_gcp_compute_network_insert_auto_create_subnets_boolean_false(self, mocker):
        """
        Given: A mocked GCP Compute service environment with a boolean False value for auto_create_sub_networks.
        When: gcp_compute_network_insert is called with auto_create_sub_networks set to boolean False.
        Then: The function should create a network with auto_create_sub_networks as False, and verify the correct API call.
        """
        from GCP import GCPServices
        from GCP import gcp_compute_network_insert

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_insert = mocker.Mock()

        mock_compute.networks.return_value = mock_networks
        mock_networks.insert.return_value = mock_insert
        mock_insert.execute.return_value = {"status": "PENDING"}

        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"name": "test-network", "auto_create_sub_networks": False, "project_id": "test-project"}

        gcp_compute_network_insert(mock_creds, args)

        expected_config = {"name": "test-network", "autoCreateSubnetworks": False}

        mock_networks.insert.assert_called_once_with(project="test-project", body=expected_config)

    def test_gcp_compute_network_insert_routing_config_regional(self, mocker):
        """
        Given: A mocked GCP Compute service environment with routing configuration set to REGIONAL mode.
        When: gcp_compute_network_insert is called with routing_config_routing_mode set to "REGIONAL".
        Then: The function should create a network with REGIONAL routing mode, and verify the correct API call.
        """
        from GCP import GCPServices
        from GCP import gcp_compute_network_insert

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_insert = mocker.Mock()

        mock_compute.networks.return_value = mock_networks
        mock_networks.insert.return_value = mock_insert
        mock_insert.execute.return_value = {"status": "PENDING"}

        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"name": "test-network", "routing_config_routing_mode": "REGIONAL", "project_id": "test-project"}

        gcp_compute_network_insert(mock_creds, args)

        expected_config = {"name": "test-network", "autoCreateSubnetworks": True, "routingConfig": {"routingMode": "REGIONAL"}}

        mock_networks.insert.assert_called_once_with(project="test-project", body=expected_config)

    def test_gcp_compute_network_insert_routing_config_global(self, mocker):
        """
        Given: A mocked GCP Compute service environment with routing mode set to GLOBAL.
        When: gcp_compute_network_insert is called with routing_config_routing_mode set to "GLOBAL".
        Then: The function should create a network with GLOBAL routing mode configuration, and verify the correct API call.
        """
        from GCP import GCPServices
        from GCP import gcp_compute_network_insert

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_insert = mocker.Mock()

        mock_compute.networks.return_value = mock_networks
        mock_networks.insert.return_value = mock_insert
        mock_insert.execute.return_value = {"status": "PENDING"}

        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"name": "test-network", "routing_config_routing_mode": "GLOBAL", "project_id": "test-project"}

        gcp_compute_network_insert(mock_creds, args)

        expected_config = {"name": "test-network", "autoCreateSubnetworks": True, "routingConfig": {"routingMode": "GLOBAL"}}

        mock_networks.insert.assert_called_once_with(project="test-project", body=expected_config)

    def test_gcp_compute_network_insert_all_options(self, mocker):
        """
        Given: A mocked GCP Compute service environment with all network configuration options specified.
        When: gcp_compute_network_insert is called with name, description, auto_create_sub_networks set to "false", and
        routing_config_routing_mode set to "GLOBAL".
        Then: The function should create a network with all specified configurations, verify the correct API call with
        proper parameter conversion, and return operation details including progress.
        """
        from GCP import GCPServices
        from GCP import gcp_compute_network_insert

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_insert = mocker.Mock()

        mock_compute.networks.return_value = mock_networks
        mock_networks.insert.return_value = mock_insert
        mock_insert.execute.return_value = {
            "status": "RUNNING",
            "kind": "compute#operation",
            "name": "operation-full",
            "id": "555666777",
            "progress": 50,
            "operationType": "insert",
        }

        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {
            "name": "FULL-Test-Network",
            "description": "Complete test network",
            "auto_create_sub_networks": "false",
            "routing_config_routing_mode": "GLOBAL",
            "project_id": "full-test-project",
        }

        result = gcp_compute_network_insert(mock_creds, args)

        expected_config = {
            "name": "FULL-Test-Network",
            "description": "Complete test network",
            "autoCreateSubnetworks": False,
            "routingConfig": {"routingMode": "GLOBAL"},
        }

        mock_networks.insert.assert_called_once_with(project="full-test-project", body=expected_config)
        assert "RUNNING" in result.readable_output
        assert result.outputs["progress"] == 50

    def test_gcp_compute_network_insert_response_with_missing_fields(self, mocker):
        """
        Given: A mocked GCP Compute service environment where the API response contains only some fields.
        When: gcp_compute_network_insert is called and the response has missing optional fields like 'kind'.
        Then: The function should handle the partial response gracefully, return available fields in outputs, and ensure
        missing fields are either absent or None without causing errors.
        """
        from GCP import gcp_compute_network_insert
        from GCP import GCPServices

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_insert = mocker.Mock()

        mock_compute.networks.return_value = mock_networks
        mock_networks.insert.return_value = mock_insert
        mock_insert.execute.return_value = {"status": "DONE", "id": "123"}

        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"name": "test-network", "project_id": "test-project"}

        result = gcp_compute_network_insert(mock_creds, args)

        assert result.outputs["status"] == "DONE"
        assert result.outputs["id"] == "123"
        assert "kind" not in result.outputs or result.outputs["kind"] is None


# gcp_compute_networks_list


class TestGCPComputeNetworksList:
    def test_gcp_compute_networks_list_default_limit(self, mocker):
        """
        Given: A mocked API environment configured to return a single network item.
        When: gcp_compute_networks_list is called with only the project_id argument.
        Then: The function must call the underlying API with the default limit (maxResults=50) and return a list containing the
        single network item.
        """
        from GCP import gcp_compute_networks_list
        from GCP import GCPServices

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_list = mocker.Mock()

        mock_response = {
            "items": [{"name": "default-network", "id": "12345", "creationTimestamp": "2023-01-01T00:00:00Z", "status": "READY"}]
        }

        mock_list.execute.return_value = mock_response
        mock_networks.list.return_value = mock_list
        mock_compute.networks.return_value = mock_networks
        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"project_id": "test-project"}
        result = gcp_compute_networks_list(mock_creds, args)

        mock_networks.list.assert_called_once_with(
            project="test-project", filter=None, maxResults=50, orderBy=None, pageToken=None
        )
        assert len(result.outputs["GCP.Compute.Networks(val.id && val.id == obj.id)"]) == 1

    def test_gcp_compute_networks_list_with_all_parameters(self, mocker):
        """
        Given: A mocked API environment configured to return a single network item and a next page token.
        When: gcp_compute_networks_list is called with custom values for limit, filters, order_by, and page_token.
        Then: The function must call the underlying API with all specified parameters and include the next page token and
        limit in the readable output.
        """
        from GCP import gcp_compute_networks_list
        from GCP import GCPServices

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_list = mocker.Mock()

        mock_response = {
            "items": [{"name": "custom-network", "id": "67890", "creationTimestamp": "2023-02-01T00:00:00Z", "status": "READY"}],
            "nextPageToken": "next-token-123",
        }

        mock_list.execute.return_value = mock_response
        mock_networks.list.return_value = mock_list
        mock_compute.networks.return_value = mock_networks
        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {
            "project_id": "test-project",
            "limit": "25",
            "filters": "name=custom*",
            "order_by": "name",
            "page_token": "prev-token",
        }
        result = gcp_compute_networks_list(mock_creds, args)

        mock_networks.list.assert_called_once_with(
            project="test-project", filter="name=custom*", maxResults=25, orderBy="name", pageToken="prev-token"
        )
        assert "next-token-123" in result.readable_output
        assert "limit=25" in result.readable_output

    def test_gcp_compute_networks_list_with_next_page_token(self, mocker):
        """
        Given: A mocked API environment configured to return an empty item list but containing a nextPageToken and a selfLink.
        When: gcp_compute_networks_list is called with only the project_id argument.
        Then: The function must correctly parse and store the nextPageToken and the selfLink in the CommandResults structured
        output under the global 'GCP.Compute(true)' context key.
        """
        from GCP import gcp_compute_networks_list
        from GCP import GCPServices

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_list = mocker.Mock()

        mock_response = {
            "items": [],
            "nextPageToken": "token-456",
            "selfLink": "https://compute.googleapis.com/compute/v1/projects/test-project/global/networks",
        }

        mock_list.execute.return_value = mock_response
        mock_networks.list.return_value = mock_list
        mock_compute.networks.return_value = mock_networks
        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"project_id": "test-project"}
        result = gcp_compute_networks_list(mock_creds, args)

        assert result.outputs["GCP.Compute(true)"]["NetworksNextPageToken"] == "token-456"
        assert (
            result.outputs["GCP.Compute(true)"]["NetworksSelfLink"]
            == "https://compute.googleapis.com/compute/v1/projects/test-project/global/networks"
        )

    def test_gcp_compute_networks_list_empty_response(self, mocker):
        """
        Given: A mocked API environment where the network list call is configured to return an empty response dictionary ({}).
        When: gcp_compute_networks_list is called with the project ID.
        Then: The structured output for networks must be an empty list, and the raw response size must be zero, confirming correct
        handling of an empty API response.
        """
        from GCP import gcp_compute_networks_list
        from GCP import GCPServices

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_list = mocker.Mock()

        mock_response = {}

        mock_list.execute.return_value = mock_response
        mock_networks.list.return_value = mock_list
        mock_compute.networks.return_value = mock_networks
        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"project_id": "test-project"}
        result = gcp_compute_networks_list(mock_creds, args)

        assert result.outputs["GCP.Compute.Networks(val.id && val.id == obj.id)"] == []
        assert len(result.raw_response) == 0

    def test_gcp_compute_networks_list_with_warning(self, mocker):
        """
        Given: A mocked API environment where the network list call is configured to return a response containing a 'warning'
        block.
        When: gcp_compute_networks_list is called with the project ID.
        Then: The function must correctly parse and store the warning block in the CommandResults structured output under the
        global 'GCP.Compute(true)' context key.
        """
        from GCP import gcp_compute_networks_list
        from GCP import GCPServices

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_list = mocker.Mock()

        mock_response = {"items": [], "warning": {"code": "DEPRECATED_RESOURCE_USED", "message": "Resource is deprecated"}}

        mock_list.execute.return_value = mock_response
        mock_networks.list.return_value = mock_list
        mock_compute.networks.return_value = mock_networks
        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"project_id": "test-project"}
        result = gcp_compute_networks_list(mock_creds, args)

        assert result.outputs["GCP.Compute(true)"]["outputsWarning"]["code"] == "DEPRECATED_RESOURCE_USED"

    def test_gcp_compute_networks_list_partial_network_data(self, mocker):
        """
        Given: A mocked API environment configured to return a list of networks, some of which contain only partial data (e.g.,
        missing 'name' or 'status' fields).
        When: gcp_compute_networks_list is called with the project ID.
        Then: The function must correctly process the list of networks, preserving the partial data structure and asserting that
        exactly two items are present in the structured output.
        """
        from GCP import gcp_compute_networks_list
        from GCP import GCPServices

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_list = mocker.Mock()

        mock_response = {"items": [{"name": "partial-network", "id": "11111"}, {"id": "22222", "status": "READY"}]}

        mock_list.execute.return_value = mock_response
        mock_networks.list.return_value = mock_list
        mock_compute.networks.return_value = mock_networks
        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"project_id": "test-project"}
        result = gcp_compute_networks_list(mock_creds, args)

        networks_data = result.outputs["GCP.Compute.Networks(val.id && val.id == obj.id)"]
        assert len(networks_data) == 2
        assert networks_data[0]["name"] == "partial-network"
        assert networks_data[0]["id"] == "11111"
        assert networks_data[1].get("name") is None
        assert networks_data[1]["id"] == "22222"

    def test_gcp_compute_networks_list_custom_limit_in_metadata(self, mocker):
        """
        Given: A mocked API environment configured with a next page token ("custom-token").
        When: gcp_compute_networks_list is called with a custom limit of "100" and a project ID.
        Then: The resulting readable output must correctly reflect the custom limit used and the presence of the next page token.
        """
        from GCP import gcp_compute_networks_list
        from GCP import GCPServices

        mock_creds = mocker.Mock()
        mock_compute = mocker.Mock()
        mock_networks = mocker.Mock()
        mock_list = mocker.Mock()

        mock_response = {"items": [], "nextPageToken": "custom-token"}

        mock_list.execute.return_value = mock_response
        mock_networks.list.return_value = mock_list
        mock_compute.networks.return_value = mock_networks
        mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_compute)

        args = {"project_id": "test-project", "limit": "100"}
        result = gcp_compute_networks_list(mock_creds, args)

        assert "limit=100" in result.readable_output
        assert "custom-token" in result.readable_output


def test_bq_dataset_policy_remove_command_remove_user(mocker):
    """
    Given:
        - Valid arguments to remove an existing user.
    When:
        - Calling bq_dataset_policy_remove_command.
    Then:
        - Ensure the patch API is called with the correct body excluding the removed user.
    """
    from GCP import bq_dataset_policy_remove_command

    creds = MagicMock()
    args = {"project_id": "test_project", "dataset_id": "test_dataset", "email": "test@test.com"}

    mock_bigquery = MagicMock()
    mock_datasets = MagicMock()
    mock_get = MagicMock()
    mock_get.execute.return_value = {
        "access": [{"role": "READER", "userByEmail": "test@test.com"}, {"role": "WRITER", "userByEmail": "other@test.com"}]
    }
    mock_datasets.get.return_value = mock_get

    mock_patch = MagicMock()
    mock_patch.execute.return_value = {
        "id": "test_dataset",
        "datasetReference": {},
        "access": [{"role": "WRITER", "userByEmail": "other@test.com"}],
    }
    mock_datasets.patch.return_value = mock_patch

    mock_bigquery.datasets.return_value = mock_datasets

    mocker.patch("GCP.GCPServices.BIGQUERY.build", return_value=mock_bigquery)

    bq_dataset_policy_remove_command(creds, args)

    mock_datasets.patch.assert_called_once_with(
        projectId="test_project", datasetId="test_dataset", body={"access": [{"role": "WRITER", "userByEmail": "other@test.com"}]}
    )


def test_bq_dataset_policy_remove_command_remove_group(mocker):
    """
    Given:
        - Valid arguments to remove an existing group.
    When:
        - Calling bq_dataset_policy_remove_command.
    Then:
        - Ensure the patch API is called with the correct body excluding the removed group.
    """
    from GCP import bq_dataset_policy_remove_command

    creds = MagicMock()
    args = {"project_id": "test_project", "dataset_id": "test_dataset", "email": "test@test.com"}

    mock_bigquery = MagicMock()
    mock_datasets = MagicMock()
    mock_get = MagicMock()
    mock_get.execute.return_value = {
        "access": [{"role": "READER", "groupByEmail": "test@test.com"}, {"role": "WRITER", "userByEmail": "other@test.com"}]
    }
    mock_datasets.get.return_value = mock_get

    mock_patch = MagicMock()
    mock_patch.execute.return_value = {
        "id": "test_dataset",
        "datasetReference": {},
        "access": [{"role": "WRITER", "userByEmail": "other@test.com"}],
    }
    mock_datasets.patch.return_value = mock_patch

    mock_bigquery.datasets.return_value = mock_datasets

    mocker.patch("GCP.GCPServices.BIGQUERY.build", return_value=mock_bigquery)

    bq_dataset_policy_remove_command(creds, args)

    mock_datasets.patch.assert_called_once_with(
        projectId="test_project", datasetId="test_dataset", body={"access": [{"role": "WRITER", "userByEmail": "other@test.com"}]}
    )


def test_bq_dataset_policy_remove_command_remove_user_not_found(mocker):
    """
    Given:
        - Arguments to remove a user that does not exist in the access list.
    When:
        - Calling bq_dataset_policy_remove_command.
    Then:
        - Ensure the patch API is not called and a 'No changes' message is returned.
    """
    from GCP import bq_dataset_policy_remove_command

    creds = MagicMock()
    email = "test@test.com"
    dataset_id = "test_dataset"
    args = {"project_id": "test_project", "dataset_id": dataset_id, "email": email}

    mock_bigquery = MagicMock()
    mock_datasets = MagicMock()
    mock_get = MagicMock()
    mock_get.execute.return_value = {"access": [{"role": "WRITER", "userByEmail": "other@test.com"}]}
    mock_datasets.get.return_value = mock_get

    mock_bigquery.datasets.return_value = mock_datasets

    mocker.patch("GCP.GCPServices.BIGQUERY.build", return_value=mock_bigquery)
    mock_debug = mocker.patch("demistomock.debug")

    result = bq_dataset_policy_remove_command(creds, args)

    assert result.readable_output == f"The provided email {email} wasn't found in access list of the dataset {dataset_id}."
    mock_datasets.patch.assert_not_called()
    mock_debug.assert_called_with(f"[GCP] Email {email} not found in access list for dataset {dataset_id}")


def test_get_credentials_marketplace_service_account(mocker):
    """
    Given:
        - Integration params with a valid service account JSON in credentials.password.
    When:
        - get_credentials is called (marketplace path).
    Then:
        - google_service_account.Credentials.from_service_account_info is called with the
          parsed JSON and the cloud-platform scope.
        - The returned credentials object is the mock service account credentials.
    """
    from GCP import get_credentials

    sa_info = {
        "type": "service_account",
        "project_id": "dummy-project-id",
        "private_key_id": "dummy-private-key-id",
        "private_key": "-----BEGIN RSA PRIVATE KEY-----\ndummy_private_key\n-----END RSA PRIVATE KEY-----\n",
        "client_email": "dummy-sa@dummy-project-id.iam.gserviceaccount.com",
        "client_id": "dummy-client-id",
        "auth_uri": "https://accounts.google.com/o/oauth2/auth",
        "token_uri": "https://oauth2.googleapis.com/token",
    }
    params = {"credentials": {"password": json.dumps(sa_info)}}
    args: dict = {}

    mock_creds = MagicMock()
    mock_from_sa = mocker.patch(
        "GCP.google_service_account.Credentials.from_service_account_info",
        return_value=mock_creds,
    )

    result = get_credentials(args, params)

    mock_from_sa.assert_called_once_with(
        sa_info,
        scopes=["https://www.googleapis.com/auth/cloud-platform"],
    )
    assert result is mock_creds


def test_get_credentials_marketplace_propagates_project_id(mocker):
    """
    Given:
        - Integration params with a service account JSON that contains project_id.
        - args dict does NOT contain project_id.
    When:
        - get_credentials is called.
    Then:
        - args['project_id'] is set to the project_id from the service account JSON.
    """
    from GCP import get_credentials

    sa_info = {
        "type": "service_account",
        "project_id": "dummy-sa-project-id",
        "private_key": "dummy_private_key",
        "client_email": "dummy-sa@dummy-sa-project-id.iam.gserviceaccount.com",
    }
    params = {"credentials": {"password": json.dumps(sa_info)}}
    args: dict = {}

    mocker.patch(
        "GCP.google_service_account.Credentials.from_service_account_info",
        return_value=MagicMock(),
    )

    get_credentials(args, params)

    assert args["project_id"] == "dummy-sa-project-id"


def test_get_credentials_marketplace_invalid_json_raises():
    """
    Given:
        - Integration params with an invalid (non-JSON) string in credentials.password.
    When:
        - get_credentials is called.
    Then:
        - DemistoException is raised with a message about invalid JSON format.
    """
    from GCP import get_credentials
    from CommonServerPython import DemistoException

    params = {"credentials": {"password": "not-valid-json"}}
    args: dict = {}

    with pytest.raises(DemistoException, match="Invalid Service Account JSON format"):
        get_credentials(args, params)


def test_get_credentials_marketplace_missing_credentials_raises(mocker):
    """
    Given:
        - Integration params with no credentials (empty password) and no project_id in args.
        - No connector ID in context (not Cortex Platform).
    When:
        - get_credentials is called.
    Then:
        - DemistoException is raised indicating project_id is missing.
    """
    from GCP import get_credentials
    from CommonServerPython import DemistoException

    params: dict = {}
    args: dict = {}

    # Simulate CTS call failing (no connector context)
    mocker.patch("GCP.get_cloud_credentials", side_effect=Exception("no connector"))

    with pytest.raises(DemistoException, match="Missing required parameter 'project_id'"):
        get_credentials(args, params)


def test_get_credentials_none_password_does_not_raise_attributeerror(mocker):
    """
    Given:
        - The 'credentials' param exists but its 'password' value is None.
        - project_id is provided in args (Cortex Platform path).
    When:
        - get_credentials is called.
    Then:
        - No AttributeError is raised when stripping the password (None is handled),
          and the CTS token path is used instead.
    """
    from GCP import get_credentials
    from google.oauth2.credentials import Credentials

    params: dict = {"credentials": {"password": None}}
    args = {"project_id": "dummy-project-id"}

    mocker.patch("GCP.get_cloud_credentials", return_value={"access_token": "dummy-access-token"})

    result = get_credentials(args, params)

    assert isinstance(result, Credentials)
    assert result.token == "dummy-access-token"


def test_get_credentials_cortex_cloud_token_path(mocker):
    """
    Given:
        - No service account JSON in params (Cortex Platform path).
        - project_id is provided in args.
        - get_cloud_credentials returns a valid access_token.
    When:
        - get_credentials is called.
    Then:
        - get_cloud_credentials is called with GCP cloud type and the project_id.
        - A Credentials object is returned with the token.
    """
    from GCP import get_credentials
    from google.oauth2.credentials import Credentials

    params: dict = {}
    args = {"project_id": "dummy-project-id"}

    mock_creds_data = {"access_token": "dummy-access-token"}
    mocker.patch("GCP.get_cloud_credentials", return_value=mock_creds_data)

    result = get_credentials(args, params)

    assert isinstance(result, Credentials)
    assert result.token == "dummy-access-token"


def test_test_module_marketplace_returns_ok(mocker):
    """
    Given:
        - Valid GCP credentials (marketplace service account).
        - params contains a valid project_id.
        - Resource Manager testIamPermissions call succeeds.
    When:
        - test_module is called.
    Then:
        - Returns "ok".
        - testIamPermissions is called with the correct project resource and permission.
    """
    from GCP import test_module
    from google.oauth2.credentials import Credentials

    creds = MagicMock(spec=Credentials)
    params = {"project_id": "dummy-project-id"}

    mock_rm = MagicMock()
    mock_rm.projects.return_value.testIamPermissions.return_value.execute.return_value = {
        "permissions": ["resourcemanager.projects.get"]
    }
    mocker.patch("GCP.GCPServices.RESOURCE_MANAGER.build", return_value=mock_rm)

    result = test_module(creds, params)

    assert result == "ok"
    mock_rm.projects.return_value.testIamPermissions.assert_called_once_with(
        resource="projects/dummy-project-id",
        body={"permissions": ["resourcemanager.projects.get"]},
    )


def _make_http_error(status: int, content: str):
    """Builds a googleapiclient HttpError with the given status and content for tests."""
    from googleapiclient.errors import HttpError

    resp = MagicMock()
    resp.status = status
    return HttpError(resp=resp, content=content.encode("utf-8"))


@pytest.mark.parametrize(
    "status, content, expected",
    [
        # 403 with the SERVICE_DISABLED reason -> disabled.
        (403, '{"error": {"status": "PERMISSION_DENIED", "message": "... SERVICE_DISABLED"}}', True),
        # 403 with the "has not been used in project" phrasing -> disabled.
        (403, "Compute Engine API has not been used in project 123 before or it is disabled.", True),
        # Matching is case-insensitive.
        (403, "SERVICE_DISABLED", True),
        # 403 but a genuine permission denial (no disabled marker) -> not disabled.
        (403, '{"error": {"message": "caller does not have permission"}}', False),
        # Right marker but wrong status -> not disabled.
        (404, "service_disabled", False),
        # Non-decodable / empty content is handled safely -> not disabled.
        ("", "", False),
    ],
)
def test_is_service_disabled_error(status, content, expected):
    """
    Given:
        - HttpErrors with various status codes and response bodies (including a non-int
          status that makes the status comparison/content handling fall through safely).
    When:
        - _is_service_disabled_error is called.
    Then:
        - Returns True only for a 403 whose content indicates the API is disabled
          (SERVICE_DISABLED / "has not been used in project"), case-insensitively; False otherwise.
    """
    from GCP import _is_service_disabled_error

    assert _is_service_disabled_error(_make_http_error(status, content)) is expected


def test_test_module_falls_back_when_resource_manager_api_disabled(mocker):
    """
    Given:
        - The Cloud Resource Manager API is disabled on the project (403 SERVICE_DISABLED).
        - The Compute API is enabled and its testIamPermissions probe succeeds.
    When:
        - test_module is called.
    Then:
        - The Resource Manager failure is skipped and the Compute probe is used.
        - "ok" is returned.
    """
    from GCP import test_module, GCPServices
    from google.oauth2.credentials import Credentials

    creds = MagicMock(spec=Credentials)
    params = {"project_id": "dummy-project-id"}

    disabled_error = _make_http_error(
        403, '{"error": {"status": "PERMISSION_DENIED", "message": "Cloud Resource Manager API ... SERVICE_DISABLED"}}'
    )
    rm_probe = mocker.patch.object(GCPServices.RESOURCE_MANAGER, "test_connectivity", side_effect=disabled_error)
    compute_probe = mocker.patch.object(GCPServices.COMPUTE, "test_connectivity", return_value=None)

    result = test_module(creds, params)

    assert result == "ok"
    rm_probe.assert_called_once_with(creds, "dummy-project-id")
    compute_probe.assert_called_once_with(creds, "dummy-project-id")


def test_test_module_non_service_disabled_403_fails_immediately(mocker):
    """
    Given:
        - The Resource Manager probe returns a genuine 403 (PERMISSION_DENIED, not SERVICE_DISABLED).
    When:
        - test_module is called.
    Then:
        - The test fails immediately and does NOT fall back to other services.
    """
    from GCP import test_module, GCPServices
    from CommonServerPython import DemistoException
    from google.oauth2.credentials import Credentials

    creds = MagicMock(spec=Credentials)
    params = {"project_id": "dummy-project-id"}

    real_403 = _make_http_error(403, '{"error": {"status": "PERMISSION_DENIED", "message": "caller lacks permission"}}')
    mocker.patch.object(GCPServices.RESOURCE_MANAGER, "test_connectivity", side_effect=real_403)
    compute_probe = mocker.patch.object(GCPServices.COMPUTE, "test_connectivity", return_value=None)

    with pytest.raises(DemistoException, match="Failed to connect to GCP project 'dummy-project-id'"):
        test_module(creds, params)
    compute_probe.assert_not_called()


def test_test_module_all_services_disabled_raises(mocker):
    """
    Given:
        - Every probed GCP service API is disabled (403 SERVICE_DISABLED).
    When:
        - test_module is called.
    Then:
        - A DemistoException is raised indicating all probed APIs are disabled.
    """
    from GCP import test_module, GCPServices
    from CommonServerPython import DemistoException
    from google.oauth2.credentials import Credentials

    creds = MagicMock(spec=Credentials)
    params = {"project_id": "dummy-project-id"}

    disabled_error = _make_http_error(403, '{"error": {"status": "PERMISSION_DENIED", "message": "SERVICE_DISABLED"}}')
    for service in GCPServices:
        mocker.patch.object(service, "test_connectivity", side_effect=disabled_error)

    with pytest.raises(DemistoException, match="all probed GCP service APIs are disabled"):
        test_module(creds, params)


def test_test_connectivity_resource_manager_uses_testiampermissions(mocker):
    """
    Given:
        - The RESOURCE_MANAGER service and a built API client.
    When:
        - test_connectivity is called.
    Then:
        - The project-level testIamPermissions endpoint is invoked with the project resource.
    """
    from GCP import GCPServices
    from google.oauth2.credentials import Credentials

    creds = MagicMock(spec=Credentials)
    mock_client = MagicMock()
    mocker.patch.object(GCPServices.RESOURCE_MANAGER, "build", return_value=mock_client)

    GCPServices.RESOURCE_MANAGER.test_connectivity(creds, "dummy-project-id")

    mock_client.projects.return_value.testIamPermissions.assert_called_once_with(
        resource="projects/dummy-project-id", body={"permissions": ["resourcemanager.projects.get"]}
    )


def test_test_connectivity_compute_uses_firewalls_list(mocker):
    """
    Given:
        - The COMPUTE service and a built API client.
    When:
        - test_connectivity is called.
    Then:
        - A project-scoped firewalls list is invoked (Compute has no project testIamPermissions).
    """
    from GCP import GCPServices
    from google.oauth2.credentials import Credentials

    creds = MagicMock(spec=Credentials)
    mock_client = MagicMock()
    mocker.patch.object(GCPServices.COMPUTE, "build", return_value=mock_client)

    GCPServices.COMPUTE.test_connectivity(creds, "dummy-project-id")

    mock_client.firewalls.return_value.list.assert_called_once_with(project="dummy-project-id", maxResults=1)


def test_test_connectivity_storage_uses_buckets_list_with_project(mocker):
    """
    Given:
        - The STORAGE service and a built API client.
    When:
        - test_connectivity is called.
    Then:
        - A project-scoped buckets list is invoked (project_id is passed as 'project', not 'bucket').
    """
    from GCP import GCPServices
    from google.oauth2.credentials import Credentials

    creds = MagicMock(spec=Credentials)
    mock_client = MagicMock()
    mocker.patch.object(GCPServices.STORAGE, "build", return_value=mock_client)

    GCPServices.STORAGE.test_connectivity(creds, "dummy-project-id")

    mock_client.buckets.return_value.list.assert_called_once_with(project="dummy-project-id", maxResults=1)


def test_test_connectivity_bigquery_uses_datasets_list(mocker):
    """
    Given:
        - The BIGQUERY service and a built API client.
    When:
        - test_connectivity is called.
    Then:
        - A project-scoped datasets list is invoked.
    """
    from GCP import GCPServices
    from google.oauth2.credentials import Credentials

    creds = MagicMock(spec=Credentials)
    mock_client = MagicMock()
    mocker.patch.object(GCPServices.BIGQUERY, "build", return_value=mock_client)

    GCPServices.BIGQUERY.test_connectivity(creds, "dummy-project-id")

    mock_client.datasets.return_value.list.assert_called_once_with(projectId="dummy-project-id", maxResults=1)


def test_test_connectivity_container_uses_clusters_list(mocker):
    """
    Given:
        - The CONTAINER service and a built API client.
    When:
        - test_connectivity is called.
    Then:
        - A project-scoped clusters list is invoked under the project's locations.
    """
    from GCP import GCPServices
    from google.oauth2.credentials import Credentials

    creds = MagicMock(spec=Credentials)
    mock_client = MagicMock()
    mocker.patch.object(GCPServices.CONTAINER, "build", return_value=mock_client)

    GCPServices.CONTAINER.test_connectivity(creds, "dummy-project-id")

    mock_client.projects.return_value.locations.return_value.clusters.return_value.list.assert_called_once_with(
        parent="projects/dummy-project-id/locations/-"
    )


def test_test_all_services_wraps_results_into_tuples(mocker):
    """
    Given:
        - One service probe succeeds and another raises.
    When:
        - test_all_services is called.
    Then:
        - It returns (service_name, success, error) tuples without raising.
    """
    from GCP import GCPServices
    from google.oauth2.credentials import Credentials

    creds = MagicMock(spec=Credentials)

    def fake_probe(self, _creds, _project_id):
        if self == GCPServices.COMPUTE:
            raise Exception("boom")

    mocker.patch.object(GCPServices, "test_connectivity", autospec=True, side_effect=fake_probe)

    results = GCPServices.test_all_services(creds, "dummy-project-id")

    results_by_name = {name: (success, error) for name, success, error in results}
    assert results_by_name[GCPServices.COMPUTE.api_name] == (False, "boom")
    assert results_by_name[GCPServices.RESOURCE_MANAGER.api_name] == (True, "")


def test_test_module_marketplace_missing_project_id_raises():
    """
    Given:
        - params does not contain project_id and there is no Service Account JSON to fall back to.
    When:
        - test_module is called.
    Then:
        - DemistoException is raised with a message about missing project_id.
    """
    from GCP import test_module
    from CommonServerPython import DemistoException
    from google.oauth2.credentials import Credentials

    creds = MagicMock(spec=Credentials)
    params: dict = {}

    with pytest.raises(DemistoException, match="Missing required parameter 'project_id'"):
        test_module(creds, params)


def test_test_module_project_id_falls_back_to_service_account_json(mocker):
    """
    Given:
        - The 'GCP Project ID' param is empty.
        - The Service Account private key JSON in credentials.password contains a project_id.
    When:
        - test_module is called.
    Then:
        - The project_id is resolved from the Service Account JSON and used for the
          connectivity test, and "ok" is returned.
    """
    from GCP import test_module
    from google.oauth2.credentials import Credentials

    creds = MagicMock(spec=Credentials)
    sa_info = {"type": "service_account", "project_id": "json-project-id", "private_key": "dummy_private_key"}
    params = {"credentials": {"password": json.dumps(sa_info)}}

    mock_rm = MagicMock()
    mock_rm.projects.return_value.testIamPermissions.return_value.execute.return_value = {
        "permissions": ["resourcemanager.projects.get"]
    }
    mocker.patch("GCP.GCPServices.RESOURCE_MANAGER.build", return_value=mock_rm)

    result = test_module(creds, params)

    assert result == "ok"
    mock_rm.projects.return_value.testIamPermissions.assert_called_once_with(
        resource="projects/json-project-id",
        body={"permissions": ["resourcemanager.projects.get"]},
    )


def test_test_module_marketplace_api_failure_raises(mocker):
    """
    Given:
        - Valid params with project_id.
        - Resource Manager testIamPermissions call raises an exception.
    When:
        - test_module is called.
    Then:
        - DemistoException is raised with a message about the failure.
    """
    from GCP import test_module
    from CommonServerPython import DemistoException
    from google.oauth2.credentials import Credentials

    creds = MagicMock(spec=Credentials)
    params = {"project_id": "dummy-project-id"}

    mock_rm = MagicMock()
    mock_rm.projects.return_value.testIamPermissions.return_value.execute.side_effect = Exception("403 Permission denied")
    mocker.patch("GCP.GCPServices.RESOURCE_MANAGER.build", return_value=mock_rm)

    with pytest.raises(DemistoException, match="Failed to connect to GCP project 'dummy-project-id'"):
        test_module(creds, params)


def test_build_http_client_no_proxy_ssl_on(mocker):
    """
    Given:
        - Default connection settings (proxy off, SSL verification on).
    When:
        - build_http_client is called.
    Then:
        - An httplib2.Http with proxy_info set to None and SSL validation enabled is returned.
    """
    import GCP

    mocker.patch("GCP.handle_proxy", return_value={})
    mock_http = mocker.patch("GCP.httplib2.Http", return_value="http-obj")

    result = GCP.build_http_client(use_proxy=False, verify_ssl=True)

    assert result == "http-obj"
    _, http_kwargs = mock_http.call_args
    assert http_kwargs["proxy_info"] is None
    assert http_kwargs["disable_ssl_certificate_validation"] is False


def test_build_http_client_insecure_disables_ssl_validation(mocker):
    """
    Given:
        - 'Trust any certificate' enabled (verify_ssl is False), no proxy.
    When:
        - build_http_client is called.
    Then:
        - An httplib2.Http is returned with SSL certificate validation disabled.
    """
    import GCP

    mocker.patch("GCP.handle_proxy", return_value={})
    mock_http = mocker.patch("GCP.httplib2.Http", return_value="http-obj")

    result = GCP.build_http_client(use_proxy=False, verify_ssl=False)

    assert result == "http-obj"
    _, http_kwargs = mock_http.call_args
    assert http_kwargs["disable_ssl_certificate_validation"] is True


def test_build_http_client_ssl_verification_simulated_handshake(mocker):
    """
    Given:
        - A simulated transport that fails the TLS handshake (SSLCertVerificationError)
          when certificate validation is ON, and succeeds when it is OFF.
    When:
        - build_http_client builds an httplib2.Http and a request is made through it,
          first with 'Trust any certificate' UNCHECKED (verify_ssl=True) then CHECKED
          (verify_ssl=False).
    Then:
        - Unchecked (verify on) -> the request raises an SSL error.
        - Checked (insecure on) -> the request succeeds.
      This proves the checkbox genuinely controls SSL certificate verification end to end.
    """
    import ssl

    import GCP

    mocker.patch("GCP.handle_proxy", return_value={})

    class FakeHttp:
        """Stand-in for httplib2.Http that honors disable_ssl_certificate_validation."""

        def __init__(self, proxy_info=None, disable_ssl_certificate_validation=False, ca_certs=None):
            self.disable_ssl_certificate_validation = disable_ssl_certificate_validation

        def request(self, uri, *args, **kwargs):
            if not self.disable_ssl_certificate_validation:
                # Verification is ON -> an untrusted/self-signed cert would fail the handshake.
                raise ssl.SSLCertVerificationError("certificate verify failed: self-signed certificate")
            return ({"status": 200}, b"{}")

    mocker.patch("GCP.httplib2.Http", side_effect=FakeHttp)

    # Unchecked -> verify_ssl True -> verification enforced -> request fails.
    http_verify_on = GCP.build_http_client(use_proxy=False, verify_ssl=True)
    with pytest.raises(ssl.SSLCertVerificationError):
        http_verify_on.request("https://self-signed.example.com")

    # Checked -> verify_ssl False -> verification skipped -> request succeeds.
    http_verify_off = GCP.build_http_client(use_proxy=False, verify_ssl=False)
    status, _ = http_verify_off.request("https://self-signed.example.com")
    assert status["status"] == 200


def test_build_http_client_proxy_sets_proxy_info(mocker):
    """
    Given:
        - 'Use system proxy settings' enabled and handle_proxy returns an https proxy with credentials.
    When:
        - build_http_client is called.
    Then:
        - httplib2.ProxyInfo is constructed with the host, port, user and password parsed
          from the proxy URL, and the resulting Http object carries that proxy_info.

    Note:
        httplib2.ProxyInfo and httplib2.Http are mocked so the test does not depend on the
        optional PySocks dependency (httplib2.socks) being installed in the environment.
    """
    import GCP

    mocker.patch("GCP.handle_proxy", return_value={"https": "https://user:pass@proxy.example.com:8080"})

    mocker.patch("GCP.httplib2.socks")  # PySocks may be absent locally
    mock_proxy_info = mocker.patch("GCP.httplib2.ProxyInfo", return_value="proxy-info-obj")
    mock_http = mocker.patch("GCP.httplib2.Http", return_value="http-obj")

    result = GCP.build_http_client(use_proxy=True, verify_ssl=True)

    assert result == "http-obj"
    # ProxyInfo built from the parsed proxy URL components.
    _, proxy_kwargs = mock_proxy_info.call_args
    assert proxy_kwargs["proxy_host"] == "proxy.example.com"
    assert proxy_kwargs["proxy_port"] == 8080
    assert proxy_kwargs["proxy_user"] == "user"
    assert proxy_kwargs["proxy_pass"] == "pass"
    # Http built with that proxy_info and SSL validation enabled (verify_ssl=True).
    _, http_kwargs = mock_http.call_args
    assert http_kwargs["proxy_info"] == "proxy-info-obj"
    assert http_kwargs["disable_ssl_certificate_validation"] is False
    # ca_certs must be passed so verification uses the system/container CA bundle.
    assert "ca_certs" in http_kwargs


def test_build_http_client_proxy_without_scheme(mocker):
    """
    Given:
        - Proxy enabled and HTTPS_PROXY env var set WITHOUT an http(s):// scheme.
    When:
        - build_http_client is called.
    Then:
        - The proxy value is normalized with an https:// prefix before parsing, so the
          host and port are extracted correctly.
    """
    import GCP

    mocker.patch("GCP.handle_proxy", return_value={"https": "proxy.example.com:3128"})

    mocker.patch("GCP.httplib2.socks")  # PySocks may be absent locally
    mock_proxy_info = mocker.patch("GCP.httplib2.ProxyInfo", return_value="proxy-info-obj")
    mocker.patch("GCP.httplib2.Http", return_value="http-obj")

    GCP.build_http_client(use_proxy=True, verify_ssl=True)

    _, proxy_kwargs = mock_proxy_info.call_args
    assert proxy_kwargs["proxy_host"] == "proxy.example.com"
    assert proxy_kwargs["proxy_port"] == 3128


def test_build_http_client_proxy_without_port_defaults(mocker):
    """
    Given:
        - Proxy enabled and the proxy URL has a scheme but no explicit port.
    When:
        - build_http_client is called.
    Then:
        - proxy_port falls back to 443 for an https:// proxy (avoiding a None port).
    """
    import GCP

    mocker.patch("GCP.handle_proxy", return_value={"https": "https://proxy.example.com"})

    mocker.patch("GCP.httplib2.socks")  # PySocks may be absent locally
    mock_proxy_info = mocker.patch("GCP.httplib2.ProxyInfo", return_value="proxy-info-obj")
    mocker.patch("GCP.httplib2.Http", return_value="http-obj")

    GCP.build_http_client(use_proxy=True, verify_ssl=True)

    _, proxy_kwargs = mock_proxy_info.call_args
    assert proxy_kwargs["proxy_host"] == "proxy.example.com"
    assert proxy_kwargs["proxy_port"] == 443


def test_get_credentials_marketplace_project_id_from_params(mocker):
    """
    Given:
        - A service account JSON in credentials.password (no project_id in args).
        - params contains a 'project_id' that differs from the JSON's project_id.
    When:
        - get_credentials is called.
    Then:
        - args['project_id'] is taken from params (priority: args > params > JSON).
    """
    import GCP
    from GCP import get_credentials

    sa_info = {"type": "service_account", "project_id": "json-project", "private_key": "dummy_private_key"}
    params = {"credentials": {"password": json.dumps(sa_info)}, "project_id": "params-project"}
    args: dict = {}

    mocker.patch.object(GCP.google_service_account.Credentials, "from_service_account_info", return_value=MagicMock())

    get_credentials(args, params)

    assert args["project_id"] == "params-project"


def test_get_credentials_marketplace_build_failure_raises(mocker):
    """
    Given:
        - A structurally valid service account JSON in credentials.password.
        - from_service_account_info raises (e.g. corrupt key material).
    When:
        - get_credentials is called.
    Then:
        - A DemistoException is raised and there is NO silent fallback to the CTS path.
    """
    import GCP
    from GCP import get_credentials
    from CommonServerPython import DemistoException

    sa_info = {"type": "service_account", "project_id": "dummy-project", "private_key": "dummy_private_key"}
    params = {"credentials": {"password": json.dumps(sa_info)}}
    args: dict = {}

    mocker.patch.object(
        GCP.google_service_account.Credentials,
        "from_service_account_info",
        side_effect=Exception("bad key"),
    )
    cts = mocker.patch("GCP.get_cloud_credentials")

    with pytest.raises(DemistoException, match="Failed to build GCP credentials from service account JSON"):
        get_credentials(args, params)

    cts.assert_not_called()  # must NOT fall back to CTS on a bad marketplace key


def test_get_credentials_cortex_cloud_missing_token_raises(mocker):
    """
    Given:
        - No service account JSON (Cortex Platform path) and a project_id in args.
        - get_cloud_credentials succeeds but returns a payload WITHOUT an access_token.
    When:
        - get_credentials is called.
    Then:
        - The explicit "token is missing" DemistoException is raised as-is and is NOT
          swallowed/re-wrapped by the CTS authentication error handler (the token check
          lives in its own try/except, separate from the CTS call).
    """
    from GCP import get_credentials
    from CommonServerPython import DemistoException

    params: dict = {}
    args = {"project_id": "dummy-project-id"}

    mocker.patch("GCP.get_cloud_credentials", return_value={})

    with pytest.raises(DemistoException, match="token is missing from CTS credentials"):
        get_credentials(args, params)


def test_get_credentials_cortex_cloud_cts_call_failure_wrapped(mocker):
    """
    Given:
        - No service account JSON (Cortex Platform path) and a project_id in args.
        - The get_cloud_credentials (CTS) call itself raises an exception.
    When:
        - get_credentials is called.
    Then:
        - The failure is reported via the CTS authentication error wrapper. This proves the
          CTS call and the token validation are in separate try/except blocks.
    """
    from GCP import get_credentials
    from CommonServerPython import DemistoException

    params: dict = {}
    args = {"project_id": "dummy-project-id"}

    mocker.patch("GCP.get_cloud_credentials", side_effect=Exception("network down"))

    with pytest.raises(DemistoException, match="Failed to authenticate with GCP via CTS"):
        get_credentials(args, params)


def test_get_credentials_marketplace_no_project_id_anywhere_raises(mocker):
    """
    Given:
        - A valid service account JSON in credentials.password that has NO 'project_id'.
        - No project_id in args and no project_id in params.
    When:
        - get_credentials is called (marketplace path).
    Then:
        - A DemistoException is raised because project_id cannot be resolved from args,
          params, or the service account JSON.
    """
    from GCP import get_credentials
    from CommonServerPython import DemistoException

    sa_info = {"type": "service_account", "private_key": "dummy_private_key"}
    params = {"credentials": {"password": json.dumps(sa_info)}}
    args: dict = {}

    mocker.patch(
        "GCP.google_service_account.Credentials.from_service_account_info",
        return_value=mocker.MagicMock(),
    )

    with pytest.raises(DemistoException, match="Missing required parameter 'project_id'"):
        get_credentials(args, params)


# ---------------------------------------------------------------------------
# YML <-> PY wiring assertion tests
#
# These tests read the integration's .yml and .py from disk and assert that
# every command, its arguments, and its output prefixes declared in the YML are
# actually wired up in the Python code. This proves the command, its args, and
# its outputs are implemented as declared.
#
# The comparison is PER-COMMAND: each YML command is resolved to its handler
# function via the command_map in main(), and only that handler function's body
# is inspected for `args.get("...")` reads and `outputs_prefix="..."` literals.
#
# Matching is STRICT and verbatim: an argument declared in the YML must appear
# in the handler exactly as `args.get("<name>")` (same casing, snake_case vs
# camelCase, etc.). Any naming difference fails the test.
#
# The .py is inspected via the AST (parsed ONCE into _PY_TREE), never via regex
# over raw text. This matters for correctness, not just for style:
#   * Comments are invisible to the AST by construction, so the commented-out
#     "currently unsupported" handlers in GCP.py cannot leak their args or their
#     output prefixes into a neighbouring live handler.
#   * Function boundaries are exact, so a `def ` inside a docstring or a string
#     literal cannot split a body in the wrong place.
#   * Non-obvious read/write forms (`args["x"]` subscripts, `outputs_prefix`
#     declared as a function parameter default) are matched structurally.
# The single deliberate exception is
# test_command_map_parser_ignores_commented_out_entries, which MUST stay on
# regex precisely because it asserts something about comments.
#
# Quick-action commands (names ending in "-quick-action") and the built-in
# "test-module" command are intentionally excluded.
# ---------------------------------------------------------------------------


def _is_included_command(command_name: str) -> bool:
    """Return True if the command should be checked (not test-module / quick-action)."""
    return command_name != "test-module" and not command_name.endswith(QUICK_ACTION_SUFFIX)


def _build_yml_spec() -> dict:
    """Extract the command specifications from the parsed integration YML.

    Returns:
        dict mapping command_name -> {"args": [arg_names], "outputs": [contextPaths]}
        for every non-quick-action, non-test-module command.
    """
    spec: dict = {}
    for command in _YML.get("script", {}).get("commands", []):
        name = command.get("name", "")
        if not _is_included_command(name):
            continue
        arg_names = [arg["name"] for arg in (command.get("arguments") or []) if arg.get("name")]
        context_paths = [out["contextPath"] for out in (command.get("outputs") or []) if out.get("contextPath")]
        spec[name] = {"args": arg_names, "outputs": context_paths}
    return spec


# Command specifications derived once from the integration YML.
_YML_SPEC = _build_yml_spec()


def _parse_command_map(tree: ast.Module) -> dict:
    """Parse the ``command_map`` dict in main(), mapping command name -> handler name.

    The source is read via the AST rather than a regex, so commented-out entries
    (such as the "currently unsupported" block at the end of the dict) are excluded
    by construction, and only the real ``command_map`` literal inside ``main()`` is
    considered - a stray ``"gcp-x": handler,`` pair elsewhere in the file is ignored.

    Skips test-module, quick-action entries and handlers that are not plain named
    functions (e.g. the ``test-module`` lambda), which we cannot introspect.

    Returns:
        dict mapping command_name -> handler_function_name.
    """
    main_fn = next(
        (node for node in tree.body if isinstance(node, ast.FunctionDef) and node.name == "main"),
        None,
    )
    assert main_fn is not None, "Could not find a top-level main() function in GCP.py"

    mapping: dict = {}
    for node in ast.walk(main_fn):
        # command_map is an annotated assignment:
        #   command_map: dict[str, Callable[[Any, dict], Any]] = {...}
        if isinstance(node, ast.AnnAssign):
            target, value = node.target, node.value
        elif isinstance(node, ast.Assign) and len(node.targets) == 1:
            target, value = node.targets[0], node.value
        else:
            continue
        if not (isinstance(target, ast.Name) and target.id == "command_map"):
            continue
        if not isinstance(value, ast.Dict):
            continue
        for key, handler in zip(value.keys, value.values):
            if not (isinstance(key, ast.Constant) and isinstance(key.value, str)):
                continue
            # Only plain named functions can be resolved to a body; this skips lambdas.
            if not isinstance(handler, ast.Name):
                continue
            if not _is_included_command(key.value):
                continue
            mapping[key.value] = handler.id

    # Guard against silent drift: if command_map is renamed, moved out of main() or
    # built dynamically, an empty mapping would make every consumer test pass vacuously.
    assert mapping, "Parsed an empty command_map from GCP.py - the parser is out of sync with the source"
    return mapping


def _top_level_functions(tree: ast.Module) -> dict:
    """Map every top-level function name in the .py to its AST node.

    Using the AST (instead of splitting the raw text on ``^def``) means a
    handler's body ends exactly where the function ends. A commented-out
    function that follows it is not absorbed into it, and a ``def`` appearing
    inside a docstring or string literal cannot start a bogus body.

    Returns:
        dict mapping function_name -> the ast.FunctionDef / ast.AsyncFunctionDef node.
    """
    functions = {node.name: node for node in tree.body if isinstance(node, ast.FunctionDef | ast.AsyncFunctionDef)}

    # Guard against silent drift: an empty mapping would make every consumer
    # test pass vacuously.
    assert functions, "Found no top-level functions in GCP.py - the parser is out of sync with the source"
    return functions


def _string_constant(node: ast.AST) -> str | None:
    """Return the value of a string-literal AST node, or None if it is not one."""
    if isinstance(node, ast.Constant) and isinstance(node.value, str):
        return node.value
    return None


def _extract_args_get(function_node: ast.AST) -> set:
    """Extract every argument name read from ``args`` within the given function node.

    Recognizes both supported read forms:
      1. ``args.get("<name>")`` (with or without a default).
      2. ``args["<name>"]`` subscript access.

    Arguments read with a non-literal key (e.g. ``args.get(some_variable)``)
    cannot be resolved statically and are deliberately ignored.
    """
    names: set = set()
    for node in ast.walk(function_node):
        # Form 1: args.get("<name>") / args.get("<name>", default)
        if (
            isinstance(node, ast.Call)
            and isinstance(node.func, ast.Attribute)
            and node.func.attr == "get"
            and isinstance(node.func.value, ast.Name)
            and node.func.value.id == "args"
            and node.args
            and (name := _string_constant(node.args[0])) is not None
        ):
            names.add(name)
        # Form 2: args["<name>"]
        elif (
            isinstance(node, ast.Subscript)
            and isinstance(node.value, ast.Name)
            and node.value.id == "args"
            and (name := _string_constant(node.slice)) is not None
        ):
            names.add(name)
    return names


def _extract_param_default_prefixes(function_node: ast.AST) -> set:
    """Extract an ``outputs_prefix`` declared as a parameter default of the function.

    Covers the ``def handler(..., outputs_prefix: str = "GCP.Some.Path")`` form,
    where the prefix is the function's own default rather than a value written
    inside its body.
    """
    if not isinstance(function_node, ast.FunctionDef | ast.AsyncFunctionDef):
        return set()

    spec = function_node.args
    # Positional defaults bind to the LAST N positional parameters.
    positional = [*spec.posonlyargs, *spec.args]
    defaults = list(spec.defaults)
    pairs = list(zip(positional[len(positional) - len(defaults) :], defaults))
    pairs += list(zip(spec.kwonlyargs, spec.kw_defaults))

    return {value for parameter, default in pairs if parameter.arg == "outputs_prefix" and (value := _string_constant(default))}


def _extract_output_prefixes(function_node: ast.AST) -> set:
    """Extract all output context prefixes declared within the given function node.

    Recognizes the supported CommandResults wiring patterns:
      1. ``outputs_prefix="GCP.Some.Path"`` keyword arguments, variable
         assignments (``outputs_prefix = "GCP.Some.Path"``), and function
         parameter defaults (``outputs_prefix: str = "GCP.Some.Path"``).
      2. Context paths used directly as ``outputs`` dict keys, e.g.
         ``"GCP.Some.Path(val.id && val.id == obj.id)": data``. The DT
         transformer suffix in parentheses is stripped.

    A prefix forwarded from a variable (``outputs_prefix=outputs_prefix``)
    resolves through pattern 1's parameter-default branch when the function
    declares its own default; it is otherwise not resolvable statically and is
    ignored.
    """
    # Pattern 1c: the prefix declared as this function's own parameter default.
    prefixes: set = _extract_param_default_prefixes(function_node)

    for node in ast.walk(function_node):
        # Pattern 1a: outputs_prefix="GCP.Some.Path" passed as a keyword argument.
        # NOTE: the literal is used verbatim (no .strip()) so that a leading or
        # trailing whitespace typo in the source (e.g. " GCP.Compute.Operations")
        # is surfaced as a real wiring defect instead of being silently masked.
        if isinstance(node, ast.Call):
            for keyword in node.keywords:
                if keyword.arg == "outputs_prefix" and (value := _string_constant(keyword.value)):
                    prefixes.add(value)
        # Pattern 1b: outputs_prefix = "..." / outputs_prefix: str = "..."
        elif isinstance(node, ast.Assign | ast.AnnAssign):
            targets = node.targets if isinstance(node, ast.Assign) else [node.target]
            is_prefix_target = any(isinstance(target, ast.Name) and target.id == "outputs_prefix" for target in targets)
            if is_prefix_target and (value := _string_constant(node.value)):
                prefixes.add(value)
        # Pattern 2: context paths used as outputs dict keys.
        elif isinstance(node, ast.Dict):
            for key in node.keys:
                # key is None for a ``**expansion`` entry, which has no literal path.
                if (value := _string_constant(key)) and value.startswith("GCP."):
                    # Strip only the trailing DT transformer such as
                    # "(val.id && val.id == obj.id)" or "(true)" so only the bare
                    # context path remains.
                    prefixes.add(re.sub(r"\(.*\)$", "", value))
    return prefixes


# Derived once from the single parse of GCP.py and reused by every check below.
_COMMAND_MAP = _parse_command_map(_PY_TREE)
_FUNCTION_NODES = _top_level_functions(_PY_TREE)


def test_command_map_parser_ignores_commented_out_entries():
    """
    Given: A command_map in GCP.py containing a commented-out block of currently
           unsupported commands.
    When: Parsing the command_map with _parse_command_map.
    Then: No commented-out entry is reported as wired, so a command that is enabled
          in the YML but left commented in the .py is still caught as missing.
    """
    # This is the one check that MUST scan the raw text rather than the AST: the
    # AST cannot see comments at all, so only a text scan can enumerate the
    # commented-out entries we expect to be absent from the parsed map.
    commented_out = set(re.findall(r'^\s*#\s*"([a-z0-9][a-z0-9\-]*)"\s*:', _PY_SOURCE, flags=re.MULTILINE))
    leaked = sorted(commented_out & _COMMAND_MAP.keys())

    assert not leaked, (
        "The following commands are commented out in GCP.py but were parsed as wired " f"in the command_map: {leaked}"
    )


def test_command_map_handlers_resolve_to_real_functions():
    """
    Given: The handler names referenced by the command_map in GCP.py.
    When: Resolving each of them against the top-level functions in the same file.
    Then: Every handler must resolve to a real function node, so that a renamed or
          deleted handler cannot make the arg/output checks pass vacuously against
          an empty body.
    """
    unresolved = sorted({handler for handler in _COMMAND_MAP.values() if handler not in _FUNCTION_NODES})

    assert not unresolved, (
        "The following handlers are referenced by the command_map in GCP.py but do "
        f"not resolve to a top-level function in that file: {unresolved}"
    )


def test_yml_commands_are_wired_in_py():
    """
    Given: The integration YML declaring command names.
    When: Comparing against the command_map wired in the .py main().
    Then: Every non-quick-action YML command must be wired in the .py.
    """
    missing = sorted(name for name in _YML_SPEC if name not in _COMMAND_MAP)
    assert not missing, (
        "The following commands are declared in GCP.yml but are NOT wired in the " f"command_map in GCP.py: {missing}"
    )


def test_yml_args_match_py_handler_verbatim():
    """
    Given: The arguments declared per command in the integration YML.
    When: Comparing (verbatim) against the args.get("...") reads in that command's
          resolved handler function in the .py.
    Then: Each YML argument name must appear exactly as-is in the handler.
          Any naming difference (snake_case vs camelCase, casing) fails.
    """
    mismatches: list = []
    for command_name in sorted(_YML_SPEC):
        handler = _COMMAND_MAP.get(command_name)
        if handler is None:
            # Missing wiring is reported by test_yml_commands_are_wired_in_py.
            continue
        handler_node = _FUNCTION_NODES.get(handler)
        if handler_node is None:
            # An unresolvable handler is reported by
            # test_command_map_handlers_resolve_to_real_functions.
            continue
        handler_args = _extract_args_get(handler_node)
        for arg_name in _YML_SPEC[command_name]["args"]:
            if arg_name in PLATFORM_STANDARD_ARGS:
                # Resolved centrally via credentials, not per-handler args.get(...).
                continue
            if arg_name not in handler_args:
                mismatches.append(f'{command_name} (handler {handler}) -> args.get("{arg_name}")')

    assert not mismatches, (
        "The following YML arguments are NOT read verbatim via args.get(...) in their "
        "command's handler in GCP.py (a naming difference such as snake_case vs "
        "camelCase means the YML and PY are out of sync):\n" + "\n".join(mismatches)
    )


def test_yml_output_prefixes_match_py_handler():
    """
    Given: The output contextPaths declared per command in the integration YML.
    When: Comparing against the outputs_prefix="..." literals in that command's
          resolved handler function in the .py.
    Then: Every YML output contextPath must be covered by an outputs_prefix declared
          in the handler (the outputs_prefix must be a leading segment of the contextPath).
    """

    def _is_covered(context_path: str, prefixes: set) -> bool:
        return any(context_path == prefix or context_path.startswith(prefix + ".") for prefix in prefixes)

    uncovered: list = []
    for command_name in sorted(_YML_SPEC):
        handler = _COMMAND_MAP.get(command_name)
        if handler is None:
            continue
        handler_node = _FUNCTION_NODES.get(handler)
        if handler_node is None:
            continue
        handler_prefixes = _extract_output_prefixes(handler_node)
        for context_path in _YML_SPEC[command_name]["outputs"]:
            if not _is_covered(context_path, handler_prefixes):
                uncovered.append(f"{command_name} (handler {handler}) -> {context_path}")

    assert not uncovered, (
        "The following YML output contextPaths are NOT covered by any output prefix "
        "(outputs_prefix=... or an outputs dict key) in their command's handler in "
        "GCP.py:\n" + "\n".join(uncovered)
    )


# ---------------------------------------------------------------------------
# Regression tests for the AST extractors used by the checks above.
#
# These run against small synthetic sources rather than GCP.py, so they pin the
# extractors' behaviour independently of the integration's current contents.
# They exist because the extractors are the trust anchor of the checks above: a
# silently over-permissive extractor would make those checks pass vacuously.
# ---------------------------------------------------------------------------


def test_extractors_ignore_commented_out_code():
    """
    Given: A function whose body is followed by a fully commented-out function that
           reads its own args and declares its own outputs_prefix.
    When: Extracting the args and output prefixes of the live function.
    Then: Nothing from the commented-out block is attributed to the live function.
          A text-based parser would glue the dead body onto the live one and let a
          handler satisfy a YML contract it does not actually implement.
    """
    source = (
        "def live_handler(creds, args):\n"
        '    name = args.get("live_arg")\n'
        '    return CommandResults(outputs_prefix="GCP.Live.Path", outputs=name)\n'
        "\n"
        "\n"
        "# def dead_handler(creds, args):\n"
        '#     name = args.get("dead_arg")\n'
        '#     return CommandResults(outputs_prefix="GCP.Dead.Path", outputs=name)\n'
    )
    live_handler = _top_level_functions(ast.parse(source))["live_handler"]

    assert _extract_args_get(live_handler) == {"live_arg"}
    assert _extract_output_prefixes(live_handler) == {"GCP.Live.Path"}


def test_extract_args_get_reads_subscript_access():
    """
    Given: A handler that reads one argument via args.get(...), another via the
           args["..."] subscript form, and a third via a non-literal key.
    When: Extracting its argument names.
    Then: Both literal forms are reported and the dynamic key is ignored, so a YML
          argument implemented only as args["name"] is not falsely reported missing.
    """
    source = (
        "def handler(creds, args):\n"
        '    a = args.get("via_get")\n'
        '    b = args["via_subscript"]\n'
        "    c = args.get(dynamic_key)\n"
        "    return a, b, c\n"
    )
    handler = _top_level_functions(ast.parse(source))["handler"]

    assert _extract_args_get(handler) == {"via_get", "via_subscript"}


def test_extract_output_prefixes_covers_all_declaration_forms():
    """
    Given: Handlers declaring an output prefix as a parameter default, as a local
           assignment, and as outputs dict keys.
    When: Extracting their output prefixes.
    Then: Every form is recognized, the DT transformer suffix is stripped from dict
          keys, and a non-GCP dict key is not treated as a context path.
    """
    source = (
        'def param_default(creds, args, outputs_prefix: str = "GCP.Storage.BucketPolicy"):\n'
        "    return CommandResults(outputs_prefix=outputs_prefix)\n"
        "\n"
        "\n"
        "def local_assignment(creds, args):\n"
        '    outputs_prefix = "GCP.Assigned.Path"\n'
        "    return CommandResults(outputs_prefix=outputs_prefix)\n"
        "\n"
        "\n"
        "def dict_keys(creds, args):\n"
        "    outputs = {\n"
        '        "GCP.Compute.Firewall(val.name && val.name == obj.name)": [],\n'
        '        "GCP.Compute(true)": {"FirewallNextToken": None},\n'
        '        "NotGCP.Other": [],\n'
        "    }\n"
        "    return CommandResults(outputs=outputs)\n"
    )
    functions = _top_level_functions(ast.parse(source))

    # The parameter default is resolved, so forwarding it as outputs_prefix=outputs_prefix
    # does not leave the handler with no prefix at all.
    assert _extract_output_prefixes(functions["param_default"]) == {"GCP.Storage.BucketPolicy"}
    assert _extract_output_prefixes(functions["local_assignment"]) == {"GCP.Assigned.Path"}
    assert _extract_output_prefixes(functions["dict_keys"]) == {"GCP.Compute.Firewall", "GCP.Compute"}


def test_extract_output_prefixes_does_not_strip_whitespace_typos():
    """
    Given: A handler whose outputs_prefix literal has a leading space typo.
    When: Extracting its output prefixes.
    Then: The value is reported verbatim, so the typo surfaces as a real wiring
          defect instead of being silently normalized away.
    """
    source = 'def handler(creds, args):\n    return CommandResults(outputs_prefix=" GCP.Compute.Operations")\n'
    handler = _top_level_functions(ast.parse(source))["handler"]

    assert _extract_output_prefixes(handler) == {" GCP.Compute.Operations"}