Skip to content

Commit

Permalink
add multi-step tutorial and examples for making a custom policy pack
Browse files Browse the repository at this point in the history
  • Loading branch information
thoward committed Jan 29, 2025
1 parent 7b95b55 commit 727cdb1
Show file tree
Hide file tree
Showing 29 changed files with 927 additions and 222 deletions.
193 changes: 82 additions & 111 deletions content/tutorials/custom-policy-pack/create-policy-pack/index.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,11 @@ weight: 3
estimated_time: 5
---

---

{{% notes type="info" %}}
Server-side enforcement of policy packs across an organization is only available in **Pulumi Business Critical**. See [pricing](/pricing/) for more details.
{{% /notes %}}

Now that we've validated the behavior of our custom policy pack, publishing them to Pulumi Cloud will allow them to be enforced across your organization. Any time you run `preview` or `update` on a stack, Pulumi Cloud will ship the policy to the client to enable policy enforcement. Policy Packs are versioned by the Pulumi Cloud so that updated policies can be published and applied incrementally, and also reverted to previous versions as needed.
Now that we've validated the behavior of our custom policy pack, publishing it to Pulumi Cloud will allow the policies to be enforced across your organization. Any time you run `pulumi preview` or `pulumi up` on a stack, Pulumi Cloud will ship the policy to the client to enable policy enforcement. Policy Packs are versioned by the Pulumi Cloud so that updated policies can be published and applied incrementally, and also reverted to previous versions as needed.

### Publish the policy pack

Expand All @@ -24,38 +22,36 @@ Navigate back to the policy pack directory, and run the following command to pub
$ pulumi policy publish <org-name>
```

The output will tell you what version of the policy pack you just published.
The output will tell you what version of the policy pack you just published.

```
Obtaining policy metadata from policy plugin
Compressing policy pack
Uploading policy pack to Pulumi Cloud
Publishing my-policy-pack to myorg
Publishing custom-policy-pack to myorg
Published as version 1.0.0
```

{{% notes type="info" %}}
**Policy Versions**: Pulumi Cloud tracks published policy packs by *version*. When a policy pack is published, it will automatically receive a monotonically increasing version number by Pulumi Cloud. The policy pack version can be specified in the `package.json` file for TypeScript/JavaScript (Node.js) packs and in the `PulumiPolicy.yaml` file for Python packs. Published policy packs are immutable, meaning that a version number can only be published to one time. Once published, the version can never be used by that policy pack again.
**Policy Versions**: Pulumi Cloud tracks published policy packs by *version*. When a policy pack is published, it will automatically receive a monotonically-increasing version number by Pulumi Cloud. The policy pack version can be specified in the `package.json` file for TypeScript/JavaScript (Node.js) packs and in the `PulumiPolicy.yaml` file for Python packs. Published policy packs are immutable, meaning that a version number can only be published to one time. Once published, the version can never be used by that policy pack again.
{{% /notes %}}

### Enforce the policy pack

You can enable the policy pack organization-wide by running:

```sh
$ pulumi policy enable myorg/my-policy-pack latest
$ pulumi policy enable myorg/custom-policy-pack latest
```

The `latest` parameter indicates that the most recent version of the policy should be enabled. You could use a version number instead, to enable a previous version.

{{% notes type="info" %}}
**Policy Groups**: Pulumi Crossguard also has a concept of *[policy groups](/docs/iac/using-pulumi/crossguard/core-concepts/#policy-groups)*, which allow you to apply certain policies only to certain stacks. The `pulumi policy enable` command, by default, enables a published policy pack to your default policy group, which applies it to all stacks. If you would like to add the policy pack to a different policy group, you can use the `--policy-group` flag. Read more about how to manage groups with `[pulumi policy group](/docs/iac/cli/commands/pulumi_policy_group/)` commands in the [Crossguard docs](/docs/iac/using-pulumi/crossguard/core-concepts/#policy-groups).
**Policy Groups**: Pulumi Crossguard also has a concept of *[policy groups](/docs/iac/using-pulumi/crossguard/core-concepts/#policy-groups)*, which allow you to apply certain policies only to certain stacks within the group. The `pulumi policy enable` command, by default, turns on a published policy pack to your default policy group, which applies it to all stacks. If you would like to add the policy pack to a different policy group, you can use the `--policy-group` flag. Read more about how to manage groups with `[pulumi policy group](/docs/iac/cli/commands/pulumi_policy_group/)` commands in the [Crossguard docs](/docs/iac/using-pulumi/crossguard/core-concepts/#policy-groups).
{{% /notes %}}


## Next Steps

Congratulations! Now that you have published your first custom policy pack, all the pieces are in place to enforce compliance across your organization. For more example policy packs, you can check out the [examples repo](https://github.com/pulumi/examples/tree/master/policy-packs). You can also find more documentation in the [CrossGuard guide](/docs/using-pulumi/crossguard/).

---
{{< tutorials/stepper >}}
243 changes: 142 additions & 101 deletions content/tutorials/custom-policy-pack/validate-policy-pack/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,167 +8,208 @@ weight: 2
estimated_time: 5
---

---

## Test the Policy Pack

Now that you’ve created your Policy Pack, lets see it in action. Well create some AWS resources that violate the policies, run the Policy Pack to check compliance, and then fix the resources to adhere to the policies.
In the previous step we created a custom policy pack. Now, let's see it in action. We'll create some AWS resources that violate the policies, run our custom policy pack to check compliance, and then fix the resources to adhere to the policies.

### Step 1: Create Non-Compliant Resources

Below are examples of non-compliant resources defined in Pulumi. Save these in a file, e.g., `non_compliant_resources.py` or `non_compliant_resources.ts`, based on your language preference.
First, create a new Pulumi project from a template:

{{< chooser language "typescript,python" />}}

{{% choosable language typescript %}}

```sh
$ mkdir custom-policy-pack-integration-test-typescript && cd custom-policy-pack-integration-test-typescript
$ pulumi new aws-typescript
```

Follow the prompts as usual to set up your project.

Below are examples of non-compliant resources defined in Pulumi. Replace to contents of `index.ts` with this code.

```typescript
import * as aws from "@pulumi/aws";

