From cc6ec37d9df665043231e3165b5ea61d5708b64c Mon Sep 17 00:00:00 2001 From: Lawrence McDaniel Date: Fri, 17 Mar 2023 14:06:51 -0600 Subject: [PATCH] Mcdaniel meta data (#49) * add module to gather environment state data of current user * add cookiecutter meta tags * force recreation of meta resource on each apply * persist current version to a file that Terraform can read and add to its state * add cookiecutter meta tags * add cookiecutter meta tags * add cookiecutter meta tags * de-abstract tags * testing * change the source of the taint. switch template_file to local_file * refactor all elements into a single null_resourc that is tainted by the last commit * add local_file resources now depent on null_resource 'environment' * lint * add meta tags * documentation * prototype a kubernetes stack module * testing * add tags to iam policy * revert to nutmeg.3 and set TUTOR_OPENEDX_COMMON_VERSION in build workflow * revert to nutmeg.2 with tutor 14.2.4 * add more settings defaults * add more template tags * add meta, module and resource tags * add meta, module and resource tags * terraform fmt -recursive * change cookiecutter meta secrets names * persist config dump to k8s secrets * ci_openedx_actions_tutor_print_dump=v1.0.2 * ci_openedx_actions_tutor_print_dump=v1.0.3 * refactor provider declarations into separate modules * rename module * rename module * documentation * documentation * add eduNEXT proxy service for scorm backends * DRY the aws s3 bucket names * ci_openedx_actions_tutor_print_dump=v1.0.4 * rename module from s3_openedx_storage to s3 * remove duplicate variable declaration * add release notes * lint --- CHANGELOG.md | 12 +- README.rst | 2 +- cookiecutter.json | 42 +-- .../.github/workflows/build-openedx.yml | 8 + ...ploy-{{cookiecutter.environment_name}}.yml | 5 +- {{cookiecutter.github_repo_name}}/VERSION | 1 + .../settings_merge.yml | 86 ++++-- .../doc/DATA_BACKUP.md | 2 +- .../doc/wordfence-firewall-screenshot.png | Bin 0 -> 279763 bytes .../common/cookiecutter_meta/README.md | 30 +++ .../common/cookiecutter_meta/main.tf | 245 ++++++++++++++++++ .../common/cookiecutter_meta/output.tf | 20 ++ .../output/cookiecutter_awscli_version.state | 1 + .../output/cookiecutter_github_branch.state | 1 + .../output/cookiecutter_github_commit.state | 1 + .../cookiecutter_github_commit_date.state | 1 + .../cookiecutter_github_repository.state | 1 + .../output/cookiecutter_global_iam_arn.state | 1 + .../output/cookiecutter_kubectl_version.state | 1 + .../output/cookiecutter_os.state | 1 + .../cookiecutter_terraform_version.state | 1 + .../output/cookiecutter_timestamp.state | 1 + .../output/cookiecutter_version.state | 1 + .../common/cookiecutter_meta/versions.tf | 22 ++ .../terraform/environments/README.md | 20 +- .../environments/modules/acm/README.md | 7 + .../environments/modules/acm/main.tf | 37 +-- .../environments/modules/acm/providers.tf | 4 + .../environments/modules/acm/versions.tf | 2 +- .../environments/modules/cloudfront/README.md | 15 ++ .../modules/cloudfront/certificate_manager.tf | 19 +- .../environments/modules/cloudfront/main.tf | 29 ++- .../modules/cloudfront/providers.tf | 4 + .../modules/cloudfront/versions.tf | 2 +- .../environments/modules/kubernetes/README.md | 33 +++ .../environments/modules/kubernetes/main.tf | 49 ++-- .../modules/kubernetes/providers.tf | 19 ++ .../modules/kubernetes/variables.tf | 6 + .../modules/kubernetes/versions.tf | 2 +- .../modules/kubernetes_ingress_clb/README.md | 13 +- .../kubernetes_ingress_clb/kubernetes.tf | 21 ++ .../modules/kubernetes_ingress_clb/main.tf | 49 +--- .../manifests/ingress.yml.tpl | 33 +++ .../manifests/proxy-service.yml.tpl | 11 + .../kubernetes_ingress_clb/providers.tf | 34 +++ .../kubernetes_ingress_clb/variables.tf | 4 + .../modules/kubernetes_secrets/README.md | 21 ++ .../modules/kubernetes_secrets/main.tf | 16 ++ .../{kubernetes.tf => providers.tf} | 0 .../modules/kubernetes_secrets/variables.tf | 6 + .../modules/kubernetes_secrets/versions.tf | 2 +- .../environments/modules/mongodb/README.md | 10 + .../modules/mongodb/kubernetes.tf | 41 +-- .../environments/modules/mongodb/main.tf | 16 ++ .../environments/modules/mongodb/providers.tf | 13 + .../environments/modules/mongodb/variables.tf | 6 + .../environments/modules/mysql/README.md | 10 + .../environments/modules/mysql/kubernetes.tf | 15 -- .../environments/modules/mysql/main.tf | 16 ++ .../environments/modules/mysql/providers.tf | 13 + .../environments/modules/mysql/variables.tf | 6 + .../environments/modules/mysql/versions.tf | 2 +- .../environments/modules/redis/README.md | 10 + .../environments/modules/redis/kubernetes.tf | 14 - .../environments/modules/redis/main.tf | 16 ++ .../environments/modules/redis/providers.tf | 13 + .../environments/modules/redis/versions.tf | 2 +- .../environments/modules/s3/README.md | 17 ++ .../modules/{s3_openedx_storage => s3}/iam.tf | 9 + .../{s3_openedx_storage => s3}/kubernetes.tf | 14 - .../{s3_openedx_storage => s3}/main.tf | 16 ++ .../openedx_backups.tf | 10 +- .../openedx_secrets.tf | 10 +- .../openedx_storage.tf | 14 +- .../{s3_openedx_storage => s3}/outputs.tf | 0 .../environments/modules/s3/providers.tf | 13 + .../{s3_openedx_storage => s3}/variables.tf | 0 .../{s3_openedx_storage => s3}/versions.tf | 2 +- .../environments/modules/vpc/README.md | 13 + .../environments/modules/vpc/main.tf | 20 ++ .../environments/modules/vpc/route53.tf | 9 +- .../environments/modules/vpc/versions.tf | 2 +- .../modules/wordpress/ebs_volume.tf | 44 +++- .../modules/wordpress/kubernetes.tf | 21 -- .../environments/modules/wordpress/main.tf | 29 +++ .../environments/modules/wordpress/outputs.tf | 2 +- .../modules/wordpress/providers.tf | 19 ++ .../modules/wordpress/variables.tf | 10 +- .../modules/wordpress/versions.tf | 2 +- .../cloudfront/terragrunt.hcl | 6 +- .../{{cookiecutter.environment_name}}/env.hcl | 3 + .../kubernetes_ingress_clb/terragrunt.hcl | 18 +- .../kubernetes_secrets/terragrunt.hcl | 6 + .../mongodb/terragrunt.hcl | 6 + .../mysql/terragrunt.hcl | 6 + .../{s3_openedx_storage => s3}/terragrunt.hcl | 14 +- .../terraform/global.hcl | 13 +- .../etc/update-motd.d/09-welcome-banner.tpl | 2 +- .../etc/update-motd.d/10-help-text.tpl | 2 +- .../stacks/modules/ec2_bastion/kubernetes.tf | 14 - .../stacks/modules/ec2_bastion/main.tf | 78 ++++-- .../stacks/modules/ec2_bastion/providers.tf | 13 + .../ec2_bastion/scripts/install-tasks.sh | 8 +- .../stacks/modules/ec2_bastion/versions.tf | 2 +- .../kubernetes/addon_ebs_csi_driver.tf | 8 +- .../stacks/modules/kubernetes/main.tf | 155 +++++++---- .../stacks/modules/kubernetes/variables.tf | 2 +- .../stacks/modules/kubernetes/versions.tf | 2 +- .../modules/kubernetes_cert_manager/main.tf | 57 +++- .../kubernetes_cert_manager/variables.tf | 6 + .../kubernetes_cert_manager/versions.tf | 2 +- .../modules/kubernetes_dashboard/main.tf | 34 ++- .../modules/kubernetes_dashboard/variables.tf | 10 +- .../modules/kubernetes_ingress_clb/main.tf | 32 ++- .../kubernetes_ingress_clb/variables.tf | 6 + .../kubernetes_ingress_clb/versions.tf | 2 +- .../modules/kubernetes_karpenter/main.tf | 52 +++- .../modules/kubernetes_karpenter/versions.tf | 2 +- .../modules/kubernetes_kubeapps/main.tf | 31 ++- .../modules/kubernetes_kubeapps/versions.tf | 2 +- .../modules/kubernetes_kubecost/main.tf | 34 ++- .../modules/kubernetes_kubecost/versions.tf | 2 +- .../modules/kubernetes_metricsserver/main.tf | 35 ++- .../kubernetes_metricsserver/versions.tf | 2 +- .../modules/kubernetes_prometheus/main.tf | 35 ++- .../modules/kubernetes_prometheus/versions.tf | 2 +- .../stacks/modules/kubernetes_vpa/main.tf | 21 +- .../stacks/modules/kubernetes_vpa/versions.tf | 2 +- .../mongodb/etc/update-motd.d/10-help-text | 3 +- .../stacks/modules/mongodb/kubernetes.tf | 14 - .../terraform/stacks/modules/mongodb/main.tf | 70 +++-- .../stacks/modules/mongodb/providers.tf | 13 + .../scripts/mongodb-install-tasks.sh.tpl | 16 +- .../scripts/mongodb-preinstall-tasks.sh.tpl | 22 +- .../stacks/modules/mongodb/versions.tf | 2 +- .../stacks/modules/mongodb_volume/main.tf | 27 +- .../stacks/modules/mysql/kubernetes.tf | 13 - .../terraform/stacks/modules/mysql/main.tf | 126 ++++++--- .../stacks/modules/mysql/providers.tf | 13 + .../stacks/modules/mysql/versions.tf | 2 +- .../stacks/modules/redis/kubernetes.tf | 14 - .../terraform/stacks/modules/redis/main.tf | 76 ++++-- .../elasticache_parameter_group/versions.tf | 2 +- .../elasticache_subnet_group/versions.tf | 2 +- .../redis/modules/elasticache/versions.tf | 2 +- .../stacks/modules/redis/providers.tf | 13 + .../stacks/modules/redis/versions.tf | 2 +- .../terraform/stacks/modules/vpc/main.tf | 25 +- .../terraform/stacks/modules/vpc/route53.tf | 8 +- .../terraform/stacks/modules/vpc/versions.tf | 2 +- .../kubernetes/terragrunt.hcl | 1 + .../kubernetes_cert_manager/terragrunt.hcl | 10 +- .../kubernetes_dashboard/terragrunt.hcl | 3 +- .../kubernetes_ingress_clb/terragrunt.hcl | 6 + .../kubernetes_karpenter/terragrunt.hcl | 1 - .../kubernetes_kubeapps/terragrunt.hcl | 3 +- .../kubernetes_kubecost/terragrunt.hcl | 3 +- .../kubernetes_metricsserver/terragrunt.hcl | 1 - .../kubernetes_prometheus/terragrunt.hcl | 1 - .../mongodb_volume/terragrunt.hcl | 1 + .../redis/terragrunt.hcl | 1 + .../varnish/terragrunt.hcl | 95 ------- 162 files changed, 2063 insertions(+), 680 deletions(-) create mode 100644 {{cookiecutter.github_repo_name}}/VERSION create mode 100644 {{cookiecutter.github_repo_name}}/doc/wordfence-firewall-screenshot.png create mode 100644 {{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/README.md create mode 100644 {{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/main.tf create mode 100644 {{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output.tf create mode 100644 {{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_awscli_version.state create mode 100644 {{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_branch.state create mode 100644 {{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_commit.state create mode 100644 {{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_commit_date.state create mode 100644 {{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_repository.state create mode 100644 {{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_global_iam_arn.state create mode 100644 {{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_kubectl_version.state create mode 100644 {{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_os.state create mode 100644 {{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_terraform_version.state create mode 100644 {{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_timestamp.state create mode 100644 {{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_version.state create mode 100644 {{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/versions.tf create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/README.md create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/providers.tf create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/README.md create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/providers.tf create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/README.md create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/providers.tf create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/manifests/proxy-service.yml.tpl create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/providers.tf create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/README.md create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/main.tf rename {{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/{kubernetes.tf => providers.tf} (100%) create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/README.md create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/providers.tf create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/README.md create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/providers.tf create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/README.md create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/providers.tf create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/README.md rename {{cookiecutter.github_repo_name}}/terraform/environments/modules/{s3_openedx_storage => s3}/iam.tf (86%) rename {{cookiecutter.github_repo_name}}/terraform/environments/modules/{s3_openedx_storage => s3}/kubernetes.tf (58%) rename {{cookiecutter.github_repo_name}}/terraform/environments/modules/{s3_openedx_storage => s3}/main.tf (52%) rename {{cookiecutter.github_repo_name}}/terraform/environments/modules/{s3_openedx_storage => s3}/openedx_backups.tf (75%) rename {{cookiecutter.github_repo_name}}/terraform/environments/modules/{s3_openedx_storage => s3}/openedx_secrets.tf (65%) rename {{cookiecutter.github_repo_name}}/terraform/environments/modules/{s3_openedx_storage => s3}/openedx_storage.tf (82%) rename {{cookiecutter.github_repo_name}}/terraform/environments/modules/{s3_openedx_storage => s3}/outputs.tf (100%) create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/providers.tf rename {{cookiecutter.github_repo_name}}/terraform/environments/modules/{s3_openedx_storage => s3}/variables.tf (100%) rename {{cookiecutter.github_repo_name}}/terraform/environments/modules/{s3_openedx_storage => s3}/versions.tf (91%) create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/README.md create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/main.tf create mode 100644 {{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/providers.tf rename {{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/{s3_openedx_storage => s3}/terragrunt.hcl (89%) create mode 100644 {{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/providers.tf create mode 100644 {{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/providers.tf create mode 100644 {{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/providers.tf create mode 100644 {{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/providers.tf delete mode 100644 {{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/varnish/terragrunt.hcl diff --git a/CHANGELOG.md b/CHANGELOG.md index 27443c58..d3f868d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [1.1.0] (2023-3-17) + +Lots of new functionality added related to gathering and storing meta data about the exact environment that was used to create AWS resources via Terraform. Also added functionality to gather and persist as much information as possible about build and deploy CI work flows. + +- add module to gather environment state data of current user +- add cookiecutter meta tags for AWS resources +- revert to installing nutmeg.2 by default +- gather and persist CI build and deploy meta data in new k8s secrets +- add scorm proxy service to backend file storage based on eduNEXT prototype + ## [1.0.26] (2023-3-8) - bug fix: settings_merge.yml PREVIEW_LMS_BASE @@ -272,7 +282,7 @@ General production release - resolved deprecation warnings in all modules - restructured terraform folders - fixed a bug that was causing multiple SSL/TLS certificates to be created in both us-east-1 as well as the environment region -- added the text 'openedx_devops' to the descriptions of all security groups, IAM roles, and IAM policies resources that are explicitly created by this repository +- added the text 'cookiecutter' to the descriptions of all security groups, IAM roles, and IAM policies resources that are explicitly created by this repository ## [0.0.3] - 2022-03-20 diff --git a/README.rst b/README.rst index 7804e513..4f592c36 100644 --- a/README.rst +++ b/README.rst @@ -141,7 +141,7 @@ Create a Github repo and push it there: git add . git commit -m "first commit" git branch -M main - git remote add origin https://github.com/lpm0073/openedx_devops.git + git remote add origin https://github.com/youraccount/{{ cookiecutter.github_repo_name }}.git git push -u origin main Now take a look at your repo. Don't forget to carefully look at the generated README. Awesome, right? diff --git a/cookiecutter.json b/cookiecutter.json index 6cbf83f8..5a82ac0a 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -16,6 +16,8 @@ "global_aws_route53_hosted_zone_id": "Z1234567ABCDE1U23DEF", "global_aws_region": "us-east-1", "global_account_id": "123456789012", + "global_google_analytics_account": "SET-ME-PLEASE", + "global_language_code": "en", "stack_add_bastion": ["Y", "N"], "stack_add_bastion_openedx_dev_environment": ["N", "Y"], "stack_add_k8s_dashboard": ["Y", "N"], @@ -26,7 +28,7 @@ "stack_add_remote_mysql": ["Y", "N"], "stack_add_remote_mongodb": ["Y", "N"], "stack_add_remote_redis": ["Y", "N"], - "ci_build_tutor_version": "15.2.0", + "ci_build_tutor_version": "14.2.4", "ci_build_kubectl_version": "1.25/stable", "kubernetes_cluster_version": "1.25", "ci_build_theme_repository": "edx-theme-example", @@ -38,7 +40,7 @@ "ci_build_xblock_org": "openedx", "ci_build_xblock_repository": "edx-ora2", "ci_build_xblock_ref": "master", - "ci_deploy_open_edx_version": "olive.1", + "ci_deploy_open_edx_version": "nutmeg.2", "ci_deploy_install_backup_plugin": ["N", "Y"], "ci_deploy_install_credentials_server": ["N", "Y"], "ci_deploy_install_discovery_service": ["Y", "N"], @@ -66,7 +68,7 @@ "ci_openedx_actions_tutor_k8s_configure_mongodb_version": "v1.0.1", "ci_openedx_actions_tutor_k8s_configure_redis_version": "v1.0.0", "ci_openedx_actions_tutor_k8s_configure_smtp_version": "v1.0.0", - "ci_openedx_actions_tutor_print_dump": "v1.0.0", + "ci_openedx_actions_tutor_print_dump": "v1.0.4", "ci_openedx_actions_tutor_plugin_build_backup_version": "v0.1.7", "ci_openedx_actions_tutor_plugin_build_credentials_version": "v1.0.0", "ci_openedx_actions_tutor_plugin_build_license_manager_version": "v0.0.2", @@ -117,26 +119,26 @@ "redis_port": 6379, "redis_family": "redis6.x", "terraform_required_version": "~> 1.3", - "terraform_aws_modules_acm": "~> 4.3", - "terraform_aws_modules_cloudfront": "~> 3.1", - "terraform_aws_modules_eks": "~> 19.4", + "terraform_aws_modules_acm": "4.3", + "terraform_aws_modules_cloudfront": "3.1", + "terraform_aws_modules_eks": "19.4", "terraform_aws_modules_iam": "~> 5.9", - "terraform_aws_modules_iam_assumable_role_with_oidc": "~> 5.10", - "terraform_aws_modules_rds": "~> 5.2", - "terraform_aws_modules_s3": "~> 3.6", - "terraform_aws_modules_sg": "~> 4.16", - "terraform_aws_modules_vpc": "~> 3.18", - "terraform_helm_cert_manager": "~> 1.11", - "terraform_helm_ingress_nginx_controller": "~> 4.4", - "terraform_helm_vertical_pod_autoscaler": "~> 6.0", - "terraform_helm_karpenter": "~> 0.16", - "terraform_helm_dashboard": "~> 6.0", - "terraform_helm_kubeapps": "~> 12.2", - "terraform_helm_kubecost": "~> 1.100", - "terraform_helm_metrics_server": "~> 3.8", + "terraform_aws_modules_iam_assumable_role_with_oidc": "5.10", + "terraform_aws_modules_rds": "5.2", + "terraform_aws_modules_s3": "3.6", + "terraform_aws_modules_sg": "4.16", + "terraform_aws_modules_vpc": "3.18", + "terraform_helm_cert_manager": "1.11", + "terraform_helm_ingress_nginx_controller": "4.4", + "terraform_helm_vertical_pod_autoscaler": "6.0", + "terraform_helm_karpenter": "0.16", + "terraform_helm_dashboard": "6.0", + "terraform_helm_kubeapps": "12.2", + "terraform_helm_kubecost": "1.100", + "terraform_helm_metrics_server": "3.8", "terraform_helm_prometheus": "39.6.0", "terraform_provider_kubernetes_version": "~> 2.16", - "terraform_provider_hashicorp_aws_version": "~> 4.48", + "terraform_provider_hashicorp_aws_version": "4.48", "terraform_provider_hashicorp_local_version": "~> 2.2", "terraform_provider_hashicorp_random_version": "~> 3.4", "terraform_provider_hashicorp_kubectl_version": "~> 1.14", diff --git a/{{cookiecutter.github_repo_name}}/.github/workflows/build-openedx.yml b/{{cookiecutter.github_repo_name}}/.github/workflows/build-openedx.yml index 9d690ec5..edaa77cb 100644 --- a/{{cookiecutter.github_repo_name}}/.github/workflows/build-openedx.yml +++ b/{{cookiecutter.github_repo_name}}/.github/workflows/build-openedx.yml @@ -39,6 +39,11 @@ jobs: aws-region: {% raw %}${{ env.AWS_REGION }}{% endraw %} tutor-version: "{{ cookiecutter.ci_build_tutor_version }}" + - name: Load additional environment specific settings + shell: bash + run: |- + echo "TUTOR_OPENEDX_COMMON_VERSION=open-release/{{ cookiecutter.ci_deploy_open_edx_version }}" >> $GITHUB_ENV + #------------------------------------------------------------------------ # Add a custom theme here. #------------------------------------------------------------------------ @@ -88,6 +93,9 @@ jobs: - name: Dump tutor config uses: openedx-actions/tutor-print-dump@{{ cookiecutter.ci_openedx_actions_tutor_print_dump }} + with: + namespace: {% raw %}${{ env.NAMESPACE }}{% endraw %} + action: build #------------------------------------------------------------------------ # Build and upload the Docker container diff --git a/{{cookiecutter.github_repo_name}}/.github/workflows/deploy-{{cookiecutter.environment_name}}.yml b/{{cookiecutter.github_repo_name}}/.github/workflows/deploy-{{cookiecutter.environment_name}}.yml index 3ecd877b..952a6f89 100644 --- a/{{cookiecutter.github_repo_name}}/.github/workflows/deploy-{{cookiecutter.environment_name}}.yml +++ b/{{cookiecutter.github_repo_name}}/.github/workflows/deploy-{{cookiecutter.environment_name}}.yml @@ -130,7 +130,7 @@ jobs: echo "TUTOR_LMS_HOST=$LMS_HOSTNAME" >> $GITHUB_ENV echo "TUTOR_CMS_HOST=$CMS_HOSTNAME" >> $GITHUB_ENV echo "TUTOR_DOCKER_IMAGE_OPENEDX=${AWS_ECR_REPOSITORY_OPENEDX}" >> $GITHUB_ENV - echo "OPENEDX_COMMON_VERSION=open-release/{{ cookiecutter.ci_deploy_open_edx_version }}" >> $GITHUB_ENV + echo "TUTOR_OPENEDX_COMMON_VERSION=open-release/{{ cookiecutter.ci_deploy_open_edx_version }}" >> $GITHUB_ENV # --------------------------------------------------------------------------------- # Configure optional tutor plugins @@ -319,6 +319,9 @@ jobs: - name: Dump tutor config uses: openedx-actions/tutor-print-dump@{{ cookiecutter.ci_openedx_actions_tutor_print_dump }} + with: + namespace: {% raw %}${{ env.NAMESPACE }}{% endraw %} + action: deploy # ----------------------------------------------------------------------- # Deploy diff --git a/{{cookiecutter.github_repo_name}}/VERSION b/{{cookiecutter.github_repo_name}}/VERSION new file mode 100644 index 00000000..ce77b519 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/VERSION @@ -0,0 +1 @@ +v1.0.27 diff --git a/{{cookiecutter.github_repo_name}}/ci/tutor-deploy/environments/{{cookiecutter.environment_name}}/settings_merge.yml b/{{cookiecutter.github_repo_name}}/ci/tutor-deploy/environments/{{cookiecutter.environment_name}}/settings_merge.yml index 0bf9a43e..00d34ba5 100644 --- a/{{cookiecutter.github_repo_name}}/ci/tutor-deploy/environments/{{cookiecutter.environment_name}}/settings_merge.yml +++ b/{{cookiecutter.github_repo_name}}/ci/tutor-deploy/environments/{{cookiecutter.environment_name}}/settings_merge.yml @@ -1,73 +1,117 @@ --- +ACTIVATION_EMAIL_SUPPORT_LINK: https://{{ cookiecutter.global_root_domain }}/support/ +AUTH_PASSWORD_VALIDATORS: +- NAME: django.contrib.auth.password_validation.UserAttributeSimilarityValidator +- NAME: common.djangoapps.util.password_policy_validators.MinimumLengthValidator + OPTIONS: + min_length: 8 +- NAME: common.djangoapps.util.password_policy_validators.MaximumLengthValidator + OPTIONS: + max_length: 75 +AWS_SES_REGION_ENDPOINT: email.{{ cookiecutter.global_aws_region }}.amazonaws.com +AWS_SES_REGION_NAME: "{{ cookiecutter.global_aws_region }}" CORS_ORIGIN_ALLOW_ALL: true CORS_ORIGIN_WHITELIST: - https://{{ cookiecutter.global_root_domain }} - https://{{ cookiecutter.environment_subdomain }}.{{ cookiecutter.global_root_domain }} - https://{{ cookiecutter.environment_studio_subdomain }}.{{ cookiecutter.environment_subdomain }}.{{ cookiecutter.global_root_domain }} - https://apps.{{ cookiecutter.environment_subdomain }}.{{ cookiecutter.global_root_domain }} +COURSE_ABOUT_VISIBILITY_PERMISSION: see_about_page +COURSE_CATALOG_VISIBILITY_PERMISSION: see_in_catalog +CREDIT_HELP_LINK_URL: https://{{ cookiecutter.global_root_domain }}/support/ CROSS_DOMAIN_CSRF_COOKIE_DOMAIN: "{{ cookiecutter.environment_subdomain }}.{{ cookiecutter.global_root_domain }}" CROSS_DOMAIN_CSRF_COOKIE_NAME: native-csrf-cookie CSRF_COOKIE_SECURE: true -CSRF_TRUSTED_ORIGINS: [] +CSRF_TRUSTED_ORIGINS: +- https://apps.{{ cookiecutter.environment_subdomain }}.{{ cookiecutter.global_root_domain }} DCS_SESSION_COOKIE_SAMESITE: lax DCS_SESSION_COOKIE_SAMESITE_FORCE_ALL: true -ACTIVATION_EMAIL_SUPPORT_LINK: https://{{ cookiecutter.global_root_domain }}/support/ -AWS_SES_REGION_ENDPOINT: email.{{ cookiecutter.global_aws_region }}.amazonaws.com -AWS_SES_REGION_NAME: "{{ cookiecutter.global_aws_region }}" -CREDIT_HELP_LINK_URL: https://{{ cookiecutter.global_root_domain }}/support/ DEFAULT_MOBILE_AVAILABLE: false DEFAULT_EMAIL_LOGO_URL: https://cdn.{{ cookiecutter.environment_subdomain }}.{{ cookiecutter.global_root_domain }} ENTERPRISE_SUPPORT_URL: https://{{ cookiecutter.global_root_domain }}/support/ ENTERPRISE_TAGLINE: "{{ cookiecutter.global_platform_name }}" FACEBOOK_API_VERSION: v12.0 FEATURES: - ENABLE_CHANGE_USER_PASSWORD_ADMIN: true - CERTIFICATES_HTML_VIEW: true - PREVIEW_LMS_BASE: "preview.{{ cookiecutter.environment_subdomain }}.{{ cookiecutter.global_root_domain }}" - ENABLE_COURSEWARE_INDEX: true - ENABLE_CSMH_EXTENDED: false - ENABLE_LEARNER_RECORDS: true - ENABLE_LIBRARY_INDEX: true - MILESTONES_APP: true - ENABLE_PREREQUISITE_COURSES: true - ENABLE_DASHBOARD_SEARCH: false + ALLOW_ALL_ADVANCED_COMPONENTS: true + ALLOW_HIDING_DISCUSSION_TAB: true AUTH_USE_OPENID_PROVIDER: false + AUTH_USE_OPENID: false AUTOMATIC_AUTH_FOR_TESTING: false + CERTIFICATES_ENABLED: true + CERTIFICATES_HTML_VIEW: true + CUSTOM_CERTIFICATE_TEMPLATES_ENABLED: true CUSTOM_COURSES_EDX: false + ENABLE_ACCOUNT_DELETION: true ENABLE_BULK_ENROLLMENT_VIEW: true + ENABLE_CHANGE_USER_PASSWORD_ADMIN: true ENABLE_COMBINED_LOGIN_REGISTRATION: true ENABLE_CORS_HEADERS: true ENABLE_COUNTRY_ACCESS: false + ENABLE_COURSEWARE_INDEX: true + ENABLE_COURSEWARE_MICROFRONTEND: false ENABLE_CREDIT_API: false ENABLE_CREDIT_ELIGIBILITY: false ENABLE_CROSS_DOMAIN_CSRF_COOKIE: true - ENABLE_DISCUSSION_HOME_PANEL: false - ENABLE_DISCUSSION_SERVICE: false + ENABLE_CSMH_EXTENDED: false + ENABLE_DASHBOARD_SEARCH: true + ENABLE_DISCUSSION_EMAIL_DIGEST: true + ENABLE_DISCUSSION_HOME_PANEL: true + ENABLE_DISCUSSION_SERVICE: true + ENABLE_DJANGO_ADMIN_SITE: true ENABLE_EDXNOTES: true ENABLE_ENROLLMENT_RESET: true ENABLE_EXPORT_GIT: false ENABLE_GRADE_DOWNLOADS: true ENABLE_INSTRUCTOR_ANALYTICS: true + ENABLE_INSTRUCTOR_EMAIL: true + ENABLE_LEARNER_RECORDS: true + ENABLE_LIBRARY_INDEX: true ENABLE_LTI_PROVIDER: false ENABLE_MKTG_SITE: false ENABLE_MOBILE_REST_API: true ENABLE_OAUTH2_PROVIDER: true + ENABLE_PEARSON_HACK_TEST: false + ENABLE_PREREQUISITE_COURSES: true ENABLE_PUBLISHER: false ENABLE_READING_FROM_MULTIPLE_HISTORY_TABLES: false ENABLE_SPECIAL_EXAMS: false ENABLE_SYSADMIN_DASHBOARD: true ENABLE_THIRD_PARTY_AUTH: true ENABLE_VIDEO_UPLOAD_PIPELINE: false + ENABLE_XBLOCK_VIEW_ENDPOINT: true + MILESTONES_APP: true + ORGANIZATIONS_APP: true + PREVENT_CONCURRENT_LOGINS: true + PREVIEW_LMS_BASE: preview.{{ cookiecutter.environment_subdomain }}.{{ cookiecutter.global_root_domain }} SHOW_FOOTER_LANGUAGE_SELECTOR: false SHOW_HEADER_LANGUAGE_SELECTOR: false +GOOGLE_ANALYTICS_ACCOUNT: {{ cookiecutter.global_google_analytics_account }} +HEARTBEAT_EXTENDED_CHECKS: +- openedx.core.djangoapps.heartbeat.default_checks.check_celery +- openedx.core.djangoapps.django_comment_common.comment_client.utils.check_forum_heartbeat ID_VERIFICATION_SUPPORT_LINK: https://{{ cookiecutter.global_root_domain }}/support/ -LANGUAGE_CODE: en +LANGUAGE_CODE: {{ cookiecutter.global_language_code }} LANGUAGE_COOKIE: openedx-language-preference +LOGIN_REDIRECT_WHITELIST: +- https://{{ cookiecutter.environment_studio_subdomain }}.{{ cookiecutter.environment_subdomain }}.{{ cookiecutter.global_root_domain }} +- https://apps.{{ cookiecutter.environment_subdomain }}.{{ cookiecutter.global_root_domain }} LOGO_URL_PNG: "https://cdn.{{ cookiecutter.environment_subdomain }}.{{ cookiecutter.global_root_domain }}" +MKTG_URL_OVERRIDES: + ABOUT: '#' + BLOG: '#' + DONATE: '#' + PRIVACY: '#' + TOS: '#' +PARENTAL_CONSENT_AGE_LIMIT: 13 PLATFORM_DESCRIPTION: "{{ cookiecutter.global_platform_description }}" PLATFORM_FACEBOOK_ACCOUNT: http://www.facebook.com/ PLATFORM_NAME: "{{ cookiecutter.global_platform_name }}" PLATFORM_TWITTER_ACCOUNT: '' +PROFILE_IMAGE_SIZES_MAP: + full: 500 + large: 120 + medium: 50 + small: 30 REGISTRATION_EXTRA_FIELDS: city: hidden confirm_email: hidden @@ -93,7 +137,13 @@ SUPPORT_SITE_LINK: https://{{ cookiecutter.global_root_domain }}/support/ TIME_ZONE: America/New_York THIRD_PARTY_AUTH_BACKENDS: - social_core.backends.google.GoogleOAuth2 +- social_core.backends.linkedin.LinkedinOAuth2 - social_core.backends.facebook.FacebookOAuth2 +- social_core.backends.azuread.AzureADOAuth2 +- common.djangoapps.third_party_auth.appleid.AppleIdAuth +- common.djangoapps.third_party_auth.identityserver3.IdentityServer3 +- common.djangoapps.third_party_auth.saml.SAMLAuthBackend +- common.djangoapps.third_party_auth.lti.LTIAuthBackend WIKI_ENABLED: false API_ACCESS_FROM_EMAIL: api-requests@{{ cookiecutter.global_root_domain }} API_ACCESS_MANAGER_EMAIL: api-access@{{ cookiecutter.global_root_domain }} diff --git a/{{cookiecutter.github_repo_name}}/doc/DATA_BACKUP.md b/{{cookiecutter.github_repo_name}}/doc/DATA_BACKUP.md index 49f89d41..b044dd29 100644 --- a/{{cookiecutter.github_repo_name}}/doc/DATA_BACKUP.md +++ b/{{cookiecutter.github_repo_name}}/doc/DATA_BACKUP.md @@ -35,7 +35,7 @@ MongoDB script source: [openedx-backup-mongodb.sh](../terraform/stacks/modules/e Terraform creates a dedicated AWS S3 bucket, {{ cookiecutter.environment_name }}-{{ cookiecutter.global_platform_name }}-{{ cookiecutter.global_platform_region }}-backup.s3.amazonaws.com, for archiving backups. This bucket does not provide public access. Note that it is preconfigured with a lifecycle policy to retain large files (greater than 1Gb) for 30 days. -See Terraform source code: [openedx_backups.tf](../terraform/environments/modules/s3_openedx_storage/openedx_backups.tf) +See Terraform source code: [openedx_backups.tf](../terraform/environments/modules/s3/openedx_backups.tf) ## Local storage diff --git a/{{cookiecutter.github_repo_name}}/doc/wordfence-firewall-screenshot.png b/{{cookiecutter.github_repo_name}}/doc/wordfence-firewall-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..8c5485a7939d43f3f57da21e5a1ff2f9f983bfc3 GIT binary patch literal 279763 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 literal 0 HcmV?d00001 diff --git a/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/README.md b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/README.md new file mode 100644 index 00000000..005793bf --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/README.md @@ -0,0 +1,30 @@ +# Cookiecutter Meta + +Collects and persists meta data about the current user's environment. Data collected is made visible to Terraform by persisting each data element its own .state file in ./output. These files in turn are exposed within Terraform using "data" declarations. + +Cookiecutter Meta is referenced by all modules contained in [environments](../../environments/) and [stacks](../../stacks/) and is ultimated formatted into AWS resource tag elements that are persisted into every AWS resource created by the Terraform scripts contained in this repository. + +## Meta Data + +Collects the following about your operating environment: + +- AWS Command-line interface version number +- The current git branch of this repository +- The most recent git commit date from this repository +- The sha of the most recent git commit from this repository +- The AWS IAM ARN which contains the key-secret in use for the awscli +- Kubectl current version +- The name and version of your computer's operating system +- Terraform current version +- Timestamp of the last time this module was executed +- Cookiecutter version + +## Usage + +Run this module separately and as needed. + +```bash + terraform init # prepare this module to run by downloading all referenced Terraform modules and providers + terraform plan # echo a work plan to the console + terraform apply # run this module +``` diff --git a/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/main.tf b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/main.tf new file mode 100644 index 00000000..3d0de335 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/main.tf @@ -0,0 +1,245 @@ +#------------------------------------------------------------------------------ +# written by: Lawrence McDaniel +# https://lawrencemcdaniel.com/ +# +# date: Mar-2023 +# +# usage: gather environment variables and add to a tags dict. This is a +# hacky solution in that we use Bash to gather data elements, and meanwhile +# Terraform lacks a good interface to send bash results back to the thread +# of control. +# as a workaround, we get bash to write its results to a file in the "output" +# folder and then we use Terraform 'data' definitions to access each result. +# +# But, it's worse than just that. Terraform also lacks a means of detecting +# state changes on the null_resource objects we declare here, as this would +# require that an 'apply' on each resource in order to run the bash code +# contained therein. thus, it's a chicken-and-egg problem. +# +# our workaround is: +# 1. always execute an "init" resource +# 2. inside this we rewrite the contents of cookiecutter_github_commit.state +# 3. we use a MD5 checksum of the file content of cookiecutter_github_commit.state +# as a taint for all resources that provide data to the tags output. +#------------------------------------------------------------------------------ + + +# ensure that a state file exists for each element we track. +resource "null_resource" "init" { + provisioner "local-exec" { + command = <<-EOT + touch ${path.module}/output/cookiecutter_awscli_version.state + touch ${path.module}/output/cookiecutter_github_branch.state + touch ${path.module}/output/cookiecutter_github_commit_date.state + touch ${path.module}/output/cookiecutter_github_commit.state + touch ${path.module}/output/cookiecutter_github_repository.state + touch ${path.module}/output/cookiecutter_iam_arn.state + touch ${path.module}/output/cookiecutter_kubectl_version.state + touch ${path.module}/output/cookiecutter_os.state + touch ${path.module}/output/cookiecutter_terraform_version.state + touch ${path.module}/output/cookiecutter_timestamp.state + touch ${path.module}/output/cookiecutter_version.state + EOT + } +} + +# rewrite the contents of cookiecutter_github_commit.state, which will +# taint our other resouces in the event that this changes the file +# contents. +resource "null_resource" "taint" { + provisioner "local-exec" { + command = <<-EOT + GIT_PARENT_DIRECTORY=$(git rev-parse --show-toplevel) + cookiecutter_github_commit=$(git -C $GIT_PARENT_DIRECTORY rev-parse HEAD) + echo $cookiecutter_github_commit > ${path.module}/output/cookiecutter_github_commit.state + EOT + } + triggers = { + timestamp = "${timestamp()}" + } +} +data "local_file" "taint" { + filename = "${path.module}/output/cookiecutter_github_commit.state" + depends_on = [ + null_resource.taint + ] +} + +resource "null_resource" "environment" { + provisioner "local-exec" { + command = <<-EOT + # common variables + GIT_PARENT_DIRECTORY=$(git rev-parse --show-toplevel) + + #------------------------------------------------------------------------------ + # 1. cookiecutter_awscli_version + # get the current version of AWS CLI running on the machine that is executing + # this module. + #------------------------------------------------------------------------------ + cookiecutter_awscli_version=$(aws --version | awk '{print $1}' | sed 's/aws-cli//') + cookiecutter_awscli_version=$(echo $cookiecutter_awscli_version | sed 's@/@@') + echo $cookiecutter_awscli_version > ${path.module}/output/cookiecutter_awscli_version.state + + #------------------------------------------------------------------------------ + # 2. cookiecutter_github_branch + # get the branch of the most recent commit + #------------------------------------------------------------------------------ + cookiecutter_github_branch=$(git -C $GIT_PARENT_DIRECTORY branch | sed 's/* //') + echo $cookiecutter_github_branch > ${path.module}/output/cookiecutter_github_branch.state + + #------------------------------------------------------------------------------ + # 3. cookiecutter_github_commit_date + # get the commit date of the most recent commit from the repo containing this code + # HINT: this will be a repo generated by the Cookiecutter (ie. {{ cookiecutter.github_repo_name }}) + #------------------------------------------------------------------------------ + cookiecutter_github_commit_date=$(date -r $(git log -1 --format=%ct) +%Y%m%dT%H%M%S) + echo $cookiecutter_github_commit_date > ${path.module}/output/cookiecutter_github_commit_date.state + + #------------------------------------------------------------------------------ + # 4. cookiecutter_github_commit + # get the sha of the most recent commit + #------------------------------------------------------------------------------ + cookiecutter_github_commit=$(git -C $GIT_PARENT_DIRECTORY rev-parse HEAD) + echo $cookiecutter_github_commit > ${path.module}/output/cookiecutter_github_commit.state + + #------------------------------------------------------------------------------ + # 5. cookiecutter_github_repository + # get the url to the remote Github repository from which this code was cloned. + #------------------------------------------------------------------------------ + cookiecutter_github_repository=$(git -C $GIT_PARENT_DIRECTORY config --get remote.origin.url) + echo $cookiecutter_github_repository > ${path.module}/output/cookiecutter_github_repository.state + + #------------------------------------------------------------------------------ + # 6. cookiecutter_global_iam_arn + # get the AWS IAM user of the key pair that AWS CLI is currently using. + #------------------------------------------------------------------------------ + cookiecutter_global_iam_arn=$(aws sts get-caller-identity | jq -r '.["Arn"] as $v | "\($v)"') + echo $cookiecutter_global_iam_arn > ${path.module}/output/cookiecutter_global_iam_arn.state + + #------------------------------------------------------------------------------ + # 7. cookiecutter_kubectl_version + # get the current version of kubectl that is running on the machine executing + # this module. + #------------------------------------------------------------------------------ + cookiecutter_kubectl_version=$(kubectl version --output=json | jq -r '.["clientVersion"].gitVersion as $v | "\($v)"') + echo $cookiecutter_kubectl_version > ${path.module}/output/cookiecutter_kubectl_version.state + + #------------------------------------------------------------------------------ + # 8. cookiecutter_os + # get the operating system of the machine running this module + #------------------------------------------------------------------------------ + echo $OSTYPE > ${path.module}/output/cookiecutter_os.state + + #------------------------------------------------------------------------------ + # 9. cookiecutter_terraform_version + # get the current version of Terraform running on the machine that is executing + # this module. + #------------------------------------------------------------------------------ + cookiecutter_terraform_version=$(terraform --version | head -n 1 | sed 's/Terraform //') + echo $cookiecutter_terraform_version > ${path.module}/output/cookiecutter_terraform_version.state + + #------------------------------------------------------------------------------ + # 10. cookiecutter_timestamp + # get the system date from the machine running this module + #------------------------------------------------------------------------------ + cookiecutter_timestamp=$(date +%Y%m%dT%H%M%S) + echo $cookiecutter_timestamp > ${path.module}/output/cookiecutter_timestamp.state + + EOT + } + + lifecycle { + replace_triggered_by = [ + data.local_file.taint.id + ] + } + + depends_on = [ + null_resource.init + ] +} + +# 1. cookiecutter_awscli_version +data "local_file" "cookiecutter_awscli_version" { + filename = "${path.module}/output/cookiecutter_awscli_version.state" + depends_on = [ + null_resource.environment + ] +} + +# 2. cookiecutter_github_branch +data "local_file" "cookiecutter_github_branch" { + filename = "${path.module}/output/cookiecutter_github_branch.state" + depends_on = [ + null_resource.environment + ] +} + +# 3. cookiecutter_github_commit_date +data "local_file" "cookiecutter_github_commit_date" { + filename = "${path.module}/output/cookiecutter_github_commit_date.state" + depends_on = [ + null_resource.environment + ] +} + +# 4. cookiecutter_github_commit +data "local_file" "cookiecutter_github_commit" { + filename = "${path.module}/output/cookiecutter_github_commit.state" + depends_on = [ + null_resource.environment + ] +} + +# 5. cookiecutter_github_repository +data "local_file" "cookiecutter_github_repository" { + filename = "${path.module}/output/cookiecutter_github_repository.state" + depends_on = [ + null_resource.environment + ] +} + +# 6. cookiecutter_global_iam_arn +data "local_file" "cookiecutter_global_iam_arn" { + filename = "${path.module}/output/cookiecutter_global_iam_arn.state" + depends_on = [ + null_resource.environment + ] +} + +# 7. cookiecutter_kubectl_version +data "local_file" "cookiecutter_kubectl_version" { + filename = "${path.module}/output/cookiecutter_kubectl_version.state" + depends_on = [ + null_resource.environment + ] +} + +# 8. cookiecutter_os +data "local_file" "cookiecutter_os" { + filename = "${path.module}/output/cookiecutter_os.state" + depends_on = [ + null_resource.environment + ] +} + +# 9. cookiecutter_terraform_version +data "local_file" "cookiecutter_terraform_version" { + filename = "${path.module}/output/cookiecutter_terraform_version.state" + depends_on = [ + null_resource.environment + ] +} + +# 10. cookiecutter_timestamp +data "local_file" "cookiecutter_timestamp" { + filename = "${path.module}/output/cookiecutter_timestamp.state" + depends_on = [ + null_resource.environment + ] +} + +# 11. cookiecutter_version +data "local_file" "cookiecutter_version" { + filename = "${path.module}/../../../VERSION" +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output.tf b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output.tf new file mode 100644 index 00000000..360f13da --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output.tf @@ -0,0 +1,20 @@ +output "tags" { + value = { + "cookiecutter/meta/terraform" = "true" + "cookiecutter/meta/version" = replace(tostring(data.local_file.cookiecutter_version.content), "\n", "") + "cookiecutter/meta/aws_iam_user" = replace(tostring(data.local_file.cookiecutter_global_iam_arn.content), "\n", "") + "cookiecutter/meta/github_repository" = replace(tostring(data.local_file.cookiecutter_github_repository.content), "\n", "") + "cookiecutter/meta/github_branch" = replace(tostring(data.local_file.cookiecutter_github_branch.content), "\n", "") + "cookiecutter/meta/github_commit" = replace(tostring(data.local_file.cookiecutter_github_commit.content), "\n", "") + "cookiecutter/meta/github_commit_date" = replace(tostring(data.local_file.cookiecutter_github_commit_date.content), "\n", "") + "cookiecutter/meta/awscli_version" = replace(tostring(data.local_file.cookiecutter_awscli_version.content), "\n", "") + "cookiecutter/meta/terraform_version" = replace(tostring(data.local_file.cookiecutter_terraform_version.content), "\n", "") + "cookiecutter/meta/kubectl_version" = replace(tostring(data.local_file.cookiecutter_kubectl_version.content), "\n", "") + "cookiecutter/meta/os" = replace(tostring(data.local_file.cookiecutter_os.content), "\n", "") + "cookiecutter/meta/timestamp" = replace(tostring(data.local_file.cookiecutter_timestamp.content), "\n", "") + } + + depends_on = [ + null_resource.environment + ] +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_awscli_version.state b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_awscli_version.state new file mode 100644 index 00000000..3d17e76e --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_awscli_version.state @@ -0,0 +1 @@ +unassigned diff --git a/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_branch.state b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_branch.state new file mode 100644 index 00000000..3d17e76e --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_branch.state @@ -0,0 +1 @@ +unassigned diff --git a/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_commit.state b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_commit.state new file mode 100644 index 00000000..3d17e76e --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_commit.state @@ -0,0 +1 @@ +unassigned diff --git a/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_commit_date.state b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_commit_date.state new file mode 100644 index 00000000..3d17e76e --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_commit_date.state @@ -0,0 +1 @@ +unassigned diff --git a/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_repository.state b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_repository.state new file mode 100644 index 00000000..3d17e76e --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_github_repository.state @@ -0,0 +1 @@ +unassigned diff --git a/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_global_iam_arn.state b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_global_iam_arn.state new file mode 100644 index 00000000..3d17e76e --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_global_iam_arn.state @@ -0,0 +1 @@ +unassigned diff --git a/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_kubectl_version.state b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_kubectl_version.state new file mode 100644 index 00000000..3d17e76e --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_kubectl_version.state @@ -0,0 +1 @@ +unassigned diff --git a/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_os.state b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_os.state new file mode 100644 index 00000000..3d17e76e --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_os.state @@ -0,0 +1 @@ +unassigned diff --git a/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_terraform_version.state b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_terraform_version.state new file mode 100644 index 00000000..3d17e76e --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_terraform_version.state @@ -0,0 +1 @@ +unassigned diff --git a/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_timestamp.state b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_timestamp.state new file mode 100644 index 00000000..3d17e76e --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_timestamp.state @@ -0,0 +1 @@ +unassigned diff --git a/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_version.state b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_version.state new file mode 100644 index 00000000..3d17e76e --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/output/cookiecutter_version.state @@ -0,0 +1 @@ +unassigned diff --git a/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/versions.tf new file mode 100644 index 00000000..8bf0e5ee --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/common/cookiecutter_meta/versions.tf @@ -0,0 +1,22 @@ +#------------------------------------------------------------------------------ +# written by: Lawrence McDaniel +# https://lawrencemcdaniel.com/ +# +# date: March-2022 +# +# usage: build an EKS cluster load balancer that uses a Fargate Compute Cluster +#------------------------------------------------------------------------------ +terraform { + required_version = "~> 1.3" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 4.48" + } + local = { + source = "hashicorp/local" + version = "~> 2.2" + } + } +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/README.md b/{{cookiecutter.github_repo_name}}/terraform/environments/README.md index e1859b8d..c42edd45 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/README.md +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/README.md @@ -1,11 +1,15 @@ -## Terragrunt Environments +# Environments -Terraform environments give you the ability to create multiple, distinct AWS VPC clouds for prod, development, QA and so on. That is, you would be create distinct RDS instances, MongoDB instances, Kubernetes Cluster instances and so on; one for each additional environment. +Cookiecutter environments give you the ability to create multiple, segregated operating environments for your Open edX installation, saving you time and effort in creating and maintaining environments for `prod`, `dev`, `test`, `qa`, `mcdaniel`, etcetera. Cookiecutter environments run on a [backend stack](../stacks/). -The envisioned implementations of additional environments would consist of environments like: `prod`, `dev`, `test`, `qa`, `mcdaniel`, etcetera. +Cookiecutter environments are logically separated, using their own sets of: -These additional environments will run on shared infrastructure, named `{{ cookiecutter.global_platform_shared_resource_identifier }}` by default, unless you have specified otherwise. However, each environment has its own data and its own Kubernetes namespace. - -The general strategy is that a common set of parameters are defined in [terraform/environments/global.hcl](./global.hcl) that each environment uses, plus, each environment maintains its own set of parameters for environment-specific settings like domain names and resource instances sizes for example. - -The difference between these two methodologies is that the former creates an entire VPC per environment, increasing your monthly AWS bill by multiples, whereas the latter simply adds additional domain records, S3 buckets, and logical databases as necessary to support the additional environments. +- cloud storage and data backup locations +- logical MySQL databases and MongoDB contentstores +- Redis cache keys +- application credentials and service accounts +- domain name and DNS entries +- ssl certificates +- ingresses +- Kubernetes namespaces and resource monitoring configurations +- Github Action build-deploy workflows diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/README.md b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/README.md new file mode 100644 index 00000000..08c4b33a --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/README.md @@ -0,0 +1,7 @@ +# Amazon Certificate Manager + +Requests ssl certificates for stack aws_region {{ cookiecutter.global_platform_region }} for ELB, adds DNS records for certificate verification, and adds a certificate to us-east-1 for AWS Cloudfront distributions. + +## Additional Features + +This module integrates [cookiecutter_meta](../../../common/cookiecutter_meta/README.md), which manages an optional additional set of AWS resource tags. diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/main.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/main.tf index 9f80a16b..beda5148 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/main.tf @@ -1,17 +1,15 @@ -#------------------------------------------------------------------------------ -# written by: Lawrence McDaniel -# https://lawrencemcdaniel.com/ -# -# date: Apr-2022 -# -# usage: Add DNS records and tls certs to stack aws_region for ELB. -# Also add certs to us-east-1 for Cloudfront distributions. -#------------------------------------------------------------------------------ -provider "aws" { - alias = "environment_region" - region = var.aws_region -} +locals { + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/environments/modules/acm" + "cookiecutter/resource/source" = "terraform-aws-modules/acm/aws" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_aws_modules_acm }}" + } + ) +} data "aws_route53_zone" "root_domain" { name = var.root_domain } @@ -23,7 +21,7 @@ data "aws_route53_zone" "environment_domain" { module "acm_root_domain_environment_region" { source = "terraform-aws-modules/acm/aws" - version = "{{ cookiecutter.terraform_aws_modules_acm }}" + version = "~> {{ cookiecutter.terraform_aws_modules_acm }}" providers = { aws = aws.environment_region @@ -35,9 +33,9 @@ module "acm_root_domain_environment_region" { subject_alternative_names = [ "*.${var.root_domain}", ] + tags = local.tags wait_for_validation = true - tags = var.tags } module "acm_environment_environment_region" { @@ -54,7 +52,14 @@ module "acm_environment_environment_region" { subject_alternative_names = [ "*.${var.environment_domain}", ] + tags = local.tags wait_for_validation = true - tags = var.tags +} + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" } diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/providers.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/providers.tf new file mode 100644 index 00000000..0e21dac8 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/providers.tf @@ -0,0 +1,4 @@ +provider "aws" { + alias = "environment_region" + region = var.aws_region +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/versions.tf index efd9a51b..02d1cb1a 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/acm/versions.tf @@ -12,7 +12,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } local = { source = "hashicorp/local" diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/README.md b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/README.md new file mode 100644 index 00000000..be13fdee --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/README.md @@ -0,0 +1,15 @@ +# Environment Specific Cloudfront Distribution + +Creates a dedicated CDN on a per-environment basis. The following resources are created and configured: + +- AWS Cloudfront distribution, sourced by its corresponding AWS S3 Bucket s3://{{ cookiecutter.global_platform_name }}-{{ cookiecutter.global_platform_region }}-{{ cookiecutter.environment_name }}-storage created in the s3 Terraform module. +- An ssl certificate with preconfigured CNAME, originating from us-east-1 as is required by Cloudfront +- A DNS record added to the environment AWS Route53 Hosted Zone + +## Note the following + +The AWS S3 bucket is configured to allow publicly accessible content. However, you must manually and explicitly make content public in order for it to be viewable from the CDN created by this module. Moreover, you should remain aware that this bucket by default contains a collections of mixed content originating from various parts of the openedx platform, including profile images, course content, grade downloads, and so on. It is possible to customize this behavior in order to segregate content that you may deem too sensitive. See [openedx-actions/tutor-plugin-enable-s3](https://github.com/openedx-actions/tutor-plugin-enable-s3), called from Github Actions Deployment workflows in this repo. + +## Additional Features + +This module integrates [cookiecutter_meta](../../../common/cookiecutter_meta/README.md), which manages an optional additional set of AWS resource tags. diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/certificate_manager.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/certificate_manager.tf index 47499a50..5918690c 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/certificate_manager.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/certificate_manager.tf @@ -1,12 +1,3 @@ -#------------------------------------------------------------------------------ -# written by: Lawrence McDaniel -# https://lawrencemcdaniel.com/ -# -# date: Feb-2022 -# -# usage: Add DNS records and tls certs to environment aws_region for ALB. -# Also add certs to us-east-1 for Cloudfront distributions. -#------------------------------------------------------------------------------ {% if cookiecutter.global_aws_region != "us-east-1" -%} #------------------------------------------------------------------------------ # SSL/TLS certs issued in the AWS region for ALB @@ -22,7 +13,7 @@ provider "aws" { module "acm_environment_domain" { source = "terraform-aws-modules/acm/aws" - version = "{{ cookiecutter.terraform_aws_modules_acm }}" + version = "~> {{ cookiecutter.terraform_aws_modules_acm }}" providers = { aws = aws.us-east-1 @@ -40,8 +31,12 @@ module "acm_environment_domain" { # adding the Usage tag as a way to differentiate this cert from the one created by # the eks clb ingress, of which we have no control. tags = merge( - var.tags, - { Usage = "Cloudfront" } + local.tags, + { Usage = "Cloudfront" }, + { + "cookiecutter/resource/source" = "terraform-aws-modules/acm/aws" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_aws_modules_acm }}" + } ) } diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/main.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/main.tf index 45407edd..9685c35b 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/main.tf @@ -17,14 +17,16 @@ locals { s3_bucket_name = var.resource_name s3_bucket_domain = "${local.s3_bucket_name}.s3.${var.aws_region}.amazonaws.com" cdn_name = "cdn.${var.environment_domain}" -} -provider "aws" { - alias = "us-east-1" - region = "us-east-1" + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/environments/modules/cloudfront" + } + ) } - data "aws_route53_zone" "environment_domain" { name = var.environment_domain @@ -64,7 +66,7 @@ resource "aws_route53_record" "cdn_environment_domain" { module "cdn_environment_domain" { source = "terraform-aws-modules/cloudfront/aws" - version = "{{cookiecutter.terraform_aws_modules_cloudfront}}" + version = "~> {{cookiecutter.terraform_aws_modules_cloudfront}}" aliases = [local.cdn_name] @@ -108,4 +110,19 @@ module "cdn_environment_domain" { acm_certificate_arn = data.aws_acm_certificate.environment_domain.arn ssl_support_method = "sni-only" } + + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "terraform-aws-modules/cloudfront/aws" + "cookiecutter/resource/version" = "{{cookiecutter.terraform_aws_modules_cloudfront}}" + } + ) +} + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" } diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/providers.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/providers.tf new file mode 100644 index 00000000..21aba178 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/providers.tf @@ -0,0 +1,4 @@ +provider "aws" { + alias = "us-east-1" + region = "us-east-1" +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/versions.tf index 2fa47171..19a35530 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/cloudfront/versions.tf @@ -10,7 +10,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } } } diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/README.md b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/README.md new file mode 100644 index 00000000..05e23329 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/README.md @@ -0,0 +1,33 @@ +# Environment Specific Kubernetes Configuration + +Adds Kubernetes [Horizontal Pod Autoscalers](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/) and [Vertical Pod Autoscalers](https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler) to the environment. + +## Horizontal Pod Autoscalers + +- cms +- cms worker +- lms +- lms worker +- discovery +- mfe +- notes +- smtp + +More: see [README](./yml/horizontalpodautoscalers/README.md) + +## Vertical Pod Autoscalers + +- cms +- cms worker +- lms +- lms worker +- discovery +- ElasticSearch +- MongoDB +- mfe +- notes +- smtp + +## Additional Features + +This module integrates [cookiecutter_meta](../../../common/cookiecutter_meta/README.md), which manages an optional additional set of AWS resource tags. diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/main.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/main.tf index f6278488..bf67b941 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/main.tf @@ -7,32 +7,41 @@ # usage: create an RDS MySQL instance. # store the MySQL credentials in Kubernetes Secrets #------------------------------------------------------------------------------ -data "aws_eks_cluster" "eks" { - name = var.resource_name -} - -data "aws_eks_cluster_auth" "eks" { - name = var.resource_name -} - -provider "kubernetes" { - host = data.aws_eks_cluster.eks.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) - token = data.aws_eks_cluster_auth.eks.token -} - -provider "kubectl" { - host = data.aws_eks_cluster.eks.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority.0.data) - token = data.aws_eks_cluster_auth.eks.token -} - #------------------------------------------------------------------------------ # Tutor deploys into this namespace, bc of a namesapce command-line argument # that we pass inside of GitHub Actions deploy workflow #------------------------------------------------------------------------------ +locals { + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/environments/modules/kubernetes" + } + ) +} + resource "kubernetes_namespace" "environment_namespace" { metadata { name = var.environment_namespace } } + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} + +resource "kubernetes_secret" "cookiecutter" { + metadata { + name = "cookiecutter" + namespace = var.environment_namespace + } + + # https://stackoverflow.com/questions/64134699/terraform-map-to-string-value + data = { + tags = jsonencode(local.tags) + } +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/providers.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/providers.tf new file mode 100644 index 00000000..0da09588 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/providers.tf @@ -0,0 +1,19 @@ +data "aws_eks_cluster" "eks" { + name = var.resource_name +} + +data "aws_eks_cluster_auth" "eks" { + name = var.resource_name +} + +provider "kubernetes" { + host = data.aws_eks_cluster.eks.endpoint + cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) + token = data.aws_eks_cluster_auth.eks.token +} + +provider "kubectl" { + host = data.aws_eks_cluster.eks.endpoint + cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority.0.data) + token = data.aws_eks_cluster_auth.eks.token +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/variables.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/variables.tf index 44fdfbf7..f2a8b392 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/variables.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/variables.tf @@ -18,3 +18,9 @@ variable "environment_name" { variable "environment_namespace" { type = string } + +variable "tags" { + description = "collection of all tags to add to this resource. execting the combination of global + environment + resouce tags." + type = map(string) + default = {} +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/versions.tf index 2f80d54d..b46c4ef2 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes/versions.tf @@ -12,7 +12,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } local = { source = "hashicorp/local" diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/README.md b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/README.md index ec020e9c..4a99f6e9 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/README.md +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/README.md @@ -1 +1,12 @@ -## Elastic Kubernetes with EC2 Worker Nodes + Classic Load Balancer +# Environment Specific Kubernetes Ingress + +Implements an Nginx-based ingress controller and AWS Classic Load Balancer for this environment. Creates the following resources: + +- Helm installed Certificate Issuer which relies on Kubernetes [cert-manager](https://cert-manager.io/) +- Open edX ingresses for lms, cms, discovery +- Open edX [MFE](https://openedx.atlassian.net/wiki/spaces/FEDX/pages/1265467645/Open+edX+and+Microfrontends) ingress +- DNS records added to AWS Route53 Hosted Zone for this environment + +## Additional Features + +This module integrates [cookiecutter_meta](../../../common/cookiecutter_meta/README.md), which manages an optional additional set of AWS resource tags. diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/kubernetes.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/kubernetes.tf index bf938df9..184fd704 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/kubernetes.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/kubernetes.tf @@ -32,3 +32,24 @@ resource "kubectl_manifest" "ingress_mfe" { aws_route53_record.wildcard, ] } + +data "aws_s3_bucket" "storage" { + id = var.s3_bucket_storage +} + +data "template_file" "proxy_service" { + template = file("${path.module}/manifests/proxy-service.yml.tpl") + vars = { + environment_domain = var.environment_domain + environment_namespace = var.environment_namespace + bucket_uri = data.aws_s3_bucket.bucket_domain_name + } +} +resource "kubectl_manifest" "proxy_service" { + yaml_body = data.template_file.proxy_service.rendered + + depends_on = [ + aws_route53_record.naked, + aws_route53_record.wildcard, + ] +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/main.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/main.tf index bbbefd07..eeef5723 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/main.tf @@ -6,42 +6,19 @@ # # usage: build an EKS cluster load balancer #------------------------------------------------------------------------------ - -#data "tls_certificate" "cluster" { -# url = data.aws_eks_cluster.eks.identity[0].oidc[0].issuer -#} - -data "aws_eks_cluster" "eks" { - name = var.shared_resource_namespace -} - -data "aws_eks_cluster" "cluster" { - name = var.shared_resource_namespace +locals { + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/environments/modules/kubernetes_ingress_clb" + } + ) } -data "aws_eks_cluster_auth" "cluster" { - name = var.shared_resource_namespace -} - -provider "kubernetes" { - host = data.aws_eks_cluster.cluster.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority.0.data) - token = data.aws_eks_cluster_auth.cluster.token -} - -provider "helm" { - kubernetes { - host = data.aws_eks_cluster.cluster.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority.0.data) - token = data.aws_eks_cluster_auth.cluster.token - } -} - -data "kubernetes_service" "ingress_nginx_controller" { - metadata { - name = "common-ingress-nginx-controller" - namespace = "kube-system" - } +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" } - -data "aws_elb_hosted_zone_id" "main" {} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/manifests/ingress.yml.tpl b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/manifests/ingress.yml.tpl index e57c1625..78279a62 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/manifests/ingress.yml.tpl +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/manifests/ingress.yml.tpl @@ -38,6 +38,25 @@ metadata: # --------------------- nginx.ingress.kubernetes.io/force-ssl-redirect: "true" nginx.ingress.kubernetes.io/backend-protocol: "HTTP" + + # mcdaniel mar-2023 + # duplicate eduNEXT scorm Xblock proxy configuration + # ------------------------------------------------------------------------- + # see (better): https://github.com/kubernetes/ingress-nginx/issues/6165#issuecomment-692684553 + # see: https://github.com/kubernetes/ingress-nginx/issues/4280 + # + # how it works in Caddy: + # ---------------------- + # @scorm_matcher { + # path /scorm-proxy/* + # } + # route @scorm_matcher { + # uri /scorm-proxy/* strip_prefix /scorm-proxy + # reverse_proxy https://codlp-global-pre-staging-storage.s3.amazonaws.com { + # header_up Host codlp-global-pre-staging-storage.s3.amazonaws.com + # } + # } + spec: tls: - hosts: @@ -55,6 +74,13 @@ spec: name: lms port: number: 8000 + - path: /scorm-proxy/ + pathType: Prefix + backend: + service: + name: scorm-proxy-service + port: + number: 8000 - host: "preview.${environment_domain}" http: paths: @@ -75,6 +101,13 @@ spec: name: cms port: number: 8000 + - path: /scorm-proxy/ + pathType: Prefix + backend: + service: + name: scorm-proxy-service + port: + number: 8000 - host: discovery.${environment_domain} http: paths: diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/manifests/proxy-service.yml.tpl b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/manifests/proxy-service.yml.tpl new file mode 100644 index 00000000..f7910a6a --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/manifests/proxy-service.yml.tpl @@ -0,0 +1,11 @@ +# +# part of the eduNEXT scorm proxy solution +# +kind: Service +apiVersion: v1 +metadata: + name: scorm-proxy-service + namespace: ${naenvironment_namespacemespace} +spec: + type: ExternalName + externalName: ${bucket_uri} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/providers.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/providers.tf new file mode 100644 index 00000000..109cd596 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/providers.tf @@ -0,0 +1,34 @@ +data "aws_eks_cluster" "eks" { + name = var.shared_resource_namespace +} + +data "aws_eks_cluster" "cluster" { + name = var.shared_resource_namespace +} + +data "aws_eks_cluster_auth" "cluster" { + name = var.shared_resource_namespace +} + +data "kubernetes_service" "ingress_nginx_controller" { + metadata { + name = "common-ingress-nginx-controller" + namespace = "kube-system" + } +} + +data "aws_elb_hosted_zone_id" "main" {} + +provider "kubernetes" { + host = data.aws_eks_cluster.cluster.endpoint + cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority.0.data) + token = data.aws_eks_cluster_auth.cluster.token +} + +provider "helm" { + kubernetes { + host = data.aws_eks_cluster.cluster.endpoint + cluster_ca_certificate = base64decode(data.aws_eks_cluster.cluster.certificate_authority.0.data) + token = data.aws_eks_cluster_auth.cluster.token + } +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/variables.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/variables.tf index 50f43a61..edf2d1a4 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/variables.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_ingress_clb/variables.tf @@ -35,3 +35,7 @@ variable "tags" { type = map(string) default = {} } + +variable "s3_bucket_storage" { + type = string +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/README.md b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/README.md new file mode 100644 index 00000000..3b3ecb08 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/README.md @@ -0,0 +1,21 @@ +# Environment Specific Open edX Credentials + +Creates Kuberenetes secrets for the following Open edX passwords and credentials in this environment: + +- Open edX admin user account name and password +- ecommerce-config +- Django application edx-secret-key for this environment +- Javascript Web Token (jwt) for lms and cms for this environment +- Open edX License Manager oauth for this environment +- MongoDB host name, port, admin account name and password for this stack +- MongoDB host name, port, openedx account name and password for this environment +- MySQL host name, port, openedx account name and password for this environment +- MySQL host name, port, Discovery Service account name and password for this environment +- MySQL host name, port, Xqueue Service account name and password for this environment +- MySQL host name, port, root account name and password for this stack +- Redis host name, port, environment key +- AWS IAM key-secret for read-write access to AWS S3 buckets for this environment + +## Additional Features + +This module integrates [cookiecutter_meta](../../../common/cookiecutter_meta/README.md), which manages an optional additional set of AWS resource tags. diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/main.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/main.tf new file mode 100644 index 00000000..4f20b140 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/main.tf @@ -0,0 +1,16 @@ +locals { + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/environments/modules/kubernetes_secrets" + } + ) +} + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/kubernetes.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/providers.tf similarity index 100% rename from {{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/kubernetes.tf rename to {{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/providers.tf diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/variables.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/variables.tf index 66af35aa..635d0d0c 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/variables.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/variables.tf @@ -17,3 +17,9 @@ variable "resource_name" { variable "root_domain" { type = string } + +variable "tags" { + description = "collection of all tags to add to this resource. execting the combination of global + environment + resouce tags." + type = map(string) + default = {} +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/versions.tf index 215ef597..5bf2ccc8 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/kubernetes_secrets/versions.tf @@ -10,7 +10,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } local = { source = "hashicorp/local" diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/README.md b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/README.md new file mode 100644 index 00000000..b151f893 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/README.md @@ -0,0 +1,10 @@ +# Environment Specific MongoDB Configuration + +Creates environment specific configuration for the Stack-level MonogDB service. Creates the following resources: + +- Kubernetes secret with MongoDB host, port, username, password +- DNS record added to the environment AWS Route53 Hosted Zone + +## Additional Features + +This module integrates [cookiecutter_meta](../../../common/cookiecutter_meta/README.md), which manages an optional additional set of AWS resource tags. diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/kubernetes.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/kubernetes.tf index 438766b8..d3e6f656 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/kubernetes.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/kubernetes.tf @@ -7,21 +7,6 @@ # usage: create environment connection resources for remote MongoDB instance. # store the MongoDB credentials in Kubernetes Secrets #------------------------------------------------------------------------------ -data "aws_eks_cluster" "eks" { - name = var.resource_name -} - -data "aws_eks_cluster_auth" "eks" { - name = var.resource_name -} - -provider "kubernetes" { - host = data.aws_eks_cluster.eks.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) - token = data.aws_eks_cluster_auth.eks.token -} - - # Retrieve the mongodb_admin connection parameters from the shared resource namespace. # we'll refer to this data for the HOST and PORT assignments on all other MySQL # secrets. @@ -55,25 +40,25 @@ resource "kubernetes_secret" "openedx" { data = { # see: https://docs.tutor.overhang.io/configuration.html # ------------------------------------------------------------------------- - MONGODB_DATABASE = substr("${var.db_prefix}_edx", -32, -1) - MONGODB_HOST = data.kubernetes_secret.mongodb_admin.data.MONGODB_HOST - MONGODB_USERNAME = "" - MONGODB_PASSWORD = "" + MONGODB_DATABASE = substr("${var.db_prefix}_edx", -32, -1) + MONGODB_HOST = data.kubernetes_secret.mongodb_admin.data.MONGODB_HOST + MONGODB_USERNAME = "" + MONGODB_PASSWORD = "" # you can harden security by adding auth # credentials here #MONGODB_USERNAME = substr("${var.db_prefix}_edx", -32, -1) #MONGODB_PASSWORD = random_password.mongodb_openedx.result - MONGODB_PORT = data.kubernetes_secret.mongodb_admin.data.MONGODB_PORT - MONGODB_USE_SSL = "false" - MONGODB_REPLICA_SET = "" - MONGODB_AUTH_MECHANISM = "" - MONGODB_AUTH_SOURCE = "admin" + MONGODB_PORT = data.kubernetes_secret.mongodb_admin.data.MONGODB_PORT + MONGODB_USE_SSL = "false" + MONGODB_REPLICA_SET = "" + MONGODB_AUTH_MECHANISM = "" + MONGODB_AUTH_SOURCE = "admin" # see: https://github.com/overhangio/tutor-forum # ------------------------------------------------------------------------- - FORUM_MONGODB_DATABASE = substr("${var.db_prefix}_cs_comments", -32, -1) - FORUM_MONGODB_USE_SSL = "false" - FORUM_MONGODB_AUTH_SOURCE = "" - FORUM_MONGODB_AUTH_MECH = "" + FORUM_MONGODB_DATABASE = substr("${var.db_prefix}_cs_comments", -32, -1) + FORUM_MONGODB_USE_SSL = "false" + FORUM_MONGODB_AUTH_SOURCE = "" + FORUM_MONGODB_AUTH_MECH = "" } } diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/main.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/main.tf index 7e92d6f2..0cefc225 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/main.tf @@ -6,3 +6,19 @@ # # usage: create environment connection resources for remote MongoDB instance. #------------------------------------------------------------------------------ +locals { + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/environments/modules/mongodb" + } + ) +} + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/providers.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/providers.tf new file mode 100644 index 00000000..57e8384d --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/providers.tf @@ -0,0 +1,13 @@ +data "aws_eks_cluster" "eks" { + name = var.resource_name +} + +data "aws_eks_cluster_auth" "eks" { + name = var.resource_name +} + +provider "kubernetes" { + host = data.aws_eks_cluster.eks.endpoint + cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) + token = data.aws_eks_cluster_auth.eks.token +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/variables.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/variables.tf index bb0d4e00..52f9a9a5 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/variables.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mongodb/variables.tf @@ -27,3 +27,9 @@ variable "environment_namespace" { variable "db_prefix" { type = string } + +variable "tags" { + description = "collection of all tags to add to this resource. execting the combination of global + environment + resouce tags." + type = map(string) + default = {} +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/README.md b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/README.md new file mode 100644 index 00000000..5e062e5b --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/README.md @@ -0,0 +1,10 @@ +# Environment Specific MySQL Configuration + +Creates environment specific configuration for the Stack-level [AWS RDS MySQL](https://aws.amazon.com/rds/) service. Creates the following resources: + +- Kubernetes secret with MySQL host, port, username, password +- DNS record added to the environment AWS Route53 Hosted Zone + +## Additional Features + +This module integrates [cookiecutter_meta](../../../common/cookiecutter_meta/README.md), which manages an optional additional set of AWS resource tags. diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/kubernetes.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/kubernetes.tf index 2f369d3e..5fde5556 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/kubernetes.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/kubernetes.tf @@ -7,21 +7,6 @@ # usage: create an RDS MySQL instance. # store the MySQL credentials in Kubernetes Secrets #------------------------------------------------------------------------------ -data "aws_eks_cluster" "eks" { - name = var.resource_name -} - -data "aws_eks_cluster_auth" "eks" { - name = var.resource_name -} - -provider "kubernetes" { - host = data.aws_eks_cluster.eks.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) - token = data.aws_eks_cluster_auth.eks.token -} - - # Retrieve the mysql_root connection parameters from the shared resource namespace. # we'll refer to this data for the HOST and PORT assignments on all other MySQL # secrets. diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/main.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/main.tf index 817aea01..1d7b7e8f 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/main.tf @@ -6,3 +6,19 @@ # # usage: create an RDS MySQL instance. #------------------------------------------------------------------------------ +locals { + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/environments/modules/mysql" + } + ) +} + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/providers.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/providers.tf new file mode 100644 index 00000000..57e8384d --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/providers.tf @@ -0,0 +1,13 @@ +data "aws_eks_cluster" "eks" { + name = var.resource_name +} + +data "aws_eks_cluster_auth" "eks" { + name = var.resource_name +} + +provider "kubernetes" { + host = data.aws_eks_cluster.eks.endpoint + cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) + token = data.aws_eks_cluster_auth.eks.token +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/variables.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/variables.tf index 1a687361..0e74a30c 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/variables.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/variables.tf @@ -35,3 +35,9 @@ variable "db_instance_id" { variable "db_prefix" { type = string } + +variable "tags" { + description = "collection of all tags to add to this resource. execting the combination of global + environment + resouce tags." + type = map(string) + default = {} +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/versions.tf index e2e8b255..a0a0a7a5 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/mysql/versions.tf @@ -12,7 +12,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } } } diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/README.md b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/README.md new file mode 100644 index 00000000..48e3ce2b --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/README.md @@ -0,0 +1,10 @@ +# Environment Specific AWS ElastiCache Redis Configuration + +Creates environment specific configuration for the Stack-level MonogDB service. Creates the following resources: + +- Kubernetes secret with [AWS ElastiCache](https://aws.amazon.com/elasticache/) Redis Service host, port, username, password +- DNS record added to the environment AWS Route53 Hosted Zone + +## Additional Features + +This module integrates [cookiecutter_meta](../../../common/cookiecutter_meta/README.md), which manages an optional additional set of AWS resource tags. diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/kubernetes.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/kubernetes.tf index f4a4cff0..c442dc45 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/kubernetes.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/kubernetes.tf @@ -7,20 +7,6 @@ # usage: create an ElastiCache Redis cache # stored cache credentials in Kubernetes Secrets. #------------------------------------------------------------------------------ -data "aws_eks_cluster" "eks" { - name = var.shared_resource_namespace -} - -data "aws_eks_cluster_auth" "eks" { - name = var.shared_resource_namespace -} - -provider "kubernetes" { - host = data.aws_eks_cluster.eks.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) - token = data.aws_eks_cluster_auth.eks.token -} - resource "kubernetes_secret" "environment_redis" { metadata { name = "redis" diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/main.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/main.tf index e69de29b..40b5a70e 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/main.tf @@ -0,0 +1,16 @@ +locals { + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/environments/modules/redis" + } + ) +} + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/providers.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/providers.tf new file mode 100644 index 00000000..35cf28f5 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/providers.tf @@ -0,0 +1,13 @@ +data "aws_eks_cluster" "eks" { + name = var.shared_resource_namespace +} + +data "aws_eks_cluster_auth" "eks" { + name = var.shared_resource_namespace +} + +provider "kubernetes" { + host = data.aws_eks_cluster.eks.endpoint + cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) + token = data.aws_eks_cluster_auth.eks.token +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/versions.tf index dd41b8e9..30701079 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/redis/versions.tf @@ -12,7 +12,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } } } diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/README.md b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/README.md new file mode 100644 index 00000000..b0f8e0e8 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/README.md @@ -0,0 +1,17 @@ +# Environment Specific Remote Storage + +Creates dedicated AWS S3 Buckets for storage, backups, and secrets management. Creates the following resources: + +- s3://{{ cookiecutter.global_platform_name }}-{{ cookiecutter.global_platform_region }}-{{ cookiecutter.environment_name }}-storage +- s3://{{ cookiecutter.global_platform_name }}-{{ cookiecutter.global_platform_region }}-{{ cookiecutter.environment_name }}-backups +- s3://{{ cookiecutter.global_platform_name }}-{{ cookiecutter.global_platform_region }}-{{ cookiecutter.environment_name }}-secrets +- AWS IAM user + key-secret to facilitate programatic bucket access via awscli from within Open edX software +- Kubernetes secret created with the namespace for this environment, containing all AWS S3 bucket meta data and credentials + +## Note the following + +The AWS S3 bucket is configured to allow publicly accessible content. However, you must manually and explicitly make content public in order for it to be viewable from the CDN created by this module. Moreover, you should remain aware that this bucket by default contains a collections of mixed content originating from various parts of the openedx platform, including profile images, course content, grade downloads, and so on. It is possible to customize this behavior in order to segregate content that you may deem too sensitive. See [openedx-actions/tutor-plugin-enable-s3](https://github.com/openedx-actions/tutor-plugin-enable-s3) and [hastexo/tutor-contrib-s3](https://github.com/hastexo/tutor-contrib-s3), called from Github Actions Deployment workflows in this repo. + +## Additional Features + +This module integrates [cookiecutter_meta](../../../common/cookiecutter_meta/README.md), which manages an optional additional set of AWS resource tags. diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/iam.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/iam.tf similarity index 86% rename from {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/iam.tf rename to {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/iam.tf index 4c3675a3..9cbd3d77 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/iam.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/iam.tf @@ -31,6 +31,15 @@ resource "random_id" "id" { resource "aws_iam_user" "user" { name = "s3-openedx-user-${random_id.id.hex}" path = "/system/s3-bucket-user/" + + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_iam_user" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) + } data "aws_iam_policy_document" "user_policy" { diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/kubernetes.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/kubernetes.tf similarity index 58% rename from {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/kubernetes.tf rename to {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/kubernetes.tf index c896cdb3..f4bae792 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/kubernetes.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/kubernetes.tf @@ -1,17 +1,3 @@ -data "aws_eks_cluster" "eks" { - name = var.kubernetes_name -} - -data "aws_eks_cluster_auth" "eks" { - name = var.kubernetes_name -} - -provider "kubernetes" { - host = data.aws_eks_cluster.eks.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) - token = data.aws_eks_cluster_auth.eks.token -} - resource "kubernetes_secret" "s3" { metadata { name = var.secret_name diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/main.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/main.tf similarity index 52% rename from {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/main.tf rename to {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/main.tf index d89bc9c8..33418b9c 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/main.tf @@ -6,3 +6,19 @@ # # usage: create an AWS S3 bucket to offload Open edX file storage. #------------------------------------------------------------------------------ + +locals { + + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/environments/modules/s3" + } + ) + +} + +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/openedx_backups.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/openedx_backups.tf similarity index 75% rename from {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/openedx_backups.tf rename to {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/openedx_backups.tf index 8777685f..ded93176 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/openedx_backups.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/openedx_backups.tf @@ -9,11 +9,19 @@ module "openedx_backup" { source = "terraform-aws-modules/s3-bucket/aws" - version = "{{ cookiecutter.terraform_aws_modules_s3 }}" + version = "~> {{ cookiecutter.terraform_aws_modules_s3 }}" bucket = var.resource_name_backup acl = "private" + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "terraform-aws-modules/s3-bucket/aws" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_aws_modules_s3 }}" + } + ) + versioning = { enabled = true } diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/openedx_secrets.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/openedx_secrets.tf similarity index 65% rename from {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/openedx_secrets.tf rename to {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/openedx_secrets.tf index 0925780e..68c1ddb4 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/openedx_secrets.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/openedx_secrets.tf @@ -9,11 +9,19 @@ module "openedx_secrets" { source = "terraform-aws-modules/s3-bucket/aws" - version = "{{ cookiecutter.terraform_aws_modules_s3 }}" + version = "~> {{ cookiecutter.terraform_aws_modules_s3 }}" bucket = var.resource_name_secrets acl = "private" + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "terraform-aws-modules/s3-bucket/aws" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_aws_modules_s3 }}" + } + ) + block_public_acls = true block_public_policy = true diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/openedx_storage.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/openedx_storage.tf similarity index 82% rename from {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/openedx_storage.tf rename to {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/openedx_storage.tf index fb7d96fd..ca7ad440 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/openedx_storage.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/openedx_storage.tf @@ -9,11 +9,19 @@ module "openedx_storage" { source = "terraform-aws-modules/s3-bucket/aws" - version = "{{ cookiecutter.terraform_aws_modules_s3 }}" + version = "~> {{ cookiecutter.terraform_aws_modules_s3 }}" bucket = var.resource_name_storage acl = "private" + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "terraform-aws-modules/s3-bucket/aws" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_aws_modules_s3 }}" + } + ) + control_object_ownership = true object_ownership = "BucketOwnerPreferred" @@ -37,13 +45,13 @@ module "openedx_storage" { "http://${var.environment_studio_domain}" ] allowed_headers = ["*"] - expose_headers = [ + expose_headers = [ "Access-Control-Allow-Origin", "Access-Control-Allow-Method", "Access-Control-Allow-Header" ] max_age_seconds = 3000 - } + } ] versioning = { enabled = false diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/outputs.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/outputs.tf similarity index 100% rename from {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/outputs.tf rename to {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/outputs.tf diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/providers.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/providers.tf new file mode 100644 index 00000000..bd888608 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/providers.tf @@ -0,0 +1,13 @@ +data "aws_eks_cluster" "eks" { + name = var.kubernetes_name +} + +data "aws_eks_cluster_auth" "eks" { + name = var.kubernetes_name +} + +provider "kubernetes" { + host = data.aws_eks_cluster.eks.endpoint + cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) + token = data.aws_eks_cluster_auth.eks.token +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/variables.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/variables.tf similarity index 100% rename from {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/variables.tf rename to {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/variables.tf diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/versions.tf similarity index 91% rename from {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/versions.tf rename to {{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/versions.tf index c6b6dd9d..f78c7ceb 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3_openedx_storage/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/s3/versions.tf @@ -12,7 +12,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } local = { source = "hashicorp/local" diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/README.md b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/README.md new file mode 100644 index 00000000..53bf9552 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/README.md @@ -0,0 +1,13 @@ +# Environment Specific Virtual Private Cloud Configuration + +Creates the following environment specific resources inside of the stack-level Virtual Private Cloud: + +- AWS Route53 Hosted Zone for management of the environment subdomain +- DNS NS records to link the AWS Route53 Hosted zone to the root domain + +## Note the following + + +## Additional Features + +This module integrates [cookiecutter_meta](../../../common/cookiecutter_meta/README.md), which manages an optional additional set of AWS resource tags. diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/main.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/main.tf new file mode 100644 index 00000000..89e103f9 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/main.tf @@ -0,0 +1,20 @@ +locals { + s3_bucket_name = var.resource_name + s3_bucket_domain = "${local.s3_bucket_name}.s3.${var.aws_region}.amazonaws.com" + cdn_name = "cdn.${var.environment_domain}" + + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/environments/modules/vpn" + } + ) +} + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/route53.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/route53.tf index ab07e0ed..7b6ffde4 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/route53.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/route53.tf @@ -6,14 +6,19 @@ # # usage: Add DNS records. #------------------------------------------------------------------------------ - data "aws_route53_zone" "root_domain" { name = var.root_domain } resource "aws_route53_zone" "environment_domain" { name = var.environment_domain - tags = var.tags + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_route53_zone" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) } resource "aws_route53_record" "environment_domain-ns" { diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/versions.tf index efd9a51b..02d1cb1a 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/vpc/versions.tf @@ -12,7 +12,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } local = { source = "hashicorp/local" diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/ebs_volume.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/ebs_volume.tf index 9e4bef3f..d4bf4b4b 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/ebs_volume.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/ebs_volume.tf @@ -35,7 +35,7 @@ resource "kubernetes_persistent_volume_claim" "wordpress" { } spec { - access_modes = ["ReadWriteOnce"] + access_modes = ["ReadWriteOnce"] storage_class_name = "gp2" resources { requests = { @@ -45,6 +45,14 @@ resource "kubernetes_persistent_volume_claim" "wordpress" { volume_name = kubernetes_persistent_volume.wordpress.metadata.0.name } + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_ebs_volume" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) + depends_on = [ kubernetes_persistent_volume.wordpress ] @@ -52,13 +60,13 @@ resource "kubernetes_persistent_volume_claim" "wordpress" { resource "kubernetes_persistent_volume" "wordpress" { metadata { - name = local.wordpressDomain + name = local.wordpressDomain labels = { "topology.kubernetes.io/region" = "${var.aws_region}" - "topology.kubernetes.io/zone" = "${aws_ebs_volume.wordpress.availability_zone}" - "ebs_volume_id" = "${aws_ebs_volume.wordpress.id}" - "name" = "${local.wordpressDomain}" - "namespace" = "${local.wordpressNamespace}" + "topology.kubernetes.io/zone" = "${aws_ebs_volume.wordpress.availability_zone}" + "ebs_volume_id" = "${aws_ebs_volume.wordpress.id}" + "name" = "${local.wordpressDomain}" + "namespace" = "${local.wordpressNamespace}" } annotations = { } @@ -66,28 +74,28 @@ resource "kubernetes_persistent_volume" "wordpress" { spec { capacity = { - storage = "${local.persistenceSize}Gi" + storage = "${local.persistenceSize}Gi" } - access_modes = ["ReadWriteOnce"] + access_modes = ["ReadWriteOnce"] storage_class_name = "gp2" persistent_volume_source { aws_elastic_block_store { volume_id = aws_ebs_volume.wordpress.id - fs_type = "ext4" + fs_type = "ext4" } } node_affinity { required { node_selector_term { match_expressions { - key = "topology.kubernetes.io/zone" + key = "topology.kubernetes.io/zone" operator = "In" - values = ["${aws_ebs_volume.wordpress.availability_zone}"] + values = ["${aws_ebs_volume.wordpress.availability_zone}"] } match_expressions { - key = "topology.kubernetes.io/region" + key = "topology.kubernetes.io/region" operator = "In" - values = ["${var.aws_region}"] + values = ["${var.aws_region}"] } } } @@ -106,7 +114,15 @@ resource "kubernetes_persistent_volume" "wordpress" { resource "aws_ebs_volume" "wordpress" { availability_zone = data.aws_subnet.private_subnet.availability_zone size = local.persistenceSize - tags = var.tags + + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_ebs_volume" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) + # local.ebsVolumePreventDestroy defaults to 'Y' # for anything other than an upper case 'N' we'll assume that diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/kubernetes.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/kubernetes.tf index ac7e7b96..5d5dfbb7 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/kubernetes.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/kubernetes.tf @@ -12,24 +12,3 @@ resource "kubernetes_namespace" "wordpress" { name = local.wordpressNamespace } } - - -data "aws_eks_cluster" "eks" { - name = var.shared_resource_namespace -} - -data "aws_eks_cluster_auth" "eks" { - name = var.shared_resource_namespace -} - -provider "kubernetes" { - host = data.aws_eks_cluster.eks.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) - token = data.aws_eks_cluster_auth.eks.token -} - -provider "kubectl" { - host = data.aws_eks_cluster.eks.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority.0.data) - token = data.aws_eks_cluster_auth.eks.token -} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/main.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/main.tf index a8e5adac..a76f0c30 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/main.tf @@ -45,6 +45,16 @@ locals { HorizontalAutoscalingMinReplicas = 1 HorizontalAutoscalingMaxReplicas = 1 externalCachePort = "11211" + + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/environments/modules/wordpress" + "cookiecutter/resource/source" = "bitnami/wordpress" + "cookiecutter/resource/version" = "{{ cookiecutter.wordpress_helm_chart_version }}" + } + ) } @@ -124,3 +134,22 @@ resource "null_resource" "wordpress_post_deployment" { helm_release.wordpress ] } + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} + +resource "kubernetes_secret" "cookiecutter" { + metadata { + name = "cookiecutter" + namespace = var.cert_manager_namespace + } + + # https://stackoverflow.com/questions/64134699/terraform-map-to-string-value + data = { + tags = jsonencode(local.tags) + } +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/outputs.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/outputs.tf index 10831d0c..5fe379d7 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/outputs.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/outputs.tf @@ -11,7 +11,7 @@ output "wordpressConfig" { } output "tags" { - value = var.tags + value = local.tags } output "wordpress-id" { diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/providers.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/providers.tf new file mode 100644 index 00000000..091ea2fc --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/providers.tf @@ -0,0 +1,19 @@ +data "aws_eks_cluster" "eks" { + name = var.shared_resource_namespace +} + +data "aws_eks_cluster_auth" "eks" { + name = var.shared_resource_namespace +} + +provider "kubernetes" { + host = data.aws_eks_cluster.eks.endpoint + cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) + token = data.aws_eks_cluster_auth.eks.token +} + +provider "kubectl" { + host = data.aws_eks_cluster.eks.endpoint + cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority.0.data) + token = data.aws_eks_cluster_auth.eks.token +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/variables.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/variables.tf index f9d58ed6..888f3f92 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/variables.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/variables.tf @@ -18,10 +18,6 @@ variable "wordpressConfig" { type = map(string) } -variable "tags" { - type = map(string) -} - variable "aws_region" { type = string } @@ -49,3 +45,9 @@ variable "resource_quota_memory" { variable "subnet_ids" { type = list(string) } + +variable "tags" { + description = "collection of all tags to add to this resource. execting the combination of global + environment + resouce tags." + type = map(string) + default = {} +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/versions.tf index f2de1571..3ab34592 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/modules/wordpress/versions.tf @@ -12,7 +12,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } local = { source = "hashicorp/local" diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/cloudfront/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/cloudfront/terragrunt.hcl index 2a81b630..00be8537 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/cloudfront/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/cloudfront/terragrunt.hcl @@ -29,7 +29,7 @@ locals { dependencies { paths = [ "../../../stacks/{{ cookiecutter.global_platform_shared_resource_identifier }}/vpc", - "../s3_openedx_storage", + "../s3", "../vpc", "../acm" ] @@ -50,8 +50,8 @@ dependency "vpc" { } -dependency "s3_openedx_storage" { - config_path = "../s3_openedx_storage" +dependency "s3" { + config_path = "../s3" # Configure mock outputs for the `validate` and `init` commands that are returned when there are no outputs available (e.g the # module hasn't been applied yet. diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/env.hcl b/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/env.hcl index 0834c282..7c11dd0f 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/env.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/env.hcl @@ -17,6 +17,9 @@ locals { environment_namespace = "${local.global_vars.locals.platform_name}-${local.global_vars.locals.platform_region}-${local.environment}" shared_resource_namespace = local.global_vars.locals.shared_resource_namespace db_prefix = replace(replace("${local.global_vars.locals.platform_name}_${local.environment}", ".", ""), "-", "") + s3_bucket_storage = "${local.environment_namespace}-storage" + s3_bucket_backup = "${local.environment_namespace}-backup" + s3_bucket_secrets = "${local.environment_namespace}-storage" tags = merge( local.global_vars.locals.tags, diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/kubernetes_ingress_clb/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/kubernetes_ingress_clb/terragrunt.hcl index ee5976b8..09e5b833 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/kubernetes_ingress_clb/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/kubernetes_ingress_clb/terragrunt.hcl @@ -14,7 +14,7 @@ locals { # Extract out common variables for reuse shared_resource_namespace = local.global_vars.locals.shared_resource_namespace root_domain = local.global_vars.locals.root_domain - services_subdomain = local.global_vars.locals.services_subdomain + services_subdomain = local.global_vars.locals.services_subdomain platform_name = local.global_vars.locals.platform_name platform_region = local.global_vars.locals.platform_region account_id = local.global_vars.locals.account_id @@ -22,6 +22,7 @@ locals { environment_namespace = local.environment_vars.locals.environment_namespace environment_domain = local.environment_vars.locals.environment_domain studio_subdomain = local.global_vars.locals.studio_subdomain + s3_bucket_storage = local.environment_vars.locals.s3_bucket_storage tags = merge( local.environment_vars.locals.tags, @@ -92,11 +93,12 @@ include { # These are the variables we have to pass in to use the module specified in the terragrunt configuration above inputs = { - aws_region = local.aws_region - environment_domain = local.environment_domain - environment_namespace = local.environment_namespace - studio_subdomain = local.studio_subdomain - shared_resource_namespace = local.shared_resource_namespace - root_domain = local.root_domain - tags = local.tags + aws_region = local.aws_region + environment_domain = local.environment_domain + environment_namespace = local.environment_namespace + studio_subdomain = local.studio_subdomain + shared_resource_namespace = local.shared_resource_namespace + root_domain = local.root_domain + s3_bucket_storage = local.s3_bucket_storage + tags = local.tags } diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/kubernetes_secrets/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/kubernetes_secrets/terragrunt.hcl index 46607c0a..04c29cc6 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/kubernetes_secrets/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/kubernetes_secrets/terragrunt.hcl @@ -15,6 +15,11 @@ locals { environment = local.environment_vars.locals.environment resource_name = local.environment_vars.locals.shared_resource_namespace root_domain = local.global_vars.locals.root_domain + + tags = merge( + local.environment_vars.locals.tags, + { Name = "${local.resource_name}" } + ) } @@ -85,4 +90,5 @@ inputs = { resource_name = local.resource_name environment_namespace = local.environment_namespace root_domain = local.root_domain + tags = local.tags } diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/mongodb/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/mongodb/terragrunt.hcl index e5ff9234..171e7eb4 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/mongodb/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/mongodb/terragrunt.hcl @@ -16,6 +16,11 @@ locals { environment_namespace = local.environment_vars.locals.environment_namespace shared_resource_namespace = local.environment_vars.locals.shared_resource_namespace db_prefix = local.environment_vars.locals.db_prefix + + tags = merge( + local.environment_vars.locals.tags, + { Name = "${local.resource_name}" } + ) } dependencies { @@ -99,4 +104,5 @@ inputs = { environment_domain = local.environment_domain environment_namespace = local.environment_namespace shared_resource_namespace = local.shared_resource_namespace + tags = local.tags } diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/mysql/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/mysql/terragrunt.hcl index 41a4a2c7..d2026b02 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/mysql/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/mysql/terragrunt.hcl @@ -17,6 +17,11 @@ locals { shared_resource_namespace = local.environment_vars.locals.shared_resource_namespace environment = local.environment_vars.locals.environment db_prefix = local.environment_vars.locals.db_prefix + + tags = merge( + local.environment_vars.locals.tags, + { Name = "${local.resource_name}" } + ) } dependencies { @@ -102,4 +107,5 @@ inputs = { environment_domain = local.environment_domain environment_namespace = local.environment_namespace shared_resource_namespace = local.shared_resource_namespace + tags = local.tags } diff --git a/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/s3_openedx_storage/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/s3/terragrunt.hcl similarity index 89% rename from {{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/s3_openedx_storage/terragrunt.hcl rename to {{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/s3/terragrunt.hcl index 1ba86879..c860c43e 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/s3_openedx_storage/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/environments/{{cookiecutter.environment_name}}/s3/terragrunt.hcl @@ -14,17 +14,13 @@ locals { # Extract out common variables for reuse kubernetes_name = local.environment_vars.locals.shared_resource_namespace aws_region = local.global_vars.locals.aws_region - resource_name_storage = "${local.environment_vars.locals.environment_namespace}-storage" - resource_name_backup = "${local.environment_vars.locals.environment_namespace}-backup" - resource_name_secrets = "${local.environment_vars.locals.environment_namespace}-secrets" + resource_name_storage = local.environment_vars.locals.s3_bucket_storage + resource_name_backup = local.environment_vars.locals.s3_bucket_backup + resource_name_secrets = local.environment_vars.locals.s3_bucket_secrets environment_domain = local.environment_vars.locals.environment_domain environment_studio_domain = "${local.environment_vars.locals.environment_studio_subdomain}.${local.environment_domain}" environment_namespace = local.environment_vars.locals.environment_namespace - - tags = merge( - local.environment_vars.locals.tags, - { Name = "${local.environment_vars.locals.environment_namespace}" } - ) + tags = local.environment_vars.locals.tags } @@ -79,7 +75,7 @@ dependency "kubernetes" { # Terragrunt will copy the Terraform configurations specified by the source parameter, along with any files in the # working directory, into a temporary folder, and execute your Terraform commands in that folder. terraform { - source = "../../modules//s3_openedx_storage" + source = "../../modules//s3" } # Include all settings from the root terragrunt.hcl file diff --git a/{{cookiecutter.github_repo_name}}/terraform/global.hcl b/{{cookiecutter.github_repo_name}}/terraform/global.hcl index 64e8e40c..8d6f4a6d 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/global.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/global.hcl @@ -19,12 +19,13 @@ locals { studio_subdomain = "{{ cookiecutter.environment_studio_subdomain }}" tags = { - "cookiecutter/platform_name" = local.platform_name - "cookiecutter/platform_region" = local.platform_region - "cookiecutter/shared_resource_identifier" = local.shared_resource_identifier - "cookiecutter/root_domain" = local.root_domain - "cookiecutter/services_subdomain" = local.services_subdomain - "cookiecutter/terraform" = "true" + "cookiecutter/global/platform_name" = local.platform_name + "cookiecutter/global/platform_region" = local.platform_region + "cookiecutter/global/shared_resource_identifier" = local.shared_resource_identifier + "cookiecutter/global/shared_resource_namespace" = local.shared_resource_namespace + "cookiecutter/global/root_domain" = local.root_domain + "cookiecutter/global/services_subdomain" = local.services_subdomain + "cookiecutter/global/aws_region" = local.aws_region } } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/etc/update-motd.d/09-welcome-banner.tpl b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/etc/update-motd.d/09-welcome-banner.tpl index 6dd7c574..96358646 100755 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/etc/update-motd.d/09-welcome-banner.tpl +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/etc/update-motd.d/09-welcome-banner.tpl @@ -5,7 +5,7 @@ # # date: aug-2022 # -# usage: print the login banner for openedx_devops cookiecutter. +# usage: print the login banner #------------------------------------------------------------------------------ COLUMNS=78 title="Welcome to the Bastion Server for ${platform_name}" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/etc/update-motd.d/10-help-text.tpl b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/etc/update-motd.d/10-help-text.tpl index aeab642b..3eb53c78 100755 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/etc/update-motd.d/10-help-text.tpl +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/etc/update-motd.d/10-help-text.tpl @@ -5,7 +5,7 @@ # # date: aug-2022 # -# usage: print the login help menu for openedx_devops cookiecutter. +# usage: print the login help menu #------------------------------------------------------------------------------ printf " Installed Applications\n" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/kubernetes.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/kubernetes.tf index 6b829f86..72e4af62 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/kubernetes.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/kubernetes.tf @@ -7,20 +7,6 @@ # usage: create an RDS MySQL instance. # store the MySQL credentials in Kubernetes Secrets #------------------------------------------------------------------------------ -data "aws_eks_cluster" "eks" { - name = var.resource_name -} - -data "aws_eks_cluster_auth" "eks" { - name = var.resource_name -} - -provider "kubernetes" { - host = data.aws_eks_cluster.eks.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) - token = data.aws_eks_cluster_auth.eks.token -} - resource "kubernetes_secret" "ssh_secret" { metadata { name = "bastion-ssh-key" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/main.tf index c834fd58..284e3d91 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/main.tf @@ -8,6 +8,16 @@ #------------------------------------------------------------------------------ locals { hostname = "bastion.${var.services_subdomain}" + + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/ec2_bastion" + "cookiecutter/module/version" = "" + } + ) + } resource "aws_instance" "bastion" { @@ -18,7 +28,14 @@ resource "aws_instance" "bastion" { subnet_id = var.subnet_ids[random_integer.subnet_id.result] monitoring = false ebs_optimized = false - tags = var.tags + + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_instance" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) vpc_security_group_ids = [ resource.aws_security_group.sg_bastion.id, @@ -28,7 +45,13 @@ resource "aws_instance" "bastion" { root_block_device { delete_on_termination = true volume_size = var.volume_size - tags = var.tags + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_instance" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) } # aws cli configuration @@ -43,13 +66,13 @@ resource "aws_instance" "bastion" { inline = [ "mkdir ~/.aws", "mkdir ~/scripts", - "rm -rf /tmp/openedx_devops", - "mkdir /tmp/openedx_devops", - "mkdir /tmp/openedx_devops/etc/", + "rm -rf /tmp/cookiecutter", + "mkdir /tmp/cookiecutter", + "mkdir /tmp/cookiecutter/etc/", "echo PATH='$HOME/scripts:$PATH' >> ~/.profile", # report what we've done so far - "echo created folder /tmp/openedx_devops", + "echo created folder /tmp/cookiecutter", "echo created folder ~/.aws", "echo created folder ~/scripts", "echo added ~/scripts to path", @@ -91,7 +114,7 @@ resource "aws_instance" "bastion" { } content = data.template_file.welcome_banner.rendered - destination = "/tmp/openedx_devops/etc/09-welcome-banner" + destination = "/tmp/cookiecutter/etc/09-welcome-banner" } provisioner "file" { @@ -103,7 +126,7 @@ resource "aws_instance" "bastion" { } content = data.template_file.help_text.rendered - destination = "/tmp/openedx_devops/etc/10-help-text" + destination = "/tmp/cookiecutter/etc/10-help-text" } # installation bootstrapper script @@ -171,7 +194,7 @@ resource "aws_instance" "bastion" { # "/home/ubuntu/scripts/install.sh", # 3.) clean up - "rm -rf /tmp/openedx_devops", + "rm -rf /tmp/cookiecutter", ] } @@ -235,11 +258,11 @@ data "aws_security_group" "stack-namespace-node" { # only allows public ssh access. resource "aws_security_group" "sg_bastion" { name_prefix = "${var.resource_name}-bastion" - description = "openedx_devops: Public ssh access" + description = "cookiecutter: Public ssh access" vpc_id = var.vpc_id ingress { - description = "openedx_devops: public ssh from anywhere" + description = "cookiecutter: public ssh from anywhere" from_port = 22 to_port = 22 protocol = "tcp" @@ -247,7 +270,7 @@ resource "aws_security_group" "sg_bastion" { } egress { - description = "openedx_devops: public ssh out to anywhere" + description = "cookiecutter: public ssh out to anywhere" from_port = 0 to_port = 0 protocol = "-1" @@ -255,7 +278,13 @@ resource "aws_security_group" "sg_bastion" { ipv6_cidr_blocks = ["::/0"] } - tags = var.tags + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_security_group" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) } @@ -263,7 +292,13 @@ resource "aws_security_group" "sg_bastion" { # add to the root domain. resource "aws_eip" "bastion" { instance = aws_instance.bastion.id - tags = var.tags + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_eip" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) } @@ -305,7 +340,13 @@ data "template_file" "update" { resource "aws_iam_user" "aws_cli" { name = "${var.resource_name}-bastion" path = "/system/bastion-user/" - tags = var.tags + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_iam_user" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) } resource "aws_iam_access_key" "aws_cli" { @@ -359,3 +400,10 @@ data "template_file" "help_text" { aws_region = var.aws_region } } + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/providers.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/providers.tf new file mode 100644 index 00000000..57e8384d --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/providers.tf @@ -0,0 +1,13 @@ +data "aws_eks_cluster" "eks" { + name = var.resource_name +} + +data "aws_eks_cluster_auth" "eks" { + name = var.resource_name +} + +provider "kubernetes" { + host = data.aws_eks_cluster.eks.endpoint + cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) + token = data.aws_eks_cluster_auth.eks.token +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/scripts/install-tasks.sh b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/scripts/install-tasks.sh index 7fd91166..1762fda2 100755 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/scripts/install-tasks.sh +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/scripts/install-tasks.sh @@ -11,12 +11,12 @@ #-------------------------------------------------------- -if [ -d "/tmp/openedx_devops/etc" ] +if [ -d "/tmp/cookiecutter/etc" ] then - sudo cp /tmp/openedx_devops/etc/09-welcome-banner /etc/update-motd.d/09-welcome-banner + sudo cp /tmp/cookiecutter/etc/09-welcome-banner /etc/update-motd.d/09-welcome-banner sudo chmod 755 /etc/update-motd.d/09-welcome-banner - sudo cp /tmp/openedx_devops/etc/10-help-text /etc/update-motd.d/10-help-text + sudo cp /tmp/cookiecutter/etc/10-help-text /etc/update-motd.d/10-help-text sudo chmod 755 /etc/update-motd.d/10-help-text # set execute permissions for only the banner components @@ -26,7 +26,7 @@ then sudo chmod 644 /etc/update-motd.d/85-fwupd sudo chmod 644 /etc/update-motd.d/88-esm-announce - echo "added openedx_devops login banner" + echo "added cookiecutter login banner" fi # setup a .kube/config file w correct permissions diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/versions.tf index 673cf644..cbaf7ad0 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/ec2_bastion/versions.tf @@ -20,7 +20,7 @@ terraform { } aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } kubernetes = { source = "hashicorp/kubernetes" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/addon_ebs_csi_driver.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/addon_ebs_csi_driver.tf index 02304a41..e848b594 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/addon_ebs_csi_driver.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/addon_ebs_csi_driver.tf @@ -39,7 +39,13 @@ resource "aws_iam_role" "AmazonEKS_EBS_CSI_DriverRole" { } ] }) - tags = var.tags + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_iam_role" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) } # 3. Attach the required AWS managed policy to the role diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/main.tf index b6c601da..e969e353 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/main.tf @@ -16,54 +16,20 @@ locals { # Used by Karpenter config to determine correct partition (i.e. - `aws`, `aws-gov`, `aws-cn`, etc.) partition = data.aws_partition.current.partition -} - -resource "aws_security_group" "worker_group_mgmt" { - name_prefix = "${var.namespace}-eks_hosting_group_mgmt" - description = "openedx_devops: Ingress CLB worker group management" - vpc_id = var.vpc_id - - ingress { - description = "openedx_devops: Ingress CLB" - from_port = 22 - to_port = 22 - protocol = "tcp" - - cidr_blocks = [ - "10.0.0.0/8", - ] - } - - tags = var.tags - -} - -resource "aws_security_group" "all_worker_mgmt" { - name_prefix = "${var.namespace}-eks_all_worker_management" - description = "openedx_devops: Ingress CLB worker management" - vpc_id = var.vpc_id - ingress { - description = "openedx_devops: Ingress CLB" - from_port = 22 - to_port = 22 - protocol = "tcp" - - cidr_blocks = [ - "10.0.0.0/8", - "172.16.0.0/12", - "192.168.0.0/16", - ] - } - - tags = var.tags + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/kubernetes" + } + ) } - module "eks" { source = "terraform-aws-modules/eks/aws" - version = "{{ cookiecutter.terraform_aws_modules_eks }}" + version = "~> {{ cookiecutter.terraform_aws_modules_eks }}" cluster_name = var.namespace cluster_version = var.kubernetes_cluster_version cluster_endpoint_private_access = true @@ -93,11 +59,15 @@ module "eks" { aws_auth_users = var.map_users tags = merge( - var.tags, + local.tags, # Tag node group resources for Karpenter auto-discovery # NOTE - if creating multiple security groups with this module, only tag the # security group that Karpenter should utilize with the following tag - { "karpenter.sh/discovery" = var.namespace } + { "karpenter.sh/discovery" = var.namespace }, + { + "cookiecutter/resource/source" = "terraform-aws-modules/eks/aws" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_aws_modules_eks }}" + } ) cluster_addons = { @@ -118,7 +88,7 @@ module "eks" { node_security_group_additional_rules = { ingress_self_all = { - description = "openedx_devops: Node to node all ports/protocols" + description = "cookiecutter: Node to node all ports/protocols" protocol = "-1" from_port = 0 to_port = 0 @@ -129,7 +99,7 @@ module "eks" { ] } port_8443 = { - description = "openedx_devops: open port 8443 to vpc" + description = "cookiecutter: open port 8443 to vpc" protocol = "-1" from_port = 8443 to_port = 8443 @@ -137,7 +107,7 @@ module "eks" { source_node_security_group = true } egress_all = { - description = "openedx_devops: Node all egress" + description = "cookiecutter: Node all egress" protocol = "-1" from_port = 0 to_port = 0 @@ -178,8 +148,16 @@ module "eks" { instance_types = ["${var.eks_service_group_instance_type}"] tags = merge( - var.tags, - { Name = "eks-${var.shared_resource_identifier}" } + local.tags, + module.cookiecutter_meta.tags, + # Tag node group resources for Karpenter auto-discovery + # NOTE - if creating multiple security groups with this module, only tag the + # security group that Karpenter should utilize with the following tag + { Name = "eks-${var.shared_resource_identifier}-{{ cookiecutter.global_platform_shared_resource_identifier }}" }, + { + "cookiecutter/resource/source" = "terraform-aws-modules/eks/aws" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_aws_modules_eks }}" + } ) } @@ -204,14 +182,82 @@ module "eks" { instance_types = ["${var.eks_hosting_group_instance_type}"] tags = merge( - var.tags, - { Name = "eks-${var.shared_resource_identifier}-hosting" } + local.tags, + module.cookiecutter_meta.tags, + # Tag node group resources for Karpenter auto-discovery + # NOTE - if creating multiple security groups with this module, only tag the + # security group that Karpenter should utilize with the following tag + { Name = "eks-${var.shared_resource_identifier}-hosting" }, + { + "cookiecutter/resource/source" = "terraform-aws-modules/eks/aws" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_aws_modules_eks }}" + } ) } } } +#============================================================================== +# SUPPORTING RESOURCES +#============================================================================== + +resource "aws_security_group" "worker_group_mgmt" { + name_prefix = "${var.namespace}-eks_hosting_group_mgmt" + description = "cookiecutter: Ingress CLB worker group management" + vpc_id = var.vpc_id + + ingress { + description = "cookiecutter: Ingress CLB" + from_port = 22 + to_port = 22 + protocol = "tcp" + + cidr_blocks = [ + "10.0.0.0/8", + ] + } + + tags = merge( + local.tags, + { Name = "eks-${var.shared_resource_identifier}-worker_group_mgmt" }, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_security_group" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) +} + +resource "aws_security_group" "all_worker_mgmt" { + name_prefix = "${var.namespace}-eks_all_worker_management" + description = "cookiecutter: Ingress CLB worker management" + vpc_id = var.vpc_id + + ingress { + description = "cookiecutter: Ingress CLB" + from_port = 22 + to_port = 22 + protocol = "tcp" + + cidr_blocks = [ + "10.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16", + ] + } + + tags = merge( + local.tags, + { Name = "eks-${var.shared_resource_identifier}-all_worker_mgmt" }, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_security_group" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) +} + + + resource "kubernetes_namespace" "namespace-shared" { metadata { name = var.namespace @@ -226,3 +272,10 @@ resource "kubernetes_namespace" "wordpress" { } depends_on = [module.eks] }{% endif -%} + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/variables.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/variables.tf index f59a6427..0f0e72dd 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/variables.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/variables.tf @@ -110,6 +110,6 @@ variable "bastion_iam_arn" { } variable "kms_key_owners" { - type = list + type = list(any) default = [] } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/versions.tf index 0a05429f..82ed8aed 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes/versions.tf @@ -18,7 +18,7 @@ terraform { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } kubectl = { source = "gavinbunney/kubectl" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_cert_manager/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_cert_manager/main.tf index 6c756223..412ed616 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_cert_manager/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_cert_manager/main.tf @@ -16,12 +16,16 @@ # helm show values jetstack/cert-manager #------------------------------------------------------------------------------ -data "template_file" "cert-manager-values" { - template = file("${path.module}/manifests/cert-manager-values.yaml.tpl") - vars = { - role_arn = module.cert_manager_irsa.iam_role_arn - namespace = var.cert_manager_namespace - } +locals { + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/kubernetes_cert_manager" + "cookiecutter/resource/source" = "jetstack/cert-manager" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_helm_cert_manager }}" + } + ) } resource "helm_release" "cert-manager" { @@ -31,7 +35,7 @@ resource "helm_release" "cert-manager" { chart = "cert-manager" repository = "jetstack" - version = "{{ cookiecutter.terraform_helm_cert_manager }}" + version = "~> {{ cookiecutter.terraform_helm_cert_manager }}" values = [ data.template_file.cert-manager-values.rendered ] @@ -40,10 +44,18 @@ resource "helm_release" "cert-manager" { #------------------------------------------------------------------------------ # SUPPORTING RESOURCES #------------------------------------------------------------------------------ +data "template_file" "cert-manager-values" { + template = file("${path.module}/manifests/cert-manager-values.yaml.tpl") + vars = { + role_arn = module.cert_manager_irsa.iam_role_arn + namespace = var.cert_manager_namespace + } +} + resource "aws_iam_policy" "cert_manager_policy" { name = "${var.namespace}-cert-manager-policy" path = "/" - description = "openedx_devops: Policy, which allows CertManager to create Route53 records" + description = "cookiecutter: Policy, which allows CertManager to create Route53 records" policy = jsonencode({ "Version" : "2012-10-17", @@ -68,15 +80,42 @@ resource "aws_iam_policy" "cert_manager_policy" { } ] }) + + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_iam_policy" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) } module "cert_manager_irsa" { source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role-with-oidc" - version = "{{ cookiecutter.terraform_aws_modules_iam_assumable_role_with_oidc }}" + version = "~> {{ cookiecutter.terraform_aws_modules_iam_assumable_role_with_oidc }}" create_role = true role_name = "${var.namespace}-cert_manager-irsa" provider_url = replace(data.aws_eks_cluster.eks.identity[0].oidc[0].issuer, "https://", "") role_policy_arns = [aws_iam_policy.cert_manager_policy.arn] oidc_fully_qualified_subjects = ["system:serviceaccount:${var.cert_manager_namespace}:cert-manager"] } + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} + +resource "kubernetes_secret" "cookiecutter" { + metadata { + name = "cookiecutter-terraform" + namespace = var.cert_manager_namespace + } + + # https://stackoverflow.com/questions/64134699/terraform-map-to-string-value + data = { + tags = jsonencode(local.tags) + } +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_cert_manager/variables.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_cert_manager/variables.tf index d9536126..799b0388 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_cert_manager/variables.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_cert_manager/variables.tf @@ -25,3 +25,9 @@ variable "aws_region" { variable "services_subdomain" { type = string } + +variable "tags" { + description = "A map of tags to add to all resources. Tags added to launch configuration or templates override these values for ASG Tags only." + type = map(string) + default = {} +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_cert_manager/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_cert_manager/versions.tf index a5b7eb4a..3ca1816b 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_cert_manager/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_cert_manager/versions.tf @@ -18,7 +18,7 @@ terraform { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } kubectl = { source = "gavinbunney/kubectl" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_dashboard/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_dashboard/main.tf index 799cb5ab..a504185f 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_dashboard/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_dashboard/main.tf @@ -25,6 +25,17 @@ # echo https://127.0.0.1:8443/ # kubectl -n default port-forward $POD_NAME 8443:8443 #----------------------------------------------------------- +locals { + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/kubernetes_dashboard" + "cookiecutter/resource/source" = "kubernetes.github.io/dashboard" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_helm_dashboard }}" + } + ) +} data "template_file" "dashboard-values" { template = file("${path.module}/yml/values.yaml") @@ -32,14 +43,33 @@ data "template_file" "dashboard-values" { resource "helm_release" "dashboard" { name = "common" - namespace = "kubernetes-dashboard" + namespace = var.dashboard_namespace create_namespace = true chart = "kubernetes-dashboard" repository = "https://kubernetes.github.io/dashboard/" - version = "~> 6.0" + version = "~> {{ cookiecutter.terraform_helm_dashboard }}" values = [ data.template_file.dashboard-values.rendered ] } + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} + +resource "kubernetes_secret" "cookiecutter" { + metadata { + name = "cookiecutter-terraform" + namespace = var.dashboard_namespace + } + + # https://stackoverflow.com/questions/64134699/terraform-map-to-string-value + data = { + tags = jsonencode(local.tags) + } +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_dashboard/variables.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_dashboard/variables.tf index 9ff6583d..b4224e5c 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_dashboard/variables.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_dashboard/variables.tf @@ -8,14 +8,16 @@ #------------------------------------------------------------------------------ variable "dashboard_namespace" { - + type = string } variable "dashboard_account_name" { - + type = string } variable "stack_namespace" { - + type = string } variable "tags" { - + description = "A map of tags to add to all resources. Tags added to launch configuration or templates override these values for ASG Tags only." + type = map(string) + default = {} } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_ingress_clb/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_ingress_clb/main.tf index 59d94d57..7de76804 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_ingress_clb/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_ingress_clb/main.tf @@ -20,6 +20,17 @@ # helm show values ingress-nginx/ingress-nginx #------------------------------------------------------------------------------ +locals { + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/kubernetes_ingress_clb" + "cookiecutter/resource/source" = "kubernetes.github.io/ingress-nginx" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_helm_ingress_nginx_controller }}" + } + ) +} data "template_file" "nginx-values" { template = file("${path.module}/yml/nginx-values.yaml") @@ -32,7 +43,7 @@ resource "helm_release" "ingress_nginx_controller" { chart = "ingress-nginx" repository = "https://kubernetes.github.io/ingress-nginx" - version = "{{ cookiecutter.terraform_helm_ingress_nginx_controller }}" + version = "~> {{ cookiecutter.terraform_helm_ingress_nginx_controller }}" values = [ data.template_file.nginx-values.rendered @@ -67,3 +78,22 @@ resource "helm_release" "ingress_nginx_controller" { } } + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} + +resource "kubernetes_secret" "cookiecutter" { + metadata { + name = "cookiecutter-terraform" + namespace = var.namespace + } + + # https://stackoverflow.com/questions/64134699/terraform-map-to-string-value + data = { + tags = jsonencode(local.tags) + } +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_ingress_clb/variables.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_ingress_clb/variables.tf index f950b4f3..065d974b 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_ingress_clb/variables.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_ingress_clb/variables.tf @@ -23,3 +23,9 @@ variable "namespace" { variable "stack_namespace" { type = string } + +variable "tags" { + description = "A map of tags to add to all resources. Tags added to launch configuration or templates override these values for ASG Tags only." + type = map(string) + default = {} +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_ingress_clb/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_ingress_clb/versions.tf index a5b7eb4a..3ca1816b 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_ingress_clb/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_ingress_clb/versions.tf @@ -18,7 +18,7 @@ terraform { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } kubectl = { source = "gavinbunney/kubectl" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_karpenter/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_karpenter/main.tf index fc5d7cf6..af9f3764 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_karpenter/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_karpenter/main.tf @@ -25,6 +25,19 @@ # FIXED. but see note below about version. # # see: https://registry.terraform.io/modules/terraform-aws-modules/iam/aws/latest/submodules/iam-role-for-service-accounts-eks +locals { + karpenter_namespace = "karpenter" + + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/kubernetes_karpenter" + "cookiecutter/resource/source" = "charts.karpenter.sh" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_helm_karpenter }}" + } + ) +} data "template_file" "karpenter-values" { template = file("${path.module}/yml/karpenter-values.yaml") @@ -32,14 +45,14 @@ data "template_file" "karpenter-values" { resource "helm_release" "karpenter" { - namespace = "karpenter" + namespace = local.karpenter_namespace create_namespace = true name = "karpenter" repository = "https://charts.karpenter.sh" chart = "karpenter" - version = "{{ cookiecutter.terraform_helm_karpenter }}" + version = "~> {{ cookiecutter.terraform_helm_karpenter }}" values = [ data.template_file.karpenter-values.rendered @@ -92,7 +105,13 @@ module "karpenter_controller_irsa_role" { } } - tags = var.tags + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks" + "cookiecutter/resource/version" = "latest" + } + ) } @@ -163,7 +182,13 @@ resource "aws_iam_role" "ec2_spot_fleet_tagging_role" { ] }) - tags = var.tags + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_iam_role" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) } resource "aws_iam_role_policy_attachment" "ec2_spot_fleet_tagging" { @@ -178,3 +203,22 @@ resource "kubectl_manifest" "vpa-karpenter" { helm_release.karpenter ] } + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} + +resource "kubernetes_secret" "cookiecutter" { + metadata { + name = "cookiecutter-terraform" + namespace = local.karpenter_namespace + } + + # https://stackoverflow.com/questions/64134699/terraform-map-to-string-value + data = { + tags = jsonencode(local.tags) + } +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_karpenter/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_karpenter/versions.tf index 0a05429f..82ed8aed 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_karpenter/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_karpenter/versions.tf @@ -18,7 +18,7 @@ terraform { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } kubectl = { source = "gavinbunney/kubectl" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubeapps/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubeapps/main.tf index e4a02b66..fdcd017b 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubeapps/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubeapps/main.tf @@ -26,6 +26,16 @@ locals { kubeapps_namespace = "kubeapps" kubeapps_account_name = "kubeapps-admin" kubeapps_ingress_hostname = "${local.kubeapps_namespace}.${var.services_subdomain}" + + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/kubernetes_kubeapps" + "cookiecutter/resource/source" = "charts.bitnami.com/bitnami/kubeapps" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_helm_kubeapps }}" + } + ) } @@ -40,7 +50,7 @@ resource "helm_release" "kubeapps" { name = "kubeapps" repository = "https://charts.bitnami.com/bitnami" chart = "kubeapps" - version = "{{ cookiecutter.terraform_helm_kubeapps }}" + version = "~> {{ cookiecutter.terraform_helm_kubeapps }}" # see https://docs.bitnami.com/kubernetes/infrastructure/kubeapps/configuration/expose-service/ set { @@ -52,3 +62,22 @@ resource "helm_release" "kubeapps" { kubernetes_namespace.kubeapps ] } + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} + +resource "kubernetes_secret" "cookiecutter" { + metadata { + name = "cookiecutter-terraform" + namespace = local.kubeapps_namespace + } + + # https://stackoverflow.com/questions/64134699/terraform-map-to-string-value + data = { + tags = jsonencode(local.tags) + } +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubeapps/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubeapps/versions.tf index 0a05429f..82ed8aed 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubeapps/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubeapps/versions.tf @@ -18,7 +18,7 @@ terraform { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } kubectl = { source = "gavinbunney/kubectl" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubecost/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubecost/main.tf index aed58b8a..a4f85697 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubecost/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubecost/main.tf @@ -23,6 +23,17 @@ #----------------------------------------------------------- locals { cost_analyzer = "cost-analyzer" + kubecost = "kubecost" + + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/kubernetes_kubecost" + "cookiecutter/resource/source" = "kubecost.github.io/cost-analyzer/" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_helm_kubecost }}" + } + ) } data "template_file" "kubecost-values" { @@ -36,15 +47,34 @@ data "template_file" "kubecost-values" { resource "helm_release" "kubecost" { name = local.cost_analyzer - namespace = "kubecost" + namespace = local.kubecost create_namespace = true repository = "https://kubecost.github.io/cost-analyzer/" chart = "cost-analyzer" - version = "{{ cookiecutter.terraform_helm_kubecost }}" + version = "~> {{ cookiecutter.terraform_helm_kubecost }}" values = [ data.template_file.kubecost-values.rendered ] } + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} + +resource "kubernetes_secret" "cookiecutter" { + metadata { + name = "cookiecutter-terraform" + namespace = local.kubecost + } + + # https://stackoverflow.com/questions/64134699/terraform-map-to-string-value + data = { + tags = jsonencode(local.tags) + } +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubecost/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubecost/versions.tf index fe721fc1..61ea499d 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubecost/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_kubecost/versions.tf @@ -18,7 +18,7 @@ terraform { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } kubectl = { source = "gavinbunney/kubectl" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_metricsserver/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_metricsserver/main.tf index d2546722..a6503d62 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_metricsserver/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_metricsserver/main.tf @@ -21,23 +21,54 @@ # helm search repo metrics-server # helm show values metrics-server/metrics-server #----------------------------------------------------------- +locals { + metrics_server = "metrics-server" + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/kubernetes_metricsserver" + "cookiecutter/resource/source" = "kubernetes-sigs.github.io/metrics-server/" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_helm_metrics_server }}" + } + ) +} data "template_file" "metrics-server-values" { template = file("${path.module}/config/metrics-server-values.yaml") vars = {} } resource "helm_release" "metrics_server" { - namespace = "metrics-server" + namespace = local.metrics_server create_namespace = true name = "metrics-server" repository = "https://kubernetes-sigs.github.io/metrics-server/" chart = "metrics-server" - version = "{{ cookiecutter.terraform_helm_metrics_server }}" + version = "~> {{ cookiecutter.terraform_helm_metrics_server }}" values = [ data.template_file.metrics-server-values.rendered ] } + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} + +resource "kubernetes_secret" "cookiecutter" { + metadata { + name = "cookiecutter-terraform" + namespace = local.metrics_server + } + + # https://stackoverflow.com/questions/64134699/terraform-map-to-string-value + data = { + tags = jsonencode(local.tags) + } +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_metricsserver/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_metricsserver/versions.tf index 0a05429f..82ed8aed 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_metricsserver/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_metricsserver/versions.tf @@ -18,7 +18,7 @@ terraform { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } kubectl = { source = "gavinbunney/kubectl" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_prometheus/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_prometheus/main.tf index 251e770f..c7f22c41 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_prometheus/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_prometheus/main.tf @@ -34,6 +34,20 @@ # kubectl delete crd servicemonitors.monitoring.coreos.com # kubectl delete crd thanosrulers.monitoring.coreos.com #----------------------------------------------------------- +locals { + cost_analyzer = "cost-analyzer" + prometheus = "prometheus" + + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/kubernetes_prometheus" + "cookiecutter/resource/source" = "prometheus-community.github.io/helm-charts/kube-prometheus-stack" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_helm_prometheus }}" + } + ) +} data "template_file" "prometheus-values" { template = file("${path.module}/yml/prometheus-values.yaml") @@ -41,7 +55,7 @@ data "template_file" "prometheus-values" { } resource "helm_release" "prometheus" { - namespace = "prometheus" + namespace = local.prometheus create_namespace = true name = "prometheus" @@ -72,3 +86,22 @@ resource "random_password" "grafana" { version = "1" } } + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} + +resource "kubernetes_secret" "cookiecutter" { + metadata { + name = "cookiecutter-terraform" + namespace = local.prometheus + } + + # https://stackoverflow.com/questions/64134699/terraform-map-to-string-value + data = { + tags = jsonencode(local.tags) + } +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_prometheus/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_prometheus/versions.tf index 0a05429f..82ed8aed 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_prometheus/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_prometheus/versions.tf @@ -18,7 +18,7 @@ terraform { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } kubectl = { source = "gavinbunney/kubectl" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_vpa/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_vpa/main.tf index 37959202..09a951f2 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_vpa/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_vpa/main.tf @@ -22,7 +22,19 @@ # NOTE: run `helm repo update` prior to running this # Terraform module. #----------------------------------------------------------- +locals { + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/kubernetes_vpa" + "cookiecutter/resource/source" = "cowboysysop.github.io/charts/vertical-pod-autoscaler" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_helm_vertical_pod_autoscaler }}" + } + ) + +} data "template_file" "vertical-pod-autoscaler-values" { template = file("${path.module}/yml/vertical-pod-autoscaler-values.yaml") vars = {} @@ -35,10 +47,17 @@ resource "helm_release" "vpa" { name = "vertical-pod-autoscaler" repository = "https://cowboysysop.github.io/charts/" chart = "vertical-pod-autoscaler" - version = "{{ cookiecutter.terraform_helm_vertical_pod_autoscaler }}" + version = "~> {{ cookiecutter.terraform_helm_vertical_pod_autoscaler }}" values = [ data.template_file.vertical-pod-autoscaler-values.rendered ] } + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_vpa/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_vpa/versions.tf index 0a05429f..82ed8aed 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_vpa/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/kubernetes_vpa/versions.tf @@ -18,7 +18,7 @@ terraform { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } kubectl = { source = "gavinbunney/kubectl" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/etc/update-motd.d/10-help-text b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/etc/update-motd.d/10-help-text index 3701d067..39de9822 100755 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/etc/update-motd.d/10-help-text +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/etc/update-motd.d/10-help-text @@ -5,8 +5,7 @@ # # date: aug-2022 # -# usage: print the login banner for openedx_devops -# cookiecutter. +# usage: print the login banner #------------------------------------------------------------------------------ diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/kubernetes.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/kubernetes.tf index f1093f3c..98cac7bf 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/kubernetes.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/kubernetes.tf @@ -7,20 +7,6 @@ # usage: create a remote MongoDB instance. # store the MySQL credentials in Kubernetes Secrets #------------------------------------------------------------------------------ -data "aws_eks_cluster" "eks" { - name = var.stack_namespace -} - -data "aws_eks_cluster_auth" "eks" { - name = var.stack_namespace -} - -provider "kubernetes" { - host = data.aws_eks_cluster.eks.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) - token = data.aws_eks_cluster_auth.eks.token -} - resource "kubernetes_secret" "mongodb_admin" { metadata { name = "mongodb-admin" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/main.tf index 6f4d716a..3bb9cfee 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/main.tf @@ -23,6 +23,15 @@ locals { ssh_private_key_filename = "${var.stack_namespace}-mongodb.pem" host_name = "mongodb.${var.services_subdomain}" + + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/mongodb" + } + ) + } # create the MongoDB instance and install configuration files. @@ -34,7 +43,14 @@ resource "aws_instance" "mongodb" { monitoring = false associate_public_ip_address = false ebs_optimized = false - tags = var.tags + + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_instance" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) vpc_security_group_ids = [ aws_security_group.sg_mongodb.id, @@ -45,7 +61,13 @@ resource "aws_instance" "mongodb" { root_block_device { delete_on_termination = true volume_size = 8 - tags = var.tags + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_instance" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) } provisioner "file" { @@ -85,7 +107,7 @@ resource "aws_instance" "mongodb" { } content = data.template_file.welcome_banner.rendered - destination = "/tmp/openedx_devops/mongodb/etc/09-welcome-banner" + destination = "/tmp/cookiecutter/mongodb/etc/09-welcome-banner" } provisioner "file" { @@ -97,7 +119,7 @@ resource "aws_instance" "mongodb" { } source = "${path.module}/etc/update-motd.d/10-help-text" - destination = "/tmp/openedx_devops/mongodb/etc/10-help-text" + destination = "/tmp/cookiecutter/mongodb/etc/10-help-text" } @@ -110,7 +132,7 @@ resource "aws_instance" "mongodb" { } content = data.template_file.aws_config.rendered - destination = "/tmp/openedx_devops/mongodb/.aws/config" + destination = "/tmp/cookiecutter/mongodb/.aws/config" } provisioner "file" { @@ -122,7 +144,7 @@ resource "aws_instance" "mongodb" { } content = data.template_file.aws_credentials.rendered - destination = "/tmp/openedx_devops/mongodb/.aws/credentials" + destination = "/tmp/cookiecutter/mongodb/.aws/credentials" } # installation bootstrapper script @@ -135,7 +157,7 @@ resource "aws_instance" "mongodb" { } source = "${path.module}/scripts/" - destination = "/tmp/openedx_devops/mongodb/scripts/" + destination = "/tmp/cookiecutter/mongodb/scripts/" } # add ssh key to the bastion @@ -185,7 +207,7 @@ resource "null_resource" "install_script" { } content = data.template_file.mongod_conf.rendered - destination = "/tmp/openedx_devops/mongodb/etc/mongod.conf" + destination = "/tmp/cookiecutter/mongodb/etc/mongod.conf" } provisioner "file" { @@ -224,7 +246,6 @@ resource "null_resource" "install_script" { #------------------------------------------------------------------------------ # SUPPORTING RESOURCES #------------------------------------------------------------------------------ - data "aws_ebs_volume" "mongodb" { most_recent = true @@ -296,11 +317,11 @@ data "aws_security_group" "stack-namespace-node" { # only allows inbound traffice to port 27017. resource "aws_security_group" "sg_mongodb" { name_prefix = "${var.stack_namespace}-mongodb" - description = "openedx_devops: MongoDB access from within VPC" + description = "cookiecutter: MongoDB access from within VPC" vpc_id = var.vpc_id ingress { - description = "openedx_devops: MongoDB access from within VPC" + description = "cookiecutter: MongoDB access from within VPC" from_port = 27017 to_port = 27017 protocol = "tcp" @@ -308,7 +329,7 @@ resource "aws_security_group" "sg_mongodb" { } ingress { - description = "openedx_devops: ssh access to MongoDB from within VPC" + description = "cookiecutter: ssh access to MongoDB from within VPC" from_port = 22 to_port = 22 protocol = "tcp" @@ -316,7 +337,7 @@ resource "aws_security_group" "sg_mongodb" { } egress { - description = "openedx_devops: public MongoDB out to anywhere" + description = "cookiecutter: public MongoDB out to anywhere" from_port = 0 to_port = 0 protocol = "-1" @@ -324,7 +345,13 @@ resource "aws_security_group" "sg_mongodb" { ipv6_cidr_blocks = ["::/0"] } - tags = var.tags + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_security_group" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) } @@ -359,7 +386,13 @@ resource "random_password" "mongodb_admin" { resource "aws_iam_user" "aws_cli" { name = "${var.stack_namespace}-mongodb" path = "/system/mongodb-user/" - tags = var.tags + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_iam_user" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) } resource "aws_iam_access_key" "aws_cli" { @@ -439,3 +472,10 @@ data "template_file" "welcome_banner" { platform_name = var.platform_name } } + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/providers.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/providers.tf new file mode 100644 index 00000000..26f4e8ad --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/providers.tf @@ -0,0 +1,13 @@ +data "aws_eks_cluster" "eks" { + name = var.stack_namespace +} + +data "aws_eks_cluster_auth" "eks" { + name = var.stack_namespace +} + +provider "kubernetes" { + host = data.aws_eks_cluster.eks.endpoint + cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) + token = data.aws_eks_cluster_auth.eks.token +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/scripts/mongodb-install-tasks.sh.tpl b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/scripts/mongodb-install-tasks.sh.tpl index a004c20e..ed5d3d9d 100755 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/scripts/mongodb-install-tasks.sh.tpl +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/scripts/mongodb-install-tasks.sh.tpl @@ -18,7 +18,7 @@ rm /home/ubuntu/scripts/*.sh.tpl # for diagnostics: dump the tmp contents. These files were # added by provisioner directives in aws_instance in main.tf -ls /tmp/openedx_devops/mongodb/ -lha +ls /tmp/cookiecutter/mongodb/ -lha # set aws-mandated permissions on the mongodb private key echo setting permission on ~/.ssh/$PRIVATE_KEY_PEM @@ -31,16 +31,16 @@ ssh-keyscan $PRIVATE_IPV4 >> $HOME/.ssh/known_hosts echo copying files from bastion tmp folder to $PRIVATE_IPV4 # prep mongodb config files, then copy to the mongodb instance -chmod +x /tmp/openedx_devops/mongodb/scripts/*.sh +chmod +x /tmp/cookiecutter/mongodb/scripts/*.sh echo copying scripts to home folder on $PRIVATE_IPV4 -scp -i ~/.ssh/$PRIVATE_KEY_PEM -r /tmp/openedx_devops/mongodb/scripts/ ubuntu@$PRIVATE_IPV4:/home/ubuntu/ +scp -i ~/.ssh/$PRIVATE_KEY_PEM -r /tmp/cookiecutter/mongodb/scripts/ ubuntu@$PRIVATE_IPV4:/home/ubuntu/ echo copying aws cli config to home folder on $PRIVATE_IPV4 -scp -i ~/.ssh/$PRIVATE_KEY_PEM -r /tmp/openedx_devops/mongodb/.aws/ ubuntu@$PRIVATE_IPV4:/home/ubuntu/ +scp -i ~/.ssh/$PRIVATE_KEY_PEM -r /tmp/cookiecutter/mongodb/.aws/ ubuntu@$PRIVATE_IPV4:/home/ubuntu/ # configure the login banner, copy login banner files from bastion tmp folder to the mongodb tmp folder -chmod 755 /tmp/openedx_devops/mongodb/etc/* +chmod 755 /tmp/cookiecutter/mongodb/etc/* echo copying login banner files to mongodb tmp folder on $PRIVATE_IPV4 -scp -i ~/.ssh/$PRIVATE_KEY_PEM -r /tmp/openedx_devops/mongodb/etc/ ubuntu@$PRIVATE_IPV4:/tmp/ +scp -i ~/.ssh/$PRIVATE_KEY_PEM -r /tmp/cookiecutter/mongodb/etc/ ubuntu@$PRIVATE_IPV4:/tmp/ # copy banner files from mongodb tmp folder to /etc/update-motd.d/ echo installing login banners on $PRIVATE_IPV4 @@ -60,11 +60,11 @@ ssh ubuntu@$PRIVATE_IPV4 -i ~/.ssh/$PRIVATE_KEY_PEM sudo chmod 644 /etc/update-m # cleanup # FIX NOTE: un-comment me -# "rm -rf /tmp/openedx_devops/mongodb", +# "rm -rf /tmp/cookiecutter/mongodb", # create an ssh shortcut inside the bastion .ssh folder for the mongodb instance # FIX NOTE: I DO NOT WORK :( -#"grep -qxF 'Host mongodb' ~/.ssh/config || cat /tmp/openedx_devops/mongodb/ssh_config >> ~/.ssh/config && echo added ssh key and config to bastion.", +#"grep -qxF 'Host mongodb' ~/.ssh/config || cat /tmp/cookiecutter/mongodb/ssh_config >> ~/.ssh/config && echo added ssh key and config to bastion.", echo configuring .ssh/config on bastion rm -f ~/.ssh/config diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/scripts/mongodb-preinstall-tasks.sh.tpl b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/scripts/mongodb-preinstall-tasks.sh.tpl index 9e7b0251..f770032a 100755 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/scripts/mongodb-preinstall-tasks.sh.tpl +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/scripts/mongodb-preinstall-tasks.sh.tpl @@ -9,16 +9,16 @@ #-------------------------------------------------------- # ensure that we have a top-level tmp folder -echo creating /tmp/openedx_devops -mkdir -p /tmp/openedx_devops +echo creating /tmp/cookiecutter +mkdir -p /tmp/cookiecutter # get rid of any legacy tmp files for mongodb -rm -rf /tmp/openedx_devops/mongodb +rm -rf /tmp/cookiecutter/mongodb # recreate the mongodb temp folder. # at this point we are now certain that this folder exists and that it's empty -echo creating /tmp/openedx_devops/mongodb -mkdir -p /tmp/openedx_devops/mongodb +echo creating /tmp/cookiecutter/mongodb +mkdir -p /tmp/cookiecutter/mongodb # ensure that no legacy private key exists for the mongodb server rm -f ~/.ssh/${ssh_private_key_filename} @@ -28,11 +28,11 @@ rm -f ~/.ssh/known_hosts touch ~/.ssh/known_hosts # setup the tmp working folders for mongodb configuration -echo creating /tmp/openedx_devops/mongodb/.aws -mkdir /tmp/openedx_devops/mongodb/.aws +echo creating /tmp/cookiecutter/mongodb/.aws +mkdir /tmp/cookiecutter/mongodb/.aws -echo creating /tmp/openedx_devops/mongodb/scripts -mkdir /tmp/openedx_devops/mongodb/scripts +echo creating /tmp/cookiecutter/mongodb/scripts +mkdir /tmp/cookiecutter/mongodb/scripts -echo creating /tmp/openedx_devops/mongodb/etc -mkdir /tmp/openedx_devops/mongodb/etc +echo creating /tmp/cookiecutter/mongodb/etc +mkdir /tmp/cookiecutter/mongodb/etc diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/versions.tf index 6ec3eb06..abeea0f6 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb/versions.tf @@ -20,7 +20,7 @@ terraform { } aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } kubernetes = { source = "hashicorp/kubernetes" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb_volume/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb_volume/main.tf index 1e7dda40..df781656 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb_volume/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mongodb_volume/main.tf @@ -7,12 +7,30 @@ # usage: create a detachable EBS volume to be used as the primary storage # volume for MongoDB. #------------------------------------------------------------------------------ +locals { + + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/mongodb_volume" + } + ) + +} # create a detachable EBS volume for the Mongodb databases resource "aws_ebs_volume" "mongodb" { availability_zone = data.aws_subnet.database_subnet.availability_zone size = var.allocated_storage - tags = var.tags + + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_ebs_volume" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) # un-comment this block if you want to prevent Terraform from destroying the Mongodb volume. lifecycle { @@ -37,3 +55,10 @@ resource "random_integer" "subnet_id" { min = 0 max = length(var.subnet_ids) - 1 } + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/kubernetes.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/kubernetes.tf index 60a72274..8d0f89ca 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/kubernetes.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/kubernetes.tf @@ -7,19 +7,6 @@ # usage: create an RDS MySQL instance. # store the MySQL credentials in Kubernetes Secrets #------------------------------------------------------------------------------ -data "aws_eks_cluster" "eks" { - name = var.resource_name -} - -data "aws_eks_cluster_auth" "eks" { - name = var.resource_name -} - -provider "kubernetes" { - host = data.aws_eks_cluster.eks.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) - token = data.aws_eks_cluster_auth.eks.token -} resource "kubernetes_secret" "mysql_root" { metadata { diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/main.tf index 79999435..952d84b8 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/main.tf @@ -7,54 +7,26 @@ # usage: create an RDS MySQL instance. #------------------------------------------------------------------------------ -resource "aws_db_subnet_group" "mysql_subnet_group" { - name = "mysql_subnet_group" - subnet_ids = var.subnet_ids - tags = var.tags -} - -module "security_group" { - source = "terraform-aws-modules/security-group/aws" - version = "{{ cookiecutter.terraform_aws_modules_sg }}" - - name = "${var.resource_name}-mysql" - description = "openedx_devops: Allow access to MySQL" - vpc_id = var.vpc_id - - # ingress - ingress_with_cidr_blocks = [ - { - from_port = 3306 - to_port = 3306 - protocol = "tcp" - description = "openedx_devops: MySQL access from within VPC" - cidr_blocks = join(",", var.ingress_cidr_blocks) - }, - ] - - egress_with_cidr_blocks = [ - { - description = "openedx_devops: Node all egress" - protocol = "-1" - from_port = 0 - to_port = 0 - type = "egress" - cidr_blocks = "0.0.0.0/0" - ipv6_cidr_blocks = "::/0" - }, - ] - tags = var.tags -} - - #------------------------------------------------------------------------------ # RDS Module # # see: https://stackoverflow.com/questions/53386811/terraform-the-db-instance-and-ec2-security-group-are-in-different-vpcs #------------------------------------------------------------------------------ +locals { + + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/mysql" + } + ) + +} + module "db" { source = "terraform-aws-modules/rds/aws" - version = "{{cookiecutter.terraform_aws_modules_rds}}" + version = "~> {{cookiecutter.terraform_aws_modules_rds}}" # required parameters (unless we like the default value) # --------------------------------------------------------------------------- @@ -112,5 +84,75 @@ module "db" { create_monitoring_role = var.create_monitoring_role monitoring_interval = var.monitoring_interval parameters = var.parameters - tags = var.tags + + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "terraform-aws-modules/rds/aws" + "cookiecutter/resource/version" = "{{cookiecutter.terraform_aws_modules_rds}}" + } + ) +} + +#------------------------------------------------------------------------------ +# SUPPORTING RESOURCES +#------------------------------------------------------------------------------ + +resource "aws_db_subnet_group" "mysql_subnet_group" { + name = "mysql_subnet_group" + subnet_ids = var.subnet_ids + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_db_subnet_group" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) +} + +module "security_group" { + source = "terraform-aws-modules/security-group/aws" + version = "~> {{ cookiecutter.terraform_aws_modules_sg }}" + + name = "${var.resource_name}-mysql" + description = "cookiecutter: Allow access to MySQL" + vpc_id = var.vpc_id + + # ingress + ingress_with_cidr_blocks = [ + { + from_port = 3306 + to_port = 3306 + protocol = "tcp" + description = "cookiecutter: MySQL access from within VPC" + cidr_blocks = join(",", var.ingress_cidr_blocks) + }, + ] + + egress_with_cidr_blocks = [ + { + description = "cookiecutter: Node all egress" + protocol = "-1" + from_port = 0 + to_port = 0 + type = "egress" + cidr_blocks = "0.0.0.0/0" + ipv6_cidr_blocks = "::/0" + }, + ] + + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "terraform-aws-modules/security-group/aws" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_aws_modules_sg }}" + } + ) +} + +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/providers.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/providers.tf new file mode 100644 index 00000000..57e8384d --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/providers.tf @@ -0,0 +1,13 @@ +data "aws_eks_cluster" "eks" { + name = var.resource_name +} + +data "aws_eks_cluster_auth" "eks" { + name = var.resource_name +} + +provider "kubernetes" { + host = data.aws_eks_cluster.eks.endpoint + cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) + token = data.aws_eks_cluster_auth.eks.token +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/versions.tf index e2e8b255..a0a0a7a5 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/mysql/versions.tf @@ -12,7 +12,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } } } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/kubernetes.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/kubernetes.tf index cf76bcbe..a530fc5d 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/kubernetes.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/kubernetes.tf @@ -7,20 +7,6 @@ # usage: create an ElastiCache Redis cache # stored cache credentials in Kubernetes Secrets. #------------------------------------------------------------------------------ -data "aws_eks_cluster" "eks" { - name = var.shared_resource_namespace -} - -data "aws_eks_cluster_auth" "eks" { - name = var.shared_resource_namespace -} - -provider "kubernetes" { - host = data.aws_eks_cluster.eks.endpoint - cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) - token = data.aws_eks_cluster_auth.eks.token -} - resource "kubernetes_secret" "redis" { metadata { name = "redis" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/main.tf index 9a2cae4e..3a6a76dd 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/main.tf @@ -10,26 +10,58 @@ #------------------------------------------------------------------------------ locals { name = var.replication_group_description + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/redis" + } + ) + } +module "redis" { + source = "./modules/elasticache" -################################################################################ -# Supporting Resources -################################################################################ + description = local.name + create_random_auth_token = var.create_random_auth_token + subnet_ids = var.subnet_ids + engine = var.engine + engine_version = var.engine_version + num_cache_clusters = var.num_cache_clusters + port = var.port + vpc_security_group_ids = [aws_security_group.redis.id] + transit_encryption_enabled = var.transit_encryption_enabled + family = var.family + node_type = var.node_type + + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/redis/modules/elasticache" + "cookiecutter/resource/version" = "latest" + } + + ) +} + +#------------------------------------------------------------------------------ +# SUPPORTING RESOURCES +#------------------------------------------------------------------------------ resource "aws_security_group" "redis" { - description = "openedx_devops: Redis" - name_prefix = local.name + description = "cookiecutter: Redis" + name_prefix = "${local.name}-redis" vpc_id = var.vpc_id ingress { - description = "openedx_devops: Redis access from within VPC" + description = "cookiecutter: Redis access from within VPC" from_port = var.port to_port = var.port protocol = "tcp" cidr_blocks = var.ingress_cidr_blocks } egress { - description = "openedx_devops: Redis out to anywhere" + description = "cookiecutter: Redis out to anywhere" from_port = 0 to_port = 0 protocol = "-1" @@ -37,23 +69,19 @@ resource "aws_security_group" "redis" { ipv6_cidr_blocks = ["::/0"] } - tags = var.tags -} - + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_security_group" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } -module "redis" { - source = "./modules/elasticache" + ) +} - description = local.name - create_random_auth_token = var.create_random_auth_token - subnet_ids = var.subnet_ids - engine = var.engine - engine_version = var.engine_version - num_cache_clusters = var.num_cache_clusters - port = var.port - vpc_security_group_ids = [aws_security_group.redis.id] - transit_encryption_enabled = var.transit_encryption_enabled - family = var.family - node_type = var.node_type - tags = var.tags +#------------------------------------------------------------------------------ +# COOKIECUTTER META +#------------------------------------------------------------------------------ +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/modules/elasticache/modules/elasticache_parameter_group/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/modules/elasticache/modules/elasticache_parameter_group/versions.tf index 11b68f53..0b19bf99 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/modules/elasticache/modules/elasticache_parameter_group/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/modules/elasticache/modules/elasticache_parameter_group/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } } } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/modules/elasticache/modules/elasticache_subnet_group/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/modules/elasticache/modules/elasticache_subnet_group/versions.tf index 11b68f53..0b19bf99 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/modules/elasticache/modules/elasticache_subnet_group/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/modules/elasticache/modules/elasticache_subnet_group/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } } } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/modules/elasticache/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/modules/elasticache/versions.tf index 11b68f53..0b19bf99 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/modules/elasticache/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/modules/elasticache/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } } } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/providers.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/providers.tf new file mode 100644 index 00000000..35cf28f5 --- /dev/null +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/providers.tf @@ -0,0 +1,13 @@ +data "aws_eks_cluster" "eks" { + name = var.shared_resource_namespace +} + +data "aws_eks_cluster_auth" "eks" { + name = var.shared_resource_namespace +} + +provider "kubernetes" { + host = data.aws_eks_cluster.eks.endpoint + cluster_ca_certificate = base64decode(data.aws_eks_cluster.eks.certificate_authority[0].data) + token = data.aws_eks_cluster_auth.eks.token +} diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/versions.tf index dd41b8e9..30701079 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/redis/versions.tf @@ -12,7 +12,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } } } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/vpc/main.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/vpc/main.tf index 3c096425..74ca77d5 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/vpc/main.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/vpc/main.tf @@ -12,11 +12,19 @@ # There are a LOT of options in this module. # see https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest #------------------------------------------------------------------------------ +locals { + tags = merge( + var.tags, + module.cookiecutter_meta.tags, + { + "cookiecutter/module/source" = "{{ cookiecutter.github_repo_name }}/terraform/stacks/modules/mysql" + } + ) - +} module "vpc" { source = "terraform-aws-modules/vpc/aws" - version = "{{ cookiecutter.terraform_aws_modules_vpc }}" + version = "~> {{ cookiecutter.terraform_aws_modules_vpc }}" create_vpc = true name = var.name cidr = var.cidr @@ -32,5 +40,16 @@ module "vpc" { one_nat_gateway_per_az = var.one_nat_gateway_per_az public_subnet_tags = var.public_subnet_tags private_subnet_tags = var.private_subnet_tags - tags = var.tags + + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "terraform-aws-modules/vpc/aws" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_aws_modules_vpc }}" + } + ) +} + +module "cookiecutter_meta" { + source = "../../../../../../../common/cookiecutter_meta" } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/vpc/route53.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/vpc/route53.tf index 8ee669c5..7b3e7941 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/vpc/route53.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/vpc/route53.tf @@ -4,7 +4,13 @@ data "aws_route53_zone" "root_domain" { resource "aws_route53_zone" "services_subdomain" { name = var.services_subdomain - tags = var.tags + tags = merge( + local.tags, + { + "cookiecutter/resource/source" = "hashicorp/aws/aws_route53_zone" + "cookiecutter/resource/version" = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + } + ) } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/vpc/versions.tf b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/vpc/versions.tf index efd9a51b..02d1cb1a 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/vpc/versions.tf +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/modules/vpc/versions.tf @@ -12,7 +12,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "{{ cookiecutter.terraform_provider_hashicorp_aws_version }}" + version = "~> {{ cookiecutter.terraform_provider_hashicorp_aws_version }}" } local = { source = "hashicorp/local" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes/terragrunt.hcl index 21292e4f..c8b5ab7f 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes/terragrunt.hcl @@ -35,6 +35,7 @@ locals { tags = merge( local.stack_vars.locals.tags, + local.global_vars.locals.tags, { "cookiecutter/name" = "${local.namespace}-eks" } ) } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_cert_manager/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_cert_manager/terragrunt.hcl index 2f7d0909..24de9d51 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_cert_manager/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_cert_manager/terragrunt.hcl @@ -7,8 +7,8 @@ # usage: build an EKS with EC2 worker nodes and ALB #------------------------------------------------------------------------------ locals { - stack_vars = read_terragrunt_config(find_in_parent_folders("stack.hcl")) - global_vars = read_terragrunt_config(find_in_parent_folders("global.hcl")) + stack_vars = read_terragrunt_config(find_in_parent_folders("stack.hcl")) + global_vars = read_terragrunt_config(find_in_parent_folders("global.hcl")) # Extract out common variables for reuse root_domain = local.global_vars.locals.root_domain @@ -16,6 +16,11 @@ locals { aws_region = local.global_vars.locals.aws_region cert_manager_namespace = "cert-manager" services_subdomain = local.global_vars.locals.services_subdomain + + tags = merge( + local.stack_vars.locals.tags, + ) + } dependencies { @@ -80,4 +85,5 @@ inputs = { cert_manager_namespace = local.cert_manager_namespace namespace = local.shared_resource_namespace services_subdomain = local.services_subdomain + tags = local.tags } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_dashboard/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_dashboard/terragrunt.hcl index 113a7682..f8cdc957 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_dashboard/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_dashboard/terragrunt.hcl @@ -18,8 +18,7 @@ locals { tags = merge( local.stack_vars.locals.tags, - local.global_vars.locals.tags, - { Name = "${local.stack_namespace}-eks" } + { Name = "${local.stack_namespace}-dashboard" } ) } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_ingress_clb/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_ingress_clb/terragrunt.hcl index f5ff40ed..9a39e440 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_ingress_clb/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_ingress_clb/terragrunt.hcl @@ -15,6 +15,11 @@ locals { namespace = "kube-system" root_domain = local.global_vars.locals.root_domain services_subdomain = local.global_vars.locals.services_subdomain + + tags = merge( + local.stack_vars.locals.tags, + { Name = "${local.stack_namespace}-ingress-controller" } + ) } dependencies { @@ -79,4 +84,5 @@ inputs = { stack_namespace = local.stack_namespace root_domain = local.root_domain services_subdomain = local.services_subdomain + tags = local.tags } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_karpenter/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_karpenter/terragrunt.hcl index fdd798e4..1b3b9d03 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_karpenter/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_karpenter/terragrunt.hcl @@ -16,7 +16,6 @@ locals { tags = merge( local.stack_vars.locals.tags, - local.global_vars.locals.tags, { Name = "${local.stack_namespace}-karpenter" } ) } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_kubeapps/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_kubeapps/terragrunt.hcl index 535df8db..96fa2a67 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_kubeapps/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_kubeapps/terragrunt.hcl @@ -17,8 +17,7 @@ locals { tags = merge( local.stack_vars.locals.tags, - local.global_vars.locals.tags, - { Name = "${local.stack_namespace}-eks" } + { Name = "${local.stack_namespace}-kubeapps" } ) } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_kubecost/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_kubecost/terragrunt.hcl index 594a538d..75558fca 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_kubecost/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_kubecost/terragrunt.hcl @@ -17,8 +17,7 @@ locals { tags = merge( local.stack_vars.locals.tags, - local.global_vars.locals.tags, - { Name = "${local.stack_namespace}-eks" } + { Name = "${local.stack_namespace}-kubecost" } ) } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_metricsserver/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_metricsserver/terragrunt.hcl index 4c2d4551..84e82582 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_metricsserver/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_metricsserver/terragrunt.hcl @@ -16,7 +16,6 @@ locals { tags = merge( local.stack_vars.locals.tags, - local.global_vars.locals.tags, { Name = "${local.stack_namespace}-metrics-server" } ) } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_prometheus/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_prometheus/terragrunt.hcl index 5addf8d0..7b3a4442 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_prometheus/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/kubernetes_prometheus/terragrunt.hcl @@ -16,7 +16,6 @@ locals { tags = merge( local.stack_vars.locals.tags, - local.global_vars.locals.tags, { Name = "${local.stack_namespace}-prometheus" } ) } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/mongodb_volume/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/mongodb_volume/terragrunt.hcl index a1f6766b..46c2dcb0 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/mongodb_volume/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/mongodb_volume/terragrunt.hcl @@ -57,4 +57,5 @@ inputs = { allocated_storage = local.mongodb_allocated_storage tags = local.tags subnet_ids = dependency.vpc.outputs.database_subnets + tags = local.tags } diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/redis/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/redis/terragrunt.hcl index 2b4cb19c..47ad4ce5 100644 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/redis/terragrunt.hcl +++ b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/redis/terragrunt.hcl @@ -17,6 +17,7 @@ locals { redis_node_type = local.stack_vars.locals.redis_node_type tags = merge( + local.global_vars.locals.tags, local.stack_vars.locals.tags, { "cookiecutter/name" = "${local.resource_name}" diff --git a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/varnish/terragrunt.hcl b/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/varnish/terragrunt.hcl deleted file mode 100644 index 294ab7e9..00000000 --- a/{{cookiecutter.github_repo_name}}/terraform/stacks/{{cookiecutter.global_platform_shared_resource_identifier}}/varnish/terragrunt.hcl +++ /dev/null @@ -1,95 +0,0 @@ -#------------------------------------------------------------------------------ -# written by: Miguel Afonso -# https://www.linkedin.com/in/mmafonso/ -# -# date: Feb-2023 -# -# usage: create a Varnish cluster -#------------------------------------------------------------------------------ -locals { - # Automatically load environment-level variables - global_vars = read_terragrunt_config(find_in_parent_folders("global.hcl")) - stack_vars = read_terragrunt_config(find_in_parent_folders("stack.hcl")) - - services_subdomain = local.global_vars.locals.services_subdomain - resource_name = local.stack_vars.locals.stack_namespace - shared_resource_namespace = local.stack_vars.locals.stack_namespace - stack_namespace = local.stack_vars.locals.stack_namespace - - tags = merge( - local.stack_vars.locals.tags, - { - "cookiecutter/name" = "${local.resource_name}" - Name = "${local.resource_name}" - } - ) -} - -dependencies { - paths = [ - "../vpc", - "../kubernetes", - ] -} - -dependency "vpc" { - config_path = "../vpc" - - # Configure mock outputs for the `validate` and `init` commands that are returned when there are no outputs available (e.g the - # module hasn't been applied yet. - mock_outputs_allowed_terraform_commands = ["init", "validate", "plan", "destroy"] - mock_outputs = { - vpc_id = "fake-vpc-id" - database_subnets = ["fake-subnetid-01", "fake-subnetid-02"] - elasticache_subnets = ["fake-elasticache-subnet-01", "fake-elasticache-subnet-02"] - vpc_cidr_block = "fake-cidr-block" - } -} - -dependency "kubernetes" { - config_path = "../kubernetes" - - # Configure mock outputs for the `validate` and `init` commands that are returned when there are no outputs available (e.g the - # module hasn't been applied yet. - mock_outputs_allowed_terraform_commands = ["init", "validate", "plan", "destroy"] - mock_outputs = { - cluster_arn = "fake-cluster-arn" - cluster_certificate_authority_data = "fake-cert" - cluster_endpoint = "fake-cluster-endpoint" - cluster_id = "fake-cluster-id" - cluster_oidc_issuer_url = "fake-oidc-issuer-url" - cluster_platform_version = "fake-cluster-version" - cluster_security_group_arn = "fake-security-group-arn" - cluster_security_group_id = "fake-security-group-id" - cluster_status = "fake-cluster-status" - cluster_version = "fake-cluster-version" - eks_managed_node_groups = "fake-managed-node-group" - fargate_profiles = "fake-fargate-profile" - node_security_group_arn = "fake-security-group-arn" - node_security_group_id = "fake-security-group-id" - oidc_provider = "fake-oidc-provider" - oidc_provider_arn = "fake-provider-arn" - } -} - -# Terragrunt will copy the Terraform configurations specified by the source parameter, along with any files in the -# working directory, into a temporary folder, and execute your Terraform commands in that folder. -terraform { - source = "../../modules//varnish" -} - -# Include all settings from the root terragrunt.hcl file -include { - path = find_in_parent_folders() -} - -# These are the variables we have to pass in to use the module specified in the terragrunt configuration above -inputs = { - - # Varnish cache identifying information - stack_namespace = local.stack_namespace - services_subdomain = local.services_subdomain - resource_name = local.resource_name - shared_resource_namespace = local.shared_resource_namespace - tags = local.tags -}