Skip to content

Commit

Permalink
Merge pull request #50 from paulRbr/multi-provider
Browse files Browse the repository at this point in the history
multi-providers: 'provider' variable now accepts multiple providers
  • Loading branch information
paulRbr authored Oct 3, 2020
2 parents e9d13a3 + 21e5f6d commit 1a0b110
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 130 deletions.
35 changes: 21 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,32 +36,39 @@ Then you will need to add the `tf-make` binary (it's a simple bash script) in yo

## Convention

This makefile assumes your terraform configuration files are stored as such:
This makefile assumes your terraform configuration files are stored in a specific directory structure:

I.e. `providers/<provider>/<env>/*.tf`

E.g. example for all supported providers:
```
providers/
├── aws
│   ├── prod
│   │   └── empty.tf
│   │   └── config.tf
│   └── test
│   └── empty.tf
│   └── config.tf
├── do
│   └── prod
│   └── empty.tf
│   └── config.tf
├── google
│   ├── prod
│   │   └── empty.tf
│   │   └── config.tf
│   └── test
│   └── config.tf
├── hetzner,scaleway
│   └── test
│   └── empty.tf
│   └── config.tf
├── hetzner
│   └── prod
│   └── empty.tf
│   └── config.tf
└── scaleway
└── prod
└── empty.tf
└── config.tf
```

I.e. `providers/<provider>/<env>/*.tf`

_Note: the `provider` name can be a combination of multiple providers when you are in a multi-cloud environment. E.g. `providers/hetzner,scaleway/prod/config.tf`._

## Commands

Expand Down Expand Up @@ -92,8 +99,8 @@ workspace Workspace infra resources
Details of the variables that can be passed to commands:


| Name | Default | Values | Description | Example |
| --------- | ------- | ------ | ----------- | ------- |
| `provider` | - | `aws`<br/>`azure`<br/>`do`<br/>`google`<br/>`hetzner`<br/>`scaleway` | Name of the cloud provider to target | With your terraform file in `provider/aws/production/production.tf` you will be able to `make dry-run provider=aws env=production` |
| `env` | - | `String` | Name of the environment you want to use | With a terraform file in `provider/google/production/production.tf` you will be able to `make dry-run provider=google env=production` |
| `args` | - | `String` | Add terraform understandable arguments | `make dry-run args='-no-color'` |
| Name | Default | Values | Description | Example |
| --------- | ------- | ------ | ----------- | ------- |
| `provider` | - | `aws`<br/>`azure`<br/>`do`<br/>`google`<br/>`hetzner`<br/>`scaleway`<br/>or any combination of those separated by commas `,` | Name of the cloud provider(s) to target | With your terraform file in `provider/aws/production/production.tf` you will be able to `make dry-run provider=aws env=production`<br/>With a terraform file in `provider/hetzner,scaleway/production/config.tf` you will be able to `make dry-run provider=hetzner,scaleway env=production` and have credentials for both providers available |
| `env` | - | `String` | Name of the environment you want to use | With a terraform file in `provider/google/production/production.tf` you will be able to `make dry-run provider=google env=production` |
| `args` | - | `String` | Add terraform understandable arguments | `make dry-run args='-no-color'` |
238 changes: 122 additions & 116 deletions terraform.sh
Original file line number Diff line number Diff line change
Expand Up @@ -34,128 +34,134 @@ valid_identifier()
echo "$1" | tr '[:lower:]' '[:upper:]' | tr -cs '[:alpha:][:digit:]\n' '_'
}

key="$(valid_identifier "${provider}")_$(valid_identifier "${env}")_KEY"
secret="$(valid_identifier "${provider}")_$(valid_identifier "${env}")_SECRET"
token="$(valid_identifier "${provider}")_$(valid_identifier "${env}")_TOKEN"

if command -v pass >/dev/null 2>&1; then
pass_key="$(pass "terraform/${provider}/${env}/access_key" || echo '')"
pass_secret="$(pass "terraform/${provider}/${env}/secret" || echo '')"
pass_token="$(pass "terraform/${provider}/${env}/token" || echo '')"

if [ -n "${pass_key}" ] && [ -n "${pass_secret}" ]; then
declare "${key}"="${pass_key}"
declare "${secret}"="${pass_secret}"
fi
if [ -n "${pass_token}" ]; then
declare "${token}"="${pass_token}"
fi
fi

if [ -n "${VAULT_ADDR}" ]; then
if [ -z "${VAULT_TOKEN}" ]; then
if [ -n "${VAULT_ROLE_ID}" ] && [ -n "${VAULT_SECRET_ID}" ]; then
declare -x "VAULT_TOKEN"=$(curl -s -X POST -d "{\"role_id\":\"${VAULT_ROLE_ID}\",\"secret_id\":\"${VAULT_SECRET_ID}\"}" "${VAULT_ADDR}/v1/auth/approle/login" | jq -r .auth.client_token)
if [ -z "${VAULT_TOKEN}" ] || [ "${VAULT_TOKEN}" == "null" ]; then
echo "Error fetching 'VAULT_TOKEN' from 'VAULT_ROLE_ID' and 'VAULT_SECRET_ID'"
exit
# Split provider variable on commas ','
while IFS=',' read -ra providers; do
for oneProvider in "${providers[@]}"; do
key="$(valid_identifier "${oneProvider}")_$(valid_identifier "${env}")_KEY"
secret="$(valid_identifier "${oneProvider}")_$(valid_identifier "${env}")_SECRET"
token="$(valid_identifier "${oneProvider}")_$(valid_identifier "${env}")_TOKEN"

