Post

Generating Your First Rego Policy with IssueOps

Generating Your First Rego Policy with IssueOps

This post walks through the end-to-end IssueOps workflow for authoring and activating an Octopus Deploy Platform Hub policy using the GitHub Copilot Coding Agent. You’ll open a policy request as a GitHub issue, watch the agent author the OCL file and what-if report, review and merge the PR, then activate the policy with the Publisher agent.

By the end, you’ll have a live policy in Platform Hub and a clear picture of what the agent gets right, where you need to review carefully, and what the workflow looks like at each handoff.

This post assumes the platform-hub repo is set up as described in Setting Up Your Platform Hub Repo. If you haven’t done that yet, start there.

Prerequisites

  • Platform-hub repo configured with the Policy Author and Publisher agent definitions
  • GitHub Copilot with agent mode enabled and access to the platform-hub repo
  • Octopus Deploy Platform Hub with the repo connected as the version control source
  • At least one project slug handy — you’ll need the exact slug, not the display name

Phase 1: Open a policy issue

Navigate to your platform-hub repo on GitHub and open a new issue. Select the 🐙 Add Octopus Deploy Policy template.

TODO: Screenshot — GitHub “New Issue” page showing the issue template selection screen with the 🐙 Add Octopus Deploy Policy template visible

Fill in the two required fields:

Describe the policy — be specific. The agent maps your description directly to input.* fields in the Platform Hub schema. The more concrete you are, the more accurately the Rego reflects your intent. Avoid describing two independent rules in one issue — the agent will flag this and ask you to split them, which adds round-trips.

Good: Require at least one enabled manual intervention step for deployments to the production environment. The step must not be in the skipped steps list.

Too vague: Improve production deployment safety.

Two rules in one: Require manual intervention AND block deployments from non-main branches.

Project(s) in scope — enter the exact Octopus project slug, not the display name. Slugs are lowercase with hyphens. If you’re unsure, check the project URL in Octopus — it’s in the path.

For a single project: payments-service

For multiple: payments-service, payments-legacy

TODO: Screenshot — Issue template filled in with a concrete policy description and project slug, before submission

Submit the issue. GitHub automatically applies the 🐙 policy and 🤖 copilot labels, which trigger the Copilot Coding Agent.


Phase 2: Copilot authors the policy

The Copilot Coding Agent picks up the issue and runs the Policy Author agent. This takes a few minutes — the agent is doing real work: parsing intent, checking existing policies for overlap, fetching live project data from Octopus via MCP, writing the OCL file, and running static evaluation against each in-scope project’s deployment process.

TODO: Screenshot — GitHub issue showing the Copilot agent status indicator (“Copilot is working on this”) with a link to the agent session log

When it’s done, the agent opens a pull request containing:

  • The new OCL file at .octopus/policies/<package_name>.ocl
  • The what-if report at octopus-agent/output-<timestamp>/reports/<package_name>.md
  • A PR comment with the full what-if report inline

TODO: Screenshot — GitHub PR opened by the Copilot agent, showing the two files changed (OCL and report) and the PR description summarising what was authored


Phase 3: Review the PR

This is the most important step in the workflow. The PR is where human judgment replaces agent speed.

Read the what-if report first

The what-if report is posted as a PR comment. Don’t read the OCL file first — read the report. It tells you what the policy will actually do to real projects before you look at the Rego that implements it.

The report has three sections:

Pass — projects whose current deployment processes already satisfy the policy. These projects will not be affected when the policy activates.

Will Be Affected — projects that will receive a warning or block. The report includes the specific reason and what the team needs to fix. For a warn policy, deployments proceed but the violation is logged. For a block policy, deployments are stopped until the process is corrected.

Unable to Evaluate Statically — conditions that depend on runtime state. The most common case is input.SkippedSteps: the agent can verify that a required step exists in the deployment process, but can’t know whether a user will choose to skip it at deployment time. The report notes what it checked and why it can’t give a definitive answer.

Here’s an example of a real what-if report output for a manual intervention policy scoped to payments-service and payments-legacy:

1
2
3
4
5
6
7
8
9
10
11
Verdict: 1 project will be affected ⚠️ | 1 project will pass ✅ | 0 projects unable to evaluate ❓

Projects: Pass (1)
| Project          | Reason                                                                                          |
|------------------|-------------------------------------------------------------------------------------------------|
| payments-legacy  | Has an enabled Octopus.Manual step ("Prod Manual intervention") scoped to Production.           |

Projects: Will Be Affected (1)
| Project          | Violation                                              | What to fix                                              |
|------------------|--------------------------------------------------------|----------------------------------------------------------|
| payments-service | No Octopus.Manual step present in deployment process.  | Add an enabled manual intervention step to the process.  |

If the “Will Be Affected” list includes projects you didn’t intend to touch, or misses projects you expected to see, that’s a scope issue in the Rego — leave a review comment and the agent will iterate.

