use-casesOctober 16, 2026

Workflow: EC2 Tag Audit with Jira Tickets and Cost Report (Clef)

EC2 Tag Audit with Jira Tickets and Cost Report (Clef)

Problem Introduction

EC2 instances launched without a team tag cannot be attributed to an owner or a cost centre. They are easy to create and easy to forget, and they keep billing while nobody knows whose they are. Finding them means listing every instance, working out who launched each one (a CloudTrail lookup), pricing them, and then chasing the right person. Raising a ticket by hand for each one is slow, and an automated job that re-files the same ticket on every run creates noise. Cost reporting adds to the problem, because finance needs to see untagged spend grouped by creator and team, not as a flat list of instance IDs.

How GRiPO Helps You in This Situation

GRiPO helps FinOps and platform teams turn an EC2 tagging audit into a single pipeline that finds untagged instances, identifies who launched them, estimates their cost, raises one Jira ticket per instance, and emails an executive cost-attribution report. Using GRiPO’s visual workflow builder, teams combine sandboxed AWS CLI discovery, the Clef decision-model plugin (clef:latest) that classifies each instance, a Claude-written ticket narrative, the Jira plugin, and an email node. All of this lives in one reusable workflow.

GRiPO Solution

With GRiPO, you can build an EC2 Tag Audit workflow that starts by running aws ec2 describe-instances in the target region for instances in running, stopped, pending and stopping states. A preparation step builds two things for the decision model:

  • A fixed question set, the same for every instance. The first question is a boolean (noul): does this instance carry at least one required team tag key? The second is a multiple-choice question: which required team tag does it carry, or none?
  • A sanitised item list. Each item holds only the instance ID, state, type and tags. The instance Name tag is deliberately removed so the model judges from tags alone. The names are kept in a separate meta list that is used only in the final report.

A second sandbox step (collectAwsData) splits the instances into tagged and untagged by comparing their tags against the required keys. For each untagged instance, and for the 25 most recently launched tagged ones as team evidence, it queries CloudTrail for the RunInstances event to find the creator identity and launch time. It then prices each untagged instance through the AWS Pricing API (on-demand, Linux, shared tenancy) and calculates two figures:

  • Estimated monthly cost: hourly rate × 730 hours. Only running and pending instances are billed; stopped instances show $0.00 because compute is not charged.
  • Estimated incurred cost: hourly rate × hours since launch. This is an upper bound that assumes the instance ran continuously.

The workflow then loops over the instances. Clef answers the two fixed questions for each one. A formatting step turns the answer into a verdict of TAGGED, UNTAGGED or UNKNOWN (probability of 0.5 or more counts as tagged) and records whether the model agrees with the deterministic tag check. An If condition lets only UNTAGGED instances continue.

For each untagged instance, the workflow does the following:

  1. Builds the attribution. It groups the instance by creator, adds cost figures, and works out the likely owning team. The team comes from the team tags on the other instances that same creator launched, and only when those all agree on one team. Otherwise it is marked unknown.
  2. Has Claude write the ticket narrative. A sandbox step runs the Claude CLI with a strict prompt. It must return raw JSON with summary, why, assessment and actions for each creator, using only facts from the data. It tries the Claude login already stored in the sandbox first, then the claudecode connection token, then the claudeapi token.
  3. Checks for an existing ticket. A JQL search finds open tickets in the project labelled untagged-resources. Any instance that already has an open ticket (matched by its untagged-instance-<id> label) is skipped and its existing ticket is reused.
  4. Creates the ticket. The Jira plugin bulk-creates up to 50 issues. Each description has a “why this ticket was raised” section, a summary table, the instance table, the existing tags, Claude’s assessment, numbered actions and a ready-to-run aws ec2 create-tags command. If Claude’s output is missing or unparseable, the ticket falls back to a plain template.
  5. Sets assignee and reporter. The ticket is assigned to JIRA_ASSIGNEE only if it has no assignee yet. The reporter is then set to JIRA_REPORTER, with both users looked up by email or name.

Instances launched by the AWS root user are flagged in the ticket as a security risk, with a recommendation to use an IAM user or role. After the ticket is assigned, the workflow builds an HTML executive report (totals, cost by team, cost by creator, and the untagged and tagged instance lists) and sends it by email.

How the loop runs

The Loop node (A5) has no nested body in the export. Its loop edge leads into the classification node, and every later node is chained on plain next edges, with no separate “done” exit. In practice this means everything from the classification node to the email runs once per instance. The If condition has no no path, so a TAGGED or UNKNOWN instance simply ends its iteration. The consequences are:

  • Each untagged instance gets its own ticket, which matches the per-instance duplicate check.
  • The creator and team summaries are built from the current instance only (finalizeAttribution filters the untagged list to the loop item), so each creator bucket holds one instance.
  • One report email is sent per untagged instance, not one consolidated email per run, and each report totals only that instance’s cost. Confirm this against a real run in the Executions tab. If you want a single consolidated report, move the report and email nodes out of the loop, or run a separate step after it finishes.

Manual Setup Required (do this before enabling)

  1. Create the connections used by the workflow:
    • clefAuth, a Bearer-token connection for the Clef endpoint (http://ai.internal.gripo.io:11434).
    • jira-basic, whose Api_token key holds base64('email:api_token') with no Basic prefix.
    • emailsecret, for the email node.
  2. Make sure the sandbox has AWS credentials with permissions for ec2:DescribeInstances, cloudtrail:LookupEvents and pricing:GetProducts.
  3. Make sure the Claude CLI is usable in the sandbox, either logged in or through a claudecode/claudeapi connection token. Without it, tickets are still created from the fallback template.
  4. Update the hardcoded values in the workflow to match your environment:
    • The Jira site https://siddiquecomputers.atlassian.net.
    • The sender workflow@gripo.io.
    • The recipient testing@gripo.io.
    • The email subject.
  5. Make sure the Jira project has the Task issue type (or whatever you set in JIRA_ISSUE_TYPE), and that the assignee and reporter users exist.

Input variables

VariableDefaultPurpose
REGIONeu-west-1AWS region to audit
TAG_KEYprod,dev,mrkt,bckndComma-separated team tag keys. An instance is “tagged” if it has any one of them
JIRA_PROJECT_KEYKANJira project for the tickets
JIRA_ISSUE_TYPETaskJira issue type
JIRA_ASSIGNEEsiddiquecomputers@gmail.comDefault assignee (email or name)
JIRA_REPORTERsiddiquecomputers@gmail.comTicket reporter (email or name)

About Clef

The workflow’s classification step calls the clef-system plugin (ask choice (dynamic), POST /v1/systemone) on http://ai.internal.gripo.io:11434, an Ollama-served model, using model tag clef:latest and the clefAuth connection. Clef returns a probability (0 to 1) for the boolean question and a choice with a confidence for the team question. Clef never sees instance names, only IDs, state, type and non-Name tags. Because the endpoint is internal, the GRiPO sandbox/worker must be able to reach ai.internal.gripo.io:11434.

Two cosmetic leftovers exist in this export: an empty expand field on getTicket (A27), and an empty reporter object ({"accountId": ""}) on setReporter (A26) next to the real fields.reporter value. Check that the empty reporter does not override the reporter in your Jira plugin version, and remove it if it does.

Automated Tag Auditing

The trigger is currently onDemand. Once the output is verified, replace it with a Schedule Trigger (for example weekly) so the audit runs continuously. Because duplicate detection is label-based, repeated runs reuse open tickets and do not file new ones for instances that already have one. The workflow only reads from AWS and writes to Jira and email. It never tags, stops or terminates an instance.