Skip to content

Commit

Permalink
add stack-validation examples (pulumi#1112)
Browse files Browse the repository at this point in the history
* add stack-validation examples

* Update readme with instructions on how to try it out
  • Loading branch information
MitchellGerdisch committed Nov 8, 2021
1 parent 569bcc4 commit 6bf0c30
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 0 deletions.
2 changes: 2 additions & 0 deletions policy-packs/stackvalidation-python/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.pyc
venv/
6 changes: 6 additions & 0 deletions policy-packs/stackvalidation-python/PulumiPolicy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
runtime:
name: python
options:
virtualenv: venv
version: 0.0.1
description: A minimal Policy Pack for AWS using Python.
32 changes: 32 additions & 0 deletions policy-packs/stackvalidation-python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Stack Validation Example - python
This provides an example of a stack validation policy.
See [Policy as Code Core Concepts](https://www.pulumi.com/docs/guides/crossguard/core-concepts/) for more information.

In a nutshell, the key differences between stack validation policies and resource validation policies are:
* Stack validation policies can check resource outputs that are unknown to Pulumi until the provider creates the resource.
* Stack validation policies can run checks that cross the entire set of resources in the stack.

The example in this folder checks two things:
* S3 bucket region: Although a bit of a contrived example, the region is an output assigned by AWS and thus is not known before the resource is created and thus is not able to be tested in a resource validation policy.
* S3 bucket count: Checks the number of S3 buckets created in the stack. This is to show that a stack validation policy has access to the entire stack and not just individual resources like resource valiation policies do.

## Try It Out
* `mkdir stack-validation && cd stack-validation`
* `mkdir policy-pack && cd policy-pack`
* Copy the stack validation policy pack here.
* Set up python virtual environment.
* `python3 -m venv ./venv`
* `./venv/bin/python -m pip install --upgrade pip setuptools wheel`
* `./venv/bin/python -m pip install -r ./requirements.txt`
* `mkdir ../test-project && cd ../test-project`
* `pulumi new aws-python`
* Note that the Pulumi program and the policy do not need to be written in the same language. So, you can actually use `pulumi new aws-typescript` for the project and this policy will still work.
* `pulumi config set aws:region us-east-1`
* Or any region other than us-west-1, or change the policy accordingly.
* Edit `__main__.py` and copy/paste the s3 bucket declaration so you are creating 2 buckets.
* Run `pulumi up --policy-pack ../policy-pack` to preview and then provision the resources.
* Note that the preview portion of the `pulumi up` will NOT trigger the "region" check but will trigger the "count" check.
* Once you enter `yes` and the resources are actually provisioned, then you will see the "region" check is triggered. This is because the bucket `region` property being checked in the policy is not known until AWS responds with the resource outputs.
* Clean Up
* `pulumi destroy -y` to clean up resources
* `pulumi stack rm dev` to completely remove the stack and project.
42 changes: 42 additions & 0 deletions policy-packs/stackvalidation-python/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from pulumi_policy import (
EnforcementLevel,
PolicyPack,
ReportViolation,
StackValidationArgs,
StackValidationPolicy,
)

required_region = "us-west-1"
max_num_buckets = 1

def s3_region_check_validator(stack: StackValidationArgs, report_violation: ReportViolation):
for resource in stack.resources:
if resource.resource_type == "aws:s3/bucket:Bucket":
if "region" in resource.props and resource.props["region"] != required_region:
report_violation(f"Bucket, {resource.name}, must be in region {required_region}")

s3_region_check = StackValidationPolicy(
name="s3-region-check",
description= "Checks the region the bucket was deployed in.",
validate=s3_region_check_validator
)

def s3_count_check_validator(stack: StackValidationArgs, report_violation: ReportViolation):
buckets = list(filter((lambda resource: resource.resource_type == "aws:s3/bucket:Bucket"), stack.resources))
if len(buckets) > max_num_buckets:
report_violation(f"No more than {max_num_buckets} bucket(s) should be created.")

s3_count_check = StackValidationPolicy(
name="s3-count-check",
description= "Checks the number of buckets created.",
validate=s3_count_check_validator
)

PolicyPack(
name="aws-python",
enforcement_level=EnforcementLevel.ADVISORY,
policies=[
s3_region_check,
s3_count_check
],
)
2 changes: 2 additions & 0 deletions policy-packs/stackvalidation-python/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pulumi-policy>=1.3.0,<2.0.0
pulumi-aws>=4.0.0,<5.0.0
2 changes: 2 additions & 0 deletions policy-packs/stackvalidation-ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/bin/
/node_modules/
2 changes: 2 additions & 0 deletions policy-packs/stackvalidation-ts/PulumiPolicy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
description: A minimal Policy Pack for AWS using TypeScript.
runtime: nodejs
29 changes: 29 additions & 0 deletions policy-packs/stackvalidation-ts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Stack Validation Example - Typescript
This provides an example of a stack validation policy.
See [Policy as Code Core Concepts](https://www.pulumi.com/docs/guides/crossguard/core-concepts/) for more information.

In a nutshell, the key differences between stack validation policies and resource validation policies are:
* Stack validation policies can check resource outputs that are unknown to Pulumi until the provider creates the resource.
* Stack validation policies can run checks that cross the entire set of resources in the stack.

The example in this folder checks two things:
* S3 bucket region: Although a bit of a contrived example, the region is an output assigned by AWS and thus is not known before the resource is created and thus is not able to be tested in a resource validation policy.
* S3 bucket count: Checks the number of S3 buckets created in the stack. This is to show that a stack validation policy has access to the entire stack and not just individual resources like resource valiation policies do.

## Try It Out
* `mkdir stack-validation && cd stack-validation`
* `mkdir policy-pack && cd policy-pack`
* Copy the stack validation policy pack here.
* `npm i`
* `mkdir ../test-project && cd ../test-project`
* `pulumi new aws-typescript`
* Note that the Pulumi program and the policy do not need to be written in the same language. So, you can actually use `pulumi new aws-python` for the project and this policy will still work.
* `pulumi config set aws:region us-east-1`
* Or any region other than us-west-1, or change the policy accordingly.
* Edit `index.ts` and copy/paste the s3 bucket declaration so you are creating 2 buckets.
* Run `pulumi up --policy-pack ../policy-pack` to preview and then provision the resources.
* Note that the preview portion of the `pulumi up` will NOT trigger the "region" check but will trigger the "count" check.
* Once you enter `yes` and the resources are actually provisioned, then you will see the "region" check is triggered. This is because the bucket `region` property being checked in the policy is not known until AWS responds with the resource outputs.
* Clean Up
* `pulumi destroy -y` to clean up resources
* `pulumi stack rm dev` to completely remove the stack and project.
40 changes: 40 additions & 0 deletions policy-packs/stackvalidation-ts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as aws from "@pulumi/aws";
import { PolicyPack, validateStackResourcesOfType } from "@pulumi/policy";

const requiredRegion = "us-west-1"
const maxNumBuckets = 1

new PolicyPack("stackvalidation-ts", {
policies: [
{
name: "s3-region-check",
description: "Checks the region the bucket was deployed in.",
enforcementLevel: "advisory", // "mandatory"
validateStack: (stack, reportViolation) => {
// stack contains all the resources created by the stack. So, loop through them to find S3 buckets and check the region output.
for (const resource of stack.resources) {
if (resource.type == "aws:s3/bucket:Bucket") {
// Check if the region property has been set yet - which it won't on initial preview since AWS hasn't returned that output property yet.
// But if is set which will be the case once the stack is created and for subsequent previews, check it.
if (resource.props.hasOwnProperty("region") && resource.props.region != requiredRegion) {
reportViolation(`Bucket, ${resource.name}, must be in region ${requiredRegion}`)
}
}
}
}
},
{
name: "s3-count-check",
description: "Checks the number of S3 buckets created in the stack.",
enforcementLevel: "advisory", // "mandatory"
validateStack: (stack, reportViolation) => {
// Gather up all the buckets in the stack.
const buckets = stack.resources.filter((resource) => resource.isType(aws.s3.Bucket))
// Make sure there no more than the allowed number of buckets.
if (buckets.length > maxNumBuckets) {
reportViolation(`No more than ${maxNumBuckets} bucket(s) should be created.`)
}
}
}
],
});
12 changes: 12 additions & 0 deletions policy-packs/stackvalidation-ts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "aws-typescript",
"version": "0.0.1",
"dependencies": {
"@pulumi/aws": "^4.0.0",
"@pulumi/pulumi": "^3.0.0",
"@pulumi/policy": "^1.3.0"
},
"devDependencies": {
"@types/node": "^10.0.0"
}
}
21 changes: 21 additions & 0 deletions policy-packs/stackvalidation-ts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"compilerOptions": {
"outDir": "bin",
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"declaration": true,
"sourceMap": false,
"stripInternal": true,
"experimentalDecorators": true,
"pretty": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"forceConsistentCasingInFileNames": true,
"strictNullChecks": true,
},
"files": [
"index.ts"
]
}

0 comments on commit 6bf0c30

Please sign in to comment.