HFCP study materials
HFCP study resources

HFCP common mistakes

HFCP · Understand the traps and how to avoid them.

Mistake 1

Treating Terraform state as an implementation detail and assuming that editing or deleting a resource block in configuration also removes the real infrastructure, or conversely that removing a resource from configuration leaves the cloud object untouched.

Candidates learn apply early and then reason about configuration as if it were the system of record for infrastructure. In practice the state file is the mapping between configuration addresses and real objects, so a resource dropped from configuration is only removed on the next plan if it is still present in state, while a resource deleted from state becomes orphaned and unmanaged without any destroy.

What to do instead

Before every destructive change, run terraform plan and read the legend symbols: a minus means the object will be destroyed, a plus means created, and a minus/plus pair means replacement. Use terraform state list and terraform state show to confirm what state believes exists, and use moved blocks or terraform state mv when renaming or relocating resources so the plan shows no destroy. Practise a deliberate remove-from-configuration exercise in a sandbox to see the destroy plan that results.

Mistake 2

Assuming that terraform apply immediately destroys objects removed from configuration when those objects are protected by prevent_destroy, or expecting prevent_destroy to protect objects that were never in state.

prevent_destroy is a plan-time guard evaluated against the resource block in configuration, not a property of the remote object. Candidates expect it to behave like a cloud-side deletion lock, so they are surprised when removing the whole resource block (rather than changing an attribute) bypasses the guard, or when deleting the object out of band is not prevented at all.

What to do instead

Treat prevent_destroy as a speed bump for accidental attribute-driven replacement, and pair it with provider-level or cloud-native deletion protection for real safety. Remember that removing the resource block entirely from configuration removes the guard along with it, so review plans for destroy actions on protected resources manually. Test the behaviour in a sandbox by adding prevent_destroy, then attempting a forced replacement, and observe the exact error.

Mistake 3

Hardcoding credentials, tokens, or secret values directly in Terraform variables, .tfvars files, or resource arguments, and then committing those files to version control.

It is the shortest path to a working apply, and many tutorials show literal values in examples. Candidates also assume that marking a variable sensitive hides it, when sensitive only redacts CLI output and does not encrypt state or prevent the value from appearing in plan files and remote state backends.

What to do instead

Use a secrets manager as the source of truth and read values at apply time through a data source or an external secrets operator, or inject them as ephemeral environment variables in CI. Mark variables sensitive for output hygiene but never rely on it for confidentiality: treat state as sensitive, use an encrypted remote backend with restricted access, and add secret scanning plus a .gitignore for .tfvars and state files. Rotate anything that has already been committed.

Mistake 4

Writing a Vault policy that grants broad wildcard paths such as path "secret/*" with capabilities ["create", "read", "update", "delete", "list"] and attaching it to an application token, believing that path globbing is a sufficient boundary.

Wildcards are convenient during development and the policy language looks like simple string matching. Candidates miss that a trailing asterisk matches any number of path segments, that list and read on parent paths can enumerate secrets, and that a token inherits the union of all attached policies, so one permissive policy widens every token it touches.

What to do instead

Grant the narrowest path and the smallest capability set that the workload needs, prefer explicit paths over globs, and separate read-only from write policies. Use Vault's policy testing and the sys/capabilities-self endpoint to verify what a token can actually do, and audit tokens with lookup-self and the token accessor. Prefer short-lived tokens issued through auth methods over long-lived static tokens.

Mistake 5

Assuming that a Vault secret engine's data is encrypted by Vault and therefore safe to read back later without the original key, or that deleting a KV version permanently removes the underlying data.

Candidates conflate the KV version 1 and version 2 semantics. In KV v2, delete creates a delete marker for the latest version while the version data remains until metadata is destroyed, and soft delete plus undelete behave differently from destroy. Similarly, the transit engine performs cryptographic operations without ever returning the key, which surprises candidates who expect to fetch plaintext keys.

What to do instead

