HashiCorp Agent Skills

repository·main·Indexed 20 days ago

https://github.com/hashicorp/agent-skills

A collection of Agent skills and Claude Code plugins designed to enhance AI coding assistants with deep knowledge of HashiCorp products. It provides specialized capabilities for Terraform (HCL code, modules, provider development, tests, and policies) and Packer (image building for AWS, Azure, and Windows, and HCP Packer registry integration). Skills can be installed via npx or as Claude Code plugins.

Tokens
111.2K
Snippets
270
Records
371
Agent score
70%

What's inside hashicorp-agent-skills

  1. Overview of HashiCorp Agent Skills products

    main

    HashiCorp Agent Skills provides specialized capabilities for HashiCorp products to assist with AI-driven development tasks.

    • Terraform: Use cases include writing HCL code, building modules, developing providers, running tests, and authoring policies.
    • Packer: Use cases include building machine images on AWS, Azure, and Windows, and integrating with the HCP Packer registry.
  2. Overview of Terraform Policy Agent Skills

    main

    The Terraform Policy Agent Skills are a collection of specialized skills designed to assist with Terraform Policy. This includes working with HCP Terraform's native policy-as-code engine for .policy.hcl and .policytest.hcl files.

    Depending on your goal, you should use one of the following specialized skills:

    • tfpolicy-author: Use this skill to write new Terraform Policies from English descriptions or to translate existing Sentinel, OPA, or Rego policies into Terraform Policy format.
    • tfpolicy-test: Use this skill to write or debug .policytest.hcl tests, create mock resources, or reason about the policy runner.
  3. Build Amazon Machine Images (AMIs) with the AWS AMI Builder skill

    main

    The aws-ami-builder skill uses Packer's amazon-ebs builder to create custom AMIs for EC2 instances.

    Note: Building AMIs incurs AWS costs for EC2 instances, EBS storage, and data transfer. Builds typically take 10-30 minutes.

    To use this skill, you must provide a Packer HCL template defining a source "amazon-ebs" block and a build block. You will also need to authenticate with AWS using environment variables, an AWS credentials file, or an IAM instance profile.

    packer {
      required_plugins {
        amazon = {
          source  = "github.com/hashicorp/amazon"
          version = "~> 1.3"
        }
      }
    }
    
    # ... (rest of template)
  4. Use the terraform-policy skill for policy authoring and testing

    main

    The terraform-policy utility skill is designed for managing Terraform Policy files, including .policy.hcl, .policytest.hcl, and converting Sentinel policies to Terraform Policy (tfpolicy).

    Use Cases

    • Writing/Converting: Create new .policy.hcl files from requirements or convert existing .sentinel policies to Terraform Policy.
    • Testing: Write or debug .policytest.hcl test files.
    • Migration: Migrating Sentinel policy libraries to Terraform Policy.

    Limitations (What NOT to use it for)

    • Do not use this for writing .tftest.hcl files for Terraform modules; use the terraform-test skill instead.
    • Do not use this for general Terraform HCL authoring; use the terraform-style-guide skill instead.
  5. Use the tfpolicy-test skill for Terraform policy testing

    main

    The tfpolicy-test skill is an expert agent designed to help you write and debug .policytest.hcl files. It assists in designing resource and module mocks, using expect_failure correctly, mocking cross-resource lookups for core::getresources(), and reasoning about the tfpolicy test runner behavior.

    Use this skill when:

    • You have an existing policy and want to write or improve tests for it.
    • You are debugging a failing or unexpectedly passing test.
    • You are writing .policytest.hcl files, policytest { targets = [...] } blocks, resource {} / module {} mocks, or using expect_failure / skip.
    • You are testing operation-aware policies and need to mock attrs and/or prior_attrs for create/update/delete scenarios.
    • You need to mock cross-resource lookups (e.g., aws_s3_bucket_versioning for core::getresources patterns).
    • You are investigating runner caveats (e.g., operations scope behavior, expect_failure limitations on data blocks).

    Do NOT use this skill when:

    • You are writing the policy itself (use tfpolicy-author instead).
    • You are converting a Sentinel test to a .policytest.hcl test (start with tfpolicy-author, then return here for refinements).
  6. Use the tfpolicy-author skill for Terraform Policy authoring

    main

    The tfpolicy-author skill is an expert agent designed for authoring Terraform Policies (.policy.hcl). It can translate natural-language requirements into policy blocks or convert existing Sentinel (.sentinel) source code into Terraform Policy equivalents.

    Use this skill when you need to:

    • Generate resource_policy, module_policy, or provider_policy blocks from descriptions (e.g., "block public RDS").
    • Convert Sentinel policies to Terraform Policy.
    • Migrate a Sentinel policy library to tfpolicy.
    • Learn about tfpolicy language features like filter, locals, enforce, input, operations, prior_attrs, and core::* functions.
    • Structure policies that use core::getresources() or core::getdatasource() for cross-resource checks.
    • Compare Sentinel and tfpolicy capabilities.

    Do NOT use this skill for:

    • Writing or debugging .policytest.hcl test files. For that, use the tfpolicy-test skill.
  7. Build Azure managed images with the azure-image-builder skill

    main

    The azure-image-builder skill allows you to build Azure managed images and Azure Compute Gallery images using Packer's azure-arm builder. This is used when creating custom images for Azure VMs.

    Note: Building Azure images incurs costs for compute, storage, and data transfer. Builds typically take 15-45 minutes depending on provisioning and the OS used.

  8. Handle missing attributes with core::try()

    main

    When testing resources, omitting an attribute in the attrs block will cause an evaluation error in the policy unless the policy explicitly handles it using core::try().

    Best Practice: Always include a "Missing attribute" test case where the target attribute is omitted entirely to ensure your policy is resilient.

    Example Policy:

    resource_policy "aws_s3_bucket" "check" {
      locals {
        # Use core::try() to handle missing attributes safely
        encryption = core::try(attrs.server_side_encryption_configuration, null)
      }
      enforce {
        condition = local.encryption != null
        error_message = "Encryption required"
      }
    }

    Example Test (Missing Attribute):

    resource "aws_s3_bucket" "no_encryption" {
      expect_failure = true
      attrs = {
        bucket = "my-bucket"
        # server_side_encryption_configuration is omitted
      }
    }
    resource_policy "aws_s3_bucket" "check" {
      locals {
        encryption = core::try(attrs.server_side_encryption_configuration, null)
      }
      enforce {
        condition = local.encryption != null
        error_message = "Encryption required"
      }
    }
  9. Use the Two-Check Pattern for Compliance

    main

    To avoid false positives when checking related attributes, use a 'Two-Check Pattern'. Checking only one attribute (like an algorithm) might pass even if the required secondary attribute (like a specific Key ID) is missing. Always combine checks using && to ensure full compliance.

    locals {
        sse_algorithm = core::try(attrs.encryption[0].sse_algorithm, "")
        kms_key_id = core::try(attrs.encryption[0].kms_master_key_id, "")
    }
    
    enforce {
        condition = local.sse_algorithm == "aws:kms" && local.kms_key_id != ""
        error_message = "Must use customer-managed KMS. Found algorithm: '${local.sse_algorithm}', key specified: ${local.kms_key_id != ""}"
    }
  10. Handle `null` attributes in test mocks using decision rules

    main

    Because core::try(attrs.field, default) only triggers the fallback when a key is absent (not when it is null), you must apply specific rules before adding a null test case to a mock:

    1. Two-step pattern (raw = core::try(attrs.field, null) then val = raw != null ? raw : []): The null is explicitly normalized. Add a pass case (no expect_failure) to verify this normalization.
    2. Explicit non-compliance check (condition = val != null && val != ""): null is intentionally non-compliant. Add a fail case (expect_failure = true).
    3. Single-step only (val = core::try(attrs.field, [])) without a null guard: null is not normalized and will crash downstream expressions (e.g., for val in null). Do NOT add a null case. Fix the policy to use the two-step pattern instead.

    Rule of thumb: Never add a null test case if the policy treats null the same as the safe default (e.g., false or []).

  11. Avoid using && for null checks in TFPolicy locals and enforce blocks

    main

    A critical behavior in TFPolicy is that the logical AND operator (&&) does NOT short-circuit in locals blocks, enforce {} blocks, or for...if predicates. Both sides of the && expression are always evaluated. If the left side is local.var != null and the right side is core::length(local.var), the policy will crash if local.var is null because the right side is still executed.

    Correct Patterns:

    • In locals or enforce blocks: Use the ternary operator (? :) to guard function calls. Ternary operators do short-circuit.
    • In for...if predicates: Use a ternary operator inside the predicate and ensure the second access is also wrapped in core::try() to prevent attribute-access errors.
    # ❌ WRONG — condition = also does NOT short-circuit; crashes when local.X is null
    enforce {
      condition = local.X != null && core::contains(["a", "b"], local.X)  # ❌ crashes!
    }
    
    # ✅ CORRECT — use ternary inside the locals block, then reference in condition
    locals {
      is_allowed = local.X != null ? core::contains(["a", "b"], local.X) : false
    }
    enforce {
      condition = local.is_allowed
    }
    
    # ❌ WRONG — for...if predicate also does NOT short-circuit
    violating = [
      for r in local.rules : r
      if core::try(r.field, null) != null && core::contains(["a", "b"], r.field)  # ❌ crashes!
    ]
    
    # ✅ CORRECT — use ternary in for...if predicate
    violating = [
      for r in local.rules : r
      if (core::try(r.field, null) != null ? core::contains(["a", "b"], core::try(r.field, "")) : false)
    ]