Updated Nov 5, 20267 min read

IaC Security: How to Secure Infrastructure as Code in 2026

Where infrastructure as code leaks credentials and misconfigurations, the checks that catch them, and how to shrink what the pipeline and your coding agents can touch.

Infrastructure as code made cloud changes reviewable, and it also put the whole security posture of an account into files that anyone with repository access can change. A Terraform pull request that opens a security group to 0.0.0.0/0 looks like any other diff, the state file it writes can hold database passwords in plain text, and the CI job that applies it usually holds credentials strong enough to delete production.

IaC security is about catching those problems before apply and shrinking what the pipeline can do when something slips through. This guide covers the main risks, the checks for each, and how Encore (infrastructure from code) keeps deploy credentials out of the repository and CI.

IaC security risks at a glance

RiskExampleCheck
MisconfigurationPublic S3 bucket, open security groupStatic scanning on every pull request
Broad IAM"Action": "*" on a service rolePolicy rules, IAM Access Analyzer
Secrets in code or statePasswords in variables, state in a shared bucketSecret scanning, encrypted and restricted state
CI credentialsLong-lived access keys in CI secretsOIDC roles scoped per repository and branch
DriftConsole change during an incidentScheduled plan and alerting
Agents with cloud accessCoding agent with an admin profileKeep cloud credentials out of the agent's reach

Scan every change for misconfiguration

Run a scanner on each pull request so a public bucket or an unencrypted volume fails the check before anyone reviews the plan. Checkov, Trivy, which now includes the checks from tfsec, and KICS cover Terraform, CloudFormation, Kubernetes manifests and more, with hundreds of built-in rules. Scan the plan as well as the source when you can, since modules and variables often decide the final values, and use Open Policy Agent with Conftest for rules specific to your organization, such as required tags or allowed regions.

Treat IAM as the main attack surface

Most damaging cloud incidents come down to a role that could do more than it needed. Write policies with specific actions and resource ARNs rather than wildcards, generate them from what a service uses where you can, and review them with IAM Access Analyzer, which flags resources shared outside your account and can generate policies from CloudTrail activity. One role per service, rather than one shared role per environment, keeps a compromised service from reaching everything else.

Keep secrets out of code and protect state

Secret values should never be in .tf files or terraform.tfvars committed to git, and a secret scanner in CI catches the ones that slip in. State is the less obvious leak: Terraform writes resource attributes to state in plain text, including generated passwords, even when a variable is marked sensitive. Store state in an encrypted, versioned backend with access limited to the pipeline, and prefer resources that keep the value in AWS Secrets Manager or Google Secret Manager so only a reference is passed around.

Give CI short-lived, scoped credentials

The pipeline that runs apply is the most privileged identity in most setups. Replace stored access keys with an OIDC trust between your CI and AWS, restrict the trust policy to the repository and the branch that may deploy, and use a read-only role for plan on pull requests and a separate role for apply after merge. Protected branches and required reviews then decide who can change infrastructure, rather than who has a copy of the keys.

Detect drift and unmanaged resources

Changes made in the console bypass every check above, and a scheduled terraform plan -detailed-exitcode catches them by exiting with code 2 when the real infrastructure differs from the code, while AWS Config or Security Command Center on GCP flags resources that break your rules whether or not IaC created them.

Keep coding agents away from cloud credentials

AI coding agents now write a large share of infrastructure changes, and a common setup gives the agent an AWS profile and lets it run terraform apply. That hands an automated process the same power as the CI role, and a mistaken prompt or a malicious instruction in a dependency can use it. The safer pattern keeps the agent in the repository, with changes deployed by a pipeline it cannot control, and narrows what the agent is able to change in the first place.

Set up the Encore app

Get started
brew install encoredev/tap/encore &&
encore app create
Copy

Run in your terminal to get started locally.

~
$ encore app create invoices --example=ts/empty

Application infrastructure from code with Encore

Encore

Encore is infrastructure from code for TypeScript and Go, with a platform that provisions and deploys into your own AWS or GCP account. Services declare the infrastructure they use in code, and the platform derives the cloud resources and networking from those declarations and provisions an IAM role for the service, as described in the security docs, so there is no hand-written policy for a reviewer to miss and no cloud credential in the repository or in CI.

