Infrastructure pipelines are security systems.
They hold credentials, change network paths, create identities, assign permissions and deploy policy. A compromised or badly designed pipeline can make an authorised but unsafe change with perfect consistency.
Security therefore cannot be a single scan added just before production.
It needs to be part of the complete delivery path.
Use controls that match the stage
A useful infrastructure pipeline progresses from cheap, credential-free checks to controlled production change:
Commit
↓
Formatting and static analysis
↓
Unit and schema tests
↓
Secret and dependency checks
↓
Plan generation
↓
Risk and policy evaluation
↓
Approval
↓
Environment deployment
↓
Post-deployment verification
↓
Evidence and monitoring
Each stage should answer a different question. Repeating the same generic scan six times does not create defence in depth.
Stage 1: validate without credentials
The earliest checks should not need access to any cloud or production system.
Examples include:
Invoke-ScriptAnalyzer -Path ./src -Recurse -Severity Warning, Error
terraform fmt -check -recursive
terraform init -backend=false
terraform validate
Also validate:
- JSON, YAML and schema syntax;
- duplicate identifiers;
- naming rules;
- unsupported environment values;
- required metadata and ownership;
- documentation-only examples that accidentally contain production values.
Fast local checks reduce feedback time and minimise the amount of untrusted code that reaches a credentialed agent.
Stage 2: test behaviour with synthetic data
Infrastructure tests should prove decision logic, not merely that a function can be imported.
Useful tests include:
- no-change behaviour;
- create, update and removal classification;
- ordering and precedence calculations;
- threshold handling;
- pagination and API error handling;
- secret redaction;
- retry limits;
- environment mapping;
- invalid and unknown inputs;
- rollback-data generation.
Mock the provider boundary for unit tests. Use a dedicated test environment for integration tests.
Production is not the integration-test environment.
Stage 3: prevent secret exposure
The repository and pipeline definition should contain no plaintext credentials.
Prefer, in order:
- workload or managed identity;
- short-lived federated credentials;
- a narrowly scoped service connection;
- a secret retrieved at runtime from an approved vault.
Avoid passing secrets on command lines because process and pipeline logs can expose arguments. Map secrets to environment variables only for the task that requires them and ensure the script never writes the complete environment or request headers.
Secret masking is a safety net, not a guarantee. If a secret scanner finds a credential in source or history, treat it as compromised and rotate it.
Stage 4: generate a deterministic plan
The plan is the security review artefact for infrastructure change.
For Terraform, a saved plan separates review from apply:
terraform plan -out=tfplan
terraform show -json tfplan > tfplan.json
The pipeline can evaluate the JSON representation for conditions such as:
- resource deletion;
- replacement of a critical resource;
- public network exposure;
- broad role assignment;
- disabled encryption or logging;
- changes outside approved regions;
- change volume above a threshold.
Saved Terraform plan files can contain sensitive values. Keep them as protected, short-lived pipeline artefacts and never commit either the binary plan or its JSON representation to source control.
The same idea applies outside Terraform. A PowerShell configuration compiler can emit a structured create, update and remove plan before calling the platform API.
Stage 5: evaluate policy and risk
Not every policy violation is equal.
Classify controls:
Block
Warn
Require approval
Allow with recorded exception
A missing owner, an exposed administrative port and a formatting problem should not all produce the same result.
Policy checks should return structured findings:
[pscustomobject]@{
RuleId = 'SEC-NET-001'
Severity = 'Block'
Resource = 'example-network-rule'
Message = 'Administrative access is open to all sources.'
Remediation = 'Restrict the source range or use private access.'
}
Exceptions need an owner, reason, approval and expiry. A permanent suppression comment is not an exception-management process.
Stage 6: protect the pipeline itself
A secure deployment script running in an unprotected pipeline is still unsafe.
Control the platform resources around it:
- repository write permissions;
- branch protection and required reviewers;
- pipeline-definition changes;
- service connections;
- agent pools;
- secure files and variable groups;
- environment permissions;
- production approval checks;
- template repositories;
- artefact access and retention.
Keep production approval and environment checks outside the application repository where the delivery platform supports that separation. Otherwise, a contributor may be able to change both the infrastructure and the rule that approves it in the same pull request.
Use templates as guard rails
A centrally maintained pipeline template can enforce mandatory stages.
A consuming pipeline can remain small:
trigger:
- main
extends:
template: pipelines/secure-infrastructure.yml
parameters:
workingDirectory: infrastructure
deploymentType: terraform
productionEnvironment: production
The template can require:
- static analysis;
- tests;
- secret scanning;
- plan publication;
- security evaluation;
- approved agent pools;
- environment deployment jobs;
- post-deployment validation.
Templates should make the safe path easier, not create an opaque platform that application teams cannot troubleshoot.
Version them, publish release notes and provide a controlled migration path for breaking changes.
Stage 7: separate identities by environment
One pipeline identity with administrator access to every environment defeats much of the benefit of staged delivery.
Use separate identities or service connections for:
Test
Development
Production
Each identity should have only the permissions required by that deployment stage.
Promotion should move a reviewed artefact and plan through environments. It should not rebuild production configuration from an unreviewed working directory.
This is the same progressive-delivery principle used in safe Cloudflare Zero Trust deployment pipelines.
Stage 8: verify after deployment
A provider reporting success does not prove the control works.
Post-deployment checks should validate externally observable behaviour:
- the expected resource exists;
- policy order is correct;
- access is allowed and denied as designed;
- logs and telemetry are arriving;
- health checks pass;
- no unexpected resource changed;
- rollback evidence was captured.
For a network or access-policy change, perform a synthetic connection test. For identity configuration, test assignment and denial. For backup configuration, verify a recovery point.
Verification should be specific to the risk of the change.
Preserve deployment evidence
A production run should make it possible to reconstruct the decision:
Commit and repository
Pipeline and template version
Dependency versions
Configuration artefact hash
Plan and security findings
Approver and approval time
Deployment identity
Target environment
Provider change identifiers
Verification results
Rollback reference
Do not retain secrets simply because they appeared in the deployment input. Evidence should prove the process without becoming a second credential store.
Fail closed, but make failures usable
Security gates that return only validation failed will be bypassed or ignored.
A useful failure tells the engineer:
- which rule failed;
- which resource caused it;
- why the condition matters;
- how to correct it;
- how to request a time-limited exception when correction is not currently possible.
Strict controls and good developer experience are not opposites.
The wider lesson
DevSecOps for infrastructure is not a product selection exercise.
It is an operating model in which:
- untrusted code is tested before credentials are introduced;
- desired changes are made visible;
- security policy is evaluated consistently;
- destructive or privileged changes receive proportionate approval;
- identities are scoped by environment;
- production behaviour is verified;
- evidence survives the deployment.
The pipeline becomes the mechanism that makes the secure process repeatable.
Further reading
- Security in DevOps
- Azure Pipelines YAML templates
- Secrets in Azure Pipelines
- Format and validate Terraform configuration
- Terraform plan command
Repository names, rule identifiers, environments and resources are synthetic. The article describes a general delivery pattern and omits production service connections, permissions, policy definitions and secrets.