# Outputs

Outputs are templates that defines *how* and *where* your grants should materialize. An Output will generate native Terraform code to be stored in your Version Control System (VCS).

## Output Spec

```hcl
output = {
    # RFC 3986 URI string that points to the location of your generated HCL code.
    # Example: github://{organization}/{repository}/path/to/my/access.tf
    location = "..."
    append = <<-EOT
    EOT
}
```

## Writing Outputs For Your Grant Kit

Outputs are written using the Grant Kit's Workflows DSL as native Terraform HCL code.

Writing Outputs consists of four steps:

1. [Define a `location`](#define-a-location).
2. [Choose an Output Strategy](#choose-an-output-strategy).
3. [Leverage Terraform Providers](#leverage-terraform-providers).
4. [Optionally interpolate variables](#optionally-interpolate-variables).

### Define a `location`

A Grant Kit's Output defines *how* and *where* your access grants should materialize. The `location` attribute represents the *where*.

The `location` defines which file and at what path you want your access changes to materialize into. This is a RFC 3986 URI string. You can configure any path and any file name. Abbey will create the paths and file for you if they don't exist.

<details>

<summary>Example <code>location</code> Configurations</summary>

```hcl
// Organization: abbeylabs
// Repository: starter-kit-quickstart
// Access changes are materialized to: access.tf at the directory root
location = "github://abbeylabs/starter-kit-quickstart/access.tf"

// Organization: jeffchao (personal, not a GitHub org)
// Repository: my-starter-kit
// Access changes are materialized to: access.tf at the abbey-managed subdirectory.
location = "github://jeffchao/my-starter-kit/abbey-managed/access.tf"

// Organization: jeffchao (personal, not a GitHub org)
// Repository: my-starter-kit
// Access changes are materialized to: access.tf
//   under `resource "github_team_membership" "my_group"`
location = "github://jeffchao/my-starter-kit/abbey-managed/access.tf#github_team_membership.my_group"

// Organization: jeffchao (personal, not a GitHub org)
// Repository: my-starter-kit
// Access changes are materialized to: access.tf
//   under `resource "okta_group_memberships" "my_group"`
//   appended to users attribute
location = "github://jeffchao/my-starter-kit/abbey-managed/access.tf#okta_group_memberships.my_group.users"
```

</details>

{% hint style="info" %}
Currently Abbey supports the `github://` scheme. Future schemes such as `file://`, `s3://`, and `https://` coming soon.
{% endhint %}

### Choose an Output Strategy

Abbey will automatically manage access through the file you specified at `location`.

Abbey supports one Output Strategy:

1. `append`: Access changes are treated as a new Terraform Resource block and appended to the access Terraform file at `location`.

### Leverage Terraform Providers

A Grant Kit's Output defines *how* and *where* your access grants should materialize. The Output's `append` block represents the *how*.

It contains a string value, typically a multiline [Heredoc string](https://developer.hashicorp.com/terraform/language/expressions/strings#heredoc-strings) for better visibility. The contents of this string is HCL code generally using OSS Terraform Providers from the [Terraform Provider Registry](https://registry.terraform.io/) that map to the resources you want Abbey to manage.

<details>

<summary>Examples of Leveraging Terraform Providers in Your Grant Kit Output</summary>

```hcl
# Statically append to your `access.tf` file the `snowflake_role_grants` resource.
append = <<-EOT
    resource "snowflake_role_grants" "pii_role" {
        role_name = "ROLE"
        users     = ["USER"]
    }
EOT

# Statically append to your `access.tf` file the `mongodbatlas_database_user`
# resource with the `readWrite` role for the database `dbforApp`.
append = <<-EOT
    resource "mongodbatlas_database_user" "test" {
        username           = "test-acc-username"
        password           = "test-acc-password"
        project_id         = "<PROJECT-ID>"
        auth_database_name = "admin"

        roles {
            role_name     = "readWrite"
            database_name = "dbforApp"
        }
    } 
EOT
```

</details>

### Optionally Interpolate Variables

In order to configure meaningful Grant Kit Outputs, you need to be able to configure dynamic strings rather that static strings from the examples above. Abbey supports this through variable interpolation.

When an access request is made, Abbey performs two layers of variable interpolation:

1. [Terraform String Interpolation](#terraform-string-interpolation).
2. Go text/template Interpolation.

#### Terraform String Interpolation

Abbey will first perform [Terraform String Interpolation](https://developer.hashicorp.com/terraform/language/expressions/strings#interpolation). This happens when an access request is made it's now time to generate Output based on your `append` block.

Since the contents of the `append` block is a Terraform string, Abbey will interpolate variables using [native Terraform syntax](https://developer.hashicorp.com/terraform/language/expressions/references).

*Usage*

* To interpolate variables, use `${some_variable_name}`.
* You can interpolate any of your existing Terraform objects such as `resource` and `data` objects.

<details>

<summary>Example</summary>

<pre class="language-hcl"><code class="lang-hcl"># This example builds on the above. It shows how you can replace
# the value at `role_name` with the `name` attribute of
# a `snowflake_role_grants` Terraform resource named `pii_readonly_role__...`.
#
# This example assumes a `snowflake_role_grants` resource
# named `pii_readonly_role` already exists.
#
# The value is replaced when when `terraform apply` is run.
append = &#x3C;&#x3C;-EOT
    resource "snowflake_role_grants" "pii_readonly__USERNAME" {
        role_name = "<a data-footnote-ref href="#user-content-fn-1">${data.snowflake_role_grants.pii_readonly_role.name}</a>"
        users     = ["USER"]
    }
EOT
</code></pre>

</details>

* You can also interpolate the resource name of the output. This can be used to create multiple of the same resources for different requests.

<details>

<summary>Example</summary>

<pre class="language-hcl"><code class="lang-hcl"># NOTE: Due to how terraform generates random values, issuing multiple requests 
# at once will cause the this following example to use the same random string.
# The random string is changed every time terraform apply is called.
append = &#x3C;&#x3C;-EOT
      resource "abbey_demo" "grant_read_write_access_${random_pet.random_pet_name.id}" {
        permission = "read_write"
        email = "{{ .user.email }}"
      }
EOT
<strong>
</strong><strong>resource "random_pet" "random_pet_name" {
</strong>  keepers = {
    first = "${timestamp()}"
  }
  length = 5
  separator = "_"
}
</code></pre>

</details>

#### Go text/template Interpolation

Next, Abbey will perform Go [text/template](https://pkg.go.dev/text/template) Interpolation. This happens after the output of [Terraform String Interpolation](#terraform-string-interpolation).

*Usage*

* To interpolate variables, use `{{ .some_variable_name }}`.
* Abbey provides [Enriched Data](https://docs.abbey.io/reference/policies#enriched-data) as variables for you to interpolate.

<details>

<summary>Example</summary>

<pre class="language-hcl"><code class="lang-hcl"># This example shows how you can replace the statically-named
# `pii_readonly` role with a dynamically-named name using the
# `snowflake.username` attribute from Enriched Data.
append = &#x3C;&#x3C;-EOT
    resource "snowflake_role_grants" "pii_readonly__<a data-footnote-ref href="#user-content-fn-2">{{ .user.snowflake.username }}</a>" {
        role_name = "ROLE"
        users     = ["USER"]
    }
EOT
</code></pre>

</details>

{% hint style="info" %}
Notice the preceding `.` in the string template. This is required to access the object.

Also notice that even though Abbey uses Go's text/template, Abbey automatically converts this to snake\_case naming so the experience is consistent across the entire Grant Kit configuration flow.
{% endhint %}

## Output Materialization

Abbey materializes output to a Terraform file based on what you configured in your `append` block and after applying [Terraform String Interpolation](#terraform-string-interpolation) and [Go text/template Interpolation](#go-text-template-interpolation).

<figure><img src="https://1502779850-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FudoTqG501gLo2MLzBG55%2Fuploads%2FrDwj0pngxS9aXx4OelcU%2FOutput_Materialization_Isoflow_Diagram_3_26_2023.png?alt=media&#x26;token=b3a1cddb-35fd-4a50-aa59-67406f7d8d21" alt="Output Materialization"><figcaption><p>Output Materialization.</p></figcaption></figure>

Output Materialization has three stages:

1. Interpolate Terraform variables.
2. Interpolate Enriched Data variables.
3. Generate Output to your `location`.

## Troubleshooting

A common deployment failure for grant kits is a misconfigured `location` field in the `output` block

* Double check it starts with `github://`
* Double check repository and username or org name is correct
  * These fields are case-sensitive so double check any upper/lowercase letters
* Double check for any extra `:` or `/`'s
* Double check the path to your output location exists
  * Note: Abbey will create the *file* for you if it doesn't exist, but will not create *directories*.

[^1]: This variable will be replaced with the `snowflake_role_grants.pii_readonly_role` resource.

[^2]: This variable will be replaced with the `snowflake.username` that you imported into Abbey as linked application data.