The Infrastructure page in the Encore dashboard with a database's configuration panel open, showing fields for vCPUs, memory, storage, availability type, max connections and backups.
Each provisioned resource can be reviewed and its configuration edited per environment in the Encore dashboard. The screenshot shows a GCP Cloud SQL database.

A TypeScript service with a database, a bucket and an API key looks like this:

// invoices/invoices.ts import { api } from "encore.dev/api"; import { secret } from "encore.dev/config"; import { SQLDatabase } from "encore.dev/storage/sqldb"; import { Bucket } from "encore.dev/storage/objects"; const db = new SQLDatabase("invoices", { migrations: "./migrations" }); const receipts = new Bucket("invoice-receipts", { versioned: false }); // The value lives in Encore's secrets manager, never in the repository. const stripeKey = secret("StripeSecretKey"); export const create = api( { expose: true, method: "POST", path: "/invoices" }, async (p: { customerId: string; amount: number }): Promise<{ id: number }> => { const row = await db.queryRow<{ id: number }>` INSERT INTO invoices (customer_id, amount) VALUES (${p.customerId}, ${p.amount}) RETURNING id`; await receipts.upload(`${row!.id}.txt`, Buffer.from(`Invoice ${row!.id}: ${p.amount}`)); // stripeKey() is available here to call the payment provider. return { id: row!.id }; }, );

The migration in invoices/migrations/1_create_invoices.up.sql creates the table:

CREATE TABLE invoices ( id SERIAL PRIMARY KEY, customer_id TEXT NOT NULL, amount INT NOT NULL );

The secret value is set with encore secret set --type prod StripeSecretKey and is not stored in the repository, as described in the secrets docs, so an agent reading the repository cannot read or leak it. On deploy, Encore provisions RDS, an S3 bucket and AWS Secrets Manager entries, and provisions an IAM role for the service. Each service also gets its own database role, and databases sit in private subnets that only accept connections from the application's compute subnets.

For the agent, that changes what a risky change looks like: it can add a bucket or a topic in the same diff as the code that uses it and run it locally with encore run, and the deploy goes through the GitHub integration and your branch protection like any other change. Deploy credentials stay with the platform, not the agent. Security-relevant settings such as instance sizes and network configuration are managed in the infrastructure configuration, outside the application code.

Best fit: teams who want developers and coding agents to change application infrastructure through code review, while deploy credentials stay with the platform and resources run in their own AWS or GCP account.

How to prioritize IaC security

Start with the two changes that remove whole classes of risk: OIDC credentials for CI and an encrypted, access-restricted state backend. Add a scanner to every pull request next, then per-service IAM roles and a scheduled drift check. For the application layer, where changes are most frequent and increasingly written by agents, declaring resources in code with Encore keeps deploy credentials with the platform rather than in the repository or CI, and Encore provisions an IAM role for the service.

Frequently asked questions

What is IaC security?

IaC security is the practice of finding and preventing security problems in infrastructure definitions such as Terraform, CloudFormation or Pulumi before they are applied, and protecting the pipeline, state and credentials that apply them.

What are the most common infrastructure as code security risks?

Misconfigured resources such as public buckets and open security groups, overly broad IAM policies, secrets stored in code or state files, long-lived cloud keys in CI, and drift from manual console changes that the code no longer describes.

Which tools scan Terraform for security issues?

Open source scanners such as Checkov, Trivy (which absorbed tfsec) and KICS check Terraform and other IaC formats against policy rules, and Open Policy Agent with Conftest lets you write your own rules against plan output.

How do I keep an AI coding agent from holding AWS credentials?

With Encore, the agent only writes application code that declares resources such as new SQLDatabase or secret(). Encore's platform provisions the resources in your AWS account. Deploy credentials for AWS or GCP stay with the platform rather than in a CI server, and Encore provisions an IAM role for the service when it deploys.

Deploy to your cloud on AWS and Google Cloud
without managing infrastructure

Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.

~/orders — claude
Claude Code
Claude Code v2.1.180Opus 5.5~/orders