The Open Policy Agent (OPA) is the open-source industry standard for policy-as-code validation. It provides a general-purpose policy engine to unify policy enforcement across your stacks.
The OPA (pronounced “oh-pa”) language (Rego) is a high-level declarative language for specifying policy as code. Atmos has native support for the OPA decision-making engine to enforce policies across all the components in your stacks (e.g. for microservice configurations).
This is powerful stuff: because you can define many policies, it's possible to apply different policies depending on where a component is defined in the stacks. For example, it could validate differently based on environments or teams.
Use Open Policy Agent (OPA) policies to validate Atmos stacks and component configurations.
Validate component config (vars, settings, backend, env, overrides and other sections) using JSON Schema
Check if the component config (including relations between different component variables) is correct to allow or deny component provisioning using
OPA/Rego policies
Atmos validate component command supports --schema-path, --schema-type and --module-paths command line arguments.
If the arguments are not provided, Atmos will try to find and use the settings.validation section defined in the component's YAML config.
# Validation schemas for OPA for validating atmos stacks and components schemas: # https://www.openpolicyagent.org opa: # Can also be set using `ATMOS_SCHEMAS_OPA_BASE_PATH` ENV var, or `--schemas-opa-dir` command-line arguments # Supports both absolute and relative paths base_path:"stacks/schemas/opa"
In the component manifest, add
the settings.validation section:
Atmos supports OPA policies for components validation in a single Rego file and in multiple Rego files.
As shown in the example above, you can define some Rego constants, modules and helper functions in a separate
file stacks/schemas/opa/catalog/constants/constants.rego, and then import them into the main policy
file stacks/schemas/opa/vpc/validate-vpc-component.rego.
You also need to specify the module_paths attribute in the component's settings.validation section.
The module_paths attribute is an array of filesystem paths (folders or individual files) to the additional modules for schema validation.
Each path can be an absolute path or a path relative to schemas.opa.base_path defined in atmos.yaml.
If a folder is specified in module_paths, Atmos will recursively process the folder and all its sub-folders and load all Rego files into the OPA
engine.
This allows you to separate the common OPA modules, constants and helper functions into a catalog of reusable Rego modules,
and to structure your OPA policies to make them DRY.
If a regex pattern in the 're_match' function contains a backslash to escape special chars (e.g. '.' or '-'),
it must be escaped with another backslash when represented as a regular Go string ('\.', '\-').
The reason is that backslash is also used to escape special characters in Go strings like newline (\n).
If you want to match the backslash character itself, you'll need four slashes.
Atmos allows enforcing custom governance rules based on metadata about Atmos commands and provides a powerful
policy evaluation mechanism by passing structured metadata to OPA policies at runtime.
This metadata enables fine-grained control over when certain actions (like terraform apply) are allowed or denied,
based on the context in which they're executed.
When Atmos runs a command, it supplies an input object to OPA policies that contains detailed contextual information, such as:
process_env: a map of the environment variables in the current process
cli_args: a list of the command line arguments and flags (e.g., executing the atmos terraform apply command will generate the ["terraform", "apply"] list)
tf_cli_vars: a map of variables with proper type conversion from the command-line -var arguments
env_tf_cli_args: a list of arguments from the TF_CLI_ARGS environment variable
env_tf_cli_vars: a map of variables with proper type conversion from the TF_CLI_ARGS environment variable
vars: a map of variables passed to the command, either via the stack config files or CLI flags
other contextual attributes that are returned from the atmos describe component command for a component in a stack
Below is an OPA policy rule to enforce infrastructure governance during command execution.
Specifically, this rule blocks the execution of atmos terraform apply if the variable foo is set to the string "foo".
validate-component.rego
# 'package atmos' is required in all Atmos OPA policies package atmos # Atmos looks for the 'errors' (array of strings) output from all OPA policies # If the 'errors' output contains one or more error messages, Atmos considers the policy failed # Don't allow `terraform apply` if the `foo` variable is set to `foo` # The `input` map contains the `cli_args` attribute (a list of the command line arguments and flags) errors[message] { count(input.cli_args) >= 2 input.cli_args[0] == "terraform" input.cli_args[1] == "apply" input.vars.foo == "foo" message = "the component can't be applied if the 'foo' variable is set to 'foo'" }
The rule checks if:
The cli_args list has at least two items
The command (first item in the cli_args list) is terraform
The subcommand (second item in the cli_args list) is apply
The variable foo is set to "foo"
If all conditions are true, the rule generates an error message.
The generated error message is added to the errors array.
Atmos interprets the presence of any messages in errors as a policy violation and blocks the operation with the
following error:
atmos terraform apply component-1 -s nonprod
the component can't be applied if the 'foo' variable is set to 'foo' exit status 1
Parse and validate arguments from the TF_CLI_ARGS environment variable.
validate-tf-cli-args.rego
package atmos # Block dangerous flags in TF_CLI_ARGS errors[message] { dangerous_flags := ["-auto-approve", "-force", "-lock=false"] flag := dangerous_flags[_] flag in input.env_tf_cli_args input.process_env.ENVIRONMENT == "production" message = sprintf("Flag '%s' is not allowed in production via TF_CLI_ARGS", [flag]) } # Require planfile for apply (positional, not a flag) errors[message] { some i input.cli_args[i] == "apply" # next token exists and is not a flag -> planfile path i+1 < count(input.cli_args) not startswith(input.cli_args[i+1], "-") # Optionally, enforce a prefix/dir policy for plan files not allowed_planfile(input.cli_args[i+1]) message = "Apply must use an approved plan file generated by 'terraform plan -out=...'" } allowed_planfile(p) { startswith(p, "plans/") } # Validate parallelism settings errors[message] { some i # equals form: -parallelism=50 startswith(input.env_tf_cli_args[i], "-parallelism=") parallelism := to_number(replace(input.env_tf_cli_args[i], "-parallelism=", "")) parallelism > 20 message = sprintf("Parallelism cannot exceed 20, got %d", [parallelism]) } errors[message] { some i # space form: -parallelism 50 input.env_tf_cli_args[i] == "-parallelism" i + 1 < count(input.env_tf_cli_args) parallelism := to_number(input.env_tf_cli_args[i+1]) parallelism > 20 message = sprintf("Parallelism cannot exceed 20, got %d", [parallelism]) }