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.
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.
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.
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.
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.
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.
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.
Run in your terminal to get started locally.
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.

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.
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.
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.
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.
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.
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.