TODO: Screenshot — PR comment showing the full what-if report with the three-section table format, verdict line at the top

Then read the OCL file

Once you’re satisfied the impact is correct, read the generated OCL file. Check:

  • Package name matches the filename (no dashes, underscores only)
  • Scope block correctly targets the right projects and execution type (not input.Runbook for deployment-only policies)
  • Conditions block has a default result := {"allowed": false} with no inline action — the violation_action header governs the default behaviour
  • violation_reason is human-readable and actionable, not generic

TODO: Screenshot — GitHub PR file diff showing the generated OCL file with the scope and conditions Rego blocks visible

Common things to push back on in review:

  • Slugs that look invented rather than real — the agent should have fetched these from Octopus via MCP, but verify them against your actual environment
  • A default evaluate := true in scope when you only want a subset of projects — this catches everything and can’t be easily narrowed after the fact
  • Missing not input.Runbook guard on a policy that’s meant for deployments only — without it, the policy also evaluates runbook runs and may cause evaluation errors if it references input.Release

Approve and merge

When the report and OCL look correct, approve and merge the PR to main. The Policy Publisher agent won’t run until the commit is on main and pushed — that’s the gate.

TODO: Screenshot — GitHub PR approval UI with the merge button, showing the branch protection check passing


Phase 4: Activate with the Policy Publisher

After the merge, trigger the Policy Publisher agent. In a Copilot Chat session on the repo, send:

1
Publish policy: require_manual_intervention_production

Replace require_manual_intervention_production with the package name of the policy you just merged (it’s the OCL filename without .ocl).

The agent runs its pre-flight checks in sequence:

  1. Confirms the OCL file exists at .octopus/policies/require_manual_intervention_production.ocl
  2. Confirms the scope block has an active evaluate if clause — not fully disabled
  3. Confirms the file is committed, not staged
  4. Queries the Octopus Platform Hub API to discover the configured default branch, then checks that your commit has been pushed there — hard stops if not

If all checks pass, it resolves the current policy version (or starts at 1.0.0 if this is the first publish), calls the publish endpoint, then calls the activate endpoint.

TODO: Screenshot — Copilot Chat session showing the publisher agent output with the pre-flight check results and the final publish summary table

The output looks like this:

1
2
3
| Policy                                    | Previous Version | Published Version | Status                   |
|-------------------------------------------|-----------------|-------------------|--------------------------|
| require_manual_intervention_production    | none            | 1.0.0             | ✅ Published & Activated  |

The policy is now live. Any deployment that falls within its scope will be evaluated against the conditions Rego.

TODO: Screenshot — Octopus Platform Hub policies list showing the newly activated policy with its version number and active status


Phase 5: Verify in Octopus

Trigger a deployment on an in-scope project to confirm the policy evaluates as expected.

For a project in the “Will Be Affected” category from the what-if report, the task log should show a policy violation in warn mode — the deployment proceeds but the violation is recorded.

TODO: Screenshot — Octopus deployment task log showing the policy evaluation result, with the violation reason text visible

For a project in the “Pass” category, no violation appears. Check the audit log under Configuration → Audit, filtered by Compliance Policy Evaluated, to confirm the policy ran and passed — an absence of a violation in the task log doesn’t mean the policy didn’t run.

TODO: Screenshot — Octopus audit log filtered to “Compliance Policy Evaluated” events, showing entries for the deployment you just triggered


Known limitations / what I’d do differently

The agent can’t verify slugs it can’t look up. The Policy Author agent fetches project slugs from Octopus via MCP, but it can’t always resolve environment slugs reliably if environments have non-obvious slug values. The safest practice is to check the verbose task log on a recent deployment for the exact input.Environment.Slug value, and verify it against what the agent put in the scope block before merging.

Multi-project policies are always warn. The agent enforces this automatically — if you put two or more project slugs in the issue, it downgrades violation_action to warn regardless of what you requested. This is by design to limit blast radius. To use block on multiple projects, author separate single-project policies and promote them to block individually after validating each one in warn mode.

The PR iterate loop can take a few minutes per round-trip. If the reviewer pushes back on the scope or conditions, the agent needs to re-run the full four-phase flow to produce an updated OCL and a new what-if report. For complex policies, budget for one or two review cycles before merge.

What-if analysis is static. The report tells you what will happen based on current deployment process definitions. If a team changes their process after the policy is active — removing a required step, for example — the next deployment will trigger a violation that the what-if report didn’t predict. The report is accurate at the moment it’s generated, not a permanent guarantee.

The publisher’s version increment is patch-only. Every publish bumps the patch version (1.0.01.0.1). There’s no built-in concept of a breaking change or a major version bump for policies. If you make a significant scope change — for example, extending a block policy from one project to five — the version history won’t distinguish that from a minor conditions tweak. A naming convention or PR label for significant changes helps maintain clarity in the audit trail.

This post is licensed under CC BY 4.0 by the author.