# Custom Rules Quickstart

ORL Rules are YAML-like files that drive the `orl` engine to modify files within a workspace.

## Step 1. Clone Rattleback

[Rattleback](https://github.com/Gomboc-AI/rattleback) includes some sample code that can be used to run checks against. We will use it for this example.

```bash
git clone git@github.com:Gomboc-AI/rattleback.git
```

## Step 2. Create a Rule

Next, let's create a simple rule that ensures `aws_s3_bucket` Terraform resources are tagged. Don't worry too much about the details at this point, we'll explain it [below](#the-rule-explained).

Create a `./rules` directory and write the following as `./rules/enforce_aws_s3_bucket_tagging.orl`.

```yaml
type: Ruleset
version: v1
metadata:
  annotations:
    purpose: tagging
  priority: 100
  name: ensure-s3-buckets-are-tagged
  display_name: Ensure S3 Buckets are tagged
  description: |
    ## Description

    All buckets have to be tagged according to the source of truth.
spec:
  template:
    language: terraform
    audit_language: ast
  rules:
    - name: ensure-tag-exists
      audit: |
        {{ aResource("aws_s3_bucket", aMissingAttribute("tags")) }}
      remediation:
        - command: insert_after
          path: body
          flags:
            prefix: "\n\n"
            indent: "  "
          value: |
            tags = {
              source = "rattleback"
            }
```

## Step 3: Remediate

The ORL engine is provided as a Docker container. It can be executed with the appropriate mount points to load the rules and remediate files.

```bash
docker run -v ./rules:/rules -v ./rattleback:/workspace gombocai/orl:latest remediate --rulespace /rules /workspace/aws/terraform/s3-default
```

After running the command, `main.tf` is modified to include a `tags` attribute for the `uut` resource with `source = "rattleback"`, just as specified by the rule. The resulting `git diff` would look like this:

```diff
diff --git a/aws/terraform/s3-default/main.tf b/aws/terraform/s3-default/main.tf
index 892167e..17751a2 100644
--- a/aws/terraform/s3-default/main.tf
+++ b/aws/terraform/s3-default/main.tf
@@ -22,6 +22,10 @@ resource "aws_s3_bucket" "uut" {
   bucket = "${local.prefix}-uut"

   force_destroy = true
+
+  tags = {
+    source = "rattleback"
+  }
 }
 resource "aws_s3_bucket_ownership_controls" "uut" {
   bucket = aws_s3_bucket.uut.id
```

{% hint style="info" %}
`orl remediate` supports a `--dry-run` option which will write the changed files to the terminal instead of disk. This can help in developing rules as you don't need to run `git restore` to undo the changes and try again.
{% endhint %}

## The Rule Explained

Here is a fully annotated version of the Rule.

```yaml
# There are five ORL types: Rule, Ruleset, Variables, Test, and Report.
# Type and version tell ORL how and when to process the file.
# "Ruleset" tells ORL it is a collection of rules that have to be executed
# as a group.
type: Ruleset
version: v1

# Metadata defines various details which control how the rule executes
# as well as provides context for reports
metadata:
  # Name uniquely identifies the rule, while display_name is copied into the
  # report and is meant as a more human readable name.
  name: ensure-s3-buckets-are-tagged
  display_name: Ensure S3 Buckets are tagged

  # Description is a markdown string that describes the purpose of the rule.
  # It is copied into the report.
  description: |
    ## Description

    All buckets have to be tagged according to the source of truth.

  # Priority defines the order of when rules are run.  Rules are run from the
  # smallest number to the largest.  Two rules with the same priority are
  # run based on sorted names.
  priority: 100

  # Annotations are a map of string key/value pairs.  They can be anything,
  # and are blindly copied into the ORL report.
  annotations:
    purpose: tagging

# The spec depends on the types.  This is the spec for a "Ruleset", which is an
# array of Rules.
spec:

  # These settings are shared with each rule below
  template:
    # The ORL language can be used to get a list of the languages
    # Only rules that match `orl remediate --language <language>` are loaded.
    language: terraform
    # This determines what form of parsing ORL uses.  Currently only AST is
    # supported.
    audit_language: ast

  # Array of the actual rules
  rules:

    # All rules in a ruleset should be uniquely named.  This name is included
    # in the report metadata.
    - name: ensure-tag-exists

      # `audit` is the search query to perform. In this case, it uses template
      # helpers that generate the actual S-expression.  The list of helpers can
      # be seen via `orl language <language>`
      audit: |
        {{ aResource("aws_s3_bucket", aMissingAttribute("tags")) }}

      # A list of remediation steps to perform on each finding
      remediation:

        # The command to run on each finding.  `insert_after` sets the cursor
        # right after the last character of the named match defined by `path`.
        # The other commands are:
        #   `audit` - Don't change any files, just log a message to the report
        #   `create` - Add value to the bottom of a file only if no named match was found
        #   `insert_before` - Insert value before the named match
        #   `replace` - Replace the contents of the named match
        #   `remove` - Remove the named match
        - command: insert_after

          # The `aResource` helper provides many named matches in the AST
          # structure.  "body" is all the text exclusively between the curly
          # braces.
          path: body
          flags:
            prefix: "\n\n" # Append an empty line before `value`
            indent: "  "   # Add in front of every line of `value`
          value: |
            tags = {
              source = "rattleback"
            }
```

## Further Reading

* [Concepts behind ORL](https://docs.gomboc.ai/orl/concepts)
* [ORL Schemas](https://docs.gomboc.ai/orl/schemas)
