A Packer build runs with credentials that can create cloud instances and publish machine images, and anything a provisioner touches during that build can end up baked into every server launched from the result. HashiCorp Packer turns HCL templates into machine images for cloud and virtualisation platforms, and HCP Packer adds a registry that records image metadata, channels and ancestry. That makes the Packer pipeline both a powerful control for consistent, hardened servers and a high value non-human identity in its own right. This guide covers how Packer and HCP Packer handle identity, how to keep builder credentials and sensitive variables out of logs and images, how to lock down the SSH and WinRM communicators, and how to use HCP Packer channels, revocation and audit logs to prove which image is trusted. It is written for platform engineers, image factory owners and the security engineers who review them. For the operating system hardening that goes inside the image, see the Server Build and Golden Image Hardening Guide.
This is an independent guide from NHI Mgmt Group, based on HashiCorp's public Packer and HCP Packer documentation as of November 2026. It is not sponsored or reviewed by HashiCorp. Product features change, so confirm settings against HashiCorp's current documentation.
Key takeaways
- Treat the build runner as a privileged identity. The credentials Packer uses can launch instances and publish images that later run production, so scope them to image building only and prefer short-lived federated credentials.
- sensitive = true masks output, not images. Marking a variable or local as sensitive replaces its string values with <sensitive> in Packer's output; keeping secrets off the image itself is a provisioner design job.
- Debug and verbose logging change the exposure. PACKER_LOG writes detailed plugin logs, and packer build -debug writes the ephemeral SSH key to the working directory, so keep both off in shared CI runners.
- Use HCP Packer channels and revocation as the trust gate. Consumers that read images through a channel inherit revocation: a revoked version makes the hcp-packer-version data source fail and the downstream build stops.
- Replace static HCP service principal keys with workload identity federation. A conditional access statement can restrict registry access to one repository and workflow, and the workload never holds a long-lived secret.
How identity and access work in HashiCorp Packer
Packer itself has no user accounts. It is a command line tool that runs wherever you start it, on a laptop, a CI runner or a dedicated build host, and it acts with whatever credentials that environment provides. Three separate identities are involved in a typical build, and each needs its own controls.
| Identity | What it does | Where it comes from | Main control |
|---|---|---|---|
| Builder credential | Creates the temporary instance or VM, snapshots it and registers the image | The cloud or hypervisor plugin, using template fields or the platform's standard credential chain | A dedicated build role scoped to image creation, short-lived where the platform supports it |
| Communicator credential | Connects to the temporary machine over SSH or WinRM so provisioners can run | A temporary key pair Packer generates, or a key, certificate, agent or password you supply | Temporary keys, removal from the image, no reuse of personal keys |
| HCP Packer registry credential | Pushes build metadata to a bucket and reads channel assignments | HCP_CLIENT_ID and HCP_CLIENT_SECRET for a service principal key, or a workload identity credential file via HCP_CRED_FILE | Project-level service principal with federation instead of a static key |
Variables, locals and sensitive values
Templates receive values through variable blocks. Packer reads them, in rising order of precedence, from PKR_VAR_<name> environment variables, auto-loaded files named *.auto.pkrvars.hcl or *.auto.pkrvars.json, and -var or -var-file on the command line. Setting sensitive = true on a variable, or on a local block, tells Packer to obfuscate its string values in output; packer inspect shows the value as <sensitive>. HashiCorp's documentation describes this masking for Packer's output, so treat the image, provisioner scripts and plugin logs as separate places a secret can land.
Pulling secrets at build time
The vault() function reads a key from a HashiCorp Vault KV store, for example vault("/secret/data/hello", "foo") for KV version 2, and needs VAULT_ADDR and VAULT_TOKEN in the environment. It works in local definitions or inline in a provisioner or post-processor. Pair it with a local block marked sensitive so the value is masked, and give the Vault token a policy that reads only the build's own paths. An aws_secretsmanager contextual function exists alongside it for teams that keep build secrets in AWS. See the Secrets Management Guide for how to structure those paths and policies.
Admin roles and privileged access
Privilege in a Packer estate sits in three places: whoever can change templates and plugin versions, whoever holds the build credentials, and whoever administers the HCP organisation and project that host the registry.
HCP organisation and project roles
HCP uses role-based access control at organisation, project and resource level, and roles inherit downwards: a user who is Viewer on the organisation and Admin on one project holds both sets of permissions in that project.
| HCP role | Level | Relevant permissions | Who should hold it |
|---|---|---|---|
| Owner (roles/owner) | Organisation | Everything, including adding owners and requesting organisation deletion | Two or three named people, used rarely |
| Admin (roles/admin) | Organisation | Manages users, groups, service principals, SSO and billing | Platform administrators |
| SSO Administrator (roles/iam.sso-admin) | Organisation | Manages SSO and SCIM configuration | Identity team |
| Owner or Admin | Project | Edits project permissions, manages project service principals, creates and deletes resources | Image factory leads |
| Contributor | Project | Creates and deletes resources, cannot edit permissions | Image engineers who manage buckets and channels |
| Viewer | Project | Reads the project and its resources | Image consumers, auditors |
- Channel assignment is a release decision: whoever can move a production channel to a new version decides what every downstream Terraform run and Packer child build picks up. Hold that right in a small group and record changes through change management.
- SSO for people: configure SSO for the HCP organisation and use the SSO default role setting so new members arrive with minimal access. See the Identity Provider and SSO Security Guide.
- Template and plugin changes: protect the repository that holds *.pkr.hcl files with branch protection and review. A one-line change to a provisioner runs with the build credential on the next pipeline run.
Builder credentials
Give each image pipeline its own cloud role that can launch the build instance in a dedicated build network, create snapshots and register images, and nothing else. NHI Mgmt Group recommends keeping static access keys out of templates and variable files entirely: use the platform's role assumption or workload identity from the CI runner so the credential expires when the job ends. See the CI/CD Pipeline Identity Security Guide.
Integrations, API tokens and service accounts
HCP service principals and workload identity federation
Packer pushes metadata to HCP Packer when HCP_CLIENT_ID and HCP_CLIENT_SECRET hold a service principal key, with the bucket set by HCP_PACKER_BUCKET_NAME or the bucket_name argument in an hcp_packer_registry block. HashiCorp's workload identity federation documentation notes that anyone with a static key can authenticate with HCP, so keys must be stored, rotated and distributed carefully. Federation removes that work: the workload presents a token from its own identity provider and receives a short-lived HCP access token.
- Supported providers: AWS, Azure, GCP, GitHub Actions, GitLab and other OIDC providers, configured on a project-level service principal.
- Conditional access statements: restrict who can exchange a token, for example jwt_claims.repository == "my-org/my-repo" and jwt_claims.workflow == "deploy" for GitHub Actions, or an aws.arn match on one IAM role.
- Credential file: created with hcp iam workload-identity-providers create-cred-file and found through HCP_CRED_FILE or ~/.config/hcp/cred_file.json. It describes how to obtain a token rather than holding a secret, but keep it in the runner's workspace, not in the image.
See the GitHub Actions OIDC Federation Hardening Guide for writing tight subject and claim conditions.
Plugins as supply chain
Builders, provisioners and post-processors are plugins. The packer block's required_plugins maps each plugin to a source address and a version constraint, and HashiCorp describes it as a way to pin approved plugin versions across a team. Pin exact versions with = for production pipelines, set required_version so an unexpected Packer release fails fast, and, where policy requires, point source addresses at an internal HTTPS mirror. HashiCorp marks Community-Supported plugins as built and maintained by community members, so review them like any third-party dependency. See the Software Supply Chain Security Guide.
Communicators: SSH and WinRM
| Option | What it does | Recommended use (NHI Mgmt Group recommendation) |
|---|---|---|
| Temporary key pair (default when no key or password is set) | Packer generates a key pair named packer_<UUID> and deletes the local private key at cleanup | Default choice for cloud builds |
| ssh_clear_authorized_keys | Removes the temporary key from ~/.ssh/authorized_keys and /root/.ssh/authorized_keys on the guest; needs sed | Set to true (off by default) so the image ships without the build key |
| ssh_agent_auth | Uses the local SSH agent; no temporary key pair is created | Interactive troubleshooting only |
| ssh_certificate_file | Authenticates with a user certificate | Where you run an SSH certificate authority with short validity |
| ssh_password | Plaintext password sent with password and keyboard-interactive methods | Local hypervisor builds only, from a sensitive variable, then removed by a provisioner |
| ssh_bastion_* options | Reach the build instance through a jump host | Private build subnets with no public address |
Provenance with HCP Packer
HCP Packer stores metadata about artifacts, not the artifacts themselves: creation time, platform, the Git commit and the source artifact each build came from. Use bucket_labels for ownership and build_labels for tool versions so every image is traceable. The hcp-sbom provisioner uploads a software bill of materials and links it to the artifact version. Downstream builds should read their base image through the hcp-packer-version and hcp-packer-artifact data sources with a channel_name, so ancestry is recorded and revocation is enforced. See the SLSA and Build Provenance Guide and the SBOM Guide.
Logging and detection
What to log
- Packer run output: keep standard CI output with sensitive values masked. Enable PACKER_LOG (any value except empty or 0) and PACKER_LOG_PATH only for a specific investigation, because the detailed log includes plugin messages that standard output omits.
- HCP audit logs: stream platform and product audit logs to AWS CloudWatch, Datadog or Splunk from the Audit log streaming page or the hcp_log_streaming_destination Terraform resource. Streaming needs an organisation-level service principal, and HCP retries failed deliveries for up to seven days.
- Cloud audit logs for the builder role: image registration, snapshot sharing and instance launches by the build identity, from the cloud provider's own audit trail.
- Repository events: merges to template and variable files, and changes to required_plugins.
Detection ideas
- Channel reassignment or artifact revocation outside a change window.
- Service principal key creation in an HCP project that uses federation.
- The build role launching instances outside the build network or sharing an image with an unknown account.
- Secret scanning hits on template repositories, CI logs or image file systems. See the Secrets in Container Images and Build Artefacts Guide.
Metrics that show it is working
- Share of pipelines authenticating to the cloud and HCP with federated, short-lived credentials (target: all production pipelines).
- Share of production deployments that resolve their image through an HCP Packer channel rather than a hard-coded image ID.
- Median age of images assigned to production channels, against your rebuild target.
- Secret scanning findings in built images per month (target: zero).
- Time from revoking a version to no new deployments using it.
Hardening baseline
| Area | Setting or practice | Why |
|---|---|---|
| Versions | required_version and required_plugins with pinned versions | Reproducible builds and reviewed plugin changes |
| Secrets | sensitive = true on every secret variable and local; values from vault() or the CI secret store, never default values or committed *.pkrvars.hcl files | Masks output and keeps secrets out of source control |
| Communicator | Temporary key pair, ssh_clear_authorized_keys = true, private build subnet via ssh_bastion_host | The image ships without the build key and the build host is not exposed |
| Provisioners | Upload secrets to a temporary path, use them, delete them in the same provisioner; add an error-cleanup-provisioner for failed runs | Failed and successful builds both leave a clean image |
| Timeouts | timeout on long provisioners; avoid continue_on_error except where later steps are safe | Stops hung builds holding credentials and half-built instances |
| Registry | hcp_packer_registry with bucket_labels and build_labels; consumers read through channels | Traceable provenance and enforceable revocation |
| Identity | Workload identity federation with conditional access statements for HCP; federated cloud roles for builders | No long-lived secrets in CI |
| Debug | No -debug or PACKER_LOG in shared CI | Debug writes the ephemeral .pem key to the working directory |
Rollout plan
- Inventory: list every Packer template, the runner it uses, the cloud credential, the HCP credential and the images it produces. Assign an owner to each.
- Scan first: run secret scanning across template repositories and one recent image from each pipeline before changing anything, so you know your starting point.
- Pin and mask: add required_version, required_plugins and sensitive = true in a pull request per pipeline; confirm with packer inspect that secret values show as <sensitive>.
- Federate: create an HCP workload identity provider for one pilot pipeline with a conditional access statement, run it alongside the old key for a week, then revoke the key. Roll back by restoring the key in the CI secret store if a run fails.
- Gate with channels: create dev, test and production channels, move consumers to channel lookups, and rehearse revoking a test version.
- Monitor: stream HCP audit logs to the SIEM and add the detections above.
Decision guidance
- Small teams with one or two images can start with pinned plugins, sensitive variables, Vault or a CI secret store, and the HCP Packer Essentials tier for metadata and channels.
- Larger estates with layered images benefit from parent and child builds through the data sources, revocation that cascades to descendants, and HCP Terraform run task validation, which HashiCorp lists in the Standard tier. Confirm current tier contents on HashiCorp's pricing pages.
- Secret on the image or at runtime? If a value is the same for every instance and must exist before first boot, consider whether it is really a secret. Instance-specific credentials belong in a runtime bootstrap from a secrets manager, not in the image. See the Secret Zero Bootstrap Guide.
Common misconfigurations
- Secrets as variable defaults: a default value in a committed *.pkr.hcl file is in source control history even if sensitive = true masks it in output. Supply values at runtime instead.
- Debug left on in CI: a pipeline copied from a troubleshooting session keeps -debug or PACKER_LOG=1, writing the ephemeral key or detailed plugin logs into shared runner storage.
- Build key left in the image: ssh_clear_authorized_keys stays at its default, so the temporary public key remains in authorized_keys on every server. The private key is deleted, but a clean image is easier to audit.
- Provisioner files never removed: a file provisioner uploads a licence key or registration token and no later step deletes it, or a failed run skips the cleanup because no error-cleanup-provisioner exists.
- Hard-coded image IDs downstream: Terraform and child builds reference an image ID directly, so revoking a version in HCP Packer has no effect on them.
- Static HCP keys shared across pipelines: one project service principal key serves every pipeline, so you cannot tell which pipeline pushed a version or revoke access for one without breaking all.
Framework mapping
| Packer control | NIST SP 800-53 Rev 5 | NIST CSF 2.0 | MITRE ATT&CK technique addressed |
|---|---|---|---|
| Pinned templates and plugins as a baseline | CM-2, CM-6, SA-10 | PR.PS-01 | T1195.002 Compromise Software Supply Chain |
| Secrets kept out of templates and images | IA-5, SC-28 | PR.DS-01 | T1552.001 Credentials In Files |
| HCP Packer ancestry, SBOM and channels | SR-4, SR-11, SI-7 | GV.SC-05 | T1525 Implant Internal Image |
| Federated, scoped build identities | AC-6, IA-5 | PR.AA-01, PR.AA-05 | T1078.004 Cloud Accounts |
| HCP audit log streaming and review | AU-2, AU-6 | DE.CM-09 | T1098 Account Manipulation |
Practitioner checklist
- Every Packer pipeline has a named owner, its own cloud build role and its own HCP identity.
- Templates set required_version and pin every plugin in required_plugins.
- Every secret variable and local has sensitive = true and no default value; no *.pkrvars.hcl file with secrets is committed.
- Communicators use temporary keys with ssh_clear_authorized_keys = true and reach build instances through a private subnet.
- Provisioners delete uploaded secrets in the same step, and an error-cleanup-provisioner handles failed runs.
- -debug and PACKER_LOG are absent from CI definitions.
- HCP access uses workload identity federation with conditional access statements; static service principal keys are revoked.
- Consumers resolve images through HCP Packer channels, and revocation has been rehearsed.
- HCP audit logs stream to the SIEM, with alerts on channel changes, revocations and new keys.
- Built images are secret-scanned before channel promotion.
Standards and references
- HashiCorp: Packer input variables and sensitive values
- HashiCorp: Packer SSH communicator
- HashiCorp: Packer packer block, required_version and required_plugins
- HashiCorp: Debugging Packer builds
- HashiCorp: HCP Packer overview
- HashiCorp: HCP workload identity federation
- HashiCorp: HCP audit log streaming
- HashiCorp: HCP access management and roles
Related NHI Mgmt Group resources: Server Build and Golden Image Hardening Guide · Infrastructure as Code Security Guide · CI/CD Pipeline Identity Security Guide · Secrets in Container Images and Build Artefacts Guide · SLSA and Build Provenance Guide