Skip to content

Commit f5c3d29

Browse files
authored
AWS Secret Manager env vars pull and merge (#48)
* Initial commit. Missing order definition. * Made tf file not fail if empty value passed * Added option to define Repo and AWS env files * Updated README
1 parent b2e204c commit f5c3d29

File tree

8 files changed

+145
-16
lines changed

8 files changed

+145
-16
lines changed

README.md

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,52 @@ The action will copy this repo to the VM and then run `docker-compose up`.
88
Your app needs a `Dockerfile` and a `docker-compose.yaml` file.
99

1010
> For more details on setting up Docker and Docker Compose, check out Bitovi's Academy Course: [Learn Docker](https://www.bitovi.com/academy/learn-docker.html)
11+
>
12+
## Environment variables
13+
14+
For envirnoment variables in your app, you can provide a `repo_env` file in your repo, a `.env` file in GitHub Secrets named `DOT_ENV`, or an AWS Secret. Then hook it up in your `docker-compose.yaml` file like:
1115

12-
For envirnoment variables in your app, provide a `.env` file in GitHub Secrets named `DOT_ENV` and hook it up in your `docker-compose.yaml` file like:
1316
```
1417
version: '3.9'
1518
services:
1619
app:
1720
env_file: .env
1821
```
1922

23+
These environment variables are merged to the .env file quoted in the following order:
24+
- Terraform passed env vars ( This is not optional nor customizable )
25+
- Repository checked-in env vars - repo_env file as default. (KEY=VALUE style)
26+
- Github Secret - Create a secret named DOT_ENV - (KEY=VALUE style)
27+
- AWS Secret - JSON style like '{"key":"value"}'
28+
2029
## Example usage
2130

2231
Create `.github/workflow/deploy.yaml` with the following to build on push.
2332

33+
### Basic example
34+
```yaml
35+
name: Basic deploy
36+
on:
37+
push:
38+
branches: [ main ]
39+
40+
jobs:
41+
EC2-Deploy:
42+
runs-on: ubuntu-latest
43+
steps:
44+
- id: deploy
45+
uses: bitovi/[email protected]
46+
with:
47+
aws_access_key_id: ${{ secrets.AWS_ACCESS_KEY_ID_PTO}}
48+
aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY_PTO}}
49+
aws_default_region: us-east-1
50+
dot_env: ${{ secrets.DOT_ENV }}
51+
```
52+
53+
### Advanced example
54+
2455
```yaml
56+
name: Advanced deploy
2557
on:
2658
push:
2759
branches: [ main ]
@@ -79,15 +111,17 @@ The following inputs can be used as `step.with` keys
79111
| `domain_name` | String | Define the root domain name for the application. e.g. bitovi.com' |
80112
| `sub_domain` | String | Define the sub-domain part of the URL. Defaults to `${org}-${repo}-{branch}` |
81113
| `tf_state_bucket` | String | AWS S3 bucket to use for Terraform state. Will be deleted if stack_destroy set to true |
82-
| `dot_env` | String | `.env` file to be used with the app |
114+
| `repo_env` | String | `.env` file containing environment variables to be used with the app. Name defaults to `repo_env`. Check **SEnvironment variables** note |
115+
| `dot_env` | String | `.env` file to be used with the app. This is the name of the [Github secret](https://docs.github.com/es/actions/security-guides/encrypted-secrets). Check **SEnvironment variables** note |
116+
| `aws_secret_env` | String | Secret name to pull environment variables from AWS Secret Manager. Check **SEnvironment variables** note |
83117
| `app_port` | String | port to expose for the app |
84118
| `lb_port` | String | Load balancer listening port. Defaults to 80 if NO FQDN provided, 443 if FQDN provided |
85119
| `lb_healthcheck` | String | Load balancer health check string. Defaults to HTTP:app_port |
86120
| `ec2_instance_profile` | String | The AWS IAM instance profile to use for the EC2 instance. Default is `${GITHUB_ORG_NAME}-${GITHUB_REPO_NAME}-${GITHUB_BRANCH_NAME}` |
87121
| `ec2_instance_type` | String | The AWS IAM instance type to use. Default is t2.small. See [this list](https://aws.amazon.com/ec2/instance-types/) for reference |
88-
| `stack_destroy` | String | Set to `true` to destroy the stack. Default is `""` - Will delete the tf_state_bucket after destroy. |
122+
| `stack_destroy` | String | Set to `true` to destroy the stack. Default is `""` - Will delete the tf_state and elb_logs bucket after the destroy action runs. |
89123
| `aws_resource_identifier` | String | Set to override the AWS resource identifier for the deployment. Defaults to `${org}-{repo}-{branch}`. Use with destroy to destroy specific resources. |
90-
| `app_directory` | String | Relative path for the directory of the app (i.e. where `Dockerfile` and `docker-compose.yaml` files are located). This is the directory that is copied to the EC2 instance. Default is the root of the repo. |
124+
| `app_directory` | String | Relative path for the directory of the app (i.e. where `Dockerfile` and `docker-compose.yaml` files are located). This is the directory that is copied to the EC2 instance. Default is the root of the repo. |
91125
| `additional_tags` | JSON | Add additional tags to the terraform [default tags](https://www.hashicorp.com/blog/default-tags-in-the-terraform-aws-provider), any tags put here will be added to all provisioned resources.|
92126

93127
## Note about resource identifiers
@@ -101,7 +135,7 @@ For some specific resources, we have a 32 characters limit. If the identifier le
101135

102136
### S3 buckets naming
103137

104-
Buckets name can be made of up to 63 characters. If the length allows us to add -tf-state, we will do so. If not, a simple -tf will be added.
138+
Buckets names can be made of up to 63 characters. If the length allows us to add -tf-state, we will do so. If not, a simple -tf will be added.
105139

106140
## Made with BitOps
107141
[BitOps](https://bitops.sh) allows you to define Infrastructure-as-Code for multiple tools in a central place. This action uses a BitOps [Operations Repository](https://bitops.sh/operations-repo-structure/) to set up the necessary Terraform and Ansible to create infrastructure and deploy to it.

action.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,17 @@ inputs:
2424
tf_state_bucket:
2525
description: 'AWS S3 bucket to use for Terraform state. Defaults to `${org}-${repo}-{branch}-tf-state`. Will be deleted with stack_destroy.'
2626
required: false
27+
repo_env:
28+
description: 'File containing environment variables to be used with the app'
29+
required: false
30+
default: 'repo_env'
2731
dot_env:
2832
description: '`.env` file to be used with the app'
2933
required: false
34+
aws_secret_env:
35+
description: 'Secret name to pull env variables from AWS Secret Manager'
36+
required: false
37+
default: ''
3038
app_port:
3139
description: 'Port to expose for the app'
3240
required: false
@@ -81,7 +89,9 @@ runs:
8189
AWS_SESSION_TOKEN: ${{ inputs.aws_session_token }}
8290
AWS_DEFAULT_REGION: ${{ inputs.aws_default_region }}
8391
TF_STATE_BUCKET: ${{ inputs.tf_state_bucket }}
92+
REPO_ENV: ${{ inputs.repo_env }}
8493
DOT_ENV: ${{ inputs.dot_env }}
94+
AWS_SECRET_ENV: ${{ inputs.aws_secret_env }}
8595
APP_PORT: ${{ inputs.app_port }}
8696
LB_PORT: ${{ inputs.lb_port }}
8797
LB_HEALTHCHECK: ${{ inputs.lb_healthcheck }}

operations/_scripts/generate/generate_app_repo.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,7 @@ if [ -n "$APP_DIRECTORY" ]; then
1616
fi
1717

1818
cp -rf "$TARGET_PATH"/* "${GITHUB_ACTION_PATH}/operations/deployment/ansible/app/${GITHUB_REPO_NAME}/"
19+
20+
echo "Copying checked in env file from repo to Ansible deployment path"
21+
22+
cp "$TARGET_PATH/$REPO_ENV" "${GITHUB_ACTION_PATH}/operations/deployment/ansible/repo.env"

operations/_scripts/generate/generate_dot_env.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ set -e
55

66
echo "In generate_dot_env.sh"
77

8-
echo "$DOT_ENV" >> "${GITHUB_ACTION_PATH}/operations/deployment/ansible/app.env"
8+
echo "$DOT_ENV" >> "${GITHUB_ACTION_PATH}/operations/deployment/ansible/ghs.env"

operations/_scripts/generate/generate_tf_vars.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ aws_resource_identifier = \"${GITHUB_IDENTIFIER}\"
6767
6868
aws_resource_identifier_supershort = \"${GITHUB_IDENTIFIER_SS}\"
6969
70+
aws_secret_env = \"${AWS_SECRET_ENV}\"
71+
7072
sub_domain_name = \"${SUB_DOMAIN}\"
7173
7274
domain_name = \"${DOMAIN_NAME}\"
Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,76 @@
11
#!/bin/bash
2+
3+
set -e
4+
5+
26
echo "BitOps Ansible before script: Merge Terraform Enviornment Variables..."
37

4-
# dotenv file
8+
# Merging order
9+
order=tf,repo,ghs,aws
10+
11+
# Ansible dotenv file -> The final destination of all
512
DOTENV_FILE="${BITOPS_ENVROOT}/ansible/app.env"
613

7-
# tf dotenv file
14+
# TF dotenv file
815
TF_DOTENV_FILE="${BITOPS_ENVROOT}/terraform/tf.env"
916

10-
echo "cat DOTENV_FILE ($DOTENV_FILE)"
11-
cat $DOTENV_FILE
17+
# Repo env file
18+
REPO_ENV_FILE="${BITOPS_ENVROOT}/ansible/repo.env"
19+
20+
# GH Secrets env file
21+
GHS_ENV_FILE="${BITOPS_ENVROOT}/ansible/ghs.env"
22+
23+
# TF AWS dotenv file
24+
AWS_SECRET_FILE="${BITOPS_ENVROOT}/terraform/aws.env"
25+
26+
# Make sure app.env is empty, if not, delete it and create one.
27+
28+
if [ -f $DOTENV_FILE ]; then
29+
rm -rf $DOTENV_FILE
30+
fi
31+
touch $DOTENV_FILE
32+
33+
# Function to merge to destination
1234

35+
function merge {
36+
if [ -s $1 ]; then
37+
echo "Merging $2 envs"
38+
cat $1 >> $DOTENV_FILE
39+
else
40+
echo "Nothing to merge from $2"
41+
fi
42+
}
1343

14-
TEMP_DOTENV_FILE="${BITOPS_ENVROOT}/ansible/tmp.env"
44+
# Function to be called based on the input string
45+
function process {
46+
case $1 in
47+
aws)
48+
# Code to be executed for option1
49+
merge $AWS_SECRET_FILE "AWS Secret"
50+
;;
51+
repo)
52+
# Code to be executed for option2
53+
merge $REPO_ENV_FILE "checked-in"
54+
;;
55+
ghs)
56+
# Code to be executed for option3
57+
merge $GHS_ENV_FILE "GH-Secret"
58+
;;
59+
tf)
60+
# Code to be executed for option3
61+
merge $TF_DOTENV_FILE "Terraform"
62+
;;
63+
*)
64+
# Code to be executed if no matching option is found
65+
echo "Invalid option"
66+
;;
67+
esac
68+
}
1569

16-
cat $TF_DOTENV_FILE >> $TEMP_DOTENV_FILE
17-
cat $DOTENV_FILE >> $TEMP_DOTENV_FILE
18-
cat $TEMP_DOTENV_FILE > $DOTENV_FILE
70+
# Read the input string and split it into an array
71+
IFS=',' read -r -a options <<< "$order"
1972

20-
echo "cat TEMP_DOTENV_FILE ($TEMP_DOTENV_FILE)"
21-
cat $TEMP_DOTENV_FILE
73+
# Loop through the array and call the process function for each element
74+
for option in "${options[@]}"; do
75+
process "$option"
76+
done
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
data "aws_secretsmanager_secret_version" "env_secret" {
2+
count = local.secret_provided ? 1 : 0
3+
secret_id = var.aws_secret_env
4+
}
5+
6+
locals {
7+
s3_secret_raw = local.secret_provided ? nonsensitive(jsondecode(data.aws_secretsmanager_secret_version.env_secret[0].secret_string)) : {}
8+
s3_secret_string = local.secret_provided ? join("\n", [for k, v in local.s3_secret_raw : "${k}=\"${v}\""]) : ""
9+
}
10+
11+
resource "local_file" "tf-secretdotenv" {
12+
count = local.secret_provided ? 1 : 0
13+
filename = format("%s/%s", abspath(path.root), "aws.env")
14+
content = local.secret_provided ? "${local.s3_secret_string}\n" : ""
15+
}
16+
17+
locals {
18+
secret_provided = ( var.aws_secret_env != "" ? true : false )
19+
}

operations/deployment/terraform/variables.tf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,11 @@ variable "aws_resource_identifier_supershort" {
7878
description = "Identifier to use for AWS resources (defaults to GITHUB_ORG-GITHUB_REPO-GITHUB_BRANCH) shortened to 30 chars"
7979
}
8080

81+
variable "aws_secret_env" {
82+
type = string
83+
description = "Secret name to pull env variables from AWS Secret Manager"
84+
}
85+
8186
variable "sub_domain_name" {
8287
type = string
8388
description = "Subdomain name for DNS record"

0 commit comments

Comments
 (0)