Code a custom module for Security Health Analytics

Premium and Enterpriseservice tiers

This page explains how to code a custom module definition by using theCommon Expression Language (CEL)andYAML.

Use the Google Cloud CLI to upload your custom module definitions toSecurity Health Analytics.

In the YAML file, a custom module definition consists of a structured setof properties that you use to define the following elements of aSecurity Health Analytics custom module:

  • The resources to scan.
  • The detection logic to use.
  • The information to provide to your security teams so that they can quicklyunderstand, triage, and resolve the detected issue.

The specific required and optional properties that make up a YAML definitionare covered inCoding steps.

Avoid creating redundant detectors

To control finding volume, avoid creating and running modules that containredundant functionality.

For example, if you create a custom module that checks for encryption keys thatare not rotated after 30 days, consider disabling the built-inSecurity Health Analytics detectorKMS_KEY_NOT_ROTATED, becauseits check, which uses a value of 90 days, would be superfluous.

For more information on disabling detectors, seeEnable and disable detectors.

Coding steps

You code the definition of a custom module forSecurity Health Analytics as a series of YAML properties, some of which containCEL expressions.

To code a custom definition module, follow these steps:

  1. Create a text file with theyaml filename extension.

  2. In the text file, create aresource_selector property and specifyone to five resource types for the custom module to scan. Aresource type cannot be specified more than once in a custom moduledefinition. For example:

    resource_selector: resource_types: ‐ cloudkms.googleapis.com/CryptoKey

    The resource types that you specify must be supported bySecurity Command Center. For a list of supported resource types, seeSupported resource types.

  3. Create apredicate property and specify one or more CEL expressionsthat check properties of the resource types to be scanned. Any propertiesthe you reference in the CEL expressions must exist in theGoogle Cloud API definition of each resource typethat you specify underresource_selector. To trigger a finding,the expression must resolve toTRUE. For example, in the followingexpression, onlyrotationPeriod values greater than2592000s triggera finding.

    predicate: expression: resource.rotationPeriod > duration("2592000s")

    For help writing CEL expressions, see the following resources:

  4. Create adescription property that explains the vulnerability ormisconfiguration that the custom module detects. This explanationappears in each finding instance to help investigators understandthe detected issue. The text must be enclosed in quotation marks.For example:

    description: "The rotation period of the identified cryptokey resource exceeds 30 days, the maximum rotation period that our security guidelines allow."
  5. Create arecommendation property that explains how to fix thedetected issue. The gcloud CLI requires an escape characterbefore certain characters, such as quotation marks. The followingexample shows the use of the backslash to escape each set of quotationmarks:

    recommendation:"Tofixthisissuegotohttps://console.cloud.google.com/security/kms.Clickthekey-ringthatcontainsthekey.Clickthekey.Click\"Editrotationperiod\".Thensettherotationperiodtoatmost30days."

    If you create or update a custom module by using theGoogle Cloud console, escape characters are not required.

  6. Create aseverity property and specify the default severity for thefindings that are created by this module. Commonly used values for theseverity property areLOW,MEDIUM,HIGH, andCRITICAL. For example,

    severity: MEDIUM
  7. Optionally, create acustom_output property and specifyadditional information to return with each finding. Specify the informationto return as one or more name-value pairs.You can return either the value of a property of the scanned resourceor a literal string. Properties must be specified asresource.PROPERTY_NAME. Literal strings must beenclosed in quotation marks. The following example shows acustom_outputdefinition that returns both a property value, the value ofrotationPeriodin the scannedCryptoKey resource, and a text string,"Excessiverotation period for CryptoKey":

     custom_output:   properties:     - name: duration       value_expression:         expression: resource.rotationPeriod     - name: note       value_expression:         expression: "'Excessive rotation period for CryptoKey'"
  8. Save the file to a location that your gcloud CLI can access.

  9. Upload the definition to Security Health Analytics with the following command:

     gcloud scc custom-modules sha create \     --organization=organizations/ORGANIZATION_ID \     --display-name="MODULE_DISPLAY_NAME" \     --enablement-state="ENABLED" \     --custom-config-from-file=DEFINITION_FILE_NAME.yaml

    Replace the following values:

    • ORGANIZATION_ID with the ID of the parentorganization of the custom module or replace the--organizationflag with either--folders or--project and specify the ID ofthe parent folder or project.
    • MODULE_DISPLAY_NAME with a name to displayas the finding category when the custom module returns findings.The name must be between 1 and 128 characters, start with a lowercaseletter, and contain alphanumeric characters or underscores only.
    • DEFINITION_FILE_NAME with the path and file name ofthe YAML file that contains the definition of the custom module.

    For more information about working with Security Health Analytics custommodules, seeUsing custom modules for Security Health Analytics.

Scan latencies for new custom modules

Creating a custom module does not trigger a new scan.

