Back to all posts

Automatically update your secrets and environment variables from a 1Password vault

Set up .env file

Create a .env file with a variable DEVELOPMENT_ENVS_UPDATED and set it to the current date.

# Update this whenever you make changes to the file
DEVELOPMENT_ENVS_UPDATED=2022-JAN-11

Set up 1Password vault

Follow the instructions on this guide to importing secrets from 1Password to set up a 1Password vault.

When you are done, you will have 4 pieces of information to assign to environment variables.

PASSWORD_TOKEN
PASSWORD_CREDENTIALS
VAULT_ID
ENV_FILE_ID

Create a file sync-env.sh

#!/bin/bash -u
 
apology () {
  echo "💬 Could not update .env file due to missing $1. Check 1Password for the latest .env file"
}
 
: ${VAULT_ID:?"$(apology VAULT_ID)"}
: ${ENV_FILE_ID:?"$(apology ENV_FILE_ID)"}
: ${PASSWORD_TOKEN:?"$(apology PASSWORD_TOKEN)"}
: ${PASSWORD_CREDENTIALS:?"$(apology PASSWORD_CREDENTIALS)"}
 
if [ ! "$(command -v docker)" ]; then
  echo "💬 Could not update .env file automatically. Check 1Password for the latest variables"
  echo "🐳 Docker daemon not installed. Install docker to fetch .env file automatically"
 
  exit 1
fi
 
docker stop op-connect-api 2>/dev/null && docker rm op-connect-api
docker stop op-connect-sync 2>/dev/null && docker rm op-connect-sync
 
echo $PASSWORD_CREDENTIALS > "1password-credentials.json"
 
docker run -d \
  --name op-connect-api \
  -p 8080:8080 \
  -v "$(pwd)/1password-credentials.json:/home/opuser/.op/1password-credentials.json" \
  -v "data:/home/opuser/.op/data" \
  1password/connect-api:latest
 
docker run -d \
  --name op-connect-sync \
  -p 8081:8080 \
  -v "$(pwd)/1password-credentials.json:/home/opuser/.op/1password-credentials.json" \
  -v "data:/home/opuser/.op/data" \
  1password/connect-sync:latest
 
gp await-port 8080
 
sleep 5
 
curl -L "http://0.0.0.0:8080/v1/vaults/$VAULT_ID/items/$ENV_FILE_ID" \
  -H "Authorization: Bearer $PASSWORD_TOKEN" \
  | jq -r .fields[0].value \
  > .env
 
echo "Stopping container…"
docker stop op-connect-api && docker rm op-connect-api
 
echo "Stopping container…"
docker stop op-connect-sync && docker rm op-connect-sync
 
rm "1password-credentials.json"
 
echo "Updated .env file"

Create a file sync-env.js

const fs = require("fs")
const path = require("path")
 
/**
 * Load environment variables from .env
 *
 * If they are out of date, or do not exist, download and update them
 * If they are newer than this file, update this file
 */
module.exports = function syncEnvFile() {
  require("dotenv").config()
 
  /** This variable will update itself when the .env does */
  const LATEST_ENVS_DATE = "2022-JAN-11"
  if (
    process.env.DEVELOPMENT_ENVS_UPDATED !==
    LATEST_ENVS_DATE
  ) {
    if (!process.env.DEVELOPMENT_ENVS_UPDATED) {
      console.error("The .env file is missing")
    } else {
      const claimedDate = new Date(
        process.env.DEVELOPMENT_ENVS_UPDATED,
      )
      const latestDate = new Date(LATEST_ENVS_DATE)
 
      if (claimedDate > latestDate) {
        const thisFilename = path.basename(__filename)
 
        console.info(
          `Updating ${thisFilename} to`,
          process.env.DEVELOPMENT_ENVS_UPDATED,
        )
        console.log("Please commit this change")
 
        const thisFile = fs.readFileSync(
          thisFilename,
          "utf8",
        )
 
        const occurrences =
          thisFile.split(LATEST_ENVS_DATE).length - 1
        if (occurrences !== 1)
          throw new Error("Cannot automatically update")
 
        fs.writeFileSync(
          thisFilename,
          thisFile.replace(
            LATEST_ENVS_DATE,
            process.env.DEVELOPMENT_ENVS_UPDATED,
          ),
          "utf-8",
        )
        return
      } else {
        console.error(
          "The .env file changed on",
          LATEST_ENVS_DATE,
        )
      }
    }
 
    const { execSync } = require("child_process")
 
    console.log(
      "Downloading latest .env file from 1Password…",
    )
    try {
      execSync("sh sync-env.sh", {
        encoding: "utf-8",
      })
 
      console.log("The .env file is now up to date")
    } catch (error) {
      console.error(error.stdout.toString())
    }
 
    process.exit(1)
  } else {
    console.log("The .env file is up to date")
  }
}

Usage

As early as possible in your application, add this line of code. In a node server, this is probably the top of your index.js, main.js, or server.js

if (process.env.NODE_ENV !== "production") {
  // Update and load .env file
  require("./sync-env")()
}

Error cases

Missing 1Password environment variables

The .env file is missing
Downloading latest .env file from 1Password…
sync-env.sh: line 7: VAULT_ID: 💬 Could not update .env file due to missing VAULT_ID. Check 1Password for the latest .env file
```
Share