Last active
March 26, 2023 09:14
-
-
Save jfut/3fbb53eb185e6b0bbfc3a337ef786991 to your computer and use it in GitHub Desktop.
aws-secret-cli
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# AWS Secrets Manager CLI for role based release files | |
# | |
# Copyright (c) 2023 Jun Futagawa (jfut) | |
# | |
# This software is released under the MIT License. | |
# http://opensource.org/licenses/mit-license.php | |
# - ENV/release/secret.conf | |
# aws_kms_key_file_key_id = xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | |
# | |
# - ENV/release/secret-tags.conf | |
# [ | |
# { | |
# "Key": "Project", | |
# "Value": "PROJECT_NAME" | |
# }, | |
# { | |
# "Key": "Environment", | |
# "Value": "ENV_NAME" | |
# } | |
# ] | |
set -uo pipefail | |
BASE_DIR="$(dirname $(dirname $(readlink -f $0)))" | |
DRY_RUN="" | |
EXCLUDE=".gitignore|.gitkeep" | |
# Mode | |
MODE_SHOW=0 | |
MODE_LIST=0 | |
MODE_DOWNLOAD=0 | |
MODE_ENCRYPT=0 | |
MODE_UPDATE=0 | |
MODE_DELETE=0 | |
KMS_KEY_CONFIG_NAME="aws_kms_key_file_key_id" | |
TAG_KEY_ENCRYPTED_FILE_SHA256SUM="ENCRYPTED_FILE_SHA256SUM" | |
BASE_TAGS="[]" | |
MANAGED_BY=$(basename "${0}") | |
# .gitignore | |
GIT_IGNORE_FILE="# Ignore all files | |
* | |
# Files not to be ignored | |
!*/ | |
!.gitignore | |
!*.encrypted" | |
# Usage | |
usage() { | |
cat << _EOF_ | |
Usage: | |
$(basename ${0}) [-n] [-l|-s|-d|-e|-u|-D] [-r RELEASE_DIR] [-t NAME1,NAME2] [-m MANAGED_BY] [-f] | |
Options: | |
-n Dry run mode | |
-l List secret | |
-s Show secret | |
-d Download secret (secret to local plain file) | |
-e Encrypt mode (local plain file to local encrypted file) | |
-u Create/Update mode (local encrypted file to secret) | |
-D Delete secret | |
-r Release directory (default: release) | |
-t Target secret name (default: "") | |
-m ManagedBy tag value (default: ${MANAGED_BY}) | |
-f Force option | |
LOCAL_NAME: | |
ENV/role-name/secret/file -> role-name/file | |
ENV/role-name/secret/file.txt -> role-name/file.txt | |
SECRET_ID: | |
ENV/role-name/secret/file -> ENV-PROJECT_NAME/role-name/file | |
ENV/role-name/secret/file.txt -> ENV-PROJECT_NAME/role-name/file.txt | |
Examples: | |
List secret | |
$0 -l | |
$0 -l -m "" | |
$0 -l -t SECRET_ID_1,SECRET_ID_2,... | |
Show secret | |
$0 -s | |
$0 -s -t SECRET_ID_1,SECRET_ID_2,... | |
$0 -s -t SECRET_ID_1,SECRET_ID_2,... -m "" | |
Download secret to local plain file | |
$0 -d | |
$0 -d -f | |
$0 -d -t SECRET_ID_1,SECRET_ID_2,... | |
$0 -d -t SECRET_ID_1,SECRET_ID_2,... -m "" -f -n | |
$0 -d -t SECRET_ID_1,SECRET_ID_2,... -m "" -f | |
$0 -d -t SECRET_ID_1,SECRET_ID_2,... -r NEW/release | |
Encrypt local plain file to local *.encrypted file | |
$0 -e -t SECRET_ID_1,SECRET_ID_2,... | |
$0 -e -t LOCAL_NAME_1,LOCAL_NAME_2,... | |
$0 -e -t SECRET_ID_1,SECRET_ID_2,... -f -n | |
$0 -e -t SECRET_ID_1,SECRET_ID_2,... -f | |
Update secret from local encrypted file | |
$0 -u -t SECRET_ID_1,SECRET_ID_2,... | |
$0 -u -t LOCAL_NAME_1,LOCAL_NAME_2,... | |
$0 -u -t SECRET_ID_1,SECRET_ID_2,... -f -n | |
$0 -u -t SECRET_ID_1,SECRET_ID_2,... -f | |
Delete secret | |
$0 -D -t SECRET_ID_1,SECRET_ID_2,... | |
$0 -D -t SECRET_ID_1,SECRET_ID_2,... -f -n | |
$0 -D -t SECRET_ID_1,SECRET_ID_2,... -f | |
_EOF_ | |
} | |
secret_show() { | |
local TARGET_NAME_ARRAY=${@} | |
# 作成済み Secret | |
CURRENT_SECRET_ID_LIST="" | |
if [[ -z "${MANAGED_BY}" ]]; then | |
CURRENT_SECRET_ID_LIST=$(aws secretsmanager list-secrets \ | |
--query "SecretList[].Name" \ | |
--output json \ | |
| jq -r '.[]' | sort) | |
else | |
CURRENT_SECRET_ID_LIST=$(aws secretsmanager list-secrets \ | |
--query "SecretList[?Tags[?Key=='ManagedBy'&&Value==\`${MANAGED_BY}\`]].Name" \ | |
--output json \ | |
| jq -r '.[]' | sort) | |
fi | |
# デフォルトは全件を対象 | |
FILTERED_SECRET_ID_LIST=${CURRENT_SECRET_ID_LIST} | |
# -t が指定されている場合、ターゲット名で絞り込み | |
if [[ ! -z "${TARGET_NAME_ARRAY}" ]]; then | |
FILTERED_SECRET_ID_LIST="" | |
for TARGET_NAME in ${TARGET_NAME_ARRAY[@]} | |
do | |
# 作成済みシークレットの配列に含まれていない場合はスキップ | |
FILTERED_SECRET_ID_LIST="${FILTERED_SECRET_ID_LIST} $(echo "${CURRENT_SECRET_ID_LIST}" | grep "^${TARGET_NAME}$")" | |
done | |
fi | |
# 結果を出力 | |
for SECRET_ID in ${FILTERED_SECRET_ID_LIST} | |
do | |
if [[ "${MODE_LIST}" -eq 1 ]]; then | |
echo "${SECRET_ID}" | |
elif [[ "${MODE_SHOW}" -eq 1 ]]; then | |
echo "- ${SECRET_ID}" | |
${DRY_RUN} aws secretsmanager get-secret-value --secret-id "${SECRET_ID}" | jq .SecretString | jq -r . | |
else | |
echo "ERROR: Invalid mode." | |
fi | |
done | |
} | |
secret_download() { | |
local RELEASE_ROOT_DIR="${1}" | |
local FORCE="${2}" | |
shift 2 | |
local TARGET_NAME_ARRAY=${@} | |
# 作成済み Secret | |
CURRENT_SECRET_ID_LIST="" | |
if [[ -z "${MANAGED_BY}" ]]; then | |
CURRENT_SECRET_ID_LIST=$(aws secretsmanager list-secrets \ | |
--query "SecretList[].Name" \ | |
--output json \ | |
| jq -r '.[]' | sort) | |
else | |
CURRENT_SECRET_ID_LIST=$(aws secretsmanager list-secrets \ | |
--query "SecretList[?Tags[?Key=='ManagedBy'&&Value==\`${MANAGED_BY}\`]].Name" \ | |
--output json \ | |
| jq -r '.[]' | sort) | |
fi | |
# デフォルトは全件を対象 | |
FILTERED_SECRET_ID_LIST=${CURRENT_SECRET_ID_LIST} | |
# -t が指定されている場合、ターゲット名で絞り込み | |
if [[ ! -z "${TARGET_NAME_ARRAY}" ]]; then | |
FILTERED_SECRET_ID_LIST="" | |
for TARGET_NAME in ${TARGET_NAME_ARRAY[@]} | |
do | |
# 作成済みシークレットの配列に含まれていない場合はスキップ | |
FILTERED_SECRET_ID_LIST="${FILTERED_SECRET_ID_LIST} $(echo "${CURRENT_SECRET_ID_LIST}" | grep "^${TARGET_NAME}$")" | |
done | |
fi | |
echo "# RELEASE_ROOT_DIR: ${RELEASE_ROOT_DIR}" | |
pushd "${RELEASE_ROOT_DIR}" > /dev/null | |
# 値を保存 | |
for SECRET_ID in ${FILTERED_SECRET_ID_LIST} | |
do | |
if [[ "${MODE_DOWNLOAD}" -eq 1 ]]; then | |
echo -n "- ${SECRET_ID}: " | |
# PROFILE-NAME/role-name/file.txt -> role-name | |
ROLE_NAME=$(echo "${SECRET_ID}" | cut -d'/' -f 2) | |
# PROFILE-NAME/role-name/file.txt -> file.txt | |
# PROFILE-NAME/role-name/dir/file.txt -> dir/file.txt | |
PLAIN_NAME=$(echo "${SECRET_ID}" | cut -d'/' -f 3-) | |
# role-name が存在しない場合はスキップ | |
if [[ -z "${PLAIN_NAME}" ]]; then | |
echo "SKIP: role-name not found." | |
continue | |
fi | |
# role-name/secret/file.txt | |
PLAIN_FILE="${ROLE_NAME}/secret/${PLAIN_NAME}" | |
if [[ "${FORCE}" -eq 1 ]] || [[ ! -f "${PLAIN_FILE}" ]]; then | |
# 強制モード、もしくは、ファイルが存在しない場合のみ値を保存 | |
echo "Save to ${PLAIN_FILE}" | |
if [[ ! -z "${DRY_RUN}" ]]; then | |
${DRY_RUN} aws secretsmanager get-secret-value --secret-id "${SECRET_ID}" \ | |
\| jq .SecretString \| jq -r . \| sed '$d' \ | |
\> "${PLAIN_FILE}" | |
else | |
local PARENT_DIR=$(dirname "${PLAIN_FILE}") | |
if [[ ! -d "${PARENT_DIR}" ]]; then | |
mkdir -p "${PARENT_DIR}" | |
fi | |
if [[ ! -f "${PARENT_DIR}/.gitignore" ]]; then | |
echo "${GIT_IGNORE_FILE}" > "${PARENT_DIR}/.gitignore" | |
fi | |
aws secretsmanager get-secret-value --secret-id "${SECRET_ID}" \ | |
| jq .SecretString | jq -r . | sed '$d' \ | |
> "${PLAIN_FILE}" | |
fi | |
else | |
echo "SKIP: already exists (Use -f option to overwrite)" | |
fi | |
else | |
echo "ERROR: Invalid mode." | |
fi | |
done | |
popd > /dev/null | |
} | |
secret_update() { | |
local RELEASE_ROOT_DIR="${1}" | |
local KEY_ID="${2}" | |
local FORCE="${3}" | |
shift 3 | |
local TARGET_NAME_ARRAY=${@} | |
echo "# RELEASE_ROOT_DIR: ${RELEASE_ROOT_DIR}" | |
pushd "${RELEASE_ROOT_DIR}" > /dev/null | |
# 暗号化モード: *.encrypted 以外の secret/ ファイルをベースに処理 | |
if [[ "${MODE_ENCRYPT}" -eq 1 ]]; then | |
LIST=$(find */secret -type f -not -name "*.encrypted" | egrep -v "${EXCLUDE}") | |
for PLAIN_FILE in ${LIST} | |
do | |
# role-name/secret/file.txt -> role-name/secret/file.txt.encrypted | |
# role-name/secret/file.plain -> role-name/secret/file.plain.encrypted | |
# role-name/secret/dir/file.plain -> role-name/secret/dir/file.plain.encrypted | |
ENCRYPTED_FILE="${PLAIN_FILE}.encrypted" | |
# role-name/secret/file.plain -> role-name | |
ROLE_NAME=$(echo "${PLAIN_FILE}" | cut -d'/' -f 1) | |
# role-name/secret/file.plain -> file.plain | |
# role-name/secret/dir/file.plain -> dir/file.plain.encrypted | |
FILE_NAME=$(echo "${PLAIN_FILE}" | cut -d'/' -f 3-) | |
# role-name/file.plain | |
# role-name/dir/file.plain | |
LOCAL_NAME="${ROLE_NAME}/${FILE_NAME}" | |
# PROFILE-NAME/role-name/file.plain | |
# PROFILE-NAME/role-name/dir/file.plain | |
SECRET_ID="${AWS_PROFILE}/${ROLE_NAME}/${FILE_NAME}" | |
# PROFILE-NAME/role-name/dir/file.plain -> PROFILE-NAME/role-name/dir/file | |
# PROFILE-NAME/role-name/file.plain -> PROFILE-NAME/role-name/file | |
SECRET_ID="${SECRET_ID%.plain}" | |
# -t が指定されている時は対象を絞り込み | |
if [[ ! -z "${TARGET_NAME_ARRAY}" ]]; then | |
IS_TARGET=0 | |
for TARGET_NAME in ${TARGET_NAME_ARRAY[@]} | |
do | |
if [[ "${TARGET_NAME}" == "${LOCAL_NAME}" ]] || [[ "${TARGET_NAME}" == "${SECRET_ID}" ]]; then | |
IS_TARGET=1 | |
fi | |
done | |
if [[ "${IS_TARGET}" -eq 0 ]]; then | |
continue | |
fi | |
fi | |
# 強制モードの時はすべて、もしくは、*.encrypted ファイルが存在しないものを処理 | |
if [[ "${FORCE}" -eq 1 ]] || [[ ! -f "${ENCRYPTED_FILE}" ]]; then | |
echo "- ${SECRET_ID}: \"${PLAIN_FILE}\" -> \"${ENCRYPTED_FILE}\"" | |
if [[ ! -z "${DRY_RUN}" ]]; then | |
${DRY_RUN} aws kms encrypt \ | |
--key-id "${KEY_ID}" \ | |
--plaintext "fileb://${PLAIN_FILE}" \ | |
--output text \ | |
--query CiphertextBlob \ | |
\> "${ENCRYPTED_FILE}" | |
else | |
aws kms encrypt \ | |
--key-id "${KEY_ID}" \ | |
--plaintext "fileb://${PLAIN_FILE}" \ | |
--output text \ | |
--query CiphertextBlob \ | |
> "${ENCRYPTED_FILE}" | |
fi | |
fi | |
done | |
fi | |
# アップデートモード: */secret/*.encrypted ファイルをベースに処理 | |
if [[ "${MODE_UPDATE}" -eq 1 ]]; then | |
# 作成済み Secret | |
# アップデートモード時は本ツール以外で追加された Secret も扱えるように ManagedBy によるフィルタリングは使用しない | |
# PROFLE-NAME/role-name/file,SHA256SUM | |
CURRENT_SECRET_ID_LIST=$(aws secretsmanager list-secrets \ | |
--query "SecretList[].[ Name,Tags[?Key==\`${TAG_KEY_ENCRYPTED_FILE_SHA256SUM}\`] | [0].Value ]" \ | |
--output json \ | |
| jq -r '.[] | @csv' | sed 's/"//g' | sort) | |
LIST=$(find */secret -type f -name "*.encrypted" | egrep -v "${EXCLUDE}") | |
for ENCRYPTED_FILE in ${LIST} | |
do | |
# role-name/secret/file.plain.encrypted -> role-name | |
ROLE_NAME=$(echo "${ENCRYPTED_FILE}" | cut -d'/' -f 1) | |
# role-name/secret/file.plain.encrypted -> file.plain.encrypted | |
# role-name/secret/dir/file.plain.encrypted -> dir/file.plain.encrypted | |
FILE_NAME=$(echo "${ENCRYPTED_FILE}" | cut -d'/' -f 3-) | |
# file.plain.encrypted -> file.plain | |
# dir/file.plain.encrypted -> dir/file.plain | |
FILE_NAME="${FILE_NAME%.encrypted}" | |
# role-name/file.plain | |
# role-name/dir/file.plain | |
LOCAL_NAME="${ROLE_NAME}/${FILE_NAME}" | |
# PROFILE-NAME/role-name/file.plain | |
# PROFILE-NAME/role-name/dir/file.plain | |
SECRET_ID="${AWS_PROFILE}/${ROLE_NAME}/${FILE_NAME}" | |
# PROFILE-NAME/role-name/file.plain -> PROFILE-NAME/role-name/file | |
# PROFILE-NAME/role-name/dir/file.plain -> PROFILE-NAME/role-name/dir/file | |
SECRET_ID="${SECRET_ID%.plain}" | |
# -t が指定されている時は対象を絞り込み | |
if [[ ! -z "${TARGET_NAME_ARRAY}" ]]; then | |
IS_TARGET=0 | |
for TARGET_NAME in ${TARGET_NAME_ARRAY[@]} | |
do | |
if [[ "${TARGET_NAME}" == "${LOCAL_NAME}" ]] || [[ "${TARGET_NAME}" == "${SECRET_ID}" ]]; then | |
IS_TARGET=1 | |
fi | |
done | |
if [[ "${IS_TARGET}" -eq 0 ]]; then | |
continue | |
fi | |
fi | |
# ローカルの暗号化ファイルの sha256sum の値を取得 | |
LOCAL_ENCRYPTED_FILE_SHA256SUM=$(sha256sum "${ENCRYPTED_FILE}" | awk '{ print $1 }') | |
# タグを組み立て | |
SECRET_TAG=$(echo "${BASE_TAGS}" \ | |
"[{\"Key\": \"${TAG_KEY_ENCRYPTED_FILE_SHA256SUM}\", \"Value\": \"${LOCAL_ENCRYPTED_FILE_SHA256SUM}\"}]" \ | |
"[{\"Key\": \"ManagedBy\", \"Value\": \"${MANAGED_BY}\"}]" \ | |
| jq -s add) | |
# echo "DEBUG: SECRET_TAG: ${SECRET_TAG}" | |
# Secret 作成済みかどうか | |
IS_CREATED=$(echo "${CURRENT_SECRET_ID_LIST}" | grep "^${SECRET_ID}," | wc -l) | |
if [[ "${IS_CREATED}" -eq 1 ]]; then | |
# 作成済みの場合、タグから Secret 作成時の暗号化ファイルの sha256sum の値を取得 | |
SECRET_ENCRYPTED_FILE_SHA256SUM=$(echo "${CURRENT_SECRET_ID_LIST}" | grep "^${SECRET_ID}," | cut -d',' -f 2) | |
# echo "DEBUG: SHA256SUM: local: ${LOCAL_ENCRYPTED_FILE_SHA256SUM}, secret: ${SECRET_ENCRYPTED_FILE_SHA256SUM}" | |
if [[ "${FORCE}" -eq 1 ]] || [[ "${LOCAL_ENCRYPTED_FILE_SHA256SUM}" != "${SECRET_ENCRYPTED_FILE_SHA256SUM}" ]]; then | |
# 更新の場合 | |
echo "- Update ${SECRET_ID} (encrypted sha256sum: ${LOCAL_ENCRYPTED_FILE_SHA256SUM})" | |
# 値更新のための一時平文ファイルの作成 | |
ENCRYPTED_FILE_BASED_PLAIN_FILE="${ENCRYPTED_FILE}.tmp" | |
trap "rm -f \"${ENCRYPTED_FILE_BASED_PLAIN_FILE}\"" ERR EXIT | |
decrypt_file "${ENCRYPTED_FILE}" "${ENCRYPTED_FILE_BASED_PLAIN_FILE}" | |
# Secret の値とタグを更新 | |
if [[ ! -z "${DRY_RUN}" ]]; then | |
${DRY_RUN} aws secretsmanager update-secret \ | |
--secret-id "${SECRET_ID}" \ | |
--kms-key-id "${KEY_ID}" \ | |
--secret-string "file://${ENCRYPTED_FILE_BASED_PLAIN_FILE}" \> /dev/null | |
${DRY_RUN} aws secretsmanager tag-resource \ | |
--secret-id "${SECRET_ID}" \ | |
--tags "${SECRET_TAG}" \> /dev/null | |
else | |
aws secretsmanager update-secret \ | |
--secret-id "${SECRET_ID}" \ | |
--kms-key-id "${KEY_ID}" \ | |
--secret-string "file://${ENCRYPTED_FILE_BASED_PLAIN_FILE}" > /dev/null | |
aws secretsmanager tag-resource \ | |
--secret-id "${SECRET_ID}" \ | |
--tags "${SECRET_TAG}" > /dev/null | |
fi | |
rm -f "${ENCRYPTED_FILE_BASED_PLAIN_FILE}" | |
fi | |
else | |
# 新規作成の場合 | |
echo "- Create ${SECRET_ID} (encrypted sha256sum: ${LOCAL_ENCRYPTED_FILE_SHA256SUM})" | |
# 値更新のための一時平文ファイルの作成 | |
ENCRYPTED_FILE_BASED_PLAIN_FILE="${ENCRYPTED_FILE}.tmp" | |
if [[ "${FORCE}" -eq 0 ]]; then | |
# -f オプション時、削除済みシークレットが存在しており新規作成がエラー時に | |
# 強制削除を実行してから再度作成を実行するため、エラーとラップを除外 | |
trap "rm -f \"${ENCRYPTED_FILE_BASED_PLAIN_FILE}\"" ERR EXIT | |
fi | |
decrypt_file "${ENCRYPTED_FILE}" "${ENCRYPTED_FILE_BASED_PLAIN_FILE}" | |
# Secret 作成 | |
if [[ ! -z "${DRY_RUN}" ]]; then | |
${DRY_RUN} aws secretsmanager create-secret \ | |
--kms-key-id "${KEY_ID}" \ | |
--name "${SECRET_ID}" \ | |
--description "${SECRET_ID}" \ | |
--secret-string "file://${ENCRYPTED_FILE_BASED_PLAIN_FILE}" \ | |
--tags "${SECRET_TAG}" \> /dev/null | |
else | |
aws secretsmanager create-secret \ | |
--kms-key-id "${KEY_ID}" \ | |
--name "${SECRET_ID}" \ | |
--description "${SECRET_ID}" \ | |
--secret-string "file://${ENCRYPTED_FILE_BASED_PLAIN_FILE}" \ | |
--tags "${SECRET_TAG}" > /dev/null | |
RET=$? | |
if [[ "${RET}" -ne 0 ]] && [[ "${FORCE}" -eq 1 ]]; then | |
# RET: 254 対象 SECRET_ID が削除予定で存在している場合 | |
# -f オプション時は、削除予定になっているシークレットを強制削除して再作成を実行 | |
# An error occurred (InvalidRequestException) when calling the CreateSecret operation | |
# You can't create this secret because a secret with this name is already scheduled for deletion. | |
# 強制削除 | |
echo "- Force delete without recovery ${SECRET_ID}" | |
aws secretsmanager delete-secret --force-delete-without-recovery --secret-id "${SECRET_ID}" | |
sleep 3 | |
# 強制削除後すぐには再作成できないためリトライしながら再作成 | |
MAX_RETRY=10 | |
RETRY=0 | |
until [[ "${RET}" -eq 0 ]] || [[ "${RETRY}" -eq "${MAX_RETRY}" ]]; | |
do | |
# Retry | |
sleep $(( RETRY++ )) | |
echo "- Retry ${RETRY}: Create ${SECRET_ID} (encrypted sha256sum: ${LOCAL_ENCRYPTED_FILE_SHA256SUM})" | |
aws secretsmanager create-secret \ | |
--kms-key-id "${KEY_ID}" \ | |
--name "${SECRET_ID}" \ | |
--description "${SECRET_ID}" \ | |
--secret-string "file://${ENCRYPTED_FILE_BASED_PLAIN_FILE}" \ | |
--tags "${SECRET_TAG}" > /dev/null 2>&1 | |
RET=$? | |
done | |
fi | |
fi | |
rm -f "${ENCRYPTED_FILE_BASED_PLAIN_FILE}" | |
fi | |
done | |
fi | |
popd > /dev/null | |
} | |
decrypt_file() { | |
local ENCRYPTED_FILE="${1}" | |
local PLAIN_FILE="${2}" | |
local PARENT_DIR=$(dirname "${PLAIN_FILE}") | |
if [[ ! -d "${PARENT_DIR}" ]]; then | |
mkdir -p "${PARENT_DIR}" | |
fi | |
if [[ ! -z "${DRY_RUN}" ]]; then | |
${DRY_RUN} aws kms decrypt \ | |
--ciphertext-blob fileb://<(cat "${ENCRYPTED_FILE}" | base64 -d) \ | |
--output text \ | |
--query Plaintext \ | |
\| base64 -d \ | |
\> "${PLAIN_FILE}" | |
else | |
aws kms decrypt \ | |
--ciphertext-blob fileb://<(cat "${ENCRYPTED_FILE}" | base64 -d) \ | |
--output text \ | |
--query Plaintext \ | |
| base64 -d \ | |
> "${PLAIN_FILE}" | |
fi | |
} | |
secret_delete() { | |
local TARGET_NAME_ARRAY=${@} | |
# -t が指定されている時のみ削除を実行 | |
if [[ ! -z "${TARGET_NAME_ARRAY}" ]]; then | |
for SECRET_ID in ${TARGET_NAME_ARRAY[@]} | |
do | |
echo "- Delete ${SECRET_ID}" | |
DELETE_SECRET_OPTIONS="" | |
if [[ "${FORCE}" -eq 1 ]]; then | |
DELETE_SECRET_OPTIONS="--force-delete-without-recovery" | |
fi | |
${DRY_RUN} aws secretsmanager delete-secret ${DELETE_SECRET_OPTIONS} --secret-id "${SECRET_ID}" | |
done | |
else | |
echo "ERROR: Use with -t option" | |
exit 1 | |
fi | |
} | |
# Main | |
main() { | |
[[ $# -lt 1 ]] && usage && exit 1 | |
local DEFAULT_RELEASE_DIR="release" | |
local RELEASE_DIR="" | |
local TARGET_NAME="" | |
local FORCE=0 | |
while getopts nlsdeuaDr:t:fm:h OPT; do | |
case "${OPT}" in | |
"n" ) | |
DRY_RUN="echo DRY_RUN:" ;; | |
"l" ) | |
MODE_LIST=1 ;; | |
"s" ) | |
MODE_SHOW=1 ;; | |
"d" ) | |
MODE_DOWNLOAD=1 ;; | |
"e" ) | |
MODE_ENCRYPT=1 ;; | |
"u" ) | |
MODE_UPDATE=1 ;; | |
"a" ) | |
MODE_ENCRYPT=1 | |
MODE_UPDATE=1 ;; | |
"D" ) | |
MODE_DELETE=1 ;; | |
"r" ) | |
RELEASE_DIR="${OPTARG}" ;; | |
"t" ) | |
TARGET_NAME="${OPTARG}" ;; | |
"m" ) | |
MANAGED_BY="${OPTARG}" ;; | |
"f" ) | |
FORCE=1 ;; | |
"h" ) | |
usage | |
exit 0 | |
;; | |
* ) | |
usage | |
exit 1 | |
;; | |
esac | |
done | |
shift $((OPTIND - 1)) | |
# unkown option | |
if [[ ! -z "${@}" ]]; then | |
echo "ERROR: Unkown option, -t ${@} ?" | |
exit 1 | |
fi | |
if [[ -z "${AWS_PROFILE}" ]]; then | |
echo "Error: AWS_PROFILE environment variable is not set" | |
exit 1 | |
fi | |
if [[ -z "${PROJECT_ENV}" ]]; then | |
echo "Error: PROJECT_ENV environment variable is not set" | |
exit 1 | |
fi | |
# config | |
local DEFAULT_ROOT_RELEASE_DIR="${BASE_DIR}/${PROJECT_ENV}/${DEFAULT_RELEASE_DIR}" | |
local CONF_FILE="${DEFAULT_ROOT_RELEASE_DIR}/secret.conf" | |
if [[ ! -f "${CONF_FILE}" ]]; then | |
echo "ERROR: ${CONF_FILE} is not found." | |
echo "Please run terragrunt apply in the base/ directory." | |
exit 1 | |
fi | |
local KEY_ID=$(cat "${CONF_FILE}" | grep "^${KMS_KEY_CONFIG_NAME}" | cut -d'=' -f 2 | awk '{ print $1 }') | |
if [[ -z "${KEY_ID}" ]]; then | |
echo "ERROR: ${KMS_KEY_CONFIG_NAME} in ${CONF_FILE} is not found." | |
echo "Please run terragrunt apply in the base/ directory." | |
exit 1 | |
fi | |
# tags | |
local CONF_TAGS_FILE="${DEFAULT_ROOT_RELEASE_DIR}/secret-tags.json" | |
if [[ -f "${CONF_TAGS_FILE}" ]]; then | |
BASE_TAGS=$(cat "${CONF_TAGS_FILE}") | |
fi | |
# Create target name array | |
declare -a TARGET_NAME_ARRAY=() | |
if [[ ! -z "${TARGET_NAME}" ]]; then | |
# dir1,dir2,... -> array[@] | |
# 末尾の空白は意味があるため削除禁止 | |
TARGET_NAME_ARRAY=(${TARGET_NAME//,/ }) | |
fi | |
# release directory | |
local RELEASE_ROOT_DIR="" | |
if [[ -z "${RELEASE_DIR}" ]]; then | |
# -r オプション指定なし | |
RELEASE_ROOT_DIR="${BASE_DIR}/${PROJECT_ENV}/${DEFAULT_RELEASE_DIR}" | |
else | |
# -r オプション指定あり | |
RELEASE_ROOT_DIR="${RELEASE_DIR}" | |
fi | |
if [[ ! -d "${RELEASE_ROOT_DIR}" ]]; then | |
mkdir -p "${RELEASE_ROOT_DIR}" | |
fi | |
if [[ "${MODE_LIST}" -eq 1 ]] || [[ "${MODE_SHOW}" -eq 1 ]]; then | |
secret_show "${TARGET_NAME_ARRAY[@]}" | |
elif [[ "${MODE_DOWNLOAD}" -eq 1 ]]; then | |
secret_download "${RELEASE_ROOT_DIR}" "${FORCE}" "${TARGET_NAME_ARRAY[@]}" | |
elif [[ "${MODE_ENCRYPT}" -eq 1 ]] || [[ "${MODE_UPDATE}" -eq 1 ]]; then | |
secret_update "${RELEASE_ROOT_DIR}" "${KEY_ID}" "${FORCE}" "${TARGET_NAME_ARRAY[@]}" | |
elif [[ "${MODE_DELETE}" -eq 1 ]]; then | |
secret_delete "${TARGET_NAME_ARRAY[@]}" | |
else | |
echo "ERROR: Invalid mode." | |
fi | |
} | |
[[ ${#BASH_SOURCE[@]} = 1 ]] && main "${@}" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment