









Terraform is a software product like any other. You use it to provision infrastructure that other applications run on. Like any other application or system that you run you want to make sure that Terraform works as intended and that it does what it is supposed to do. If not, you want to know why.
In this blog post, we will explore what Terraform observability is, how it works, and why you should care about it. Note that everything covered in this blog post is also applicable to OpenTofu.
What we’ll cover:
Terraform observability is the practice of collecting logs, metrics, and traces from every component involved in a Terraform run: the version-control system, the runner, the Terraform binary, the state backend, the provider APIs, and the infrastructure that comes out the other side. It answers why a run behaved the way it did, not just whether it passed.
There are two ways of looking at Terraform observability:
Both of these are important in modern infrastructure management.
The second category is broad, and it involves instrumenting the various infrastructure components that you provision, configuring log, metrics, and trace collection, setting up dashboards, and providing developers with access to these observability tools.
The exact details of how you do this are highly dependent on your context, and it is outside the scope of this blog post to cover this. In the rest of this blog post, we will focus on Terraform observability, where the Terraform environment itself is the center of attention.
Observability is sometimes confused with monitoring. These are related, but different, concepts.
Monitoring is the process of collecting, aggregating, visualizing and alerting on data describing a process or system. This data comes in the form of metrics, logs, traces, etc.
A few examples of metrics that are of interest in a Terraform environment are:
Metrics by themselves do not necessarily say if something is good or bad; they just report numerical values of something you are monitoring. For instance, if your apply operation takes 300 seconds to complete, is that good or bad? It’s difficult to say from this single value, but by monitoring this metric over time you can start to understand what range of values are expected.
Logs are text-based messages collected from various components of your infrastructure. In a typical Terraform environment you would collect logs from the Terraform binary, your worker machines, your VCS repository, and any other system that is involved in the process.
This leads us to observability. Observability is the ability to understand the inner workings of a system as a whole using the metrics, logs, traces, and other signals the system emits. Observability allows us to understand why something occurred when we are diagnosing a problem.
Observability is usually split into three signal types: metrics, logs, and traces. All three apply to Terraform, but not equally well.
terraform plan -json, which emits a change_summary with counts of resources to add, change, remove, and import, and your automation platform, which knows queue depth, worker saturation, and run durations. Both are covered below.TF_LOG_CORE and TF_LOG_PROVIDER. They are what tell you why a run behaved the way it did.OTEL_TRACES_EXPORTER=otlp, but HashiCorp marks it in the source as experimental and not a committed interface, so it can change or disappear between releases.Terragrunt has proper support through TG_TELEMETRY_TRACE_EXPORTER, and the HCP Terraform agent exports through TFC_AGENT_OTLP_ADDRESS. For everything else, instrument the pipeline that calls Terraform rather than Terraform itself.
Terraform observability involves observing and understanding the behavior of your broader Terraform environment. This involves a number of related components:
Understanding how Terraform behaves in your environment means understanding the full environment with all related systems, from source code to a running workload. This is the goal of Terraform observability.
Observability as code is the practice of defining and managing observability configurations, including dashboards, alerts, logs, metrics, and traces, through version-controlled code rather than manual UI setup. It applies infrastructure-as-code principles to monitoring, enabling consistent, repeatable, and automated deployment of observability across environments.
In the previous section, we learned about a few different components that are typically of interest in Terraform observability.
Most of these are dependent on the specific automation platform you use to run Terraform. This could be a completely self-hosted environment with virtual machines running on-premises, all the way up to a fully-managed environment with GitHub Enterprise Cloud and an automation platform such as Spacelift or HCP Terraform.
In the following sections, we will go through details of how to implement Terraform observability, without diving deep into the technical details of how this is done, since this will vary widely between environments.
There are many available options for Terraform state storage. Arguably, the most popular options are:
A fully-managed state backend generally gives you insights in terms of audit logs, run history with change diffs, and more. These are fully abstracted solutions that do not require any explicit configuration. Since those are handled for you, this section will focus on the managed object storage services.
If your organization provisions infrastructure on AWS, chances are you are using an AWS S3 Terraform backend. The underlying AWS S3 service offers a few metrics and logs that are important for your Terraform observability:
Check which metrics and logs are enabled by default, and enable any non-default logs if you think they can help you with your Terraform observability.
A resource drift is when the actual state of a resource does not correspond to what is recorded in your Terraform state file for that same resource. This occurs when someone or something modifies the resource outside of your Terraform workflow.
Detecting resource drift is important if you intend for Terraform to be the source of truth for your infrastructure. If you used Terraform to provision a resource, then it is very likely that your intention is to provide future updates to the resource via Terraform and not through different tools or processes.
Automation platforms such as Spacelift and HCP Terraform often include some level of drift detection for you. But if you run Terraform in your own environment you can easily set up drift detection on your own. The general idea is to run a plan operation at regular intervals:
$ terraform plan -refresh-only -detailed-exitcodeYou must add the -refresh-only flag to not report planned changes coming from new code pushes to the repository. Also, add the -detailed-exitcode flag to the command so that Terraform exits with an exit code of 2 if there are planned changes (i.e. drift).
An example of what this looks like in a GitHub Actions workflow is this:
name: Resource drift detection
on:
workflow_dispatch: # allows manual triggers
schedule:
- cron: "0 6 * * *" # run daily at 06:00 UTC
jobs:
drift-detection:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: hashicorp/setup-terraform@v4
# authenticate to your cloud, e.g. AWS, Azure, GCP, …
# steps omitted for brevity …
- run: terraform init -input=false
- id: plan
run: terraform plan -refresh-only -detailed-exitcode -input=false
continue-on-error: true
- name: Fail on plan error
if: steps.plan.outputs.exitcode == '1'
run: |
echo "::error::Terraform plan failed"
exit 1
- name: Report drift
if: steps.plan.outputs.exitcode == '2'
run: |
echo "::warning::Drift detected"
exit 1You can easily implement a similar approach to drift detection on other platforms.
What should you do when drift is detected? If the drift is unexpected and the drifted configuration is not desired:
If the detected drift comes from a manual configuration that should be persisted to the state file you can instead run terraform apply -refresh-only and update your Terraform configuration to match.
If you run Terraform plan and apply operations with its default behavior, you will get a subset of all the output information that Terraform produces. In a happy-path scenario, this is usually more than enough information.
However, sometimes you need additional logs to understand what Terraform is doing when you are triaging an issue or trying to understand an error you encounter.
To achieve this you must set an explicit value of the environment variable named TF_LOG. You can set this to one of TRACE, DEBUG, INFO, WARN, or ERROR. The TRACE level is the most verbose level, and the ERROR level is the least verbose. An example of setting this flag for a given plan operation looks like this:
$ TF_LOG=TRACE terraform planYou can also set it for all future Terraform commands in the current shell session:
$ export TF_LOG=TRACE
$ terraform plan
…There is also a special value of JSON that you can set. This is the same as setting TF_LOG to TRACE, but produces logs in JSON format that are easier to parse for automation purposes.
The logs you get when running Terraform come from two sources: the Terraform core and the Terraform providers that you use in your configuration. You can handle the verbosity of each type of logs by setting TF_LOG_CORE and TF_LOG_PROVIDER to separate values:
$ export TF_LOG_CORE=TRACE
$ export TF_LOG_PROVIDER=ERROR
$ terraform plan
…If you manage your own Terraform runners (e.g. a virtual machine or a container) you can persist the logs to a file by setting the TF_LOG_PATH environment variable to a file path:
$ export TF_LOG_PATH=/var/logs/terraform.logNote that you must set an explicit value for TF_LOG (or TF_LOG_CORE and TF_LOG_PROVIDER) together with TF_LOG_PATH.
You may not want to have verbose logging enabled at all times, because the amount of logs can quickly get out of hand. However, you should implement an easy way to enable debug logging when required.
An important detail to notice is that TRACE and DEBUG logs may contain sensitive data from your resources, data sources and other parts of your Terraform configuration. For this reason you should only use these log levels when the situation demands it.
In the previous section, we saw how to collect logs from your Terraform operations. You could also extract metrics.
For instance, to get the number of planned new resources you can run (using the jq utility on Mac/Linux):
$ terraform plan -json | \
jq -r 'select(.type == "change_summary") | .changes.add'This will output the number of resources that will be added. You can replace “add” by “change”, “import” or “remove” to get those metrics as well.
Another metric you can produce is the number of resources contained in your state file:
$ terraform show -json | \
jq '[.values.root_module | .. | .resources? // empty | .[] | select(.mode == "managed")] | length'The syntax of this statement is out of scope of this blog post. The basic idea is that you must take care to only count managed resources, not data sources or anything else.
Push these metrics to your metrics collection system.
The automation platform you use to run Terraform usually has a lot to say about the health of your environment. If you use self-hosted virtual machines or Kubernetes clusters to run Terraform, you will have access to a wealth of metrics and logs describing how the underlying machines behave.
Apart from understanding the health of individual machines, you should have a collective understanding of how many workers are available, how many are busy, what the typical idle time for a worker is between runs, and more.
For managed automation platforms, you can start by reading the documentation to learn about what metrics and logs you can extract from the platform.
You interact a lot with your Terraform providers with every Terraform operation you perform. You should include monitoring of your provider platform as part of your Terraform environment. An important part of this is audit logs that describe who did what and when it happened.
The big cloud providers offer managed services that collect audit logs (e.g. CloudTrail on AWS and activity logs on Microsoft Azure).
Avoid treating audit logs as a checkbox for an audit, make sure you know how to find relevant information within them.
The previous sections have gone through different types of data you should collect to understand your Terraform environment. Data is no good if you do not have a way to observe it. A good entrypoint to observability is a dashboard.
Fill your dashboard with important numbers split into different panels and charts. The exact design of your dashboard depends a lot on what your Terraform environment looks like.
If you only have a few Terraform configurations (or even a single one) you could include a lot of detailed data, including logs. For a large environment, you should create sensible summaries for your full environment without adding too detailed information about any single Terraform operation.
Keep the following best practices in mind when working with Terraform observability.
Track Terraform as any other workload in your environment. Monitor metrics around plan and apply durations, time spent in queues waiting for an available agent/worker, the number of resource changes applied, how many resources are managed by Terraform, success and failure rates, and more.
With an understanding of the baseline for these metrics, you can more easily determine when one or more metrics are outside of expected values. These can be clues to help you understand other observed behaviors in the Terraform environment.
Terraform operations fail. The reasons for failure can be almost anything; a few examples include:
Many types of errors can be caught at an early stage using testing with the Terraform test framework and the terraform test command, code formatting with terraform fmt and validation with terraform validate. There are also many third-party tools available that help you in this regard.
When you alert on failing Terraform operations, you want the alerts to be actionable. For instance, alert on:
An alert should require manual intervention. If possible, you can automate the action instead and avoid alerting a human.
There are two major dependencies for any Terraform configuration:
Each Terraform plan and apply operation will interact with the state file in your selected state backend. If you use a managed service such as AWS S3 for state storage you should monitor the health of the AWS region where your bucket lives, and monitor the bucket itself and each interaction that Terraform has with it.
If you see many failing Terraform operations at the same time as your primary AWS region experiences issues, you could quickly get an idea of what is going on and what to do about it.
Another important aspect to monitor is state locking. The exact mechanism of state locking varies and could include additional resources (e.g. DynamoDB for legacy state locking for AWS S3).
Likewise, the providers you use interact with the target platforms you are provisioning infrastructure on. These provider platforms can experience issues that have a negative impact on your Terraform environment. Another common issue to monitor is provider API rate limits.
You should explicitly track resource drift for every resource that Terraform is managing. If you don’t, you lose much of the benefits of managing infrastructure with Terraform. Drift can also lead to unexpected behavior during your next apply operation.
If you track resource drift and alert your platform engineers when it is introduced in your environment, you have a better chance of handling it in a controlled fashion rather than encountering it in a panic during an outage.
It is easy to set a value of the TF_LOG environment variable locally when running Terraform, but do you know how to do it in your current Terraform automation platform?
A good idea is to implement an easy way to set the TF_LOG environment variable when required, even in production workflows. This could be a simple true/false flag input to your automation pipeline.
Operational logs explain why something happened, including provider versions, API calls, command execution order, and more. However, you should also keep track of your audit logs. These are the logs that say when something happened and who performed the action.
Make sure you can answer these types of questions using your audit logs:
Spacelift connects to and orchestrates the infrastructure tooling your teams already use: infrastructure as code, version control systems, observability tools, governance and compliance tooling, and cloud providers.
Spacelift runs CI/CD workflows for OpenTofu, Terraform, Terragrunt, Pulumi, AWS CloudFormation, AWS CDK, Kubernetes, and Ansible. It also supports observability integrations with Prometheus and Datadog, so run outcomes, timings, and account metrics land in the dashboards your team already watches.
With Spacelift, you get:
driftDetection field in webhooks and a drift_detection field in plan and trigger policy input, so you can require human approval before anything reconciles.To learn more about Spacelift, create a free account or book a demo with one of our engineers.
Terraform observability comes in two types:
In this blog post, we covered the first type of Terraform observability.
Enabling observability for your Terraform environment means doing one or more of the following things:
Together, these provide insights into your Terraform environment and can help you understand why something happens.
Keep the following best practices in mind when working with Terraform observability:
Track plan and apply duration, time spent queued waiting for a worker, resource counts by change type, drifted resource count, run success rate, and provider API error and throttle rate. None of them mean anything in isolation, so baseline them for a few weeks and alert on deviation rather than on absolute thresholds.
Terraform CLI has an OTLP trace exporter behind OTEL_TRACES_EXPORTER=otlp, but HashiCorp marks it in the source as experimental and not a committed interface, so it can change or be removed between releases. Terragrunt has proper support through TG_TELEMETRY_TRACE_EXPORTER and the HCP Terraform agent exports through TFC_AGENT_OTLP_ADDRESS, but for production you are better off instrumenting the pipeline that calls Terraform.
There is no single tool: you assemble it from your state backend’s telemetry, Terraform’s own outputs (TF_LOG for logs, terraform plan -json for metrics), and your automation platform’s metrics. All of it lands in whatever you already run, whether that is Prometheus and Grafana, Datadog, CloudWatch, or an OpenTelemetry Collector fanning out to several backends.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。