Authorization and Amazon Verified Permissions - A New Way to Manage Permissions Part XII: Terraform
Tags: aws, avp, authorization, terraform
Published: 2024-01-15 22:46:29

Welcome back to my blog post series dedicated to building authorization using Cedar and Amazon Verified Permissions. In a previous blogpost we've learned about [batch authorization](https://dev.to/aws-builders/authorization-and-amazon-verified-permissions-a-new-way-to-manage-permissions-part-xi-batch-authorization-mj8). Today, we will take a look at how to build AVP with one of the most popular Infrastructure as Code (IaC) tool - [Terraform](https://www.terraform.io/).

## Introduction

Let's assume that we would like to recreate one of the scenarios from avp-cli in Terraform, for example, the [documents scenario](https://github.com/Pigius/avp-cli/blob/main/scenarios/documentsScenario/documentsScenario.json). This is a basic scenario with a document management platform schema and two policies (`Allow all users to view all documents` and `Forbid user X from viewing any documents`).

To recreate this, we need:


![policy store](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l7tx6zmpqeh8z5fvjq5j.png)


A **policy store**, which is a container for our policies. In such a container, we keep all policies, as well as the schema. It's important to remember that later when using AVP for authorization requests, we send requests against a specific policy store.

A **schema** is the structure of all available entities for a given policy store. In short, it defines what entities are available for our authorization system, including available actions, principals, and resources. The schema not only informs us about the available entities but also validates the correctness of authorization requests.

Finally, we will also need to add our **policies**.

## Terraform provider for AVP

In the context of AWS, a Terraform provider is a plugin that allows Terraform to interact with AWS services and resources. It provides the necessary tools and interfaces for managing the lifecycle of various AWS resources through Terraform's infrastructure as code approach.

