Using Sentinel Policy to enforce continuous deployment windows
Sentinel, is the HashiCorp framework for policy as code management, it is built to be embedded in existing software to enable fine-grained, logic-based policy decisions. A policy describes under what circumstances specific behaviors are allowed. In our last couple of posts, we looked at the concepts behind policy as code and how you can use it with the HashiCorp enterprise products like Terraform Enterprise. In this post, we are going to examine how Sentinel Policy and the Sentinel Simulator can be used to ensure your Continuous Delivery system only deploys your application within a specified time window.
Continuous Delivery (CD) is the next level to Continuous Integration (CI), in addition to building and testing your application, you also automatically deploy built artifacts into your production environment. It is synonymous with agile and moving fast, and while you will undoubtedly have unit, integration, functional, and a whole host of other tests covering your work, defects also include misunderstood scope or missing elements from a specification. Sometimes it is not until the code hits a production environment and we see anomalies in logging or other metrics that we realize there is a problem. For this reason, many teams choose to operate a deployment window; the code is only deployed automatically during set hours when there is appropriate staffing to monitor and potentially revert a buggy release.
In the same way that we can embed Sentinel into a pipeline to enforce policy for Terraform plans, or Vault secrets, we can also enforce policy in a continuous delivery pipeline.
The examples in this post use the free to use Sentinel Simulator: sentinel v0.1.0 Binaries | HashiCorp Releases
» Writing Sentinel Policy
The first thing we need to do is to write our policy for our deployment window, this is only going to allow deployments between the hours of 9 am and 5 pm.
import "time"
timespace = time.now
// Note: Time is CET by default
is_weekday = rule { timespace.day not in ["saturday", "sunday"] }
is_deployment_hours = rule { timespace.hour > 8 and timespace.hour < 17 }
main = rule {
is_deployment_hours and is_weekday
}
» Testing Sentinel Policy
To ensure our Sentinel policy is functioning correctly we can write tests to check the rules. Test files are stored in a top-level test
folder with the tests for each policy in a sub-folder with the same name as the policy. In our example our folder structure is going to look something like the following:
$ tree
.
├── deployment_window.sentinel
└── test
├── deployment_window
│ ├── fail_deployment_hours.json
│ ├── fail_weekday.json
│ └── pass.json
Tests are declared using a simple JSON format, to ensure that we do not have to change the clock on our computer or wait until a particular time to test our policy, we are going to use one of the in-built features of the Simulator which allows mocking for any of the imported functions:
{
"mock": {
"time": {
"now": {
"day": "tuesday",
"hour": 9
}
}
},
"test": {
"is_deployment_hours": true,
"is_weekday": true,
"main": true
}
}
We are mocking time.now
and setting a fixed hour and day; we also set out the conditions for a successful test. In our case the rules is_deployment_hours
, is_weekday
, and main
should all evaluate to true.
To test our policy we use the sentinel test --verbose
command, the --verbose
flag gives us additional output regarding the rules in the test.
$ sentinel test --verbose deployment_window.sentinel
PASS - test/deployment_window/pass.json
trace:
TRUE - deployment_window.sentinel:9:1 - Rule "main"
TRUE - deployment_window.sentinel:10:2 - is_deployment_hours
TRUE - deployment_window.sentinel:7:30 - timespace.hour > 8 and timespace.hour < 17
TRUE - deployment_window.sentinel:7:30 - timespace.hour > 8
TRUE - deployment_window.sentinel:7:53 - timespace.hour < 17
TRUE - deployment_window.sentinel:10:26 - is_weekday
TRUE - deployment_window.sentinel:6:21 - timespace.day not in ["saturday", "sunday"]
TRUE - deployment_window.sentinel:7:1 - Rule "is_deployment_hours"
TRUE - deployment_window.sentinel:7:30 - timespace.hour > 8
TRUE -
deployment_window.sentinel:7:53 - timespace.hour < 17
TRUE - deployment_window.sentinel:6:1 - Rule "is_weekday"
» CI Deployment Workflow
Now we have our policy and some tests verifying its correctness, we can setup our CI job. For this example we are getting meta and have a repository containing all of our Sentinel policy, this should only be released during our allowed hours. To create the automated process for testing and deployment, we need to setup a workflow using CircleCI; the principles will be the same regardless of your CI environment.
The CircleCI configuration contains two jobs, one to test our Sentinel policy, and one which deploys it to GitHub releases. In the below snippet we can see the steps for the deploy job, the full configuration can be found at the following link: https://github.com/nicholasjackson/sentinel_aws_example/blob/master/.circleci/config.yml
version: 2.0
# ...
deploy:
machine:
enabled: true
steps:
- checkout
- run:
name: install dependencies
command: |
wget https://releases.hashicorp.com/sentinel/0.1.0/sentinel_0.1.0_linux_amd64.zip
unzip sentinel_0.1.0_linux_amd64.zip
wget https://github.com/aktau/github-release/releases/download/v0.7.2/linux-amd64-github-release.tar.bz2
tar -xvf linux-amd64-github-release.tar.bz2
- run:
name: check deployment window
command: ./sentinel apply deployment_window.sentinel
- run:
name: release
command: |
./bin/linux/amd64/github-release release \
--user nicholasjackson \
--repo sentinel_aws_example \
--tag v0.1.0 \
--name "Latest sentinel policy"
#...
To ensure that our project only deploys during our defined hours we execute our policy with the Sentinel Simulator command: sentinel apply deployment_window.sentinel
. If the policy passes then the command returns a status code 0
, if it fails it returns status code 1
, and if the policy contains errors it returns status code 2
.
- run:
name: check deployment window
command: ./sentinel apply deployment_window.sentinel
Now when we push our code to CircleCI the job will first run the test job and when that passes the deploy job will run.
Because one of the conditions in our deploy step is to apply our Sentinel policy, we can now be sure that we only release inside our deployment window. When the policy passes, our job continues and creates a new release in GitHub.
If we try to deploy outside the window the deploy job fails, and nothing is released.
» Summary
This post has shown how HashiCorp's Sentinel Policy framework can be used to enforce a deployment window for your Continuous Delivery process. The code repository accompanying this post can be found here: GitHub - nicholasjackson/sentinel_aws_example: Example Sentinel policy to check AWS providers
For more information on Sentinel and how it integrates into HashiCorp enterprise products, please follow the below links:
Sign up for the latest HashiCorp news
More blog posts like this one
HashiCorp 2024 year in review
The future looks bright as we look back at what we and our customers accomplished this year.
HashiCorp Ambassador call for submissions 2025
The submission window for HashiCorp Ambassador — our program to recognize individuals for knowledge sharing, mentorship, and kindness in the community — is now open.
HashiCorp at re:Invent 2024: Infrastructure Lifecycle Management with AWS
A recap of HashiCorp infrastructure news and developments on AWS from the past year, from a new provider launch to simplifying infrastructure provisioning and more.