Learn the KV v2 lifecycle explicitly: write creates a version, delete removes the version's data marker, undelete restores it, destroy permanently removes a version, and metadata deletion removes all versions. In the transit engine, use encrypt and decrypt endpoints and never expect the key material back; use datakey generation when the caller needs the material. Verify behaviour in a dev server with the CLI before relying on it in automation.

Mistake 6

Configuring a security group or firewall rule that references another security group by name or ID without understanding that the reference grants access to all members of that group, and then assuming the rule is equivalent to a CIDR-scoped rule.

Security-group references are the idiomatic way to express service-to-service access, so candidates copy the pattern without reasoning about membership. Because membership is dynamic, an instance launched later into the referenced group silently gains access, which is invisible in a static review of the rule.

What to do instead

Model trust boundaries explicitly: use dedicated security groups per tier with descriptive names, document which workloads are members, and avoid attaching a broadly used group as a source. Where the platform supports it, prefer identity-based or service-mesh authorization for east-west traffic and keep network rules as a coarse outer boundary. Review membership during change review, not just the rule definitions.

Mistake 7

Treating a service mesh sidecar as optional and debugging application connectivity without checking whether the proxy is injected, whether the application listens on the expected interface, and whether mesh authorization policy is denying the request.

Candidates reason about service-to-service calls as plain TCP and forget that in a mesh, traffic is intercepted by the proxy and subject to mutual TLS identity and authorization policy. Symptoms such as connection resets or 503 responses look like application bugs but often originate in proxy configuration or policy.

What to do instead

Establish a triage order: confirm injection and proxy readiness, check the proxy's configuration and cluster view, inspect authorization policy for the destination identity, then look at the application. Use the mesh's built-in diagnostics (proxy status, configuration dump, and access logs) rather than guessing, and reproduce with a minimal client pod that carries a known identity.

Mistake 8

Assuming that a Terraform Cloud or HCP Terraform workspace run has the same environment, credentials, and variable precedence as the local CLI, and being surprised when a plan behaves differently in remote runs.

Remote runs execute in a managed environment with variables supplied by the workspace rather than the shell. Candidates forget that workspace variables, variable sets, and Terraform variables have a defined precedence, that environment variables from the local shell are absent, and that credentials must come from a workspace-scoped identity rather than a local profile.

What to do instead

Reproduce the run locally with the same variable inputs, and inspect the run's variable resolution and environment in the workspace settings. Use variable sets for shared values, mark sensitive variables appropriately, and prefer dynamic provider credentials over long-lived static keys stored in workspace variables. When a plan differs, compare the run's Terraform version and provider versions with the local ones before changing code.

Mistake 9

Building CI/CD pipelines that run terraform apply automatically on every merge to the main branch with no plan review, no approval gate, and no concurrency control.

The pipeline works in the happy path, so candidates stop there. Without a saved plan that is reviewed and then applied, drift and unintended destroys reach production. Without concurrency control, two runs can apply against the same state simultaneously, producing lock contention or, worse, interleaved changes.

What to do instead

Split the pipeline into a plan stage that produces a saved plan artifact and a separate apply stage that consumes exactly that plan after human or policy approval. Enable state locking, serialize applies per workspace with a concurrency group, and fail the pipeline when the plan contains unexpected destroys. Keep credentials short-lived and scoped to the pipeline identity.

Mistake 10

Using long-lived static cloud access keys and Vault root tokens as the foundation of automation, and storing them as CI secrets that are never rotated.

Static credentials are the easiest thing to wire up and they keep working, so there is no immediate pressure to change them. Candidates underestimate that a leaked long-lived key has unlimited lifetime, and that a root token bypasses every policy boundary Vault is meant to enforce.

What to do instead

Adopt short-lived, identity-based credentials: workload identity or OIDC federation for CI to obtain cloud credentials, and Vault auth methods such as AppRole, Kubernetes, or JWT/OIDC to issue scoped tokens with a TTL. Reserve root tokens for break-glass initialization, revoke them after use, and generate new ones only when needed. Add rotation and expiry monitoring so stale credentials surface before they become an incident.