> For the complete documentation index, see [llms.txt](https://docs.gomboc.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.gomboc.ai/orl/agent-skills/examples/maintaining-rules.md).

# Maintaining Rules

{% hint style="warning" %}
This is only supported with [gomboc-enterprise-skills](/orl/agent-skills/gomboc-enterprise-skills.md).
{% endhint %}

Keep a rule's allowlist up to date; for example, rotating pre-approved AMIs monthly. Make the base rule easy to patch, create an ORL patch rule driven by a variable, then run a periodic workflow to apply updates.

## Prerequisites

One-time setup for the maintenance loop.

### Step 1: Patchable Rule

Start from the [policy-as-code](/orl/agent-skills/examples/policy-as-code-rule.md) example and add this to the prompt so the generated rule is easy to update later:

```markdown
**Important**: I will need to update the list of AMIs frequently, so make sure the rule is easy to update later.
```

<details>

<summary>Modified Rule</summary>

The modified rule matches the policy-as-code example, with the AMI list centralized in `skip_finding` for easier patching.

```yaml
---
type: Ruleset
version: v1
metadata:
  name: "ensure-ami-is-in-approved-allowlist"
  display_name: "Ensure AMI is from the approved allowlist"
  description: |
    ## Description

    Ensure that EC2 instances, launch configurations, and launch templates use only
    approved AMIs. Resources referencing an AMI that is not in the allowlist will be
    flagged with an audit error but will not be automatically remediated.
  annotations:
    contributed-by: user
    provider: AWS
    resource: aws_instance, aws_launch_configuration, aws_launch_template
spec:
  template:
    language: terraform
    audit_language: ast
    skip_finding: |
      let approved_amis = ["ami-0a1b2c3d4e5f67890", "ami-0987654321fedcba0", "ami-0ff11223344556677", "ami-0abcdef1234567890", "ami-0123456789abcdef0"];
      let ami = trim($.value, "\"");
      !hasPrefix($.value, "\"") || ami in approved_amis
    remediation:
      - command: audit
        flags:
          level: ERROR
        value: "(PaC) The AMI is not in the approved allowlist. Valid AMIs are: ami-0a1b2c3d4e5f67890, ami-0987654321fedcba0, ami-0ff11223344556677, ami-0abcdef1234567890, ami-0123456789abcdef0"
  rules:
    - name: ensure-aws_instance-uses-approved-ami
      audit: |
        {{ aResource("aws_instance", anAttribute("ami")) }}

    - name: ensure-aws_launch_template-uses-approved-ami
      audit: |
        {{ aResource("aws_launch_template", anAttribute("image_id")) }}

    - name: ensure-aws_launch_configuration-uses-approved-ami
      audit: |
        {{ aResource("aws_launch_configuration", anAttribute("image_id")) }}
```

</details>

### Step 2: Generate the Patch

Generate a patch rule that updates the allowlist from a variable:

```markdown
/gomboc:fix

I have an existing rule `ensure-ami-is-in-approved-allowlist` that enforces an AMI allowlist.  I want to create a new rule that can update that existing rule using the fact that `orl` is a supported language.  The new rule should use the `vars.custom.ami_ids` variable as a comma-separated list of AMIs that should be used.  This should replace the AMI list in `skip_finding` as an EXPR-style list (see existing rule format), as well as the remediation value as human-readable text.

**Important**: The rule should skip itself (using `skip_expression`) if the variable is not set.  If skipped the `skip_reason` should be in the form "Variable 'vars.custom.ami_ids' is not defined. It should be the complete list of valid AMIs".
```

{% hint style="warning" %}
This prompt is simplified for demo purposes. Depending on how you set up your update workflow, you may need to add additional protections like only patching rules by name or annotation.
{% endhint %}

#### Rule Output Example

```yaml
---
type: Ruleset
version: v1
metadata:
  name: orl/ensure-ami-allowlist-from-custom-variable
  display_name: "Sync AMI Allowlist in ORL Rules from vars.custom.ami_ids"
  description: |
    ## Description

    Updates an ORL rule's approved AMI allowlist to use `vars.custom.ami_ids`
    (a comma-separated list of approved AMI IDs) instead of a hardcoded list.

    Patches two locations in the target ORL rule file:
    1. `spec.template.skip_finding` — rewrites the `let approved_amis = [...]` line
       as an Expr list derived from vars.custom.ami_ids.
    2. `spec.template.remediation[].value` — rewrites the human-readable AMI list
       at the end of the audit message with the value of vars.custom.ami_ids.

    The rule skips itself entirely when `vars.custom.ami_ids` is not defined.
  annotations:
    contributed-by: in-context
    gomboc-ai/remediation-assessment: FULL_REMEDIATION
    gomboc-ai/description-plain: "Replaces hardcoded AMI allowlists in ORL rules with the value from vars.custom.ami_ids."
  priority: 100
spec:
  template:
    language: orl
    audit_language: ast
    skip_expression: "vars?.custom?.ami_ids == nil || vars.custom.ami_ids == \"\""
    skip_reason: "Variable 'vars.custom.ami_ids' is not defined. It should be the complete list of valid AMIs"
  rules:
    - name: ensure-skip-finding-uses-ami-ids-from-variable
      audit: |
        (block_mapping_pair
          key: (flow_node (plain_scalar (string_scalar) @_key))
          value: (block_node (block_scalar) @skip_finding_val)
          (#eq? @_key "skip_finding")
          (#match? @skip_finding_val "let approved_amis")
        )
      remediation:
        - command: replace
          path: skip_finding_val
          value: |-
            |
                  let approved_amis = {{ "[\"" + join(map(split(vars.custom.ami_ids, ","), trim(#, " ")), "\", \"") + "\"]" }};
                  let ami = trim($.value, "\"");
                  !hasPrefix($.value, "\"") || ami in approved_amis

    - name: ensure-remediation-value-uses-ami-ids-from-variable
      audit: |
        (block_mapping_pair
          key: (flow_node (plain_scalar (string_scalar) @_key))
          value: (flow_node (double_quote_scalar) @remediation_val)
          (#eq? @_key "value")
          (#match? @remediation_val "ami-[0-9a-f]")
        )
      remediation:
        - command: replace
          path: remediation_val
          value: '"(PaC) The AMI is not in the approved allowlist. Valid AMIs are: {{ vars.custom.ami_ids }}"'
```

## Update Workflow

The update workflow consists of the steps that are run repeatedly when the underlying rule needs to be updated.

### Step 1: Pull updated values

Filter to images tagged `Approved=true`. This command returns a comma-separated list of image IDs:

```bash
aws ec2 describe-images --owners self --filters "Name=tag:Approved,Values=true" --query "join(',', Images[*].ImageId)" --output text
```

### Step 2: Write the variable

Take the string from the first step and replace `<ami list here>` in the template below. This file should be placed in the same directory as the file you will be patching. We will use `./rules` for this.

```yaml
---
type: Variables
version: v1
metadata:
  name: custom
  description: Custom variables for AMI allowlist management
data:
  ami_ids: "<ami list here>"
```

### Step 3: Pull

Pull both rules from the Rules Service:

```markdown
/gomboc:orl rules pull -o rules --search '(eq $.name "ensure-ami-is-in-approved-allowlist")'
/gomboc:orl rules pull -o patches --search '(eq $.name "orl/ensure-ami-allowlist-from-custom-variable")'
```

If you store the rules in the workflow repository instead, skip this step.

### Step 4: Patch

Now we should have the rule to be patched and the variables in `./rules`, and the patch in `./patches`. To patch the rules, use the following prompt:

```markdown
/gomboc:orl-remediation-expert --language orl --rulespace ./patches ./rules
```

This tells ORL to patch rules in `./rules` using rules in `./patches`. Variables are automatically loaded from `./rules`.

### Step 5: Publish

With the rule updated, run the following prompt:

```markdown
/gomboc:orl publish ./rules
```

This publishes the patched rule to the Rules Service.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.gomboc.ai/orl/agent-skills/examples/maintaining-rules.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
