Importing Secrets

Learn how to easily import your existing secrets into Doppler

There are a variety of ways you can import secrets into Doppler from third party systems.

  • Many of our Automated Sync integrations have the ability to import secrets when first setting up a sync (this is by far the easiest way to accomplish this if the sync supports it).
  • Manually by using the Import feature in our dashboard.
  • Manually by scripting the Doppler CLI (or API) and the third party's CLI or API.

Automated Sync Integrations

If a third party's API supports reading secrets in plain text, it's possible to import secrets automatically when setting up the sync in Doppler. In many cases, we've added support for this and you can choose whether or not to import secrets when creating the sync.

Manual Import via Dashboard

If you don't have many projects to import secrets from and they're already in .env, JSON, or YAML format, then doing it by hand may be the easiest way! You can accomplish this by simply creating a new, empty project in Doppler, opening up one of the environments (e.g., dev) and then clicking the additional options arrow on the Add First Secret button.

You'll then be presented with a modal window where you can paste in your secrets and import them.

Manual Import via Script

If you have a large number of projects and environments you need to import, then scripting is definitely the way to go. You can use the Doppler CLI (or API) combined with the third party's CLI or API that you're importing from to easily get your secrets into Doppler. When using the CLI, the key to this process is using the doppler secrets upload command, which accepts .env, JSON, or YAML input. You can find some examples showing how you might do this for a few different services below. They're meant to be generic examples that can be used as a jumping off point, but if they might accomplish what you need as-is!

AWS Secrets Manager

#!/bin/bash

# Check if jq is installed
if ! command -v jq &> /dev/null; then
    echo "jq could not be found, please install jq to process JSON data."
    exit 1
fi

# Check if aws is installed
if ! command -v aws &> /dev/null; then
    echo "aws could not be found, please install the aws CLI."
    exit 1
fi

# Check if doppler is installed
if ! command -v doppler &> /dev/null; then
    echo "doppler could not be found, please install the doppler CLI."
    exit 1
fi

# Configuration Variables
STRIP_SECRET_PATH="false" # strip path from secret name (e.g., changes 'some/path/secret-name' to 'secret-name')
DOPPLER_PROJECT_NAME="your-doppler-project-name"
DOPPLER_CONFIG_NAME="your-doppler-config-name"
AWS_SECRET_ARN="your-aws-secret-arn"

# Get the name of the secret from its metadata
secret_name=$(aws secretsmanager describe-secret --secret-id "$AWS_SECRET_ARN" --query 'Name' --output text)

# Strip the path from the secret name
if [ "$STRIP_SECRET_PATH" = "true" ]; then
    secret_name=$(echo "$secret_name" | awk -F/ '{print $NF}')
fi

# Convert secret name to uppercase and replace disallowed characters with underscores
NORMALIZED_SECRET_NAME=$(echo "$secret_name" | tr '[:lower:]' '[:upper:]' | sed 's/[^A-Z0-9]/_/g')

# Fetch the secret value from AWS Secrets Manager
secret_value=$(aws secretsmanager get-secret-value --secret-id "$AWS_SECRET_ARN" --query 'SecretString' --output text)

SECRETS_JSON=""
# Determine if the secret value is valid JSON
if echo "$secret_value" | jq -e . &> /dev/null; then
    # It's JSON, process all keys to uppercase and save
    SECRETS_JSON=$(echo "$secret_value" | jq 'with_entries(.key |= ascii_upcase)')
else
    # It's plaintext, use the formatted secret name
    SECRETS_JSON="{ \"$NORMALIZED_SECRET_NAME\": \"$secret_value\" }"
fi

# Check if the transformation was successful
if ! ( echo "$SECRETS_JSON" | jq -e . &> /dev/null ); then
    echo "Failed to process JSON data."
    exit 1
fi

# Upload the JSON file to Doppler
doppler secrets upload --project $DOPPLER_PROJECT_NAME --config $DOPPLER_CONFIG_NAME <(echo "$SECRETS_JSON")

If the secret you're importing contains many key/value secret pairs, it will import all of those as separate secrets. If the secret is a plaintext secret, it will import that as a single secret where the name is the normalized path (i.e., it's converted to uppercase and has any character that isn't alphanumeric converted to an underscore). For example, devops/some-app-name/dev/stripe-api-key would be imported as DEVOPS_SOME_APP_NAME_DEV_STRIPE_API_KEY. You can set STRIP_SECRET_PATH to true to have that imported as STRIPE_API_KEY instead.

GitHub Actions

GitHub's API doesn't provide a way to fetch the actual secret values for GitHub Action secrets, so our integration syncs don't provide an import option. If you have existing secrets in GitHub that you would like to import into Doppler, the only way to do that is via a GitHub Action workflow like this:

name: Export Secrets to Doppler

on: workflow_dispatch

