Protect secrets in Azure Pipelines

Azure DevOps Services | Azure DevOps Server 2022 | Azure DevOps Server 2020

This article provides best practices on protecting secrets in Azure Pipelines. A secret is anything that you want to tightly control access to, such as API keys, passwords, certificates, or cryptographic keys.

Azure Pipelines doesn't generate secret values. However, you might need to add a secret to a pipeline to store sensitive data like an API key. To learn more about setting secret variables, see Set secret variables.

Don't use secrets if another method is available

The best method to protect a secret isn't to have a secret in the first place. Check to see if your pipeline can use a different method than using a secret to perform a task.

  • Use managed identities:

    • Consider using managed identities instead of handling secrets directly.
    • Managed identities allow your applications and services to authenticate security with Azure services without requiring explicit credentials.
    • You can use managed identities to access other Azure services.
  • Azure CLI task:

    • If you're using the Azure CLI task, in your pipeline, consider using the addSpnToEnvironment setting to access service principal details in script without explicitly passing secrets.

For more information, see Use service principals & managed identities

Use secret variables

Never store sensitive values as plaintext in an Azure Pipelines .yml file.

Secret variables can be used for private information like passwords, IDs, and other identifying data that you wouldn't want exposed in a pipeline. We recommend that you set secret variables with Azure Key Vault. You can also set secret variables in the UI or in a variable group. We don't recommend using a logging command to set a secret variable. When you set a secret with a logging command, anyone who can access your pipeline can also see the secret.

Secret variables are encrypted and can be used in pipelines without exposing their values. Although their values aren't exposed, never echo secrets as output and don't pass secrets on the command line. Instead, we suggest that you map your secrets into environment variables.

When you create a secret, follow variable naming guidelines and make sure that your secret name doesn't disclose sensitive information.

Limit access to secret variables

To limit access to secrets in Azure DevOps, follow these best practices:

  • Store your secrets in Azure Key Vault. With Azure Key Vault, you can then use Azure's role-based access control model to limit access to a secret or group of secrets.
  • Set secret variables in the UI for a pipeline. Secret variables set in the pipeline settings UI for a pipeline are scoped to the pipeline where they're set. So, you can have secrets that only visible to users with access to that pipeline.
  • Set secrets in a variable group. Variable groups follow the library security model. You can control who can define new items in a library, and who can use an existing item.

Don't write secrets to logs

Azure Pipelines attempts to scrub secrets from logs wherever possible, but it's not foolproof. Avoid echoing secrets to the console, using them in command line parameters, or logging them to files. Be cautious when you use Azure CLI commands that output sensitive information. Use the None output format, and if you need to retrieve a secret from an Azure CLI call, Use none output format and retrieve security information to a secret variable.

Don't use structured data as secrets

Avoid using structured data formats like JSON, XML, or YAML, to encapsulate secret values, including control characters such as carriage return, \r, and line feed,\n. Instead, create individual secrets for each sensitive value. This approach ensures better redaction accuracy and minimizes the risk of exposing sensitive data inadvertently.

Audit how secrets are handled

To audit how secrets are used in Azure Pipelines, follow these best practices:

  • Review source code: Examine the source code of the repository hosting the pipeline. To ensure secrets get handled correctly, check any tasks used in the pipeline. For instance, verify that secrets aren't inadvertently sent to unintended hosts or explicitly printed to log output.
  • Inspect run logs: After testing valid and invalid inputs, view the run logs for your pipeline. Ensure that secrets are properly redacted and not exposed. Sometimes, errors in commands or tools might inadvertently leak secrets into error logs. While Azure Pipelines attempts to scrub secrets from logs, manual review is still essential.

Audit and rotate secrets

To audit and rotate secrets, follow these best practices:

  • Review registered secrets: Periodically assess the secrets registered in your pipelines. Confirm that they're still necessary, and remove any that are no longer needed, which helps reduce clutter and potential security risks.
  • Rotate secrets: Regularly rotate secrets to minimize the window of time during which a compromised secret could be exploited. By changing secrets periodically, you enhance security.
  • Choose the right authentication method
    • Types of secrets used:
      • Personal access tokens (PATs): These tokens are used for authentication. Follow security best practices when choosing the right authentication method. You can manage PATs using the REST API.
      • Secret variables: Use secret variables to securely store sensitive information like API keys, passwords, or other credentials within your pipeline.
      • Azure Key Vault secrets: Use Azure Key Vault to store and manage secrets securely.
      • Service connections: These service connections allow your pipeline to connect to external services (for example, Azure, GitHub, Docker Hub). Ensure proper configuration and secure handling of service connection secrets.

Use YAML templates

Instead of including inline scripts with secret parameters directly in your pipeline YAML, use templates. This approach enhances security by abstracting sensitive information away from the main pipeline.

To implement this approach, create a separate YAML file for your script and then store that script in a separate, secure repository. You can then reference the template and pass a secret variable in your YAML as a parameter. The secure variable should come from Azure Key Vault, a variable group, or the pipeline UI. For more information on using templates, see the Template usage reference.

Limit secrets with branch policies and variable group permissions

To make sure that secrets are tied to the main branch and not accessible to random branches, you can use a combination of variable group permissions, conditional job insertion, and branch policies.

With branch policies, you can enforce build validation policies that only allow builds from the main branch. Then, you can use variable group permissions to make sure that only authorized pipelines have access the secrets stored in your variable group. Last, you can use a condition in your pipeline to make sure that the variable group can only be referenced by a push to the main branch.

jobs:
- job: ExampleJob
  condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
  pool:
    vmImage: 'ubuntu-latest'
  steps:
  - script: echo "This runs only for the main branch"
    displayName: 'Conditional Step'
  variables:
  - group: your-variable-group-name

Next steps