Write Custom Policies

Make Policies Flexible with Variants

Use variants to change what a policy checks based on different conditions

Variants are checks that behave differently based on conditions you define. They're alternative versions of checks.

For example, suppose you want to ensure that Remote Desktop Protocol (RDP) is restricted from the internet. You want to perform this check both in GCP projects and in Terraform files. You can do this by creating one variant for GCP projects and another for Terraform files:

  • The GCP variant queries if the asset is a GCP project and checks RDP access using the GCP resource.

  • The Terraform variant queries if the asset is a Terraform file and checks RDP access using the Terraform resource.

  • If the asset is neither a GCP project nor a Terraform file, cnspec doesn't execute an RDP check.

Filters

cnspec relies on filters to determine which variant to run against an asset. A filter is a condition written in MQL. Any fields you can query about any resources can be the basis for a filter.

To learn more about filters, read Limit Target Assets with Filters.

Create variants

A bundle has two pieces:

  • The policy's groups reference the parent check by UID.
  • The top-level queries section holds the parent check (with its variants list) and each variant's definition (with its filter and MQL).
policies:
  - uid: okta-security-example-with-variants
    name: Example of a policy that uses variants
    version: '1.0.0'
    scoring_system: highest impact
    authors:
      - name: Lunalectric
        email: security@lunalectric.com
    groups:
      - title: Password requirements
        checks:
          - uid: password-minimum-length
queries:
  - uid: password-minimum-length
    title: Minimum password length
    impact: 30
    variants:
      - uid: password-minimum-length-runtime
      - uid: password-minimum-length-terraform-hcl
      - uid: password-minimum-length-terraform-plan
      - uid: password-minimum-length-terraform-state
  - uid: password-minimum-length-runtime
    title: Minimum password length - runtime variant
    filters: asset.platform == "okta-org"
    impact: 30
    mql: |
      okta.policies.password.all( settings['password']['complexity']['minLength'] >= 15 )
  - uid: password-minimum-length-terraform-hcl
    title: Minimum password length - Terraform HCL variant
    filters: asset.platform == "terraform-hcl" && terraform.providers.one( nameLabel == "okta" )
    impact: 30
    mql: |
      terraform.resources.where( nameLabel == /okta_policy_password/ ).all( arguments['password_min_length'] == /var/ || arguments['password_min_length'] >= 15 )
  - uid: password-minimum-length-terraform-plan
    title: Minimum password length - Terraform plan variant
    filters: asset.platform == "terraform-plan" && terraform.plan.resourceChanges.contains( providerName == /okta/ )
    impact: 30
    mql: |
      terraform.plan.resourceChanges.where( type == /okta_policy_password/ ).all( change.after['password_min_length'] >= 15 )
  - uid: password-minimum-length-terraform-state
    title: Minimum password length - Terraform state variant
    filters: asset.platform == "terraform-state" && terraform.state.resources.contains( type == /okta_policy_password/ )
    impact: 30
    mql: |
      terraform.state.resources.where( type == /okta_policy_password/ ).all( values['password_min_length'] >= 15 )

The groups block on lines 9-12 includes the password-minimum-length check in the policy. Lines 17-21 list the four variants for that check:

  • Lines 22-27 define the password-minimum-length-runtime variant. The filter on line 24 tells cnspec to run this variant only when the asset is an Okta organization. Line 27 is the check to run when that filter matches.
  • Lines 28-33 define the password-minimum-length-terraform-hcl variant. The filter on line 30 tells cnspec to run this variant only on Okta Terraform HCL files. Line 33 is the check to run.
  • Lines 34-39 define the password-minimum-length-terraform-plan variant. The filter on line 36 tells cnspec to run this variant only on Okta Terraform plans. Line 39 is the check to run.
  • Lines 40-45 define the password-minimum-length-terraform-state variant. The filter on line 42 tells cnspec to run this variant only on Okta Terraform state files. Line 45 is the check to run.

Use one property for multiple variants

Often you use variants to ensure that different types of assets have one common property, as in the example above. All of the variants in the okta-security-example-with-variants policy check that the minimum password length is 15; they just check the value using different resources for different assets.

For efficiency and easier maintenance, you can write all four variants to use one property instead of defining 15 multiple times:

policies:
  - uid: okta-security-example-with-variants
    name: Example of a policy that uses variants
    version: '1.0.0'
    scoring_system: highest impact
    authors:
      - name: Lunalectric
        email: security@lunalectric.com
    groups:
      - title: Password requirements
        checks:
          - uid: password-minimum-length
queries:
  - uid: password-minimum-length
    title: Minimum password length
    impact: 30
    variants:
      - uid: password-minimum-length-runtime
      - uid: password-minimum-length-terraform-hcl
      - uid: password-minimum-length-terraform-plan
      - uid: password-minimum-length-terraform-state
  - uid: password-minimum-length-runtime
    title: Minimum password length - runtime variant
    filters: asset.platform == "okta-org"
    impact: 30
    mql: |
      okta.policies.password.all( settings['password']['complexity']['minLength'] >= props.minPass )
  - uid: password-minimum-length-terraform-hcl
    title: Minimum password length - Terraform HCL variant
    filters: asset.platform == "terraform-hcl" && terraform.providers.one( nameLabel == "okta" )
    impact: 30
    mql: |
      terraform.resources.where( nameLabel == /okta_policy_password/ ).all( arguments['password_min_length'] == /var/ || arguments['password_min_length'] >= props.minPass )
  - uid: password-minimum-length-terraform-plan
    title: Minimum password length - Terraform plan variant
    filters: asset.platform == "terraform-plan" && terraform.plan.resourceChanges.contains( providerName == /okta/ )
    impact: 30
    mql: |
      terraform.plan.resourceChanges.where( type == /okta_policy_password/ ).all( change.after['password_min_length'] >= props.minPass )
  - uid: password-minimum-length-terraform-state
    title: Minimum password length - Terraform state variant
    filters: asset.platform == "terraform-state" && terraform.state.resources.contains( type == /okta_policy_password/ )
    impact: 30
    mql: |
      terraform.state.resources.where( type == /okta_policy_password/ ).all( values['password_min_length'] >= props.minPass )
props:
  - uid: minPass
    title: Minimum password length
    mql: '15'

Tip: To check for errors in the policy bundles you write, run cnspec policy lint BUNDLE-NAME.mql.yaml. For BUNDLE-NAME, substitute the name of your file.

To learn more about properties, read Define Properties.

Next steps

On this page