If we check the support for the Terraform AWS Provider [here](https://github.com/hashicorp/terraform-provider-aws/issues/32158) (state for the date of publishing this article), we will see that the service is not yet fully supported. Last week, after more than half a year, support for creating a policy store was added. Additionally, we have the configuration to add template policies. However, the identity source is in the form of a PR draft, and there is no PR yet for the ability to create policies.

Does this mean that we currently cannot use AVP with Terraform? Not necessarily, as the [Cloud Control Provider](https://github.com/hashicorp/terraform-provider-awscc) comes to our rescue.

What is cool about that provider, is that it's auto-generated straight from AWS CloudFormation schemas, meaning it's always in sync with the latest AWS features - no more waiting for manual updates! 

Weekly deployments (always on Thursday) with the newest AWS enhancements. This provider taps into the AWS Cloud Control API so you can expect new AWS offerings (supported via Cloudformation) in Terraform almost as soon as they're released.

## Let's build!

The example we will create is not intended for production use; it has been created for educational purposes. However, in the future, I will add more advanced projects with Terraform to the [repository](https://github.com/Pigius/avp-terraform), also more suited for production use.

For our example, we will need two types of resources from the Terraform:

- [policy store](https://registry.terraform.io/providers/hashicorp/awscc/latest/docs/resources/verifiedpermissions_policy_store)
- [policy](https://registry.terraform.io/providers/hashicorp/awscc/latest/docs/resources/verifiedpermissions_policy)

Now that we know everything, we can start building the project in Terraform. At the end, a link to the repository will be provided, which you can easily clone.

Let's assume we have a new repository, and we start with an empty `main.tf` file. In order to configure the provider, you will need to employ the configuration blocks shown here, while specifying your preferred region:


main.tf
```hcl
terraform {
  required_providers {
    awscc = {
      source  = "hashicorp/awscc"
      version = "~> 0.68.0"
    }
  }
}

provider "awscc" {
  region = "eu-west-1"
}
```

## Create a policy store

Now our focus will be to add a configuration for a policy store. First, we need to define the `validation_settings`. It specifies the validation setting for this policy store. In our use-case, we would like to be `strict` as we will add schema and check the validation of our authorization requests, let's add this code to our `main.tf`

```hcl
resource "awscc_verifiedpermissions_policy_store" "policy_store" {
  validation_settings = { mode = "STRICT" }
}
```
This line declares a new resource of type `awscc_verifiedpermissions_policy_store`, which is provided by the AWS CC provider. The `policy_store` is a local name given to this resource instance, which can be used to refer to it within the Terraform configuration. We've added validation settings setup as strict.

At this point, we could easily deploy our configuration via:

```shell
AWS_PROFILE=your-profile name terraform init
AWS_PROFILE=your-profile name terraform apply
```
Which will create an empty policy store with validation settings.

## Adding Schema
Now we want to add a schema, which you can find [here](https://github.com/Pigius/avp-cli/blob/main/scenarios/documentsScenario/schema.json). As you can see, it is a JSON file that we need to have. Let's add a `schema.json` file to our main project directory, and then update our configuration:

```hcl
resource "awscc_verifiedpermissions_policy_store" "policy_store" {
  validation_settings = { mode = "STRICT" }
  schema              = { cedar_json = file("${path.module}/schema.json") }
}
```
This new line defines the schema for the policy store using the `cedar_json` attribute. 
The `file("${path.module}/schema.json")` function reads the contents of a file named `schema.json` located in the same directory as the Terraform module `(${path.module}` refers to the current module's directory). 

The content of this file, which should be in JSON format, is used as the schema for the policy store.

Now when we deploy changes we will schema in our policy store.


![Schema](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eek6fvysfs3i7tezddgd.png)



## Adding policies

Now, it's time for the final step, adding policies:

- [this allow policy](https://github.com/Pigius/avp-cli/blob/main/scenarios/documentsScenario/allow_policy.cedar) in the root directory under the name of `allow_policy.cedar`
- [this deny policy](https://github.com/Pigius/avp-cli/blob/main/scenarios/documentsScenario/deny_policy.cedar) in the root directory under the name of `deny_policy.cedar`

Later, we can add this config to `main.tf`:

```hcl
resource "awscc_verifiedpermissions_policy" "allow_policy" {
  policy_store_id = awscc_verifiedpermissions_policy_store.policy_store.id
  definition = {
    static = {
      statement   = file("${path.module}/allow_policy.cedar")
      description = "Allow all users to view all documents"
    }
  }
}

resource "awscc_verifiedpermissions_policy" "forbid_policy" {
  policy_store_id = awscc_verifiedpermissions_policy_store.policy_store.id
  definition = {
    static = {
      statement   = file("${path.module}/deny_policy.cedar")
      description = "Forbid user X from viewing any documents"
    }
  }
}
```
- The `resource "awscc_verifiedpermissions_policy" "allow_policy":` declares a new resource of type `awscc_verifiedpermissions_policy`, named `allow_policy`. This resource represents a policy in the AVP Policy Store (similar to the `forbid_policy`).
- `policy_store_id = awscc_verifiedpermissions_policy_store.policy_store.id:` This line specifies the ID of the Policy Store where this policy will be created. It references the id of the previously defined `awscc_verifiedpermissions_policy_store` resource named `policy_store`.

Inside the definition block of the policy we have:

- `static:` Indicates that this is a static policy definition.
- `statement = file("${path.module}/allow_policy.cedar"):` The policy statement is defined in an external file named
`allow_policy.cedar`. 

The file function reads the contents of this file, which contain the policy written in Cedar policy language. This policy is expected to specify conditions under which access is allowed.

The repo for this project can be found [here](https://github.com/Pigius/avp-terraform/tree/main/basic-example) with proper readme. I've made small improvements to the project (adding outputs to see policy store id and region), and when you deploy it you can see:

```
wscc_verifiedpermissions_policy_store.policy_store: Creating...
awscc_verifiedpermissions_policy_store.policy_store: Creation complete after 1s [id=VUdm7eCYdN3xW8CeJrrVx8]
awscc_verifiedpermissions_policy.allow_policy: Creating...
awscc_verifiedpermissions_policy.forbid_policy: Creating...
awscc_verifiedpermissions_policy.allow_policy: Creation complete after 9s [id=VKo7ca1kCNHSpVhLfHgrYs|VUdm7eCYdN3xW8CeJrrVx8]
awscc_verifiedpermissions_policy.forbid_policy: Creation complete after 9s [id=VN4qXzoMRhLqxa51KY3WPr|VUdm7eCYdN3xW8CeJrrVx8]

Apply complete! Resources: 3 added, 0 changed, 0 destroyed.

Outputs:

aws_region = "eu-west-1"
policy_store_id = "VUdm7eCYdN3xW8CeJrrVx8"
```


![Policies](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j0pekc2j2v88enk240m9.png)



## Final words

As you can see, it's quite simple to create a basic project with AVP using Terraform. That's all for today. I encourage you to experiment on your own.

If you're interested in more TF templates, I've recreated all of the AVP-CLI scenarios in TF, you can find them [here](https://github.com/Pigius/awesome-amazon-verified-permissions-and-cedar/tree/main/terraform).

 In the next blogpost, you can read about usage [AVP with Cloudformation](https://dev.to/aws-builders/authorization-and-amazon-verified-permissions-a-new-way-to-manage-permissions-part-xiii-cloudformation-47d2).