# 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](/orl/concepts.md)
* [ORL Schemas](/orl/schemas.md)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.gomboc.ai/orl/quickstart.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