if command -v pass >/dev/null 2>&1; then
pass_key="$(pass "terraform/${oneProvider}/${env}/access_key" || echo '')"
pass_secret="$(pass "terraform/${oneProvider}/${env}/secret" || echo '')"
pass_token="$(pass "terraform/${oneProvider}/${env}/token" || echo '')"

if [ -n "${pass_key}" ] && [ -n "${pass_secret}" ]; then
declare "${key}"="${pass_key}"
declare "${secret}"="${pass_secret}"
fi
if [ -n "${pass_token}" ]; then
declare "${token}"="${pass_token}"
fi
else
echo "'VAULT_TOKEN' or ( 'VAULT_ROLE_ID' and 'VAULT_SECRET_ID' ) must be set!"
exit
fi
fi

if [ "${vault_aws}" == "true" ]; then
if [ -z "${vault_path}" ]; then
vault_path="aws"
fi

if [ -z "${vault_aws_role}" ]; then
echo "'vault_aws_role' variable must be set"
exit
fi

# We use STS by default but if we need to perform IAM actions we can't use it
if [ "${vault_aws_iam}" == "true" ]; then
creds=$(curl -s -X GET -H "X-Vault-Token: ${VAULT_TOKEN}" -d "{\"ttl\":\"${vault_ttl}\"}" "${VAULT_ADDR}/v1/${vault_path}/creds/${vault_aws_role}" | jq .data)
else
creds=$(curl -s -X GET -H "X-Vault-Token: ${VAULT_TOKEN}" -d "{\"ttl\":\"${vault_ttl}\"}" "${VAULT_ADDR}/v1/${vault_path}/sts/${vault_aws_role}" | jq .data)
declare -x "AWS_SESSION_TOKEN"=$(echo ${creds} | jq -r .security_token)
fi

if [ -z "$(echo ${creds})" ] || [ "$(echo ${creds} | jq -r .access_key)" == "null" ]; then
echo "Unable to fetch AWS credentials from Vault"
exit
fi

declare -x "AWS_ACCESS_KEY_ID"=$(echo ${creds} | jq -r .access_key)
declare -x "AWS_SECRET_ACCESS_KEY"=$(echo ${creds} | jq -r .secret_key)

echo "Fetched AWS credentials from Vault."
fi
fi

case $provider in
aws)
if [ -z "${AWS_ACCESS_KEY_ID}" ]; then
declare -x "AWS_ACCESS_KEY_ID=${!key}"
declare -x "AWS_SECRET_ACCESS_KEY=${!secret}"
if [ -n "${VAULT_ADDR}" ]; then
if [ -z "${VAULT_TOKEN}" ]; then
if [ -n "${VAULT_ROLE_ID}" ] && [ -n "${VAULT_SECRET_ID}" ]; then
declare -x "VAULT_TOKEN"=$(curl -s -X POST -d "{\"role_id\":\"${VAULT_ROLE_ID}\",\"secret_id\":\"${VAULT_SECRET_ID}\"}" "${VAULT_ADDR}/v1/auth/approle/login" | jq -r .auth.client_token)
if [ -z "${VAULT_TOKEN}" ] || [ "${VAULT_TOKEN}" == "null" ]; then
echo "Error fetching 'VAULT_TOKEN' from 'VAULT_ROLE_ID' and 'VAULT_SECRET_ID'"
exit
fi
else
echo "'VAULT_TOKEN' or ( 'VAULT_ROLE_ID' and 'VAULT_SECRET_ID' ) must be set!"
exit
fi
fi

if [ -n "${!token}" ]; then
declare -x "AWS_SESSION_TOKEN=${!token}"
if [ "${vault_aws}" == "true" ]; then
if [ -z "${vault_path}" ]; then
vault_path="aws"
fi

if [ -z "${vault_aws_role}" ]; then
echo "'vault_aws_role' variable must be set"
exit
fi

# We use STS by default but if we need to perform IAM actions we can't use it
if [ "${vault_aws_iam}" == "true" ]; then
creds=$(curl -s -X GET -H "X-Vault-Token: ${VAULT_TOKEN}" -d "{\"ttl\":\"${vault_ttl}\"}" "${VAULT_ADDR}/v1/${vault_path}/creds/${vault_aws_role}" | jq .data)
else
creds=$(curl -s -X GET -H "X-Vault-Token: ${VAULT_TOKEN}" -d "{\"ttl\":\"${vault_ttl}\"}" "${VAULT_ADDR}/v1/${vault_path}/sts/${vault_aws_role}" | jq .data)
declare -x "AWS_SESSION_TOKEN"=$(echo ${creds} | jq -r .security_token)
fi

if [ -z "$(echo ${creds})" ] || [ "$(echo ${creds} | jq -r .access_key)" == "null" ]; then
echo "Unable to fetch AWS credentials from Vault"
exit
fi

declare -x "AWS_ACCESS_KEY_ID"=$(echo ${creds} | jq -r .access_key)
declare -x "AWS_SECRET_ACCESS_KEY"=$(echo ${creds} | jq -r .secret_key)

echo "Fetched AWS credentials from Vault."
fi
fi
;;
azurerm)
if [ -z "${ARM_CLIENT_ID}" ]; then
declare -x "ARM_CLIENT_ID=${!key}"
declare -x "ARM_CLIENT_SECRET=${!secret}"
fi
;;
"do")
if [ -z "${DIGITALOCEAN_TOKEN}" ]; then
declare -x "DIGITALOCEAN_TOKEN=${!secret}"
fi
;;
google)
if [ -z "${GOOGLE_CREDENTIALS}" ]; then
declare -x "GOOGLE_CREDENTIALS=${!secret}"
fi
;;
hetzner)
if [ -z "${HCLOUD_TOKEN}" ]; then
declare -x "HCLOUD_TOKEN=${!secret}"
fi
;;
scaleway)
if [ -z "${SCW_ACCESS_KEY}" ]; then
declare -x "SCW_ACCESS_KEY=${!key}"
declare -x "SCW_SECRET_KEY=${!secret}"
declare -x "SCW_DEFAULT_ORGANIZATION_ID=${!token}"
fi
# This is a hack to be able to use S3 tf backend on a scaleway object storage
# and because terraform doesn't allow interpolation in backend config:
# https://github.com/hashicorp/terraform/issues/13022
if [ -z "${AWS_ACCESS_KEY_ID}" ]; then
declare -x "AWS_ACCESS_KEY_ID=${!key}"
declare -x "AWS_SECRET_ACCESS_KEY=${!secret}"
fi
;;
ovh)
if [ -z "${OS_PASSWORD}" ]; then
declare -x "OS_USERNAME=${!key}"
declare -x "OS_PASSWORD=${!secret}"
elif [ -z "${OS_AUTH_TOKEN}" ]; then
declare -x "OS_AUTH_TOKEN=${!secret}"
fi
if [ -z "${OVH_APPLICATION_KEY}" ]; then
declare -x "OVH_APPLICATION_KEY=${!key}"
declare -x "OVH_APPLICATION_SECRET=${!secret}"
declare -x "OVH_CONSUMER_KEY=${!token}"
fi
;;
esac

case $oneProvider in
aws)
if [ -z "${AWS_ACCESS_KEY_ID}" ]; then
declare -x "AWS_ACCESS_KEY_ID=${!key}"
declare -x "AWS_SECRET_ACCESS_KEY=${!secret}"

if [ -n "${!token}" ]; then
declare -x "AWS_SESSION_TOKEN=${!token}"
fi
fi
;;
azurerm)
if [ -z "${ARM_CLIENT_ID}" ]; then
declare -x "ARM_CLIENT_ID=${!key}"
declare -x "ARM_CLIENT_SECRET=${!secret}"
fi
;;
"do")
if [ -z "${DIGITALOCEAN_TOKEN}" ]; then
declare -x "DIGITALOCEAN_TOKEN=${!secret}"
fi
;;
google)
if [ -z "${GOOGLE_CREDENTIALS}" ]; then
declare -x "GOOGLE_CREDENTIALS=${!secret}"
fi
;;
hetzner)
if [ -z "${HCLOUD_TOKEN}" ]; then
declare -x "HCLOUD_TOKEN=${!token}"
fi
;;
scaleway)
if [ -z "${SCW_ACCESS_KEY}" ]; then
declare -x "SCW_ACCESS_KEY=${!key}"
declare -x "SCW_SECRET_KEY=${!secret}"
declare -x "SCW_DEFAULT_ORGANIZATION_ID=${!token}"
fi
# This is a hack to be able to use S3 tf backend on a scaleway object storage
# and because terraform doesn't allow interpolation in backend config:
# https://github.com/hashicorp/terraform/issues/13022
if [ -z "${AWS_ACCESS_KEY_ID}" ]; then
declare -x "AWS_ACCESS_KEY_ID=${!key}"
declare -x "AWS_SECRET_ACCESS_KEY=${!secret}"
fi
;;
ovh)
if [ -z "${OS_PASSWORD}" ]; then
declare -x "OS_USERNAME=${!key}"
declare -x "OS_PASSWORD=${!secret}"
elif [ -z "${OS_AUTH_TOKEN}" ]; then
declare -x "OS_AUTH_TOKEN=${!secret}"
fi
if [ -z "${OVH_APPLICATION_KEY}" ]; then
declare -x "OVH_APPLICATION_KEY=${!key}"
declare -x "OVH_APPLICATION_SECRET=${!secret}"
declare -x "OVH_CONSUMER_KEY=${!token}"
fi
;;
esac

done
done <<< "${provider}"

if [ -n "$debug" ]; then
declare -x "TF_LOG=$debug"
Expand Down

0 comments on commit 1a0b110

Please sign in to comment.