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
groupsreference the parent check by UID. - The top-level
queriessection holds the parent check (with itsvariantslist) 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
filters: asset.platform == "okta-org"
mql: |
okta.policies.password.all( settings['password']['complexity']['minLength'] >= 15 )
- uid: password-minimum-length-terraform-hcl
filters: asset.platform == "terraform-hcl" && terraform.providers.one( nameLabel == "okta" )
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
filters: asset.platform == "terraform-plan" && terraform.plan.resourceChanges.contains( providerName == /okta/ )
mql: |
terraform.plan.resourceChanges.where( type == /okta_policy_password/ ).all( change.after['password_min_length'] >= 15 )
- uid: password-minimum-length-terraform-state
filters: asset.platform == "terraform-state" && terraform.state.resources.contains( type == /okta_policy_password/ )
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-25 define the
password-minimum-length-runtimevariant. The filter on line 23 tells cnspec to run this variant only when the asset is an Okta organization. Line 25 is the check to run when that filter matches. - Lines 26-29 define the
password-minimum-length-terraform-hclvariant. The filter on line 27 tells cnspec to run this variant only on Okta Terraform HCL files. Line 29 is the check to run. - Lines 30-33 define the
password-minimum-length-terraform-planvariant. The filter on line 31 tells cnspec to run this variant only on Okta Terraform plans. Line 33 is the check to run. - Lines 34-37 define the
password-minimum-length-terraform-statevariant. The filter on line 35 tells cnspec to run this variant only on Okta Terraform state files. Line 37 is the check to run.
The variants don't set their own title or impact. They inherit both from the parent check, and cnspec policy lint warns about a variant that defines them (rule query-variant-uses-non-default-fields).
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. Define the property in the policy's props section (lines 9-12), and reference it in each variant as props.minPass:
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
props:
- uid: minPass
title: Minimum password length
mql: '15'
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
filters: asset.platform == "okta-org"
mql: |
okta.policies.password.all( settings['password']['complexity']['minLength'] >= props.minPass )
- uid: password-minimum-length-terraform-hcl
filters: asset.platform == "terraform-hcl" && terraform.providers.one( nameLabel == "okta" )
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
filters: asset.platform == "terraform-plan" && terraform.plan.resourceChanges.contains( providerName == /okta/ )
mql: |
terraform.plan.resourceChanges.where( type == /okta_policy_password/ ).all( change.after['password_min_length'] >= props.minPass )
- uid: password-minimum-length-terraform-state
filters: asset.platform == "terraform-state" && terraform.state.resources.contains( type == /okta_policy_password/ )
mql: |
terraform.state.resources.where( type == /okta_policy_password/ ).all( values['password_min_length'] >= props.minPass )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
-
To explore checks interactively and test MQL against live targets, read Create Checks in cnspec Shell.
-
If you're ready to create your own policy: To learn how to set up, validate, and store policy bundles, read Manage Policies.
-
Learn how to write MQL queries and checks.
-
Read about all the different resources from which MQL can retrieve information.