Policy as Code
|
This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production; see the section bibliography for the reference material consulted while preparing these pages. |
Reviewers miss things, and a pull request that adds a database open to the whole Internet looks just like one that adds a tag. Policy as code writes the organisation’s rules (allowed instance sizes, mandatory encryption, no public ingress, approved module sources) as versioned, tested programs that run against every change, so the rule is enforced by the pipeline and not by memory. This page maps the places where such rules can run, from the cheapest (a linter on a laptop) to the strictest (a policy engine inside HCP Terraform that can stop a run), and shows the same Bookshelf rule, "nothing in the network may accept traffic from 0.0.0.0/0", in each of the engines.
It is the last page of the quality group. It builds on
The Dependency Graph and the Plan (the plan is the input of most
policy engines), on CLI Essentials (plan -out and show -json), and it
complements Code Quality and Security Scanning (static
scanners with built-in rules) and Testing (assertions about your own module). The
pipeline wiring lives in CI/CD and Automation, and the platform that
runs Sentinel, OPA and Terraform Policy server-side is described in
HCP Terraform and Terraform Enterprise.
The baseline is Terraform 1.17 (1.17.0 was released on October 21, 2026), checked against the HCP Terraform policy enforcement documentation, the Sentinel documentation, the Terraform Policy documentation (reference version v0.5.x), and the documentation of Conftest, Checkov and TFLint. Terraform Policy is the youngest of these and its documentation and the changelog disagree about its maturity; see Maturity: read this before relying on it.
Why policy as code, and where it runs
A policy is a rule over data that Terraform already produces. There are four natural places to evaluate it, each trading speed for authority.
| Stage | What the rule can see | Typical tools |
|---|---|---|
Editor and pre-commit |
The |
TFLint (built-in and custom rules), Checkov, Trivy |
Pull request (CI), before apply |
The JSON plan from |
Conftest with OPA, Checkov plan scanning, Sentinel CLI, |
Run in HCP Terraform or Terraform Enterprise |
Plan, configuration, state and run metadata; can block the run |
Sentinel, OPA, Terraform Policy |
After apply |
Real infrastructure |
Cloud-native services (outside Terraform’s scope) |
Three properties make a policy engine different from a linter:
-
It sees the plan, not just the code. A module that builds an
instance_classfrom three variables can only be judged once the values are known. The plan is the first artifact in which they are. -
It has an enforcement level. A rule may be advisory (a warning in the run), or it may stop the run, with or without an override. A linter has only pass and fail.
-
It is applied by the platform, not by the author. A policy set attached to a workspace or project runs whether or not the author’s pipeline remembers to call it. That is the control that matters for compliance.
Policies are also software. Keep them in a repository, review them, unit-test them with mocks, and release them with a version, exactly as for the modules they govern.
The plan that engines read contains sensitive values in clear text when it is rendered with show -json, so the file is as
sensitive as state; the official page warns about this. Treat plan artifacts accordingly (see
Sensitive Data, Ephemeral Values and Write-Only Arguments).
# produce the plan every CI-side engine below consumes
terraform plan -out=tfplan -input=false
terraform show -json tfplan > tfplan.json
Source: Terraform Docs — terraform show.
The JSON document has a stable, versioned shape (format_version, currently 1.0). Its most useful keys for policies
are resource_changes (one entry per resource instance, each with address, mode, type, name and a change object
holding actions, before, after, after_unknown, before_sensitive and after_sensitive), planned_values,
prior_state, configuration, variables and output_changes. Consumers must ignore properties they do not recognise,
because minor versions add them.
Source: Terraform Docs — JSON Output Format.
Choosing an engine
| Sentinel | OPA (Rego) | Terraform Policy | Linter rules | |
|---|---|---|---|---|
Language |
Sentinel, a purpose-built DSL |
Rego |
HCL |
Go plugin, Rego (TFLint OPA ruleset), Python or YAML (Checkov) |
Runs in HCP Terraform |
Yes (workspaces) |
Yes (workspaces) |
Yes (workspaces and Stacks) |
No |
Enforcement levels |
advisory, soft-mandatory, hard-mandatory |
advisory, mandatory |
advisory, mandatory-overridable, mandatory |
Exit code and severity |
Can run before providers download |
No |
No |
Yes, for provider and module policies at setup time |
Yes (static) |
Can query other resources or data sources |
Manual iteration over plan data |
Manual iteration over plan data |
|
No |
A policy set can contain policies of one framework only, but sets of different frameworks can be applied to the same workspace. That allows an incremental migration: keep existing Sentinel sets and add Terraform Policy or OPA sets next to them.
Source: Terraform Docs — Manage policy sets.
Policy sets and enforcement levels
Whatever the language, HCP Terraform and Terraform Enterprise organise policies the same way. A policy set is a collection of policies applied globally, to chosen projects, workspaces or Stacks, or to workspaces carrying chosen tags, with optional exclusions. Each policy has an enforcement level that decides what a failure does to the run.
| Framework | Levels | Meaning of a failure |
|---|---|---|
Sentinel |
|
Advisory logs a warning and the run continues. Soft-mandatory stops the run unless someone with permission overrides it. Hard-mandatory stops the run and cannot be overridden by the policy level alone. |
OPA |
|
Advisory warns. Mandatory stops the run; a user with the Manage Policy Overrides permission can override it. |
Terraform Policy |
|
In HCL the middle level is written |
Source: Terraform Docs — Manage policy sets.
A Sentinel policy set has a second override: enabling the set option that allows overriding mandatory failures lets administrators or team owners override even hard-mandatory failures, and the page states that this setting takes precedence over each policy’s level. That is a deliberate escape hatch, so decide who may use it before enabling it.
A policy set reaches HCP Terraform in three ways: pasted in the web UI (experiments only), connected to a VCS repository
so that pushes update the set, or pushed from a pipeline through the API or the tfe_policy_set resource of the TFE
provider. The Free edition includes one policy set of up to five policies; VCS and API publishing need the Standard or
Premium edition. Sentinel checks (the original mechanism) support Sentinel up to 0.40.x and can read cost estimation
data. Evaluations are required for OPA, can be used by Sentinel sets of the Agent type, allow overrides and Sentinel
runtime pinning, but cannot read cost estimation data. Evaluations run on a self-managed agent pool only on the Premium
edition and when the pool accepts policy jobs; otherwise they run on HashiCorp’s infrastructure.
Source: Terraform Docs — Manage policy sets.
Sentinel
Sentinel is HashiCorp’s embedded policy language. A policy file ends in .sentinel, declares the data it needs with
import, and evaluates to a main rule. HCP Terraform provides four imports for Terraform policies and does not allow
custom imports.
| Import | Content |
|---|---|
|
The plan produced by |
|
The parsed configuration files. |
|
The state at plan time. |
|
Run metadata: workspace, organization, variables, whether the run is a destroy, and so on. |
New policies should use tfplan/v2, the version designed for Terraform 0.12 and later. It is loaded by path and given an
alias, and its data comes from the same JSON that terraform show -json prints. Its top-level keys are
terraform_version, variables (root-module variables, indexed by name), planned_values (a predicted state; unknown
values are omitted), resource_changes (every resource and data source, indexed by address), resource_drift (the
changes Terraform detected outside its control, same shape as resource_changes), output_changes, and raw (the
unprocessed plan, for experts). In a resource_changes entry, mode is managed or data, change.actions lists the
operations in execution order (a normal replacement is ["delete", "create"], a create-before-destroy replacement is
["create", "delete"]), change.before is null for a pure create and change.after is null for a pure delete, and
change.after_unknown tells you that a value that is missing from after is merely unknown until apply.
The following policy restricts the Bookshelf database size. It filters resource_changes the way the import’s
documentation does, skips instances that the plan deletes (their after is null) and uses else so that a value which is
still unknown does not abort the policy.
import "tfplan/v2" as tfplan
allowed_classes = ["db.t4g.micro", "db.t4g.small"]
databases = filter tfplan.resource_changes as _, rc {
rc.mode is "managed" and
rc.type is "aws_db_instance" and
"delete" not in rc.change.actions
}
main = rule {
all databases as _, rc {
(rc.change.after.instance_class else "") in allowed_classes
}
}
Source: Terraform Docs — Sentinel tfplan/v2 import. The filter is the documented one; the rule and the allowed list are Bookshelf illustrations.
Sentinel’s all and any stop at the first failing element, which is fast but gives a poor message. The documentation
recommends a helper that loops over every instance, records every violation with its full resource address, and skips
computed attributes, and then a main rule that calls it. HCP Terraform has no server-side mock, so test with the
Sentinel command-line tool and mock files for the imports.
The policy is attached to a workspace through a policy set. The set’s repository has a sentinel.hcl file at its root,
and every local policy file sits in the same directory and ends in .sentinel.
policy "restrict-db-instance-class" {
source = "./restrict-db-instance-class.sentinel"
enforcement_level = "soft-mandatory"
}
Source: Terraform Docs — Sentinel policy sets from VCS. The block shape is the documented one; the name, file and level are Bookshelf choices.
The same file can also define module blocks (shared Sentinel code, for example a source pointing at a local .sentinel file under modules/)
and a policy can use a remote source URL with a checksum, which is how policies published in the Terraform Registry are
consumed.
The older tfplan import used in the Sentinel product documentation (tfplan.resources.<type> with applied values) is
still shown in the Sentinel docs, but it exposes a flattened view. The v2 import mirrors the JSON plan and is the better
starting point for new work.
Source: Sentinel Docs — Terraform.
OPA and Rego
Open Policy Agent policies are written in Rego, a declarative query language. HCP Terraform runs OPA through policy evaluations and passes the plan and the run as input documents.
-
input.planis the plan JSON, includingformat_version,output_changes,planned_values,resource_changesandterraform_version. -
input.runcarries run data:id,created_at,created_by,is_destroy,speculative,variables,organization,projectandworkspace(withname,auto_apply,tags,execution_modeandvcs_repo).
Source: Terraform Docs — Define OPA policies.
The set is described by policies.hcl (or policies.json) in the repository root. Each policy block names a Rego
query, typically a package plus a rule, such as data.terraform.policy1.deny. The query must return an array, and an
empty array means the policy passed. description and enforcement_level are optional, and the level defaults to
advisory when omitted. Rego files must end in .rego and may live in other directories of the repository.
policy "no-world-open-ingress" {
query = "data.terraform.policies.no_world_open_ingress.deny"
enforcement_level = "mandatory"
description = "Bookshelf network rules must not accept traffic from 0.0.0.0/0"
}
Source: Terraform Docs — OPA policy sets from VCS. The block shape is the documented one; the names are Bookshelf illustrations.
The matching Rego collects a message for every offending rule. The resource type and attributes
(aws_vpc_security_group_ingress_rule, cidr_ipv4) are those of the AWS provider resource.
package terraform.policies.no_world_open_ingress
import rego.v1
deny := [msg |
some rc in input.plan.resource_changes
rc.type == "aws_vpc_security_group_ingress_rule"
not "delete" in rc.change.actions
rc.change.after.cidr_ipv4 == "0.0.0.0/0"
msg := sprintf("%s allows ingress from 0.0.0.0/0", [rc.address])
]
Source: Terraform Docs — Define OPA policies for the input document, AWS provider — aws_vpc_security_group_ingress_rule for the attributes. The rule itself is a Bookshelf illustration.
Test the Rego with opa test, as the official page does for its examples, and keep the test fixtures small plan fragments.
OPA in HCP Terraform is pass or fail with a warning level; it has no soft-mandatory level.
Conftest against the JSON plan
The same Rego can run before the code reaches HCP Terraform, or in any CI system, with Conftest, a test runner for
structured data built on OPA. By default Conftest reads policies from the policy directory (override it with --policy
or -p), evaluates rules in the main package (override it with --namespace, or use --all-namespaces) and recognises
rules named deny, violation and warn, including names with a suffix such as deny_myrule. deny and violation
rules fail the run; warn rules only warn. A file argument, or - for standard input, selects the data.
For a Terraform plan the document root is the plan itself, so the resources are at input.resource_changes and not under
input.plan.
package main
import rego.v1
deny contains msg if {
some rc in input.resource_changes
rc.type == "aws_vpc_security_group_ingress_rule"
not "delete" in rc.change.actions
rc.change.after.cidr_ipv4 == "0.0.0.0/0"
msg := sprintf("%s allows ingress from 0.0.0.0/0", [rc.address])
}
terraform plan -out=tfplan -input=false
terraform show -json tfplan > tfplan.json
conftest test tfplan.json
Source: Conftest documentation for the policy directory, namespaces and rule names, Terraform Docs — terraform show and Terraform Docs — JSON Output Format for the plan JSON. The Conftest page does not document Terraform plan input itself; that usage follows from the JSON document layout.
Conftest is installed from a release archive, brew install conftest, scoop install conftest, mise, the
openpolicyagent/conftest container image or go install; the release assets and images can be verified with GitHub
artifact attestations and cosign. Pin the version in CI.
Source: Conftest — Installation.
In a pipeline this is one step between plan and apply:
- name: Policy check
run: |
terraform show -json tfplan > tfplan.json
conftest test tfplan.json --policy policy
Source: Conftest documentation. The step is a sketch for the workflow shown in CI/CD and Automation.
The two Rego variants differ only in the document root (input.plan in HCP Terraform, input in Conftest) and in the way
a failure is reported (an array returned to the platform, a deny set in Conftest). A policy written for both can
normalise the root first, for example by reading input.plan when it exists and input otherwise.
Terraform Policy
Terraform Policy is HashiCorp’s HCL-based framework, designed so that rules read like Terraform configuration and can refer to any resource a provider defines. It adds three things the older engines lack: helpers to look up related resources and data sources, evaluation before providers and modules are downloaded, and evaluation of values that are only known after apply.
Source: Terraform Docs — Terraform Policy.
Maturity: read this before relying on it
The sources disagree, and the page states the disagreement rather than choosing a side.
-
The Terraform 1.17.0 changelog (October 21, 2026) says Terraform Policy is now generally available, and that the
-policiesflag forplan,applyandqueryno longer needs an experimental build or the-allow-experimental-featuresflag. -
The Terraform Policy documentation still carries a BETA label and the warning not to use beta features in production, on the overview, install, policy-writing, compare and reference pages. Its version selector marks v0.5.x as latest and v0.1.x to v0.4.x as beta; the install page states that Terraform Policy requires Terraform v1.16 or later, and the HCP Terraform policy enforcement overview still mentions "v1.16alpha or later" and enabling pre-releases in the organization settings.
-
The
terraform planreference page (1.17.x) does not document a-policiesoption. Theterraform querypage and the changelog do. -
The
mainbranch changelog lists the-policiesflag ofterraform queryas an experiment of the unreleased 1.18.
Treat the framework as new: pin the tfpolicy version, expect syntax changes between releases, check the documentation
version that matches the CLI, and start with advisory policies. Re-read the changelog before upgrading.
Files, blocks and the tfpolicy CLI
The recommended repository layout keeps policies, tests and optional plugins apart.
./
├── policies/
│ ├── resource-tagging.policy.hcl
│ └── security-rules.policy.hcl
├── tests/
│ ├── resource-tagging.policytest.hcl
│ └── security-rules.policytest.hcl
├── plugins/
│ ├── src/
│ └── bin/
└── README.md
Source: Terraform Docs — Set up your project for Terraform policy. The tree is abbreviated from the documented one.
Policy files end in .policy.hcl and test files in .policytest.hcl. Unlike a Terraform configuration, each file in a
directory is evaluated separately. A file that contains resource or provider policies starts with a policy block whose
required_providers declares the provider the policies are written against. The policy blocks are provider_policy,
resource_policy and module_policy; inside them, attrs. reads the attributes of the provider, resource or module,
meta. reads arguments such as a source, and prior_attrs. (resource policies only) reads the previous values on update
and delete. enforce blocks hold a condition and an error_message, locals and input blocks add values and
parameters, filter limits which objects a policy applies to, and operations limits the lifecycle operations. Most
Terraform operators and library functions work, plus core::getresources, core::getdatasource, core::gethttprequest
and core::semverconstraint. Custom functions can be written as Go plugins and called as plugin::name::function.
The Bookshelf rule, in Terraform Policy:
policy {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 6.0.0, < 7.0.0"
}
}
}
resource_policy "aws_vpc_security_group_ingress_rule" "no_world_open" {
enforcement_level = "mandatory_overridable"
enforce {
condition = attrs.cidr_ipv4 != "0.0.0.0/0"
error_message = "Bookshelf network rules must not accept traffic from 0.0.0.0/0"
}
}
Source: Terraform Docs — Terraform policy examples. The structure and the enforcement_level value follow the documented examples; the resource and condition are a Bookshelf illustration.
Its test file mocks resources and marks the one that must be rejected with expect_failure = true; targets points to the
policy file under test.
policytest {
targets = ["../policies/no_world_open.policy.hcl"]
}
resource "aws_vpc_security_group_ingress_rule" "pass_private" {
attrs = {
cidr_ipv4 = "10.0.0.0/16"
ip_protocol = "tcp"
from_port = 5432
to_port = 5432
}
}
resource "aws_vpc_security_group_ingress_rule" "fail_world_open" {
expect_failure = true
attrs = {
cidr_ipv4 = "0.0.0.0/0"
ip_protocol = "tcp"
from_port = 443
to_port = 443
}
}
The tfpolicy command-line tool (installed with brew install hashicorp/tap/tfpolicy or from the HashiCorp releases
site; checksums and signatures are published) has validate, test and version commands.
$ tfpolicy validate --policies=policies/
Success! All policies are valid.
$ tfpolicy test --policies=policies/ --tests=tests/
# no_world_open.policytest.hcl... running
# resource.aws_vpc_security_group_ingress_rule.pass_private... pass
# resource.aws_vpc_security_group_ingress_rule.fail_world_open... pass
# no_world_open.policytest.hcl... pass
Source: Terraform Docs — Terraform policy examples. The output follows the documented format; the file and resource names are illustrative.
A policy with core::getresources shows the main advantage over the other engines: it can check a related resource
without iterating the plan by hand. The official example requires that the S3 bucket named by a CloudTrail trail has a
private ACL, and a second one (prior_attrs.size) forbids shrinking a volume on update. Provider and module policies
(for example a condition on meta.source that requires the official AWS provider address) are evaluated at setup time, so a forbidden module
source can be refused during initialization.
The -policies flag and HCP Terraform
Three evaluation stages exist. Setup-time policies (provider and module policies) run during workspace initialization, before providers or modules are downloaded. Plan-time policies run against the plan when the referenced attributes are available. Apply-time policies run after apply for values that were "known after apply"; violations are then reported after the change has happened, so someone must fix them and redeploy.
Source: Terraform Docs — Terraform Policy.
In HCP Terraform the repository is connected as a VCS-backed policy set and targeted at projects, workspaces or tags; a
run evaluates it and reports results. Locally, the Terraform CLI flag evaluates explicit policy directories. The only
command whose reference documents the flag at the time of writing is terraform query, where policies are evaluated
against the resources discovered by list blocks (see
Import and Query for the prerequisites, such as include_resource = true).
terraform query -policies=./policies
terraform query -policies=./policies/security -policies=./policies/tagging
Source: Terraform Docs — terraform query.
The changelog entry for 1.17.0 also names plan and apply as accepting the flag. The CLI reference of those commands did
not list it when this page was written, so run terraform plan -help on your version before putting it in a pipeline.
Source: Terraform CHANGELOG — v1.17.
Custom rules in Checkov and TFLint
Static scanners run on source, need no HCP Terraform and fit pre-commit hooks. Both accept your own rules, which is the lightest form of policy as code. Their built-in rule sets and installation are covered in Code Quality and Security Scanning. They see configuration, not plan values, so they cannot judge a value computed from variables.
Checkov custom policies
Checkov supports custom policies written in Python, which can check the status of configuration attributes, and in YAML,
which can also check the connection between resource types and combine conditions with and, or and not. A YAML
policy has metadata (name, ID, category, optional guideline, optional severity of INFO, LOW, MEDIUM, HIGH or
CRITICAL) and a definition. A Python policy extends BaseResourceCheck, sets name, id, supported_resources and
categories, implements scan_resource_conf (every attribute value arrives as a list, so the value is read from index
[0]) and registers itself by instantiating the class at module level. The directory needs an __init__.py, file names
must be unique, and the directory is passed with --external-checks-dir.
from __future__ import annotations
from typing import Any
from checkov.common.models.enums import CheckResult, CheckCategories
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
class BookshelfDbEncrypted(BaseResourceCheck):
def __init__(self) -> None:
name = "Ensure Bookshelf databases are encrypted at rest"
id = "CKV_BOOKSHELF_1"
supported_resources = ("aws_db_instance",)
categories = (CheckCategories.ENCRYPTION,)
super().__init__(name=name, id=id, categories=categories, supported_resources=supported_resources)
def scan_resource_conf(self, conf: dict[str, list[Any]]) -> CheckResult:
encrypted = conf.get("storage_encrypted")
if encrypted and encrypted[0]:
return CheckResult.PASSED
return CheckResult.FAILED
check = BookshelfDbEncrypted()
checkov -d . --external-checks-dir my_extra_checks
Source: Checkov Docs — Python Custom Policies, Checkov Docs — YAML Custom Policies. The structure follows the documented example; the check ID, class and attribute are Bookshelf illustrations.
TFLint custom rules
TFLint rules beyond the built-in ones come from ruleset plugins, independent binaries that talk to TFLint over gRPC. To
write one in Go, create a repository named with the prefix tflint-ruleset- from the official template, enable it in .tflint.hcl with a
plugin block, build it with make install (which installs it under the .tflint.d/plugins directory of your home), and add rules with the
plugin SDK. To distribute it so that tflint --init can install it, publish GitHub Releases with the required assets,
checksums and a signature.
plugin "template" {
enabled = true
}
For a few organisation rules, the OPA ruleset lets you write them in Rego instead, in the .tflint.d/policies directory of the project (or of your home directory). The ruleset is documented as experimental, with frequent breaking changes, and cannot judge values
that are unknown, null or undefined in a static view of the code.
plugin "opa" {
enabled = true
version = "0.11.0"
source = "github.com/terraform-linters/tflint-ruleset-opa"
}
package tflint
import rego.v1
deny_invalid_s3_bucket_name contains issue if {
buckets := terraform.resources("aws_s3_bucket", {"bucket": "string"}, {})
name := buckets[_].config.bucket
not startswith(name.value, "example-com-")
issue := tflint.issue(`Bucket names should always start with "example-com-"`, name.range)
}
Source: TFLint OPA ruleset — README. The rule is the README’s own example.
The README’s rule of thumb: start with the OPA ruleset for a small number of rules and a small team; move to a custom Go ruleset when you have many complex rules or distribute them to many teams.
Source: TFLint OPA ruleset — README.
A layered strategy for Bookshelf
| Layer | Bookshelf practice |
|---|---|
Pre-commit |
|
Pull request |
|
Platform |
HCP Terraform policy sets attached by project: |
Exceptions |
A named team may override soft failures; the Sentinel set-level override stays disabled for production projects. |
Change of policy |
Policies live in their own repository, with tests ( |
Roll out new rules in advisory mode, measure the failures for a sprint, fix the modules, and only then raise the level. A mandatory rule that blocks every run teaches teams to look for the override rather than for the cause.
What changed recently
-
Terraform 1.17.0 (October 21, 2026): the changelog announces Terraform Policy as generally available and the
-policiesflag forplan,applyandquerywithout an experimental build; the documentation still labels the framework beta (see Maturity: read this before relying on it). Query results for policies can also beUnknownorN/A. -
Terraform Policy docs: reference version v0.5.x is the latest, and v0.1.x to v0.4.x are marked beta. The
tfpolicyCLI needs Terraform 1.16 or later according to its install page. -
Sentinel in HCP Terraform: policy evaluations allow overrides and pinning of the Sentinel runtime (documented from Sentinel 0.23.1, with agent 1.13.1 or later), while checks remain limited to Sentinel 0.40.x.
-
OpenTofu: has no Sentinel, OPA or Terraform Policy integration, because those are HCP Terraform features. Conftest, Checkov and TFLint work with its plans and sources (plans are rendered with
tofu show -json), and platforms such as Spacelift or env0 provide their own OPA integration.
Pitfalls
-
Evaluating a plan without handling unknown values:
afteromits them. Useelsein Sentinel,object.getin Rego, and readafter_unknown. -
Forgetting deleted resources:
change.afteris null when the action is a pure delete. -
Policies on
aws_security_groupinline rules: the provider recommends separate rule resources, so a policy must cover both shapes if the code base mixes them. -
Confusing the Rego document root:
input.planin HCP Terraform, the plan itself in Conftest. -
Relying on a static linter for values that come from variables.
-
Treating the plan JSON as harmless: it can contain secrets in clear text.
-
Making a brand-new policy mandatory on day one.