Salesforce, Python, SQL, & other ways to put your data where you need it

Need event music? 🎸

Live and recorded jazz, pop, and meditative music for your virtual conference / Zoom wedding / yoga class / private party with quality sound and a smooth technical experience

Censoring secrets from logs in infrastructure as code

15 Oct 2025 🔖 devops security
💬 EN

Table of Contents

This is not exhaustive, but here’re a few coding hygiene tidbits for making sure that when you need to use a secret in your infrastructure as code (IaC), it doesn’t leak into its execution runtime’s system logs.

Azure Pipelines

Authenticate secretlessly

As of late, you can authenticate an Azure Pipeline’s runtime into most Microsoft cloud-hosted services secretlessly. No more rotating keys; yay!

Just tell your identity provider (“IdP”) – e.g. Entra ID – to be expecting your Azure Pipelines Service Connection, and tell your Azure Pipelines Service Connection to present itself to your IdP over OIDC-based Workload Identity Federation, and you’re all set.

Mask inter-step secrets

If you’re passing a secret from one YAML CI/CD pipeline step to the next…

…Then set the step output’s value using the ##vso[task.setvariable...;isSecret=true] syntax.

This should make Azure Pipelines automatically mask the value of any the variables in the logs generated from running the steps.


GitHub Actions

Authenticate secretlessly

As of late, you can authenticate a GitHub Actions Workflow’s runtime into most Microsoft cloud-hosted services secretlessly. No more rotating keys; yay!

Just tell your IdP – to be expecting your GitHub repository, and tell your GitHub Workflow to present itself to your IdP over OIDC-based Workload Identity Federation, and you’re all set.

Mask inter-step secrets

If you’re passing a secret from one YAML CI/CD pipeline step to the next…

…Then set the step output’s value using the ##vso[task.setvariable...;isSecret=true] syntax.

…Then set the step output’s value using the ::add-mask:: syntax.

This should make GitHub Actions automatically mask the value of any the variables in the logs generated from running the steps.


HCL

I believe these are true of both frameworks / CLIs that use the Hashicorp Configuration Language (“HCL”) – Terraform and OpenTofu.

Leverage existing authenticated system state

Determine whether you even need to leverage an explicit secret in the first place.

Often, a running Terraform/OpenTofu module can authenticate into target infrastructure secretlessly.

For example, if the host runtime is running a logged-in Azure CLI, Azure-related modules can piggyback on the Azure CLI’s authenticated state. (Which you already read about how to set up secretlessly in Azure Pipelines and in GitHub Actions above.)

Mask secrets between modules

When crossing module boundaries, always add sensitive = true flags to to all relevant input variable parameter definitions and to all relevant output variable return-type definitions.

Terraform/OpenTofu should automatically censor the value of any such variables in the output of terraform/tofu command execution and log messages.

Mask secrets within a module

Within a module, use ephemeral syntax to output secrets to subsequent resources.

Also, be sure you’re only passing such a secret into resource providers that offer properly secured _wo (write-only) input parameters.

This not only keeps such values out of logs, but also out of Terraform’s / OpenTofu’s state file.


Ansible

Blegh. This is the one that drives me nuts. I can’t stand how bad the secrets masking/censorship is, compared to the other frameworks I’ve got in this article.

Disable logs of secretful tasks

When invoking an Ansible task, if it will need to be passed a secret as input, or if its logs (including error logs) might expose a secret generated during its runtime…

…Then add a no_log: True flag to the task’s attributes when invoking it.

Ansible should, when run in a non-debugging context, suppress all logs that the task flagged no_log: True would normally emit.

Nuisance known issue

Unlike many other frameworks/languages shown here, Ansible doesn’t offer a syntax for merely censoring/masking secrets, while preserving the rest of the log output.

Many developers find this frustrating when they wish they could see the rest of an error message.

Workaround

The best available workaround I’ve been able to find is to modularize your Ansible code so that each task invocation is only responsible for one real-world job.

By limiting the scope of the task whose logs are suppressed, the inconvenience of missing such logs can be minimized.

Debug mode vulnerability known issue

Thought you’d handled everything by adding no_log? Think again!

Ansible ignores no_log, and displays all logs, including any secrets they might contain, when running on a controller node whose ANSIBLE_DEBUG environment variable is set to True.

Please be extremely cautious when choosing to execute an Ansible playbook.

Is there any chance that the playbook might run in “debug mode,” or that someone might accidentally reconfigure it to do so?

What steps can you take, on your team, to ensure that that can’t happen?

How can you avoid this situation, in all contexts where the control node’s runtime logs might persist somewhere that is difficult to truly erase? For example:

* On a runtime that auto-logs to DataDog.
* On a CI/CD pipeline platform that persists logs for later review by the team.

PowerShell

Leverage existing authenticated system state

Determine whether you even need to leverage an explicit secret in the first place.

Often, a running PowerShell script can authenticate into target infrastructure secretlessly.

For example, if the host runtime is running a logged-in Azure CLI or Azure PowerShell, then Azure-related PowerShell modules can piggyback on that authenticated state.

Mask inter-function-call secrets

at parameter definition

When authoring a reusable function, if it will need to be passed a secret as input…

…Then define the input variable parameter’s data type as [SecureString], not [String].

at invocation

Furthermore, be sure that whatever code calls your reusable function does not accidentally leak the value of the secret while buiding a SecureString.

For example, piping the output of az keyvault secret show directly into ConvertTo-SecureString should help prevent the output of az keyvault secret show from accidentally ending up in PowerShell’s success stream.

$powershell_memory_cached_secret_value = ( `
    az keyvault secret show `
    --vault-name 'YourKeyVaultName' `
    --name 'YourSecretName' `
    --query 'value' `
    --output 'tsv' `
) | ConvertTo-SecureString `
    -AsPlainText `
    -Force
--- ---