
I had Claude Code itself create a skill that turns Security Hub detection items into issue ticket text for customers.
This page has been translated by machine translation. View original
Hello, I'm Hayashi.
How do you handle the items detected by Security Hub?
Security Hub is a service that automatically checks your AWS environment settings against security best practices and detects violations with IDs like "EC2.9".
When working with customers to address these detections, I create tickets in an issue management tool (we use Backlog), but the structure of the ticket text is almost the same every time. I kept repeating the work thinking "I wrote something similar before..."
I thought "wouldn't this be easier if I made it a Claude Code skill?" and when I tried it, I found that just by providing the control ID and target resources, it now produces text that can be used almost as-is, so I'd like to share it.
By the way, the skill itself was also created by Claude Code.
What are Skills (Agent Skills)?
Simply put, it's a mechanism for teaching Claude Code custom procedures and rules.
Just by placing a SKILL.md file with procedures and output rules under ~/.claude/skills/, Claude Code automatically loads the skill that matches the request and acts according to its content.
For details on how it works and how to create it, there are already great articles on DevelopersIO, so I won't explain it in this article.
These articles are helpful for understanding the skill mechanism and how to create them.
What I Created
I created a folder called ~/.claude/skills/securityhub-issue/ and placed the following 2 files in it.
securityhub-issue/
├── SKILL.md # How to receive input / output rules
└── templates/
└── issue-template.md # Template for ticket text
The input is just two things: the control ID and the list of target resources.
The resource list is fine to paste directly from the management console, and the skill side parses and formats it.
Since the control title and severity are determined by ID, I made them optional inputs.
If provided, that takes precedence; if not provided, the skill supplements them, explicitly states that it supplemented them, and prompts for confirmation.
The output is Markdown with the following structure. It's used by pasting into the issue management tool.
- Issue summary (ID / title / severity)
- Details
- Purpose
- Detection reason
- Actions to take
- Completion criteria
- Reference information
- Closing comment
Trying It Out
Let's use EC2.9 as an example. I ask Claude Code as follows.
Please create a ticket for EC2.9. The targets are as follows.
# Paste the resource list copied from the console here
Here is the generated ticket text. The key point is that the "Actions to take" section lists both remediation and suppression approaches side by side.
The account ID, resource names, and Name tags have been replaced with fictional ones.
## Issue Summary
We will address the control detected by Security Hub.
- **ID**: EC2.9
- **Title**: EC2 instances should not have a public IPv4 address
- **Severity**: HIGH
## Details
### Purpose
Minimizing the attack surface by reducing EC2 instances directly reachable from the internet
### Detection Reason
A public IPv4 address is assigned to the target EC2 instances.
Instances with public IPs are directly accessible from the internet, and depending on security group settings, there is a risk of unintended access.
### Actions to Take
Please confirm whether a public IP is necessary for the target instances.
- **Approach A (Remediation)**: If a public IP is not needed, remove the assignment. As a recurrence prevention measure, also confirm the subnet's auto-assignment settings.
- **Approach B (Suppression)**: If required for business reasons, change the workflow status of the Security Hub finding to **`SUPPRESSED`**.
Note that newly launched instances will be detected again, so if you wish to continuously suppress them, please consider using Security Hub automation rules.
Please refer to "Reference Information" for procedures.
> **Note**
> If you remove the public IP, in addition to connections from outside, outbound communication from instances that do not go through NAT Gateway etc. will also become unavailable.
> Please confirm the current usage in advance.
#### Target Instance List
| Account ID | Region | Instance ID | Name Tag |
| --- | --- | --- | --- |
| 123456789012 | ap-northeast-1 | i-0123456789abcdef0 | bastion-server |
| 123456789012 | ap-northeast-1 | i-0fedcba9876543210 | web-server-01 |
### Completion Criteria
- [ ] For instances where a public IP is not needed, the assignment has been removed and the finding status is **`PASSED`**
- [ ] For instances required for business reasons, the finding status is **`SUPPRESSED`** and the suppression reason is recorded in the notes
## Reference Information
- **Remediation Procedure**
[【Security Hub修復手順】[EC2.9] Amazon EC2 インスタンスは、パブリック IPv4 アドレスを未設定にすることをお勧めします | DevelopersIO](https://dev.classmethod.jp/articles/securityhub-fsbp-remediation-ec2-9/)
Text that can be registered almost as-is was output.
Honestly, the first time I ran it, I thought "do I even need to write this myself anymore?"
That said, this level of quality didn't come out right from the start.
The early output was polished but somehow felt "AI-like," and I was making edits here and there every time.
From there, I kept adding rules to SKILL.md, and it has grown into its current form.
Key Points in SKILL.md
The full text of SKILL.md contains business-specific descriptions so I'll omit it, but the overall structure is as follows.
# Security Hub Remediation Response Issue Ticket Skill
## Information to Receive as Input
## Output Rules
### Writing Style and Tone
### Level of Detail (Initial Response)
### Structure (Required Sections)
### How to Write Notes (Business Impact)
### How to Write the Target List
### How to Write Reference Information
### Including Both Suppression (SUPPRESSED) Approaches
### Considerations by Control Type
## Template
Among these, I'll introduce excerpts of the rules that have been particularly effective.
Tightly Constraining Writing Style and Tone
Text generated by AI is polished, but when left as-is, it tends to retain a sense of "written by AI."
There were overly formal expressions, anxiety-inducing language, and repeating the same request in both the body text and the closing.
Since this is text to be presented to customers, I made these tendencies explicit as output rules and eliminated them.
### Writing Style and Tone
- Since this is for customer issue management, write in polite "desu/masu" style
- Write "Purpose" in noun-ending style, not ending with verbs like "〜いたします"
- Avoid expressions that excessively induce anxiety; write concisely based on facts
- Avoid overly conversational expressions like "お聞かせください" and use expressions appropriate for text communication in an issue management tool
- Do not repeat the same request or explanation (such as requests for impact confirmation) in the main text, notes, and closing comment. Consolidate into one place
- Do not use self-deprecating expressions like "存じます"
There are detailed rules listed, but each one is a trace of something that appeared in the output and was corrected.
For example, the prohibition of "存じます" is simply because it's a phrasing I don't normally use, and when it appears in tickets, it doesn't feel like my own writing. Being able to write down even such personal quirks in word choice is a subtly convenient aspect of skills.
The list of rules has become a list of "reasons why text looks AI-generated," which is kind of interesting to look back on.
Having Both "Remediation" and "Suppression" Approaches Listed Side by Side
In Security Hub responses, detection doesn't always mean immediate remediation — there are cases where something is "necessary for business reasons," like a public IP for EC2.9.
In such cases, instead of fixing the setting, the common practice is suppression, where you change the finding status to SUPPRESSED to treat it as "a detection that has been reviewed and accepted."
I made it a rule to present this in a way that lets customers make the decision, rather than unilaterally deciding on the remediation approach ourselves.
- **Approach A (Remediation)**: Correct the setting and make the control **`PASSED`**
- **Approach B (Suppression)**: If required for business reasons, change the Security Hub finding's workflow status to **`SUPPRESSED`**
- Include both Approach A and B cases in the completion criteria
- Clearly state that the final decision is left to the customer
Not Going Too Deep in the Initial Ticket
AI tends to write suggestions like "We recommend switching to Session Manager for bastion servers."
Even if the suggestion itself is correct, it can be off-target in an initial ticket where the usage situation hasn't been interviewed yet.
When such preemptive text appeared, I added this rule.
### Level of Detail (Initial Response)
- In the initial ticket where the customer's usage situation has not been interviewed, do not bring forward in-depth operational proposals
- Limit to "confirming usage situation and necessity" and "requesting a decision on the response approach"
Changing the Target List Table by Resource Type
Since the target differs by control — "account/region," "instance," "bucket," etc. — I instructed it to change the heading and columns according to the type. I also made the Name tag column mandatory to improve identifiability.
Also, since the order of rows tends to be scattered when pasting from the management console, I added a rule to "group/sort rows by Name tag."
### How to Write the Target List
Since targets differ depending on the nature of the control, change the heading and table columns according to the type.
- Account/region-level settings (SSM, Config, etc.): Heading "Target Account/Region List", columns: Account ID | Region
- EC2 instances: Heading "Target Instance List", columns: Account ID | Region | Instance ID | Name Tag
- Security groups: Heading "Target Security Group List", columns: Account ID | Region | Security Group ID | Name Tag
- S3 buckets: Heading "Target Bucket List", columns: Account ID | Region | Bucket Name
- IAM users/roles: Heading "Target IAM Resource List", columns: Account ID | Resource Name (ARN)
The above are examples; judge and construct appropriate headings and columns based on the resource information provided as input.
Notes on ordering and columns:
- For instances etc., **include a Name Tag column** to improve resource identifiability.
- Sort/group rows by Name Tag for readability (do not paste in the same order as the ID listing).
Ask Back Before Generating If Input Is Insufficient
I also included the following rule in SKILL.md.
If there is insufficient input, confirm the missing items with the user before generating.
In particular, if "detection reason," "business impact," or "remediation procedure" is unclear, supplement with general information based on the control's content,
explicitly indicate the inferred parts, and prompt the user for confirmation.
For text to be presented to customers, the most important thing is not to have inferences written as facts.
I added the rule to make inferred parts explicit at an early stage.
Having Claude Code Write the Skill Itself, and Growing It Through Review
As I wrote at the beginning, this SKILL.md is not something I wrote from scratch. I asked Claude Code to "create a skill for creating Security Hub ticket text" and had it create it.
At first I was skeptical about whether it would be properly created, but a complete package with everything from the folder structure to the template appeared in a few minutes.
The subsequent operation is simple:
- Generate ticket text with the skill
- Manually fix concerning parts or point them out
- Ask to "reflect the current feedback in the rules in
SKILL.md"
Repeat.
All of the style-related rules like "Purpose in noun-ending style" and "no duplicate requests" were added through this loop, and I could feel the number of edits decreasing with each iteration.
Rather than refining the prompt each time, the feedback accumulates in the skill. This is what I like most about it.
It's also subtly satisfying to have my "writing preferences" verbalized and preserved in a file.
Points to Note
What the skill does on your behalf is only the "writing" part. When using it, I make sure to observe the following two points.
- Always have a person review the generated ticket text before creating the ticket
I check every time whether the control ID, title, and target list match the input, and whether the reference links actually exist.
Technical judgment and responsibility for the ticket content remain with the human. - Handle customer information within the scope of your organization's generative AI usage policy
Account IDs and resource names are customer configuration information.
Please check your organization's rules and data handling conditions under your contracts before use.
Summary
- Document creation that is routine but "slightly different each time" is well-suited for being turned into a skill
- Even implicit knowledge like writing style and tone, when made explicit and turned into rules, stabilizes output quality
- Having Claude Code write the skill itself, and adding rules with each review, is an easy operation
If you have tasks where you think "I keep writing similar text every time," please try turning them into skills.
Since you can have Claude Code create the initial template, it's easier to get started than you might think.
I hope this article is helpful to someone.
