Teams can use terraform enforce secure application settings by defining approved security baselines as code, validating them before deployment, and retaining Terraform plans, pipeline logs, and configuration exports as evidence. For ECC – 2 : 2024 control 1-6-3, Terraform is particularly effective for enforcing secure configuration, hardening, diagnostic logging, and release gates, but it must be combined with secure coding, software-source governance, and security testing processes.
The practical objective is not to treat Terraform as a compliance document. It is to make approved application settings repeatable, reviewable, difficult to bypass, and traceable to the cybersecurity requirements document required by ECC practice 1-6-3.
What can be automated versus what still requires human evidence?
Terraform is strongest where a setting can be expressed as a desired technical state. For example, it can require HTTPS, disable FTP deployment, require TLS 1.2 or above, prevent public access, configure diagnostic logs, and attach managed identities. These controls support 1-6-3-5, which requires configuration review, hardening, and patching before go-live and periodically afterward.
| ECC 1-6-3 area | What Terraform can automate | What remains a controlled process |
|---|---|---|
| 1-6-3-1 Secure coding standards | Require approved deployment settings, runtime versions, managed identity, and secure defaults. | Approve coding standards, train developers, conduct code review, and demonstrate adherence in source repositories. |
| 1-6-3-2 Trusted tools and libraries | Pin Terraform provider versions and use approved module registries. | Maintain the approved software and library list, license records, supplier reviews, and dependency approval process. |
| 1-6-3-3 Compliance testing | Block deployment when policy or configuration tests fail. | Perform penetration testing, application security testing, access-management review, and architecture review. |
| 1-6-3-4 Secure integration | Deploy private endpoints, managed identities, and secure API-related platform settings. | Perform SIT, API security testing, authentication-flow testing, and integration assessment. |
| 1-6-3-5 Configuration and hardening | Apply and continuously compare approved application settings with deployed settings. | Approve the baseline, authorize exceptions, review patch impact, and accept residual risk. |
A useful rule for a GRC consultant is: automate the control implementation where possible, but preserve human approval for the requirement, exception, test result, and production release decision. A Terraform plan proves intended infrastructure change; it does not prove that an API integration was penetration-tested or that a development library is properly licensed.
For example, an 85-person Riyadh-based GRC consultancy and MSP, Najd Governance Services, manages twelve Azure subscriptions for Saudi clients. Its client portal uses Azure App Service, Azure Key Vault, Log Analytics, and an API connection to a ticketing platform. The delivery workflow requires a change ticket, a reviewed Terraform plan, a successful API test record, and a production approval before release. Terraform-based application-setting enforcement handles the platform baseline, while the engagement manager retains the testing and approval records.
How can terraform enforce secure application settings before release?
Start with a version-controlled module that represents the approved application hardening baseline. The module should not contain live secrets. Instead, use managed identities and secret references, and store the Terraform state in a protected remote backend with restricted access and encryption.
The following Azure App Service example establishes practical settings for a production web application. It disables insecure deployment protocols, requires HTTPS and TLS 1.2, restricts public network exposure, sends diagnostic data to the central workspace, and prevents a production deployment without a change reference.
terraform {
required_version = ">= 1.6.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.110"
}
}
}
provider "azurerm" {
features {}
}
variable "environment" {
type = string
}
variable "change_ticket" {
type = string
}
variable "private_endpoint_enabled" {
type = bool
}
variable "log_analytics_workspace_id" {
type = string
}
resource "azurerm_linux_web_app" "portal" {
name = "ngs-client-portal-prod"
resource_group_name = "rg-ngs-portal-prod"
location = "Saudi Central"
service_plan_id = "/subscriptions/8c3d6f1a-4e3d-4b68-a7f0-1b89c1229a09/resourceGroups/rg-ngs-portal-prod/providers/Microsoft.Web/serverfarms/asp-ngs-prod"
https_only = true
public_network_access_enabled = false
identity {
type = "SystemAssigned"
}
site_config {
always_on = true
ftps_state = "Disabled"
minimum_tls_version = "1.2"
health_check_path = "/health"
ip_restriction_default_action = "Deny"
scm_ip_restriction_default_action = "Deny"
application_stack {
dotnet_version = "8.0"
}
}
app_settings = {
"WEBSITE_RUN_FROM_PACKAGE" = "1"
"ASPNETCORE_ENVIRONMENT" = "Production"
"KeyVault__Uri" = "https://kv-ngs-prod.vault.azure.net/"
"Db__ConnectionString" = "@Microsoft.KeyVault(SecretUri=https://kv-ngs-prod.vault.azure.net/secrets/PortalDbConnection)"
}
lifecycle {
precondition {
condition = var.environment != "prod" || (var.private_endpoint_enabled && length(var.change_ticket) > 5)
error_message = "Production requires an enabled private endpoint and an approved change ticket reference."
}
}
tags = {
Environment = var.environment
ECC_Control = "1-6-3-5"
Change_Ticket = var.change_ticket
Configuration_Baseline = "webapp-hardening-v3.2"
}
}
resource "azurerm_monitor_diagnostic_setting" "portal" {
name = "diag-ngs-client-portal-prod"
target_resource_id = azurerm_linux_web_app.portal.id
log_analytics_workspace_id = var.log_analytics_workspace_id
enabled_log {
category_group = "allLogs"
}
metric {
category = "AllMetrics"
enabled = true
}
}
Before approving this module, document each setting in the secure configuration standard. For example, the standard should state that production web applications must use HTTPS, must not permit FTP, must use the organization’s approved runtime version, and must use centralized logging. That approved standard is the governing evidence; the Terraform code is implementation evidence.
Do not use Terraform variables to pass database passwords or API tokens unless there is no feasible alternative. Sensitive Terraform variables can still appear in state. Key Vault references, workload identities, and tightly controlled state access better support confidentiality and evidence quality.
How should the Terraform baseline be integrated into CI/CD?
The deployment pipeline should run formatting, validation, security scanning, planning, review, and controlled application in separate stages. Production applies should use a protected environment and short-lived federated credentials rather than stored cloud administrator passwords. This supports an auditable release process and reduces the opportunity for direct console changes.
name: Terraform Production Release
on:
pull_request:
branches: [main]
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
validate-and-plan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- name: Format and validate
run: |
terraform fmt -check -recursive
terraform init -backend-config=prod.backend.hcl
terraform validate
- name: Security scan
run: |
docker run --rm -v "$PWD:/src" aquasec/tfsec:v1.28.10 /src
- name: Create reviewed plan
run: |
terraform plan -out=plan.tfplan \
-var="environment=prod" \
-var="change_ticket=CHG-2026-01482" \
-var="private_endpoint_enabled=true"
- name: Save plan as release evidence
uses: actions/upload-artifact@v4
with:
name: production-plan
path: plan.tfplan
apply:
needs: validate-and-plan
if: github.ref == 'refs/heads/main'
environment: production
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
- name: Apply approved plan
run: |
terraform init -backend-config=prod.backend.hcl
terraform apply -auto-approve plan.tfplan
In an approved implementation, pin GitHub Actions, container images, Terraform providers, and internal modules to reviewed versions or immutable digests. This is an operational application of 1-6-3-2: the pipeline itself uses development tools and libraries that must come from trusted and licensed sources. Maintain the provider lock file, approved action inventory, and exception records alongside the project evidence.
Which monitoring and alerting hooks should be added?
Terraform enforcement is not complete if configuration drift is discovered only during an annual review. Schedule a read-only Terraform plan daily or weekly against production, publish the result to the service desk, and investigate any change that was not linked to an approved change record. Azure Activity Logs and App Service diagnostics should feed the central Log Analytics workspace or SIEM.
A useful alert query identifies web application configuration writes, including settings changed directly through the portal or command line:
AzureActivity
| where ResourceProviderValue =~ "MICROSOFT.WEB"
| where OperationNameValue has_any (
"Microsoft.Web/sites/config/write",
"Microsoft.Web/sites/write"
)
| where ActivityStatusValue == "Succeeded"
| project TimeGenerated, Caller, OperationNameValue,
ResourceGroup, Resource, SubscriptionId, CorrelationId
| order by TimeGenerated desc
Create a high-priority alert when this query detects an out-of-pipeline configuration change in a production subscription. Route it to the SOC or managed monitoring team and create a service-management ticket containing the affected application, caller identity, correlation ID, and required remediation deadline. Retain the alert, ticket, Terraform drift result, and remediation plan as periodic-review evidence for 1-6-3-5.
What failure modes need handling in the automation?
- Precondition failure: If a production deployment lacks a change ticket or private endpoint confirmation, stop at planning. The requester must correct the release input; do not bypass the check with a manual portal deployment.
- Security-scan failure: Treat a finding such as public network access or weak TLS as a failed control. Record a risk exception only when the designated representative approves compensating controls and an expiry date.
- Terraform state lock: Investigate the active pipeline before force-unlocking state. Force-unlocking without confirming the prior job can create conflicting changes and unreliable evidence.
- Key Vault reference failure: If the application cannot resolve a secret, verify the managed identity, Key Vault access, private DNS, and secret version. Never replace the reference with a plain-text connection string to restore service quickly.
- Post-release health failure: Roll back to the prior approved Terraform version or deployment artifact, preserve logs and test results, and open a problem record when the hardening baseline caused an unexpected application dependency.
- Direct configuration drift: Reconcile production back to the approved code after validating business impact. If the direct change is legitimate, update code, obtain approval, and rerun the pipeline so the baseline remains authoritative.
For Saudi client engagements, the next practical step is to map your approved secure configuration standard to a Terraform module and review its evidence flow against ECC 1-6-3 with the system owner before the next production release.