// S3 bucket without versioning enabled
const s3Bucket = new aws.s3.Bucket("nonCompliantBucket", {
versioning: {
enabled: false, // Policy violation
},
});

// EC2 instance with a non-compliant instance type
const ec2Instance = new aws.ec2.Instance("nonCompliantInstance", {
instanceType: "m5.large", // Policy violation
ami: "ami-0c55b159cbfafe1f0", // Replace with a valid AMI ID
});

// Resource with no tags
const untaggedResource = new aws.s3.Bucket("untaggedBucket", {
tags: {}, // Policy violation
});
{{< example-program-snippet path="custom-policy-pack-integration-test-typescript" file="index.non-compliant.ts" language="typescript" >}}
```

{{% /choosable %}}

{{% choosable language python %}}

```sh
$ mkdir custom-policy-pack-integration-test-python && cd custom-policy-pack-integration-test-python
$ pulumi new aws-python
```

Follow the prompts as usual to set up your project.

Below are examples of non-compliant resources defined in Pulumi. Replace to contents of `__main__.py` with this code.

```python
import pulumi_aws as aws

# S3 bucket without versioning enabled
s3_bucket = aws.s3.Bucket("nonCompliantBucket",
versioning={
"enabled": False, # Policy violation
}
)

# EC2 instance with a non-compliant instance type
ec2_instance = aws.ec2.Instance("nonCompliantInstance",
instance_type="m5.large", # Policy violation
ami="ami-0c55b159cbfafe1f0", # Replace with a valid AMI ID
)