Security Health Analytics doesn't start using a new custom module until eitherof the following:

  • The first batch scan after you create the custom module. Depending onwhen you create a custom module in your batch-scan schedule, you mighthave to wait up to 24 hours before Security Health Analytics starts using thecustom module.
  • A change to a target resource triggers a real-time scan.

Example custom module definition

The following example shows a completed custom module definition thattriggers a finding if the value of therotationPeriod property of acloudkms.googleapis.com/CryptoKey resource is greater than 2,592,000seconds (30 days). The example returns two optional values in thecustom_output section: the value ofresource.rotationPeriod and a noteas a text string.

In the example, note the following elements:

  • The type of asset or resource to check is listed in theresource_selectorsection underresource_types.
  • The check that the module performs on the resources, itsdetection logic,is defined in thepredicate section preceded byexpression.
  • Two custom source properties,duration andviolation, are definedin thecustom_output section.
  • The explanation of the issue that was detected is specified in thedescription property.
  • The guidance for remediating the detected issue isspecified on therecommendation property. Because quotation marks appearin the guidance, a backslash escape character is required before eachquotation mark.
severity:HIGHdescription:"Regularkeyrotationhelpsprovideprotectionagainstcompromisedkeys,andlimitsthenumberofencryptedmessagesavailabletocryptanalysisforaspecifickeyversion."recommendation:"Tofixthisissuegotohttps://console.cloud.google.com/security/kms.Clickthekey-ringthatcontainsthekey.Clickthekey.Click\"Editrotationperiod\".Thensettherotationperiodtoatmost30days."resource_selector:resource_types:-cloudkms.googleapis.com/CryptoKeypredicate:expression:resource.rotationPeriod > duration("2592000s")custom_output:properties:-name:durationvalue_expression:expression:resource.rotationPeriod-name:violationvalue_expression:expression:"'ExcessiverotationperiodforCryptoKey'"

Referencing resource and policy properties in custom modules

Regardless of which method you use to create a custom module—by using theGoogle Cloud console or by writing the definition yourself—youneed to be able to look up the properties that you can evaluate in thecustom module. You also need to know how to reference those properties in acustom module definition.

Find the properties of a resource or policy

The properties of a Google Cloud resource are defined in the APIdefinition of the resource. You can find this definition by clicking theresource name onSupported resource types.

You can find the properties of a policy in the IAMAPI reference documentation. For properties of a policy, seePolicy.

Reference a resource property in a custom module definition

When you create a custom module, all direct references to the propertyof a scanned resource must begin withresource, followed by any parentproperties and finally the target property. The properties are separatedby a period, using a JSON-style dot notation.

The following are examples of resource properties and how they can be retrieved:

  • resourceName: stores the full name of a resource in Cloud Asset Inventory, forexample,//cloudresourcemanager.googleapis.com/projects/296605646631.
  • resource.rotationPeriod: whererotationPeriod is a property ofresource.
  • resource.metadata.name: wherename is a sub-property ofmetadata,which is a sub-property ofresource.

Reference IAM policy properties

You can create CEL expressions that evaluate the IAM policyof a resource by referencing the resource's IAM policy properties.The only properties available are bindings and roles within bindings.

When referencing IAM policy properties,policy is thetop-level property.

The following are examples of IAM policy properties and how theycan be retrieved:

  • policy.bindings, wherebindings is a property ofpolicy.
  • policy.version, whereversion is a property ofpolicy.

For more examples, seeExample CEL expressions.

For information about the properties of a policy, seePolicy.

Writing CEL expressions

When you create a custom module, you use CEL expressions to evaluatethe properties of the scanned resource. Optionally, you can also useCEL expressions to define customname-value pairs that return additionalinformation with your findings.

Whether you are creating a custom module in the Google Cloud consoleor writing your custom module definition yourself in a YAML file,the CEL expressions you define are the same.

CEL expressions for detection logic

You code the detection logic of a custom module by using CELexpressions with standard CEL operators to evaluate the properties ofthe scanned resources.

Your expressions can be simple checks of a single value or more complexcompound expressions that check multiple values or conditions. Either way,the expression must resolve to a booleantrue to trigger a finding.

If you are creating a custom module in the Google Cloud console,you write these expressionsin theExpression editor on theConfigure module page.

If you are coding a custom module in a YAML file, you add these expressionsunder thepredicate property.