jobs:
  export-to-doppler:
    runs-on: ubuntu-latest
    name: export GitHub secrets to Doppler
    steps:
    - name: Install Doppler CLI
      uses: dopplerhq/cli-action@v3
    - name: Upload Secrets to Doppler
      run: doppler secrets upload --project "$DOPPLER_TARGET_PROJECT" --config "$DOPPLER_TARGET_CONFIG"  --silent <(echo "$GITHUB_SECRETS" | jq 'del(.github_token, .DOPPLER_TOKEN, .DOPPLER_TARGET_PROJECT, .DOPPLER_TARGET_CONFIG) | with_entries( .key |= ascii_upcase )')
      shell: bash
      env:
        DOPPLER_TOKEN: ${{ secrets.DOPPLER_TOKEN }}
        DOPPLER_TARGET_PROJECT: ${{ secrets.DOPPLER_TARGET_PROJECT }}
        DOPPLER_TARGET_CONFIG: ${{ secrets.DOPPLER_TARGET_CONFIG }}
        GITHUB_SECRETS: ${{ toJson(secrets) }}

This workflow uses the workflow_dispatch trigger, so it will only run when manually triggered to do so. It expects three secrets to exist on your repo:

  • DOPPLER_TARGET_PROJECT - The Doppler project you want to export your secrets to.
  • DOPPLER_TARGET_CONFIG - The Doppler config you want to export your secrets to.
  • DOPPLER_TOKEN - A Doppler access token with the permissions required to write secrets to the previously defined Doppler project and config.

The workflow will then take all the GitHub Action secrets for the repository, remove the github_token (which GitHub automatically injects), DOPPLER_TOKEN, DOPPLER_TARGET_PROJECT and DOPPLER_TARGET_CONFIG secrets, convert any remaining secret names to uppercase (which is required for secrets in Doppler) and then upload them to the specified config.

HashiCorp Vault

This script assumes the Doppler CLI is already logged in and that the Vault CLI is already communicating with your Vault install.

#!/usr/bin/env bash

# Check if jq is installed
if ! command -v jq &> /dev/null; then
    echo "jq could not be found, please install jq to process JSON data."
    exit 1
fi

# Check if vault is installed
if ! command -v vault &> /dev/null; then
    echo "vault could not be found, please install the Vault CLI."
    exit 1
fi

# Check if doppler is installed
if ! command -v doppler &> /dev/null; then
    echo "doppler could not be found, please install the Doppler CLI."
    exit 1
fi

# This is the path used to list all apps/services that are going to be imported.
# The names at this path are used to create Doppler projects. It's then
# expected that the next element name in the path will map to specific Doppler
# environments in the Doppler project and contain the secret KVs you want in that
# environment.
#
# For example, assume you set this to "kv/devops/" and when you list that you
# the next level of the path contains "backend", "frontend", and "worker" (so
# "kv/devops/backend/", "kv/devops/frontend/", and "kv/devops/worker/"). This
# script would ensure that a Doppler project exists called "backend", "frontend"
# and "worker". Now, the next path level would map to environments like "prd",
# "stg", "qa", and "dev" (so "kv/devops/backend/prd", "kv/devops/backend/stg", etc).
BASE_PATH="kv/devops/"

for service in $(vault kv list --format=json $BASE_PATH | jq -r '.[] | gsub("/$"; "")')
do
  echo "Importing secrets for $service"
  echo "==============================================="
  if ! doppler projects get "$service" > /dev/null 2>&1; then
    echo "Creating $service project..."
    doppler projects create --silent $service
  fi

  for environment in $(vault kv list --format=json "$BASE_PATH/$service" | jq -r '.[] | gsub("/$"; "")')
  do
    if ! doppler environments get --project "$service" "$environment" > /dev/null 2>&1; then
      echo "Creating ${service}.${environment} environment..."
      doppler environments create --project "$service" --silent "$environment" "$environment"
    fi
    echo "Uploading secrets for ${service}.${environment} environment..."
    # Note that secret keys that are imported (i.e., secret names) must be in
    # UPPER_SNAKE_CASE. The jq command below will convert secret names to that
    # format. So, if you had a KV entry where the key was "mysql-db-user", that
    # would be imported as "MYSQL_DB_USER". Secret names also cannot begin with
    # a number, so "9-test-secret" would be imported as "__TEST_SECRET".
    doppler secrets upload --silent --project "$service" --config "$environment" <(vault kv get -format=json "$BASE_PATH/$service/$environment" | jq '.data.data | to_entries | map( {key:.key|gsub("(^[0-9]{1}|[^0-9A-Za-z_])";"_")|ascii_upcase, value:.value} ) | from_entries')
  done

  echo
done

The above script will import secrets given a particular BASE_PATH you supply. It will assume the first level of entries at that path location are the services or apps you want Doppler projects for and that the next path level corresponds with the environments you want in that project and that those will contain the secrets for said environments. You can also use this as a jumping-off point for customizing the import process however you need!