# Resource with no tags
untagged_resource = aws.s3.Bucket("untaggedBucket",
tags={} # Policy violation
)
{{< example-program-snippet path="custom-policy-pack-integration-test-python" file="non-compliant.py" language="python" >}}
```

{{% /choosable %}}

This Pulumi project defines an S3 Bucket, a Security Group, and an EC2 instance. As written, these violate our custom policies in the following ways:

- The Bucket has a `tag` property, but it's empty.
- The Bucket has a non-compliant prefix.
- The Security Group has no `tags` property.
- The EC2 instance uses a non-compliant instance type.

So, if we run `pulumi preview` on these with our custom policy pack applied, we should see four policy violations. Let's try it!

### Step 2: Run the Policy Pack

Run `pulumi up` in the directory where you defined the non-compliant resources. Pulumi will evaluate the Policy Pack against these resources and report any violations.
From the root of the Pulumi project, run `pulumi preview` with the `--policy-pack` option, pointing to the directory containing our custom policies. Pulumi will evaluate the policy pack against these resources and report any violations.

{{< chooser language "typescript,python" />}}

{{% choosable language typescript %}}

```bash
pulumi up
```sh
$ pulumi preview --policy-pack ../custom-policy-pack-typescript
Loading policy packs...

Type Name Plan
+ pulumi:pulumi:Stack custom-policy-pack-integration-test-typescript-dev create
+ ├─ aws:s3:BucketV2 my-bucket create
+ ├─ aws:ec2:Instance web-server create
+ └─ aws:ec2:SecurityGroup ssh-security-group create

Policies:
[email protected] (local: ../custom-policy-pack-typescript)
- [mandatory] all-aws-resources-must-have-tags (aws:ec2/securityGroup:SecurityGroup: ssh-security-group)
Ensures all AWS resources have at least one tag.
All AWS resources must have at least one tag.
- [mandatory] all-aws-resources-must-have-tags (aws:s3/bucketV2:BucketV2: my-bucket)
Ensures all AWS resources have at least one tag.
All AWS resources must have at least one tag.
- [mandatory] ec2-instance-type-restricted (aws:ec2/instance:Instance: web-server)
Ensures EC2 instances use approved instance type.
Invalid instance type: 't3.micro'. EC2 instances must use 't2.micro' instance type.
- [mandatory] s3-product-prefix (aws:s3/bucketV2:BucketV2: my-bucket)
Ensures S3 buckets have the correct product prefix.
Invalid prefix: 'something-unexpected-'. S3 buckets must use 'myproduct-' prefix.
```

Example output for violations:
{{% /choosable %}}

```plaintext
Policy violations:
- s3-versioning-enabled: S3 buckets must have versioning enabled.
- ec2-instance-type-restricted: EC2 instances must use the 't2.micro' instance type.
- all-resources-must-have-tags: All resources must have at least one tag.
{{% choosable language python %}}

```sh
$ pulumi preview --policy-pack ../custom-policy-pack-python

Loading policy packs...

Type Name Plan
+ pulumi:pulumi:Stack custom-policy-pack-integration-test-python-dev create
+ ├─ aws:s3:BucketV2 my-bucket create
+ ├─ aws:ec2:Instance web-server create
+ └─ aws:ec2:SecurityGroup ssh-security-group create

Policies:
[email protected] (local: ../custom-policy-pack-python)
- [mandatory] all-aws-resources-must-have-tags (aws:ec2/securityGroup:SecurityGroup: ssh-security-group)
Ensures all AWS resources have at least one tag.
All AWS resources must have at least one tag.
- [mandatory] all-aws-resources-must-have-tags (aws:s3/bucketV2:BucketV2: my-bucket)
Ensures all AWS resources have at least one tag.
All AWS resources must have at least one tag.
- [mandatory] ec2-instance-type-restricted (aws:ec2/instance:Instance: web-server)
Ensures EC2 instances use approved instance type.
Invalid instance type: 't3.micro'. EC2 instances must use 't2.micro' instance type.
- [mandatory] s3-product-prefix (aws:s3/bucketV2:BucketV2: my-bucket)
Ensures S3 buckets have the correct product prefix.
Invalid prefix: 'something-unexpected-'. S3 buckets must use 'myproduct-' prefix.
```

{{% /choosable %}}

### Step 3: Fix the Resources

Update the resources to comply with the policies.
Ok, now that we see the expected violations, let's update the resources to comply with the policies.

We need to:

- Update the Bucket to use the correct prefix: `myproduct-`
- Update the Bucket to have at least one tag
- Update the Security Group to have at least one tag
- Update the EC2 instance to use the correct instance type: `t2.micro`

{{< chooser language "typescript,python" />}}

{{% choosable language typescript %}}

```typescript
// S3 bucket with versioning enabled
const compliantS3Bucket = new aws.s3.Bucket("compliantBucket", {
versioning: {
enabled: true, // Compliant
},
});

// EC2 instance with a compliant instance type
const compliantEc2Instance = new aws.ec2.Instance("compliantInstance", {
instanceType: "t2.micro", // Compliant
ami: "ami-0c55b159cbfafe1f0", // Replace with a valid AMI ID
});

// Resource with tags
const taggedResource = new aws.s3.Bucket("taggedBucket", {
tags: {
Environment: "Production", // Compliant
},
});
Replace to contents of `index.ts` with this code.