Regardless of whether you are using the Google Cloud console or a YAMLfile, CEL expressions that evaluate resource properties, must conform to thefollowing rules:

  • The properties that you specify in a CEL expression must be propertiesof the scanned resource, as defined in the API definition of the resourcetype.
  • If a custom module evaluates multiple resource types, the properties thatyou specify in the CEL expressions must be common to each resource typethe custom module evaluates.

    For example, if you define a custom module calledinvalid_cryptokey that checks two resource types:cloudkms.googleapis.com/CryptoKey andcloudkms.googleapis.com/CryptoKeyVersion, you could write the followingexpression, because both theCryptoKey andCryptoKeyVersion resourcetypes include thename property:

    predicate:resource.name.matches("projects/project1/locations/us-central1/keyRings/keyring1/cryptoKeys/.*")

    However, you can't specify the following expression in theinvalid_cryptokey custom module because theimportTime androtationPeriod properties that the expression evaluates are not sharedby both resource types:

    predicate:resource.importTime >= timestamp("2022-10-02T15:01:23Z") || resource.rotationPeriod > duration("2592000s")
  • All enums in a CEL expression must be represented asstrings. For example, the following is a valid expression for thecloudkms.googleapis.com/CryptoKeyVersion resource type:

    resource.state = "PENDING_GENERATION"
  • The result of the CEL expressions that you define in thepredicate propertymust be a Boolean. A finding is triggered only if the result istrue.

For more information about CEL, see the following:

Example CEL expressions

The following table lists some CEL expressions that can be used toevaluate resource properties. You can use these both in theGoogle Cloud console and in YAML custom module definitions.

Resource typeExplanationCEL expression
Any resource with an IAM policyIAM policy check to identify members from outside domain!policy.bindings.all(binding, binding.members.all(m ,!m.endsWith('@gmail.com')))
cloudkms.googleapis.com/CryptoKeyCloud KMS key rotation period checkhas(resource.rotationPeriod) && resource.rotationPeriod > duration('60h')
Multiple resource types with a single policyCheck if the resource name starts withdev ordevAccess based on resource type(resource.type == 'compute.googleapis.com/Disk' && resource.name.startsWith('projects/PROJECT_ID/regions/REGION/disks/devAccess')) || (resource.type == 'compute.googleapis.com/Instance ' && resource.name.startsWith('projects/PROJECT_ID/zones/REGION/instances/dev-'))
compute.googleapis.com/NetworkVirtual Private Cloud peering rule to match network peersresource.selfLink.matches('https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/networks/default') || resource.peerings.exists(p, p.network.matches('https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/networks/shared$'))
cloudfunctions.googleapis.com/CloudFunctionOnly allow internal ingress traffic for Cloud Run functionhas(resource.ingressSettings) && resource.ingressSettings.matches('ALLOW_INTERNAL_ONLY')
compute.googleapis.com/InstanceResource name matches patternresource.name.matches('^gcp-vm-(linux|windows)-v\\d+$')
serviceusage.googleapis.com/ServiceOnly allow storage-related APIs to be enabledresource.state == 'ENABLED' && !( resource.name.matches('storage-api.googleapis.com') || resource.name.matches('bigquery-json.googleapis.com') || resource.name.matches('bigquery.googleapis.com') || resource.name.matches('sql-component.googleapis.com') || resource.name.matches('spanner.googleapis.com'))
sqladmin.googleapis.com/InstanceOnly allowlisted public IPs are allowed(resource.instanceType == 'CLOUD_SQL_INSTANCE' && resource.backendType == 'SECOND_GEN' && resource.settings.ipConfiguration.ipv4Enabled ) && !(resource.ipAddresses.all(ip, ip.type != 'PRIMARY' || ip.ipAddress.matches('IP_ADDRESS'))))
dataproc.googleapis.com/ClusterCheck if project IDs in a Dataproc cluster contain the substrings testing or developmenthas(resource.projectId) && resource.projectId.contains('testing') || resource.projectId.contains('development')

CEL expressions for custom finding properties

Optionally, to return additional information with each finding that can beused in finding queries, you can define up to ten custom propertiesto return with the findings that are generated by your custom modules.

The custom properties appear in the source properties of the finding in theits JSON and under theSource properties tab of the finding details inGoogle Cloud console.

You define custom properties asname-value pairs.

If you are creating a custom module in the Google Cloud console,you define thename-value pairs in theCustom finding propertiessection on theDefine finding details page.

If you are coding a custom module in a YAML file, you list thename-valuepairs asproperties under thecustom_output property.

Regardless of whether you are using the Google Cloud console or a YAMLfile, the following rules apply:

  • Specifyname as a text string without quotation marks.
  • Specifyvalue as one of the following:

    • To return the value of a property, specify the property in the followingformat:

      RESOURCE_TYPE.PROPERTY.PROPERTY_TO_RETURN

    In the example:

    • RESOURCE_TYPE can be eitherresource orpolicy.
    • PROPERTY one or more parent properties of theproperty that contains the value to return.
    • PROPERTY_TO_RETURN is the property that containsthe value to return.

    • To return a text string, enclose the string in quotation marks.

The following example shows twoname-value pairs properly defined undercustom_output in a YAML file:

custom_output:  properties:    - name: duration      value_expression:        expression: resource.name    - name: property_with_text      value_expression:        expression: "'Note content'"

What's next

To test, submit, view, and update custom modules, see the following pages:

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2026-02-20 UTC.