diff --git a/.gitignore b/.gitignore
index 3aa5e24..e3b4830 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,11 +7,12 @@ bin
# terraform
**/.terraform/*
+*.auto.tfvars
+*.terraform.lock.hcl
*.tfstate
*.tfstate.*
-*.auto.tfvars
+*_override.tf
+*_override.tf.json
crash.log
override.tf
override.tf.json
-*_override.tf
-*_override.tf.json
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..31dd5e5
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,10 @@
+repos:
+ - repo: git://github.com/antonbabenko/pre-commit-terraform
+ rev: v1.47.0
+ hooks:
+ - id: terraform_docs
+ - id: terraform_fmt
+ - repo: git://github.com/pre-commit/pre-commit-hooks
+ rev: v3.4.0
+ hooks:
+ - id: check-merge-conflict
diff --git a/README.md b/README.md
index a9ae516..3707128 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-### Moot - A Serverless Release Dashboard
+# Moot - A Serverless Release Dashboard
AWS Serverless solution deployed with Terraform which implements a single-page-application dashboard. This dashboard creates releases that are intended to trigger continuous integration (CI) production deploy pipelines. All that is needed to kick off a release is a version number.
@@ -17,13 +17,26 @@ This solution utilises the following services:
- S3 + Cloudfront (frontend)
- SSM Parameter Store (secrets management)
-#### Installation
+## Requirements
+
+The following tools must be installed in order to fully deploy Moot
+
+- [yarn](https://yarnpkg.com/getting-started/install) -- used to build the frontend locally
+- [awscli](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) -- used to aws s3 sync the frontend assets
+- [go](https://golang.org/doc/install#download) -- requires at least version 16 because of `go modules`
+
+## Installation
+
+The below snippet will fully deploy the dashboard (backend + frontend). In this instance, I am deploying to a cheap Route53 domain I purchased for testing purposes (`moot.link`).
+
+If your AWS account does not have a Route53 hosted zone, remove the `hosted_zone_name` and `fqdn_alias` lines to use Cloudfront's default certificate and dns.
```hcl
module "moot" {
source = "github.com/seanturner026/moot.git"
name = "moot"
+ aws_profile = "default"
admin_user_email = var.admin_user_email
enable_delete_admin_user = false
github_token = var.github_token
@@ -36,24 +49,30 @@ module "moot" {
}
```
-#### Workflows
+## Workflows
- Standard Deploy: Merges the HEAD branch into the BASE (e.g. main) branch, creates release based on BASE branch
- Hotfix Deploy: Creates release based on the BASE branch
-
-#### Repositories View
+## Repositories View
![alt text](https://github.com/seanturner026/moot/blob/main/assets/repositories.png?raw=true)
-#### Add Repository View
+## Add Repository View
![alt text](https://github.com/seanturner026/moot/blob/main/assets/repositories-add.png?raw=true)
-#### Users View
+## Users View
![alt text](https://github.com/seanturner026/moot/blob/main/assets/users.png?raw=true)
-## Terraform Providers
+## Terraform Information
+
+
+## Requirements
+
+No requirements.
+
+## Providers
| Name | Version |
|------|---------|
@@ -62,23 +81,26 @@ module "moot" {
| external | n/a |
| null | n/a |
-## Terraform Inputs
+## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| admin\_user\_email | Controls the creation of an admin user that is required to initially gain access to the
dashboard.
If access to the dashboard is completely lost, do the following
• `var.enable_delete_admin_user = true`
• `terraform apply`
• `var.enable_delete_admin_user = false`
• `terraform apply`
If the initial admin user should no longer be able to access the dashboard, revoke access by
setting `var.enable_delete_admin_user = true` and running `terraform apply` | `string` | `""` | no |
+| aws\_profile | AWS Profile Name from `~/.aws/config that can be used for local execution. This profile is used
to preform the following actions:
• `aws s3 sync`: Sync bundle produced by `yarn` to build to s3
• `cognito-idp admin-create-user`: Creates an admin cognito user for dashboard access
• `cognito-idp admin-delete-user`: Deletes an admin cognito user if the user should not
have access to the dashboard anymore, OR, if there is no way for the user to regain access.
• `cognito-idp list-users`: Obtains the admin user's ID in order to write the ID to the
DynamodDB table.
` | `string` | `""` | no |
| enable\_api\_gateway\_access\_logs | Enables API Gateway access logging to cloudwatch for the default stage. | `bool` | `false` | no |
| enable\_delete\_admin\_user | Destroys the admin user.
Set this value to true to destroy the user, and to false to recreate the user. | `bool` | `false` | no |
| fqdn\_alias | ALIAS for the Cloudfront distribution, S3, Cognito and API Gateway. Must be in the form of
`example.com`. | `string` | `""` | no |
-| github\_token | Token for Github. | `string` | `""` | no |
-| gitlab\_token | Token for Gitlab. | `string` | `""` | no |
+| github\_token | Token for Github. | `string` | `"42"` | no |
+| gitlab\_token | Token for Gitlab. | `string` | `"42"` | no |
| hosted\_zone\_name | Name of AWS Route53 Hosted Zone for DNS. | `string` | `""` | no |
| name | Name to be applied to all resources. | `string` | `"release_dashboard"` | no |
-| slack\_webhook\_url | URL to send slack message payloads to. | `string` | `""` | no |
+| slack\_webhook\_url | URL to send slack message payloads to. | `string` | `"42"` | no |
| tags | Map of tags to be applied to resources. | `map(string)` | `{}` | no |
-## Terraform Outputs
+## Outputs
| Name | Description |
|------|-------------|
| cloudfront\_domain\_name | FQDN of Cloudfront Distribution that can be used for DNS. |
+
+
diff --git a/locals.tf b/locals.tf
index b0b16b7..a680dd6 100644
--- a/locals.tf
+++ b/locals.tf
@@ -1,4 +1,11 @@
locals {
+
+ aws_profile = var.aws_profile != "" ? var.aws_profile : "default"
+ frontend_module_comprehension = [for module in jsondecode(file("${path.root}/.terraform/modules/modules.json"))["Modules"] : module if length(regexall("vuejs_frontend", module.Key)) > 0][0]
+ frontend_module_path = "${path.root}/${local.frontend_module_comprehension.Dir}"
+ main_module_name = split(".terraform/modules/", path.module)[1]
+ main_module_path = "./.terraform/modules/${local.main_module_name}"
+
ssm_parameters = {
client_pool_secret = {
description = "Cognito User Pool client secret."
@@ -22,11 +29,6 @@ locals {
lambda_binary_exists = { for key, _ in local.lambdas : key => fileexists("${path.module}/bin/${key}") }
}
- frontend_module_comprehension = [for module in jsondecode(file("${path.root}/.terraform/modules/modules.json"))["Modules"] : module if length(regexall("vuejs_frontend", module.Key)) > 0][0]
- frontend_module_path = "${path.root}/${local.frontend_module_comprehension.Dir}"
- main_module_path = "./.terraform/modules/${local.main_module_name}"
- main_module_name = split(".terraform/modules/", path.module)[1]
-
lambdas = {
auth = {
description = "Administrates user login, token refreshes, and password resets."
diff --git a/r_cognito.tf b/r_cognito.tf
index 15c6651..6a6d654 100644
--- a/r_cognito.tf
+++ b/r_cognito.tf
@@ -63,19 +63,3 @@ resource "aws_cognito_identity_pool" "this" {
tags = var.tags
}
-
-resource "null_resource" "create_admin_user" {
- count = var.admin_user_email != "" && !var.enable_delete_admin_user ? 1 : 0
-
- provisioner "local-exec" {
- command = "aws --region ${data.aws_region.current.name} cognito-idp admin-create-user --user-pool-id ${aws_cognito_user_pool.this.id} --username ${var.admin_user_email} --user-attributes Name=email,Value=${var.admin_user_email}"
- }
-}
-
-resource "null_resource" "delete_admin_user" {
- count = var.admin_user_email != "" && var.enable_delete_admin_user ? 1 : 0
-
- provisioner "local-exec" {
- command = "aws --region ${data.aws_region.current.name} cognito-idp admin-delete-user --user-pool-id ${aws_cognito_user_pool.this.id} --username ${var.admin_user_email}"
- }
-}
diff --git a/r_lambda.tf b/r_lambda.tf
index 32db45d..dc423eb 100644
--- a/r_lambda.tf
+++ b/r_lambda.tf
@@ -1,62 +1,3 @@
-resource "null_resource" "go_setup" {
-
- triggers = {
- hash_go_mod = filemd5("${local.main_module_path}/go.mod")
- hash_go_sum = filemd5("${local.main_module_path}/go.sum")
- }
-
- provisioner "local-exec" {
- command = "cp -f ${local.main_module_path}/go.mod ."
- }
-
- provisioner "local-exec" {
- command = "cp -f ${local.main_module_path}/go.sum ."
- }
-}
-
-resource "null_resource" "lambda_build" {
- for_each = local.lambdas
- depends_on = [null_resource.go_setup]
-
- triggers = {
- binary_exists = local.null.lambda_binary_exists[each.key]
-
- hash_main = join("", [
- for file in fileset("${path.module}/cmd/${each.key}", "*.go") : filemd5("${path.module}/cmd/${each.key}/${file}")
- ])
-
- hash_util = join("", [
- for file in fileset("${path.module}/internal/util", "*.go") : filemd5("${path.module}/internal/util/${file}")
- ])
- }
-
- provisioner "local-exec" {
- command = "export GO111MODULE=on"
- }
-
- provisioner "local-exec" {
- command = "cd ${local.main_module_path} && GOOS=linux go build -ldflags '-s -w' -o ./bin/${each.key} ./cmd/${each.key}/."
- }
-}
-
-resource "null_resource" "lambda_test" {
- for_each = local.lambdas
-
- triggers = {
- hash_main = join("", [
- for file in fileset("${path.module}/cmd/${each.key}", "*.go") : filemd5("${path.module}/cmd/${each.key}/${file}")
- ])
-
- hash_util = join("", [
- for file in fileset("${path.module}/internal/util", "*.go") : filemd5("${path.module}/internal/util/${file}")
- ])
- }
-
- provisioner "local-exec" {
- command = "cd ${local.main_module_path} && go test ./cmd/${each.key}"
- }
-}
-
resource "aws_lambda_function" "this" {
depends_on = [null_resource.lambda_build, null_resource.lambda_test]
for_each = local.lambdas
diff --git a/r_null.tf b/r_null.tf
index 5e2fd28..abe3876 100644
--- a/r_null.tf
+++ b/r_null.tf
@@ -5,18 +5,102 @@ resource "null_resource" "build_frontend" {
}
provisioner "local-exec" {
- command = "cd ${local.frontend_module_path} && yarn install"
+ interpreter = ["/bin/bash", "-c"]
+ command = "cd ${local.frontend_module_path} && yarn install"
}
provisioner "local-exec" {
- command = "cd ${local.frontend_module_path} && echo \"VUE_APP_API_GATEWAY_ENDPOINT=${aws_apigatewayv2_api.this.api_endpoint}\" > .env"
+ interpreter = ["/bin/bash", "-c"]
+ command = "cd ${local.frontend_module_path} && echo \"VUE_APP_API_GATEWAY_ENDPOINT=${aws_apigatewayv2_api.this.api_endpoint}\" > .env"
}
provisioner "local-exec" {
- command = "cd ${local.frontend_module_path} && yarn build"
+ interpreter = ["/bin/bash", "-c"]
+ command = "cd ${local.frontend_module_path} && yarn build"
}
provisioner "local-exec" {
- command = "cd ${local.frontend_module_path} && aws s3 sync --cache-control 'max-age=604800' dist/ s3://${aws_s3_bucket.this.id}"
+ interpreter = ["/bin/bash", "-c"]
+ command = "cd ${local.frontend_module_path} && AWS_DEFAULT_PROFILE=${local.aws_profile} aws s3 sync --cache-control 'max-age=604800' dist/ s3://${aws_s3_bucket.this.id}"
+ }
+}
+
+resource "null_resource" "go_setup" {
+
+ triggers = {
+ hash_go_mod = filemd5("${local.main_module_path}/go.mod")
+ hash_go_sum = filemd5("${local.main_module_path}/go.sum")
+ }
+
+ provisioner "local-exec" {
+ interpreter = ["/bin/bash", "-c"]
+ command = "cp -f ${local.main_module_path}/go.mod ."
+ }
+
+ provisioner "local-exec" {
+ interpreter = ["/bin/bash", "-c"]
+ command = "cp -f ${local.main_module_path}/go.sum ."
+ }
+}
+
+resource "null_resource" "lambda_build" {
+ for_each = local.lambdas
+ depends_on = [null_resource.go_setup]
+
+ triggers = {
+ binary_exists = local.null.lambda_binary_exists[each.key]
+
+ hash_main = join("", [
+ for file in fileset("${path.module}/cmd/${each.key}", "*.go") : filemd5("${path.module}/cmd/${each.key}/${file}")
+ ])
+
+ hash_util = join("", [
+ for file in fileset("${path.module}/internal/util", "*.go") : filemd5("${path.module}/internal/util/${file}")
+ ])
+ }
+
+ provisioner "local-exec" {
+ interpreter = ["/bin/bash", "-c"]
+ command = "export GO111MODULE=on"
+ }
+
+ provisioner "local-exec" {
+ interpreter = ["/bin/bash", "-c"]
+ command = "cd ${local.main_module_path} && GOOS=linux go build -ldflags '-s -w' -o ./bin/${each.key} ./cmd/${each.key}/."
+ }
+}
+
+resource "null_resource" "lambda_test" {
+ for_each = local.lambdas
+
+ triggers = {
+ hash_main = join("", [
+ for file in fileset("${path.module}/cmd/${each.key}", "*.go") : filemd5("${path.module}/cmd/${each.key}/${file}")
+ ])
+
+ hash_util = join("", [
+ for file in fileset("${path.module}/internal/util", "*.go") : filemd5("${path.module}/internal/util/${file}")
+ ])
+ }
+
+ provisioner "local-exec" {
+ interpreter = ["/bin/bash", "-c"]
+ command = "cd ${local.main_module_path} && go test ./cmd/${each.key}"
+ }
+}
+
+resource "null_resource" "create_admin_user" {
+ count = var.admin_user_email != "" && !var.enable_delete_admin_user ? 1 : 0
+
+ provisioner "local-exec" {
+ command = "aws --region ${data.aws_region.current.name} cognito-idp admin-create-user --user-pool-id ${aws_cognito_user_pool.this.id} --username ${var.admin_user_email} --user-attributes Name=email,Value=${var.admin_user_email}"
+ }
+}
+
+resource "null_resource" "delete_admin_user" {
+ count = var.admin_user_email != "" && var.enable_delete_admin_user ? 1 : 0
+
+ provisioner "local-exec" {
+ command = "aws --region ${data.aws_region.current.name} cognito-idp admin-delete-user --user-pool-id ${aws_cognito_user_pool.this.id} --username ${var.admin_user_email}"
}
}
diff --git a/terraform_examples/cloudfront_dns/.terraform.lock.hcl b/terraform_examples/cloudfront_dns/.terraform.lock.hcl
deleted file mode 100644
index 80c6ea3..0000000
--- a/terraform_examples/cloudfront_dns/.terraform.lock.hcl
+++ /dev/null
@@ -1,75 +0,0 @@
-# This file is maintained automatically by "terraform init".
-# Manual edits may be lost in future updates.
-
-provider "registry.terraform.io/hashicorp/archive" {
- version = "2.2.0"
- hashes = [
- "h1:2K5LQkuWRS2YN1/YoNaHn9MAzjuTX8Gaqy6i8Mbfv8Y=",
- "zh:06bd875932288f235c16e2237142b493c2c2b6aba0e82e8c85068332a8d2a29e",
- "zh:0c681b481372afcaefddacc7ccdf1d3bb3a0c0d4678a526bc8b02d0c331479bc",
- "zh:100fc5b3fc01ea463533d7bbfb01cb7113947a969a4ec12e27f5b2be49884d6c",
- "zh:55c0d7ddddbd0a46d57c51fcfa9b91f14eed081a45101dbfc7fd9d2278aa1403",
- "zh:73a5dd68379119167934c48afa1101b09abad2deb436cd5c446733e705869d6b",
- "zh:841fc4ac6dc3479981330974d44ad2341deada8a5ff9e3b1b4510702dfbdbed9",
- "zh:91be62c9b41edb137f7f835491183628d484e9d6efa82fcb75cfa538c92791c5",
- "zh:acd5f442bd88d67eb948b18dc2ed421c6c3faee62d3a12200e442bfff0aa7d8b",
- "zh:ad5720da5524641ad718a565694821be5f61f68f1c3c5d2cfa24426b8e774bef",
- "zh:e63f12ea938520b3f83634fc29da28d92eed5cfbc5cc8ca08281a6a9c36cca65",
- "zh:f6542918faa115df46474a36aabb4c3899650bea036b5f8a5e296be6f8f25767",
- ]
-}
-
-provider "registry.terraform.io/hashicorp/aws" {
- version = "3.42.0"
- constraints = ">= 3.37.0"
- hashes = [
- "h1:C6/yDp6BhuDFx0qdkBuJj/OWUJpAoraHTJaU6ac38Rw=",
- "zh:126c856a6eedddd8571f161a826a407ba5655a37a6241393560a96b8c4beca1a",
- "zh:1a4868e6ac734b5fc2e79a4a889d176286b66664aad709435aa6acee5871d5b0",
- "zh:40fed7637ab8ddeb93bef06aded35d970f0628025b97459ae805463e8aa0a58a",
- "zh:68def3c0a5a1aac1db6372c51daef858b707f03052626d3427ac24cba6f2014d",
- "zh:6db7ec9c8d1803a0b6f40a664aa892e0f8894562de83061fa7ac1bc51ff5e7e5",
- "zh:7058abaad595930b3f97dc04e45c112b2dbf37d098372a849081f7081da2fb52",
- "zh:8c25adb15a19da301c478aa1f4a4d8647cabdf8e5dae8331d4490f80ea718c26",
- "zh:8e129b847401e39fcbc54817726dab877f36b7f00ff5ed76f7b43470abe99ff9",
- "zh:d268bb267a2d6b39df7ddee8efa7c1ef7a15cf335dfa5f2e64c9dae9b623a1b8",
- "zh:d6eeb3614a0ab50f8e9ab5666ae5754ea668ce327310e5b21b7f04a18d7611a8",
- "zh:f5d3c58055dff6e38562b75d3edc908cb2f1e45c6914f6b00f4773359ce49324",
- ]
-}
-
-provider "registry.terraform.io/hashicorp/external" {
- version = "2.1.0"
- hashes = [
- "h1:LTl5CGW8wiIEe16AC4MtXN/95xWWNDbap70zJsBTk0w=",
- "zh:0d83ffb72fbd08986378204a7373d8c43b127049096eaf2765bfdd6b00ad9853",
- "zh:7577d6edc67b1e8c2cf62fe6501192df1231d74125d90e51d570d586d95269c5",
- "zh:9c669ded5d5affa4b2544952c4b6588dfed55260147d24ced02dca3a2829f328",
- "zh:a404d46f2831f90633947ab5d57e19dbfe35b3704104ba6ec80bcf50b058acfd",
- "zh:ae1caea1c936d459ceadf287bb5c5bd67b5e2a7819df6f5c4114b7305df7f822",
- "zh:afb4f805477694a4b9dde86b268d2c0821711c8aab1c6088f5f992228c4c06fb",
- "zh:b993b4a1de8a462643e78f4786789e44ce5064b332fee1cb0d6250ed085561b8",
- "zh:c84b2c13fa3ea2c0aa7291243006d560ce480a5591294b9001ce3742fc9c5791",
- "zh:c8966f69b7eccccb771704fd5335923692eccc9e0e90cb95d14538fe2e92a3b8",
- "zh:d5fe68850d449b811e633a300b114d0617df6d450305e8251643b4d143dc855b",
- "zh:ddebfd1e674ba336df09b1f27bbaa0e036c25b7a7087dc8081443f6e5954028b",
- ]
-}
-
-provider "registry.terraform.io/hashicorp/null" {
- version = "3.1.0"
- hashes = [
- "h1:xhbHC6in3nQryvTQBWKxebi3inG5OCgHgc4fRxL0ymc=",
- "zh:02a1675fd8de126a00460942aaae242e65ca3380b5bb192e8773ef3da9073fd2",
- "zh:53e30545ff8926a8e30ad30648991ca8b93b6fa496272cd23b26763c8ee84515",
- "zh:5f9200bf708913621d0f6514179d89700e9aa3097c77dac730e8ba6e5901d521",
- "zh:9ebf4d9704faba06b3ec7242c773c0fbfe12d62db7d00356d4f55385fc69bfb2",
- "zh:a6576c81adc70326e4e1c999c04ad9ca37113a6e925aefab4765e5a5198efa7e",
- "zh:a8a42d13346347aff6c63a37cda9b2c6aa5cc384a55b2fe6d6adfa390e609c53",
- "zh:c797744d08a5307d50210e0454f91ca4d1c7621c68740441cf4579390452321d",
- "zh:cecb6a304046df34c11229f20a80b24b1603960b794d68361a67c5efe58e62b8",
- "zh:e1371aa1e502000d9974cfaff5be4cfa02f47b17400005a16f14d2ef30dc2a70",
- "zh:fc39cc1fe71234a0b0369d5c5c7f876c71b956d23d7d6f518289737a001ba69b",
- "zh:fea4227271ebf7d9e2b61b89ce2328c7262acd9fd190e1fd6d15a591abfa848e",
- ]
-}
diff --git a/terraform_examples/cloudfront_dns/main.tf b/terraform_examples/cloudfront_dns/main.tf
index a16e0f1..ee7bf1b 100644
--- a/terraform_examples/cloudfront_dns/main.tf
+++ b/terraform_examples/cloudfront_dns/main.tf
@@ -2,6 +2,7 @@ module "moot" {
source = "github.com/seanturner026/moot.git"
name = "moot"
+ aws_profile = "default"
admin_user_email = var.admin_user_email
enable_delete_admin_user = false
github_token = var.github_token
diff --git a/terraform_examples/complete/main.tf b/terraform_examples/complete/main.tf
index 3913786..419dd75 100644
--- a/terraform_examples/complete/main.tf
+++ b/terraform_examples/complete/main.tf
@@ -2,6 +2,7 @@ module "moot" {
source = "github.com/seanturner026/moot.git"
name = "moot"
+ aws_profile = "default"
admin_user_email = var.admin_user_email
enable_delete_admin_user = false
github_token = var.github_token
diff --git a/variables.tf b/variables.tf
index 87facc6..e2eb4bd 100644
--- a/variables.tf
+++ b/variables.tf
@@ -10,6 +10,22 @@ variable "tags" {
default = {}
}
+variable "aws_profile" {
+ type = string
+ description = <<-DESC
+ AWS Profile Name from `~/.aws/config that can be used for local execution. This profile is used
+ to preform the following actions:
+
+ • `aws s3 sync`: Sync bundle produced by `yarn` to build to s3
+ • `cognito-idp admin-create-user`: Creates an admin cognito user for dashboard access
+ • `cognito-idp admin-delete-user`: Deletes an admin cognito user if the user should not
+ have access to the dashboard anymore, OR, if there is no way for the user to regain access.
+ • `cognito-idp list-users`: Obtains the admin user's ID in order to write the ID to the
+ DynamodDB table.
+ DESC
+ default = ""
+}
+
variable "admin_user_email" {
type = string
description = <<-DESC