```python
{{< example-program-snippet path="custom-policy-pack-integration-test-typescript" file="index.ts" language="typescript" >}}
```

{{% /choosable %}}

{{% choosable language python %}}

Replace to contents of `__main__.py` with this code.

```python
# S3 bucket with versioning enabled
compliant_s3_bucket = aws.s3.Bucket("compliantBucket",
versioning={
"enabled": True, # Compliant
}
)

# EC2 instance with a compliant instance type
compliant_ec2_instance = aws.ec2.Instance("compliantInstance",
instance_type="t2.micro", # Compliant
ami="ami-0c55b159cbfafe1f0", # Replace with a valid AMI ID
)

# Resource with tags
tagged_resource = aws.s3.Bucket("taggedBucket",
tags={
"Environment": "Production", # Compliant
}
)
{{< example-program-snippet path="custom-policy-pack-integration-test-python" file="__main__.py" language="python" >}}
```

{{% /choosable %}}

### Step 4: Verify Compliance

Run `pulumi up` again. This time, no policy violations should be reported.
Run `pulumi preview` again. This time, no policy violations should be reported.

{{< chooser language "typescript,python" />}}

{{% choosable language typescript %}}

```bash
pulumi up
```sh
$ pulumi preview --policy-pack ../custom-policy-pack-typescript

Loading policy packs...

Type Name Plan
+ pulumi:pulumi:Stack custom-policy-pack-integration-test-typescript-dev create
+ ├─ aws:s3:BucketV2 my-bucket create
+ ├─ aws:ec2:Instance web-server create
+ └─ aws:ec2:SecurityGroup ssh-security-group create

Policies:
[email protected] (local: ../custom-policy-pack-typescript)
```

Expected output:
{{% /choosable %}}

{{% choosable language python %}}

```sh
$ pulumi preview --policy-pack ../custom-policy-pack-python

Loading policy packs...

```plaintext
Resources:
3 created
Type Name Plan
+ pulumi:pulumi:Stack custom-policy-pack-integration-test-python-dev create
+ ├─ aws:s3:BucketV2 my-bucket create
+ ├─ aws:ec2:Instance web-server create
+ └─ aws:ec2:SecurityGroup ssh-security-group create

No policy violations found. The deployment is compliant with all policies.
Policies:
[email protected] (local: ../custom-policy-pack-python)
```

Congratulations! You’ve successfully created and tested a Policy Pack with Pulumi CrossGuard.
{{% /choosable %}}

Congratulations! You've successfully created and tested a policy pack with Pulumi CrossGuard.

## Next Steps

You have successfully created, deployed, and tested a custom policy pack. To learn more about using Pulumi CrossGuard and policies, explore the following:
To learn more about using Pulumi CrossGuard and policies, explore the following:

- [Pulumi Crossguard: Policy as Code Documentation](https://www.pulumi.com/docs/guides/crossguard/)
- [Advanced Policy Examples](https://github.com/pulumi/examples/tree/master/policy)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
test:
PULUMI_CONFIG_PASSPHRASE="foo" pulumi login --local
PULUMI_CONFIG_PASSPHRASE="foo" pulumi install
-PULUMI_CONFIG_PASSPHRASE="foo" pulumi cancel --stack organization/custom-policy-pack-integration-test-python/dev --yes
-PULUMI_CONFIG_PASSPHRASE="foo" pulumi destroy --stack organization/custom-policy-pack-integration-test-python/dev --yes --refresh --remove
PULUMI_CONFIG_PASSPHRASE="foo" pulumi stack init organization/custom-policy-pack-integration-test-python/dev --non-interactive
PULUMI_CONFIG_PASSPHRASE="foo" pulumi stack select organization/custom-policy-pack-integration-test-python/dev
-PULUMI_CONFIG_PASSPHRASE="foo" pulumi config set aws:region us-west-2
PULUMI_CONFIG_PASSPHRASE="foo" pulumi preview --policy-pack ../custom-policy-pack-python
.PHONY: test
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
name: custom-policy-pack-integration-test-python
description: A minimal AWS Python Pulumi program
runtime:
name: python
options:
toolchain: pip
virtualenv: venv
config:
pulumi:tags:
value:
pulumi:template: aws-python
Loading

0 comments on commit 727cdb1

Please sign in to comment.