{"id":164950,"date":"2026-03-28T21:46:54","date_gmt":"2026-03-28T18:46:54","guid":{"rendered":"https:\/\/computingforgeeks.com\/?p=164950"},"modified":"2026-03-28T21:46:55","modified_gmt":"2026-03-28T18:46:55","slug":"claude-code-github-actions-infrastructure","status":"publish","type":"post","link":"https:\/\/computingforgeeks.com\/claude-code-github-actions-infrastructure\/","title":{"rendered":"Set Up Claude Code in GitHub Actions for Infrastructure Review"},"content":{"rendered":"\n<p>Claude Code has an official GitHub Action that reviews pull requests automatically. Most guides show it reviewing application code. This one uses it for infrastructure: Terraform configs, Ansible playbooks, Kubernetes manifests. When someone opens a PR that changes a security group or adds a database server, Claude Code reviews the diff, flags security issues, checks for missing tags, and posts its findings as a PR comment.<\/p>\n\n\n\n<p>This guide is the final spoke in the <a href=\"https:\/\/computingforgeeks.com\/claude-code-devops-engineers\/\" target=\"_blank\" rel=\"noreferrer noopener\">Claude Code for DevOps Engineers<\/a> series. Everything below was tested on a real GitHub repository (<code>techviewleo-dot\/infra-demo<\/code>) with real Action runs. The workflow YAML, the action logs, the screenshots are all from actual execution.<\/p>\n\n\n\n<p><em>Tested <strong>March 2026<\/strong> | anthropics\/claude-code-action@v1, GitHub Actions, Sonnet 4.6, Terraform files<\/em><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What You Need<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A GitHub repository with infrastructure code (Terraform, Ansible, Kubernetes manifests)<\/li>\n\n<li>An <a href=\"https:\/\/console.anthropic.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Anthropic API key<\/a> (stored as a GitHub Actions secret)<\/li>\n\n<li>The Claude GitHub App installed on your repository<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Install the Claude GitHub App<\/h2>\n\n\n\n<p>The <code>anthropics\/claude-code-action<\/code> requires the Claude GitHub App for authentication. Go to <a href=\"https:\/\/github.com\/apps\/claude\" target=\"_blank\" rel=\"noreferrer noopener\">github.com\/apps\/claude<\/a> and click <strong>Install<\/strong>:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/02-claude-app-install.png\" alt=\"Claude GitHub App page showing Install button with description of PR review capabilities\" class=\"wp-image-164947\" width=\"1024\" height=\"408\" title=\"\" srcset=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/02-claude-app-install.png 2474w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/02-claude-app-install-300x120.png 300w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/02-claude-app-install-1024x408.png 1024w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/02-claude-app-install-768x306.png 768w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/02-claude-app-install-1536x612.png 1536w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/02-claude-app-install-2048x816.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Select <strong>Only select repositories<\/strong> and pick the repo you want Claude to review. This limits Claude&#8217;s access to just the repositories you choose, not your entire account:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/03-claude-app-repo-select.png\" alt=\"Claude GitHub App Install and Authorize dialog showing repository selection for techviewleo-dot\/infra-demo\" class=\"wp-image-164948\" width=\"1024\" height=\"852\" title=\"\" srcset=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/03-claude-app-repo-select.png 2044w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/03-claude-app-repo-select-300x250.png 300w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/03-claude-app-repo-select-1024x853.png 1024w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/03-claude-app-repo-select-768x639.png 768w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/03-claude-app-repo-select-1536x1279.png 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>The app requests read access to actions and metadata, plus read\/write access to code, discussions, issues, PRs, and workflows. Click <strong>Install &amp; Authorize<\/strong> to complete the setup.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Set Up the Workflow<\/h2>\n\n\n\n<p>Create <code>.github\/workflows\/claude-review.yml<\/code> in your infrastructure repository. This workflow triggers on two events: pull requests that change Terraform files, and PR comments containing <code>@claude<\/code> for on-demand reviews.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>name: Claude Code Review\non:\n  pull_request:\n    paths:\n      - 'terraform\/**'\n      - '*.tf'\n  issue_comment:\n    types: [created]\n\njobs:\n  review:\n    runs-on: ubuntu-latest\n    if: >\n      github.event_name == 'pull_request' ||\n      (github.event_name == 'issue_comment' &&\n       github.event.issue.pull_request &&\n       contains(github.event.comment.body, '@claude'))\n    permissions:\n      contents: read\n      pull-requests: write\n      issues: write\n      id-token: write\n    steps:\n      - uses: actions\/checkout@v4\n        with:\n          fetch-depth: 0\n\n      - name: Claude Code Review\n        uses: anthropics\/claude-code-action@v1\n        with:\n          anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}\n          prompt: |\n            Review the Terraform changes in this PR for:\n            1. Security issues (open ports, missing encryption)\n            2. Missing tags (every resource needs Environment and ManagedBy)\n            3. Best practices and naming conventions\n            4. Cost implications<\/code><\/pre>\n\n\n\n<p>Three things to note about this configuration:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>id-token: write<\/code> is required.<\/strong> The action uses OIDC token exchange for authentication with the Claude GitHub App. Without it, you get <code>Unable to get ACTIONS_ID_TOKEN_REQUEST_URL<\/code><\/li>\n\n<li><strong>The <code>if<\/code> condition on <code>issue_comment<\/code><\/strong> ensures the action only triggers on PR comments (not issue comments) that contain <code>@claude<\/code><\/li>\n\n<li><strong><code>fetch-depth: 0<\/code><\/strong> gives Claude Code access to the full git history, so it can see what changed between the PR branch and main<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Store the API key as a secret<\/h3>\n\n\n\n<p>Add your Anthropic API key as a repository secret. Using the gh CLI:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>gh secret set ANTHROPIC_API_KEY --repo your-org\/your-repo<\/code><\/pre>\n\n\n\n<p>Paste the key when prompted. The secret is encrypted and only available to workflow runs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Trigger a Review on a Real PR<\/h2>\n\n\n\n<p>We tested this by creating a PR that adds a database server to a staging VPC. The Terraform file has deliberate security issues: PostgreSQL port 5432 open to <code>0.0.0.0\/0<\/code>, SSH open to the internet, missing <code>Environment<\/code> and <code>ManagedBy<\/code> tags, and an unencrypted root volume.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>resource \"aws_instance\" \"database\" {\n  ami           = \"ami-0c55b159cbfafe1f0\"\n  instance_type = \"t3.large\"\n  subnet_id     = aws_subnet.public.id\n\n  vpc_security_group_ids = [aws_security_group.database.id]\n\n  root_block_device {\n    volume_size = 100\n    volume_type = \"gp3\"\n  }\n\n  tags = {\n    Name = \"demo-database\"\n  }\n}\n\nresource \"aws_security_group\" \"database\" {\n  name   = \"demo-database-sg\"\n  vpc_id = aws_vpc.main.id\n\n  ingress {\n    from_port   = 5432\n    to_port     = 5432\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0\/0\"]\n  }\n\n  ingress {\n    from_port   = 22\n    to_port     = 22\n    protocol    = \"tcp\"\n    cidr_blocks = [\"0.0.0.0\/0\"]\n  }\n}<\/code><\/pre>\n\n\n\n<p>When the PR is opened, the Claude Code Review workflow triggers automatically:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/pr-overview.png\" alt=\"GitHub pull request showing Claude Code Review workflow check passing on Terraform infrastructure change\" class=\"wp-image-164949\" width=\"1920\" height=\"1061\" title=\"\" srcset=\"https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/pr-overview.png 1920w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/pr-overview-300x166.png 300w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/pr-overview-1024x566.png 1024w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/pr-overview-768x424.png 768w, https:\/\/computingforgeeks.com\/wp-content\/uploads\/2026\/03\/pr-overview-1536x849.png 1536w\" sizes=\"auto, (max-width: 1920px) 100vw, 1920px\" \/><\/figure>\n\n\n\n<p>The action runs Claude Code with Sonnet 4.6, which reads the PR diff, analyzes the Terraform changes, and reviews against the prompt criteria. In our test, the review completed in 29 seconds at a cost of $0.13.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">On-Demand Reviews with @claude<\/h2>\n\n\n\n<p>The <code>issue_comment<\/code> trigger lets any team member request a review by commenting <code>@claude<\/code> on a PR. This is useful when you push additional commits and want a fresh review, or when you want Claude to focus on a specific aspect:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@claude Review this Terraform change for security issues, missing tags, and cost implications.<\/code><\/pre>\n\n\n\n<p>The action triggers within seconds. In our testing, the on-demand review ran for 6 turns (Claude read the diff, checked the CLAUDE.md rules, analyzed the security groups, and prepared its review) and completed in under 30 seconds.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Customize the Review with CLAUDE.md<\/h2>\n\n\n\n<p>The action reads your repository&#8217;s <code>CLAUDE.md<\/code> file, which means you can encode your team&#8217;s infrastructure standards. The action follows these rules during every review.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Infrastructure Review Rules\n- Flag any security group with 0.0.0.0\/0 ingress on SSH (port 22)\n- Check all resources have Environment and ManagedBy tags\n- Verify no hardcoded credentials or secrets\n- Flag missing encryption settings on storage resources\n- Check CIDR blocks for overlapping ranges<\/code><\/pre>\n\n\n\n<p>This turns Claude from a generic code reviewer into an infrastructure-aware reviewer that knows your team&#8217;s tagging policy, security requirements, and naming conventions. The <a href=\"https:\/\/computingforgeeks.com\/claude-code-dot-claude-directory-guide\/\" target=\"_blank\" rel=\"noreferrer noopener\">.claude directory guide<\/a> covers the full configuration system.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What the Action Catches<\/h2>\n\n\n\n<p>Based on our testing with deliberately insecure Terraform configurations, Claude Code consistently flags:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Issue Category<\/th><th>What It Catches<\/th><th>Detection Rate<\/th><\/tr><\/thead><tbody><tr><td>Open security groups<\/td><td><code>0.0.0.0\/0<\/code> on SSH, database ports, RDP<\/td><td>Excellent<\/td><\/tr><tr><td>Missing tags<\/td><td>Resources without Environment, ManagedBy, Owner tags<\/td><td>Excellent (when specified in CLAUDE.md)<\/td><\/tr><tr><td>Unencrypted storage<\/td><td>EBS volumes, S3 buckets, RDS without encryption<\/td><td>Good<\/td><\/tr><tr><td>Overly permissive IAM<\/td><td><code>Action: \"*\"<\/code>, <code>Resource: \"*\"<\/code> policies<\/td><td>Good<\/td><\/tr><tr><td>Public subnets for databases<\/td><td>DB instances in public subnets<\/td><td>Good<\/td><\/tr><tr><td>Missing lifecycle rules<\/td><td>No prevent_destroy on stateful resources<\/td><td>Fair<\/td><\/tr><tr><td>Cost implications<\/td><td>Large instance types, NAT gateways, multi-AZ<\/td><td>Fair (mentions but doesn&#8217;t estimate cost)<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Gotchas We Hit During Setup<\/h2>\n\n\n\n<p>Three issues that burned real time during our testing. Save yourself the debugging.<\/p>\n\n\n\n<p><strong><code>id-token: write<\/code> permission is mandatory.<\/strong> Without it the action fails with <code>Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable<\/code>. This is not mentioned prominently in the action&#8217;s README. Add it to the workflow permissions alongside <code>contents: read<\/code> and <code>pull-requests: write<\/code>.<\/p>\n\n\n\n<p><strong>The Claude GitHub App must be installed.<\/strong> The action uses OIDC to exchange tokens with the Claude GitHub App. If the app isn&#8217;t installed on your repository, you get <code>Claude Code is not installed on this repository<\/code> after three retry attempts. Install it at <a href=\"https:\/\/github.com\/apps\/claude\" target=\"_blank\" rel=\"noreferrer noopener\">github.com\/apps\/claude<\/a>.<\/p>\n\n\n\n<p><strong>Use <code>prompt<\/code>, not <code>direct_prompt<\/code>.<\/strong> The action&#8217;s input parameter for the review instructions is <code>prompt<\/code>. Using <code>direct_prompt<\/code> (which appears in some older examples) produces a warning and the prompt is ignored.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Cost and Performance<\/h2>\n\n\n\n<p>From our test runs reviewing a ~60-line Terraform file addition:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Model<\/strong>: Sonnet 4.6 (default, configurable)<\/li>\n\n<li><strong>Duration<\/strong>: 29-60 seconds per review<\/li>\n\n<li><strong>Cost<\/strong>: $0.13 per review (Sonnet pricing)<\/li>\n\n<li><strong>Turns<\/strong>: 2-6 (reads diff, checks rules, analyzes, prepares review)<\/li>\n<\/ul>\n\n\n\n<p>At $0.13 per review, a team pushing 20 PRs per day spends about $2.60\/day on automated infrastructure review. That&#8217;s significantly cheaper than a human reviewer&#8217;s time for catching obvious security and compliance issues.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Headless Mode for Custom Pipelines<\/h2>\n\n\n\n<p>If the GitHub Action doesn&#8217;t fit your workflow, Claude Code&#8217;s <code>-p<\/code> flag runs non-interactively. Pipe any input directly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>terraform plan -out=tfplan\nterraform show -json tfplan | claude -p \"Review this Terraform plan. Flag security issues, unexpected destroys, and missing tags.\"<\/code><\/pre>\n\n\n\n<p>This works in any CI system: GitLab CI, Jenkins, CircleCI, Buildkite. Set <code>ANTHROPIC_API_KEY<\/code> as an environment variable and restrict with <code>--allowedTools \"Bash,Read\"<\/code> to limit what Claude Code can execute in the pipeline.<\/p>\n\n\n\n<p>For reviewing changed files without a full plan:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git diff main --name-only -- '*.tf' | claude -p \"Review these changed Terraform files for security issues\"<\/code><\/pre>\n\n\n\n<p>The <a href=\"https:\/\/computingforgeeks.com\/claude-code-cheat-sheet\/\" target=\"_blank\" rel=\"noreferrer noopener\">Claude Code cheat sheet<\/a> covers all headless mode flags and output formats.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Complete Series<\/h2>\n\n\n\n<p>This is the final spoke in the Claude Code for DevOps series. Each article covers a different infrastructure tool with real demos on real systems:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/computingforgeeks.com\/claude-code-devops-engineers\/\" target=\"_blank\" rel=\"noreferrer noopener\">Set Up Claude Code for DevOps Engineers<\/a> (pillar with safety rules and permissions)<\/li>\n\n<li><a href=\"https:\/\/computingforgeeks.com\/claude-code-ssh-server-management\/\" target=\"_blank\" rel=\"noreferrer noopener\">Manage Servers with Claude Code via SSH<\/a><\/li>\n\n<li><a href=\"https:\/\/computingforgeeks.com\/claude-code-docker-guide\/\" target=\"_blank\" rel=\"noreferrer noopener\">Build and Debug Docker Containers with Claude Code<\/a><\/li>\n\n<li><a href=\"https:\/\/computingforgeeks.com\/claude-code-terraform-guide\/\" target=\"_blank\" rel=\"noreferrer noopener\">Deploy Infrastructure with Claude Code and Terraform<\/a><\/li>\n\n<li><a href=\"https:\/\/computingforgeeks.com\/claude-code-ansible-guide\/\" target=\"_blank\" rel=\"noreferrer noopener\">Generate and Debug Ansible Playbooks with Claude Code<\/a><\/li>\n\n<li><a href=\"https:\/\/computingforgeeks.com\/claude-code-kubernetes-guide\/\" target=\"_blank\" rel=\"noreferrer noopener\">Deploy and Debug Kubernetes Apps with Claude Code<\/a><\/li>\n<\/ul>\n\n","protected":false},"excerpt":{"rendered":"<p>Claude Code has an official GitHub Action that reviews pull requests automatically. Most guides show it reviewing application code. This one uses it for infrastructure: Terraform configs, Ansible playbooks, Kubernetes manifests. When someone opens a PR that changes a security group or adds a database server, Claude Code reviews the diff, flags security issues, checks &#8230; <a title=\"Set Up Claude Code in GitHub Actions for Infrastructure Review\" class=\"read-more\" href=\"https:\/\/computingforgeeks.com\/claude-code-github-actions-infrastructure\/\" aria-label=\"Read more about Set Up Claude Code in GitHub Actions for Infrastructure Review\">Read more<\/a><\/p>\n","protected":false},"author":3,"featured_media":164951,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[39034,690,299],"tags":[],"class_list":["post-164950","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai","category-dev","category-how-to"],"_links":{"self":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/164950","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/comments?post=164950"}],"version-history":[{"count":1,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/164950\/revisions"}],"predecessor-version":[{"id":164984,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/posts\/164950\/revisions\/164984"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/media\/164951"}],"wp:attachment":[{"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/media?parent=164950"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/categories?post=164950"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/computingforgeeks.com\/wp-json\/wp\/v2\/tags?post=164950"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}