A human and robot hand drawing each other

Securing CI/CD with secrets and variables

This guide will cover how to manage sensitive information at the organizational level, understand the role of variables in your workflows, and implement OpenID Connect (OIDC) for robust cloud authentication. This guide is designed to enhance both the security and efficiency of your automated processes.

In this guide, you will:

  • Learn how to securely manage secrets at the organizational level, providing a unified, high-level security framework for all your repositories

  • Understand the role of variables, how they differ from secrets, and how to employ them for more efficient workflows

  • Discover how to implement OpenID Connect for robust, automated authentication with your cloud providers, reducing third-party risks and administrative overhead

Organizational secrets and variables


When it comes to safeguarding your automated workflows, secrets are non-negotiable. These encrypted assets hold sensitive information like API tokens and credentials. Lets go over how to add and manage secrets at the organizational level.

When it comes to safeguarding your automated workflows, secrets are non-negotiable. These encrypted assets hold sensitive information like API tokens and credentials. Let’s go over how to add and manage secrets at the organizational level.

1. Navigate to settings.

Go to the settings of your organization. Under the Secrets and Variables section, select the Actions tab to access a dashboard showing all organization-level secrets.

2. Add a secret.

Click New Organization Secret. When naming your secret, be explicit. For instance, if it's an API token for Azure, a name like Azure_API_Token would be apt.

3. Input the value.

Input the secret value. This is the critical, encrypted information the secret name will map to. Make sure it remains confidential.

4. Define scope.

You can either make this secret accessible to all repositories, limit it to private or internal repositories, or handpick specific repositories. Given you're setting this at the organizational level, think strategically about its scope.

5. Confirm.

Click Add Secret to finalize its creation.


Variables serve a slightly different purpose than secrets but are equally vital in automating your workflows. Variables are generally used for information that is less sensitive but still essential for your processes. Think of them as public usernames or settings that multiple repositories in your organization might need to access. You'll still define their scope in the same way, and decide whether they are accessible to all repositories or only specific ones.

Repository- and environment-level settings

Your organizational configuration sets the stage, but fine-grained control comes into play at the repository and environment levels. It's here that you can dial in specific requirements for different projects or even stages within a project. 

Define repository-level settings

1. Navigate to your repository.

Go to the repository where you need to set or modify secrets and variables. Within the repository, head over to the Settings tab and find the Secrets and Variables section.

2. Add or edit.

Much like at the organizational level, you can add new secrets or variables here. The mechanics are the same: Name the secret or variable, provide its value, and define its scope. Let’s click on the Actions tab and then add a secret named MY_GITHUB_SECRET and assign the value to be anything you want hidden (we will come back to this soon).

Secrets vs. variables demonstration 

Now that we have added a repository-level secret, let’s run through a quick demo to see the difference between how Actions handles secrets and environment variables.

1. Create a workflow file.

If you need a refresher on how to create a new workflow file, please refer to our Automation Essentials learning pathway.

Paste the YAML configuration below into your new workflow file for our demo workflow. Comments explain what each section does.

name: Echo Secrets and Variables
      - main  # Trigger this action when changes are pushed to the main branch

    runs-on: ubuntu-latest

      MY_VARIABLE: "This is not a secret"  # This is a public environment variable

    - name: Checkout code
      uses: actions/checkout@v2

    - name: Echo environment variable
      run: echo "My environment variable is $MY_VARIABLE" # This is your variable that will not be masked
    - name: Echo GitHub secret
      run: echo "My GitHub secret is ${{ secrets.MY_GITHUB_SECRET }}" # This is the secret you added in a previous step

2. Run the workflow.

After pushing this YAML file to your repository, follow these steps to view the logs:

  • Navigate to the Actions tab in your GitHub repository

  • Select the relevant workflow run

  • Click on the build job

  • Scroll down to view the logs for both the Echo environment variable and Echo GitHub secret steps

  • Here you'll see the difference in how Actions processes secrets and environment variables

Phew! Glad we kept that secret hidden. 

Environment level:

1. Find the Environment tab.

Still in your repository settings, locate the Environments tab. Select the environment where you'd like to store secrets.

2. Add secrets.

Click on New Environment Secret and follow the same steps as before to add your secret. Remember, environment-specific secrets override any organizational or repository secrets with the same name during a workflow in that particular environment.

By taking a tiered approach to your settings, starting at the organizational level and drilling down to repository and environment specifics, you establish a robust and highly customizable security framework for your automated workflows. This fine-grained control enables you to adhere to the principle of least privilege, ensuring that you only expose secrets and variables where they are genuinely needed. Whether it's restricting a database connection string to a production environment or specifying a secret just for a single repository, this approach ensures optimal security without sacrificing functionality.

Improved security with OpenID Connect

OpenID Connect (OIDC) provides multiple advantages for enterprise security and efficiency. It effectively eliminates the risks associated with third-party secret management, as your authentication tokens are generated and stored within your chosen cloud environment. These tokens automatically expire after a brief period, which minimizes the risk and administrative burden of manual secret rotation. OIDC also offers multi-cloud support for OIDC-compliant cloud providers such as Microsoft Azure, Amazon Web Services (AWS), or Google Cloud Platform (GCP). This support enables a consistent, secure authentication process regardless of whether your workflows are operating. In essence, OIDC elevates your security posture while also simplifiying the authentication mechanism across diverse cloud environments.

Our GitHub App credentials needed organization-level permissions for us to manage our repositories and code at scale. Despite the broad permissions, we’ve maintained a high level of security by using HashiCorp Vault and OpenID Connect. We configured our system so that these credentials are only accessible to the workflow running terraform apply, and only on the default branch of our automation repository. This narrow scope of access ensures that, even internally, these high-privilege tokens are safeguarded in specific, tightly-controlled scenarios.

Sanjito Kurniawan
Sanjito Kurniawan // Senior Platform Engineer // HelloFresh

Up next: Advanced workflow configurations in GitHub Actions

Now that you can secure your CI/CD pipelines with secrets and variables, it's time to dive deeper. The next guide will provide you with the tools you need to optimize your workflows like a pro. From learning about managing workflow concurrency and leveraging conditional statements to augmenting workflows with in-house scripts, this guide is designed to accelerate your development pipeline.