From 78d7d395a2e7e941a003e6e3b3d141f29df73800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johannes=20W=C3=BCrbach?= Date: Thu, 6 Jun 2024 11:52:07 +0200 Subject: [PATCH] feat: toggleable backstage --- .github/workflows/ci.yaml | 3 - README.md | 76 +++++++-- examples/with-backstage/README.md | 154 ------------------ examples/with-backstage/backstage-aws.tf | 14 -- examples/with-backstage/backstage-github.tf | 68 -------- .../with-backstage/backstage-humanitec.tf | 136 ---------------- .../with-backstage/terraform.tfvars.example | 23 --- main.tf | 96 +++++------ modules/base/README.md | 4 + modules/base/humanitec.tf | 37 ++++- modules/github/README.md | 49 ++++++ .../aws-github.tf => modules/github/aws.tf | 3 +- modules/github/main.tf | 35 ++++ modules/github/providers.tf | 13 ++ modules/github/terraform.tfvars.example | 12 ++ .../github}/variables.tf | 38 ++--- modules/portal-backstage/README.md | 50 ++++++ modules/portal-backstage/main.tf | 44 +++++ .../portal-backstage/providers.tf | 27 +-- .../portal-backstage/terraform.tfvars.example | 24 +++ modules/portal-backstage/variables.tf | 40 +++++ .../with-backstage/main.tf => providers.tf | 45 ++++- terraform.tfvars.example | 13 +- variables.tf | 22 ++- 24 files changed, 500 insertions(+), 526 deletions(-) delete mode 100644 examples/with-backstage/README.md delete mode 100644 examples/with-backstage/backstage-aws.tf delete mode 100644 examples/with-backstage/backstage-github.tf delete mode 100644 examples/with-backstage/backstage-humanitec.tf delete mode 100644 examples/with-backstage/terraform.tfvars.example create mode 100644 modules/github/README.md rename examples/with-backstage/aws-github.tf => modules/github/aws.tf (96%) create mode 100644 modules/github/main.tf create mode 100644 modules/github/providers.tf create mode 100644 modules/github/terraform.tfvars.example rename {examples/with-backstage => modules/github}/variables.tf (50%) create mode 100644 modules/portal-backstage/README.md create mode 100644 modules/portal-backstage/main.tf rename examples/with-backstage/provider.tf => modules/portal-backstage/providers.tf (51%) create mode 100644 modules/portal-backstage/terraform.tfvars.example create mode 100644 modules/portal-backstage/variables.tf rename examples/with-backstage/main.tf => providers.tf (53%) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index c96a235..fedee6e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -37,8 +37,5 @@ jobs: - name: Terraform Lint run: make lint - - name: Stub GitHub App credentials (required for validation) - run: cd ./examples/with-backstage && docker run --rm -e STUB_FILE=1 -v $(pwd):/pwd ghcr.io/humanitec-architecture/create-gh-app - - name: Terraform Validate run: make validate diff --git a/README.md b/README.md index 01a866a..7ef8e19 100644 --- a/README.md +++ b/README.md @@ -58,11 +58,7 @@ This plane is where the actual infrastructure exists including clusters, databas ## How to spin up your Humanitec AWS Reference Architecture -This repo contains an implementation of part of the Humanitec Reference Architecture for an Internal Developer Platform. - -To install an implementation containing add-ons, follow the separate README. We currently feature these add-ons: - -* [Base layer plus Backstage](examples/with-backstage/) +This repo contains an implementation of part of the Humanitec Reference Architecture for an Internal Developer Platform, including Backstage as optional Portal solution. This repo covers the base layer of the implementation for AWS. @@ -101,13 +97,13 @@ This reference architecture implementation uses Terraform. You will need to do t For example: - ``` + ```shell export HUMANITEC_TOKEN="my-humanitec-api-token" ``` 5. Run terraform: - ``` + ```shell terraform init terraform plan terraform apply @@ -125,13 +121,13 @@ Check for the existence of key elements of the reference architecture. This is a 1. Set the `HUMANITEC_ORG` environment variable to the ID of your Humanitec Organization (must be all lowercase): - ``` + ```shell export HUMANITEC_ORG="my-humanitec-org" ``` 2. Verify the existence of the Resource Definition for the EKS cluster in your Humanitec Organization: - ``` + ```shell curl -s https://api.humanitec.io/orgs/${HUMANITEC_ORG}/resources/defs/ref-arch \ --header "Authorization: Bearer ${HUMANITEC_TOKEN}" \ | jq .id,.type @@ -139,20 +135,20 @@ Check for the existence of key elements of the reference architecture. This is a This should output: - ``` + ```shell "ref-arch" "k8s-cluster" ``` 3. Verify the existence of the newly created EKS cluster: - ``` + ```shell aws eks list-clusters --region ``` This should output: - ``` + ```json { "clusters": [ "ref-arch", @@ -161,17 +157,55 @@ Check for the existence of key elements of the reference architecture. This is a } ``` +### Enable a portal (optional) + +#### Portal Prerequisites + +Backstage requires a GitHub connection, which in turn needs: + +* A GitHub organization and permission to create new repositories in it. Go to to create a new org (the "Free" option is fine). Note: is has to be an organization, a free account is not sufficient. +* Create a classic github personal access token with `repo`, `workflow`, `delete_repo` and `admin:org` scope [here](https://github.com/settings/tokens). +* Set the `GITHUB_TOKEN` environment variable to your token. + + ```shell + export GITHUB_TOKEN="my-github-token" + ``` + +* Set the `GITHUB_ORG_ID` environment variable to your GitHub organization ID. + + ```shell + export GITHUB_ORG_ID="my-github-org-id" + ``` + +* Install the GitHub App for Backstage into your GitHub organization + * Run `docker run --rm -it -e GITHUB_ORG_ID -v $(pwd):/pwd -p 127.0.0.1:3000:3000 ghcr.io/humanitec-architecture/create-gh-app` ([image source](https://github.com/humanitec-architecture/create-gh-app/)) and follow the instructions: + * “All repositories” ~> Install + * “Okay, […] was installed on the […] account.” ~> You can close the window and server. + +#### Portal Usage + +* Enable `with_backstage` inside your `terraform.tfvars` and configure the additional variables that a required for Backstage. +* Perform another `terraform apply` + +#### Verify portal setup + +* [Fetch the DNS entry](https://developer.humanitec.com/score/getting-started/get-dns/) of the Humanitec Application `backstage`, Environment `development`. +* Open the host in your browser. +* Click the "Create" button and scaffold your first application. + ### Cleaning up Once you are finished with the reference architecture, you can remove all provisioned infrastructure and the resource definitions created in Humanitec with the following: -1. Ensure you are (still) logged in with `aws`. +1. Delete all Humanitec Applications scaffolded using the Portal, if you used one, but not the `backstage` app itself. -2. Ensure you still have the `HUMANITEC_TOKEN` environment variable set to an appropriate Humanitec API token with the `Administrator` role on the Humanitec Organization. +2. Ensure you are (still) logged in with `aws`. -3. Run terraform: +3. Ensure you still have the `HUMANITEC_TOKEN` environment variable set to an appropriate Humanitec API token with the `Administrator` role on the Humanitec Organization. - ``` +4. Run terraform: + + ```shell terraform destroy ``` @@ -184,25 +218,33 @@ Once you are finished with the reference architecture, you can remove all provis |------|---------| | terraform | >= 1.3.0 | | aws | ~> 5.17 | +| github | ~> 5.38 | | helm | ~> 2.12 | | humanitec | ~> 1.0 | | kubernetes | ~> 2.25 | +| random | ~> 3.5 | ### Modules | Name | Source | Version | |------|--------|---------| | base | ./modules/base | n/a | +| github | ./modules/github | n/a | +| github\_app | github.com/humanitec-architecture/shared-terraform-modules | v2024-06-06//modules/github-app | +| portal\_backstage | ./modules/portal-backstage | n/a | ### Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | aws\_account\_id | AWS Account (ID) to use | `string` | n/a | yes | -| aws\_region | AWS Region to deploy into | `string` | n/a | yes | +| aws\_region | AWS region | `string` | n/a | yes | +| github\_org\_id | GitHub org id | `string` | n/a | yes | +| humanitec\_ci\_service\_user\_token | Humanitec CI Service User Token | `string` | n/a | yes | | humanitec\_org\_id | Humanitec Organization ID | `string` | n/a | yes | | disk\_size | Disk size in GB to use for EKS nodes | `number` | `20` | no | | instance\_types | List of EC2 instances types to use for EKS nodes | `list(string)` |
[
"t3.large"
]
| no | +| with\_backstage | Deploy Backstage | `bool` | `false` | no | ## Learn more diff --git a/examples/with-backstage/README.md b/examples/with-backstage/README.md deleted file mode 100644 index 7eb54b4..0000000 --- a/examples/with-backstage/README.md +++ /dev/null @@ -1,154 +0,0 @@ -# AWS reference architecture with Backstage - -Provisions the AWS reference architecture connected to Humanitec and installs Backstage. - -## Prerequisites - -* The same prerequisites as the [base reference architecture](../../README.md#prerequisites), plus the following items. -* A GitHub organization and permission to create new repositories in it. Go to to create a new org (the "Free" option is fine). Note: is has to be an organization, a free account is not sufficient. -* Create a classic github personal access token with `repo`, `workflow`, `delete_repo` and `admin:org` scope [here](https://github.com/settings/tokens). -* Set the `GITHUB_TOKEN` environment variable to your token. - - ```bash - export GITHUB_TOKEN="my-github-token" - ``` - -* Set the `GITHUB_ORG_ID` environment variable to your GitHub organization ID. - - ```bash - export GITHUB_ORG_ID="my-github-org-id" - ``` - -* [Node.js](https://nodejs.org) installed locally. -* Install the GitHub App for Backstage into your GitHub organization - * Run `docker run --rm -it -e GITHUB_ORG_ID -v $(pwd):/pwd -p 127.0.0.1:3000:3000 ghcr.io/humanitec-architecture/create-gh-app` ([image source](https://github.com/humanitec-architecture/create-gh-app/)) and follow the instructions: - * “All repositories” ~> Install - * “Okay, […] was installed on the […] account.” ~> You can close the window and server. - -## Usage - -Follow the same steps as for the [base layer](../../README.md#usage), applying these modifications: - -* Execute `cd ./examples/with-backstage` after cloning the repo. Execute all subsequent commands in this directory. -* In particular, use the `./examples/with-backstage/terraform.tfvars.example` file as the basis for your `terraform.tfvars` file. It defines additional variables needed to setup and configure Backstage. - -## Verify your result - -Check for the existence of key elements of the backstage module. This is a subset of all elements only. For a complete list of what was installed, review the Terraform code. - -1. Perform the [verification steps of the base installation](../../README.md) if you have not already done so. -2. Verify the existence of the Backstage Application in your Humanitec Organization: - - ``` - curl -s https://api.humanitec.io/orgs/${HUMANITEC_ORG}/apps/backstage \ - --header "Authorization: Bearer ${HUMANITEC_TOKEN}" - ``` - - This should output a JSON formatted representation of the Application like so: - - ``` - {"id":"backstage","name":"backstage","created_at":"2023-10-02T13:44:27Z","created_by":"s-d3e94a0e-8b53-29f9-b666-27548b7e06e0","envs":[{"id":"development","name":"Development","type":"development"}]} - ``` - - You can also check for the Application in the [Humanitec Platform Orchestrator UI](https://app.humanitec.io). - -3. Connect to your EKS cluster via `kubectl`. See the [AWS documentation](https://docs.aws.amazon.com/eks/latest/userguide/create-kubeconfig.html) or use this command: - - ``` - aws eks update-kubeconfig --region --name ref-arch - ``` - -4. Get the elements in the newly created Kubernetes namespace: - - ``` - kubectl get all -n backstage-development - ``` - - You should see - * a `deployment`, `replicaset`, running `pod`, and `service` for Backstage - * a `statefulset`, running `pod`, and `service` for PostgreSQL database used by Backstage. - - Note: it may take up to ten minutes after the `terraform apply` completed until you actually see those resources. The Backstage application needs to built and deployed via a GitHub action out of the newly created repository in your GitHub organization. - -## Cleaning up - -Once you are finished with the reference architecture, you can remove all provisioned infrastrcuture and the resource definitions created in Humanitec with the following: - -1. Delete all Humanitec applications scaffolded using Backstage, but not the `backstage` app itself. - -2. Follow the [base reference architecture cleanup instructions](../../README.md#cleaning-up). - -## Terraform docs - - -### Requirements - -| Name | Version | -|------|---------| -| terraform | >= 1.3.0 | -| aws | ~> 5.17 | -| github | ~> 5.38 | -| helm | ~> 2.12 | -| humanitec | ~> 1.0 | -| kubernetes | ~> 2.25 | -| random | ~> 3.5 | - -### Providers - -| Name | Version | -|------|---------| -| aws | ~> 5.17 | -| github | ~> 5.38 | -| humanitec | ~> 1.0 | -| random | ~> 3.5 | - -### Modules - -| Name | Source | Version | -|------|--------|---------| -| backstage\_ecr | terraform-aws-modules/ecr/aws | ~> 1.6 | -| backstage\_mysql | github.com/humanitec-architecture/resource-packs-in-cluster | v2024-06-05//humanitec-resource-defs/mysql/basic | -| backstage\_postgres | github.com/humanitec-architecture/resource-packs-in-cluster | v2024-06-05//humanitec-resource-defs/postgres/basic | -| base | ../../modules/base | n/a | -| iam\_github\_oidc\_provider | terraform-aws-modules/iam/aws//modules/iam-github-oidc-provider | ~> 5.30 | -| iam\_github\_oidc\_role | terraform-aws-modules/iam/aws//modules/iam-github-oidc-role | ~> 5.30 | - -### Resources - -| Name | Type | -|------|------| -| [aws_iam_policy.ecr_push_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | -| [github_actions_organization_secret.backstage_humanitec_token](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_organization_secret) | resource | -| [github_actions_organization_variable.backstage_aws_region](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_organization_variable) | resource | -| [github_actions_organization_variable.backstage_aws_role_arn](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_organization_variable) | resource | -| [github_actions_organization_variable.backstage_cloud_provider](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_organization_variable) | resource | -| [github_actions_organization_variable.backstage_humanitec_org_id](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_organization_variable) | resource | -| [github_repository.backstage](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/repository) | resource | -| [humanitec_application.backstage](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/application) | resource | -| [humanitec_resource_definition_criteria.backstage_mysql](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/resource_definition_criteria) | resource | -| [humanitec_resource_definition_criteria.backstage_postgres](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/resource_definition_criteria) | resource | -| [humanitec_value.app_config_backend_auth_keys](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/value) | resource | -| [humanitec_value.aws_default_region](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/value) | resource | -| [humanitec_value.backstage_cloud_provider](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/value) | resource | -| [humanitec_value.backstage_github_app_client_id](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/value) | resource | -| [humanitec_value.backstage_github_app_client_secret](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/value) | resource | -| [humanitec_value.backstage_github_app_id](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/value) | resource | -| [humanitec_value.backstage_github_app_private_key](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/value) | resource | -| [humanitec_value.backstage_github_app_webhook_secret](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/value) | resource | -| [humanitec_value.backstage_github_org_id](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/value) | resource | -| [humanitec_value.backstage_humanitec_org](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/value) | resource | -| [humanitec_value.backstage_humanitec_token](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/value) | resource | -| [random_bytes.backstage_service_to_service_auth_key](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/bytes) | resource | - -### Inputs - -| Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| -| aws\_account\_id | AWS Account (ID) to use | `string` | n/a | yes | -| aws\_region | AWS region | `string` | n/a | yes | -| github\_org\_id | GitHub org id | `string` | n/a | yes | -| humanitec\_ci\_service\_user\_token | Humanitec CI Service User Token | `string` | n/a | yes | -| humanitec\_org\_id | Humanitec Organization ID | `string` | n/a | yes | -| disk\_size | Disk size in GB to use for EKS nodes | `number` | `20` | no | -| instance\_types | List of EC2 instances types to use for EKS nodes | `list(string)` |
[
"t3.large"
]
| no | - diff --git a/examples/with-backstage/backstage-aws.tf b/examples/with-backstage/backstage-aws.tf deleted file mode 100644 index c0b157c..0000000 --- a/examples/with-backstage/backstage-aws.tf +++ /dev/null @@ -1,14 +0,0 @@ - -# Create ECR repository for the backstage image -# Source https://github.com/terraform-aws-modules/terraform-aws-ecr -module "backstage_ecr" { - source = "terraform-aws-modules/ecr/aws" - version = "~> 1.6" - - repository_name = "backstage" - repository_image_scan_on_push = false - repository_image_tag_mutability = "MUTABLE" - create_lifecycle_policy = false - - repository_force_delete = true -} diff --git a/examples/with-backstage/backstage-github.tf b/examples/with-backstage/backstage-github.tf deleted file mode 100644 index 7e683f0..0000000 --- a/examples/with-backstage/backstage-github.tf +++ /dev/null @@ -1,68 +0,0 @@ -# Configure GitHub variables & secrets for Backstage itself and for all scaffolded apps - -locals { - github_app_credentials_file = "github-app-credentials.json" - github_app_credentials = jsondecode(file("${path.module}/${local.github_app_credentials_file}")) - github_app_id = local.github_app_credentials["appId"] - github_app_client_id = local.github_app_credentials["clientId"] - github_app_client_secret = local.github_app_credentials["clientSecret"] - github_app_private_key = local.github_app_credentials["privateKey"] - github_webhook_secret = local.github_app_credentials["webhookSecret"] -} - -locals { - backstage_repo = "backstage" -} - -resource "github_actions_organization_variable" "backstage_cloud_provider" { - variable_name = "CLOUD_PROVIDER" - visibility = "all" - value = local.cloud_provider -} - -resource "github_actions_organization_variable" "backstage_aws_region" { - variable_name = "AWS_REGION" - visibility = "all" - value = var.aws_region -} - -resource "github_actions_organization_variable" "backstage_aws_role_arn" { - variable_name = "AWS_ROLE_ARN" - visibility = "all" - value = module.iam_github_oidc_role.arn -} - -resource "github_actions_organization_variable" "backstage_humanitec_org_id" { - variable_name = "HUMANITEC_ORG_ID" - visibility = "all" - value = var.humanitec_org_id -} - -resource "github_actions_organization_secret" "backstage_humanitec_token" { - secret_name = "HUMANITEC_TOKEN" - visibility = "all" - plaintext_value = var.humanitec_ci_service_user_token -} - -# Backstage repository itself - -resource "github_repository" "backstage" { - name = local.backstage_repo - description = "Backstage" - - visibility = "public" - - template { - owner = "humanitec-architecture" - repository = "backstage" - } - - depends_on = [ - module.base, - module.backstage_ecr, - module.iam_github_oidc_role, - humanitec_application.backstage, - humanitec_resource_definition_criteria.backstage_postgres, - github_actions_organization_secret.backstage_humanitec_token, - ] -} diff --git a/examples/with-backstage/backstage-humanitec.tf b/examples/with-backstage/backstage-humanitec.tf deleted file mode 100644 index edd6e1f..0000000 --- a/examples/with-backstage/backstage-humanitec.tf +++ /dev/null @@ -1,136 +0,0 @@ -resource "humanitec_application" "backstage" { - id = "backstage" - name = "backstage" -} - -# Configure required values for backstage - -resource "humanitec_value" "backstage_github_org_id" { - app_id = humanitec_application.backstage.id - key = "GITHUB_ORG_ID" - description = "" - value = var.github_org_id - is_secret = false -} - -resource "humanitec_value" "backstage_github_app_id" { - app_id = humanitec_application.backstage.id - key = "GITHUB_APP_ID" - description = "" - value = local.github_app_id - is_secret = false -} - -resource "humanitec_value" "backstage_github_app_client_id" { - app_id = humanitec_application.backstage.id - key = "GITHUB_APP_CLIENT_ID" - description = "" - value = local.github_app_client_id - is_secret = true -} - -resource "humanitec_value" "backstage_github_app_client_secret" { - app_id = humanitec_application.backstage.id - key = "GITHUB_APP_CLIENT_SECRET" - description = "" - value = local.github_app_client_secret - is_secret = true -} - -resource "humanitec_value" "backstage_github_app_private_key" { - app_id = humanitec_application.backstage.id - key = "GITHUB_APP_PRIVATE_KEY" - description = "" - value = indent(2, local.github_app_private_key) - is_secret = true -} - -resource "humanitec_value" "backstage_github_app_webhook_secret" { - app_id = humanitec_application.backstage.id - key = "GITHUB_APP_WEBHOOK_SECRET" - description = "" - value = local.github_webhook_secret - is_secret = true -} - -resource "humanitec_value" "backstage_humanitec_org" { - app_id = humanitec_application.backstage.id - key = "HUMANITEC_ORG_ID" - description = "" - value = var.humanitec_org_id - is_secret = false -} - -resource "humanitec_value" "backstage_humanitec_token" { - app_id = humanitec_application.backstage.id - key = "HUMANITEC_TOKEN" - description = "" - value = var.humanitec_ci_service_user_token - is_secret = true -} - -resource "humanitec_value" "backstage_cloud_provider" { - app_id = humanitec_application.backstage.id - key = "CLOUD_PROVIDER" - description = "" - value = local.cloud_provider - is_secret = false -} - -resource "humanitec_value" "aws_default_region" { - app_id = humanitec_application.backstage.id - key = "AWS_DEFAULT_REGION" - description = "" - value = var.aws_region - is_secret = false -} - -resource "random_bytes" "backstage_service_to_service_auth_key" { - length = 24 -} - -resource "humanitec_value" "app_config_backend_auth_keys" { - app_id = humanitec_application.backstage.id - key = "APP_CONFIG_backend_auth_keys" - description = "Backstage service-to-service-auth keys" - value = jsonencode([{ - secret = random_bytes.backstage_service_to_service_auth_key.base64 - }]) - is_secret = true -} - -# Configure required resources for backstage - -locals { - res_def_prefix = "backstage-" -} - -# in-cluster postgres - -module "backstage_postgres" { - source = "github.com/humanitec-architecture/resource-packs-in-cluster?ref=v2024-06-05//humanitec-resource-defs/postgres/basic" - - prefix = local.res_def_prefix -} - -resource "humanitec_resource_definition_criteria" "backstage_postgres" { - resource_definition_id = module.backstage_postgres.id - app_id = humanitec_application.backstage.id - - force_delete = true -} - -# Configure required resources for scaffolded apps - -# in-cluster mysql - -module "backstage_mysql" { - source = "github.com/humanitec-architecture/resource-packs-in-cluster?ref=v2024-06-05//humanitec-resource-defs/mysql/basic" - - prefix = local.res_def_prefix -} - -resource "humanitec_resource_definition_criteria" "backstage_mysql" { - resource_definition_id = module.backstage_mysql.id - env_type = module.base.environment -} diff --git a/examples/with-backstage/terraform.tfvars.example b/examples/with-backstage/terraform.tfvars.example deleted file mode 100644 index 7b5799e..0000000 --- a/examples/with-backstage/terraform.tfvars.example +++ /dev/null @@ -1,23 +0,0 @@ - -# AWS Account (ID) to use -aws_account_id = "" - -# AWS region -aws_region = "" - -# Disk size in GB to use for EKS nodes -disk_size = 20 - -# GitHub org id -github_org_id = "" - -# Humanitec CI Service User Token -humanitec_ci_service_user_token = "" - -# Humanitec Organization ID -humanitec_org_id = "" - -# List of EC2 instances types to use for EKS nodes -instance_types = [ - "t3.large" -] \ No newline at end of file diff --git a/main.tf b/main.tf index 574c2c9..4fc7302 100644 --- a/main.tf +++ b/main.tf @@ -1,66 +1,58 @@ +# AWS reference architecture -terraform { - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.17" - } - helm = { - source = "hashicorp/helm" - version = "~> 2.12" - } - humanitec = { - source = "humanitec/humanitec" - version = "~> 1.0" - } - kubernetes = { - source = "hashicorp/kubernetes" - version = "~> 2.25" - } - } - required_version = ">= 1.3.0" +module "base" { + source = "./modules/base" + + region = var.aws_region + instance_types = var.instance_types + disk_size = var.disk_size } -provider "humanitec" { - org_id = var.humanitec_org_id +module "github" { + count = var.with_backstage ? 1 : 0 + + source = "./modules/github" + + humanitec_org_id = var.humanitec_org_id + humanitec_ci_service_user_token = var.humanitec_ci_service_user_token + aws_region = var.aws_region + github_org_id = var.github_org_id + + depends_on = [module.base] } -provider "aws" { - region = var.aws_region - allowed_account_ids = [var.aws_account_id] +# Configure GitHub variables & secrets for Backstage itself and for all scaffolded apps + +locals { + github_app_credentials_file = "github-app-credentials.json" } +module "github_app" { + count = var.with_backstage ? 1 : 0 -module "base" { - source = "./modules/base" + # Not pinned as we don't have a release yet + # tflint-ignore: terraform_module_pinned_source + source = "github.com/humanitec-architecture/shared-terraform-modules?ref=v2024-06-06//modules/github-app" - region = var.aws_region - instance_types = var.instance_types - disk_size = var.disk_size + credentials_file = "${path.module}/${local.github_app_credentials_file}" } -provider "kubernetes" { - host = module.base.eks_cluster_endpoint - cluster_ca_certificate = base64decode(module.base.eks_cluster_certificate_authority_data) +# Deploy Backstage as Portal - exec { - api_version = "client.authentication.k8s.io/v1beta1" - command = "aws" - # This requires the awscli to be installed locally where Terraform is executed - args = ["eks", "get-token", "--cluster-name", module.base.eks_cluster_name] - } -} +module "portal_backstage" { + count = var.with_backstage ? 1 : 0 + + source = "./modules/portal-backstage" + + humanitec_org_id = var.humanitec_org_id + humanitec_ci_service_user_token = var.humanitec_ci_service_user_token + + github_org_id = var.github_org_id + github_app_client_id = module.github_app[0].client_id + github_app_client_secret = module.github_app[0].client_secret + github_app_id = module.github_app[0].app_id + github_app_private_key = module.github_app[0].private_key + github_webhook_secret = module.github_app[0].webhook_secret -provider "helm" { - kubernetes { - host = module.base.eks_cluster_endpoint - cluster_ca_certificate = base64decode(module.base.eks_cluster_certificate_authority_data) - - exec { - api_version = "client.authentication.k8s.io/v1beta1" - command = "aws" - # This requires the awscli to be installed locally where Terraform is executed - args = ["eks", "get-token", "--cluster-name", module.base.eks_cluster_name] - } - } + depends_on = [module.github] } diff --git a/modules/base/README.md b/modules/base/README.md index bc26e2b..8b325a6 100644 --- a/modules/base/README.md +++ b/modules/base/README.md @@ -32,6 +32,8 @@ Module that provides the reference architecture. |------|--------|---------| | aws\_eks | terraform-aws-modules/eks/aws | ~> 20.2 | | aws\_vpc | terraform-aws-modules/vpc/aws | ~> 5.1 | +| default\_mysql | github.com/humanitec-architecture/resource-packs-in-cluster | v2024-06-05//humanitec-resource-defs/mysql/basic | +| default\_postgres | github.com/humanitec-architecture/resource-packs-in-cluster | v2024-06-05//humanitec-resource-defs/postgres/basic | | ebs\_csi\_irsa\_role | terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks | ~> 5.30 | ### Resources @@ -44,6 +46,8 @@ Module that provides the reference architecture. | [humanitec_resource_account.cluster_account](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/resource_account) | resource | | [humanitec_resource_definition.k8s_cluster_driver](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/resource_definition) | resource | | [humanitec_resource_definition.k8s_namespace](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/resource_definition) | resource | +| [humanitec_resource_definition_criteria.default_mysql](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/resource_definition_criteria) | resource | +| [humanitec_resource_definition_criteria.default_postgres](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/resource_definition_criteria) | resource | | [humanitec_resource_definition_criteria.k8s_cluster_driver](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/resource_definition_criteria) | resource | | [humanitec_resource_definition_criteria.k8s_namespace](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/resource_definition_criteria) | resource | | [random_password.external_id](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/password) | resource | diff --git a/modules/base/humanitec.tf b/modules/base/humanitec.tf index b7394ba..d950e11 100644 --- a/modules/base/humanitec.tf +++ b/modules/base/humanitec.tf @@ -1,4 +1,10 @@ -# Humanitec resource definition to connect the cluster to Humanitec + +locals { + res_def_prefix = "default-" +} + +# Configure default resources for example apps + locals { ingress_address = data.kubernetes_service.ingress_nginx_controller.status[0].load_balancer[0].ingress[0].hostname @@ -6,6 +12,8 @@ locals { data "aws_elb_hosted_zone_id" "main" {} +# k8s-cluster + resource "humanitec_resource_account" "cluster_account" { id = var.cluster_name name = var.cluster_name @@ -41,6 +49,8 @@ resource "humanitec_resource_definition_criteria" "k8s_cluster_driver" { env_type = var.environment } +# k8s-namespace + resource "humanitec_resource_definition" "k8s_namespace" { driver_type = "humanitec/echo" id = "default-namespace" @@ -57,3 +67,28 @@ resource "humanitec_resource_definition" "k8s_namespace" { resource "humanitec_resource_definition_criteria" "k8s_namespace" { resource_definition_id = humanitec_resource_definition.k8s_namespace.id } + + +# in-cluster postgres + +module "default_postgres" { + source = "github.com/humanitec-architecture/resource-packs-in-cluster?ref=v2024-06-05//humanitec-resource-defs/postgres/basic" + + prefix = local.res_def_prefix +} + +resource "humanitec_resource_definition_criteria" "default_postgres" { + resource_definition_id = module.default_postgres.id + env_type = var.environment +} + +module "default_mysql" { + source = "github.com/humanitec-architecture/resource-packs-in-cluster?ref=v2024-06-05//humanitec-resource-defs/mysql/basic" + + prefix = local.res_def_prefix +} + +resource "humanitec_resource_definition_criteria" "default_mysql" { + resource_definition_id = module.default_mysql.id + env_type = var.environment +} diff --git a/modules/github/README.md b/modules/github/README.md new file mode 100644 index 0000000..ca5c11f --- /dev/null +++ b/modules/github/README.md @@ -0,0 +1,49 @@ +# GitHub + +This module prepares a GitHub Organization to be used for scaffolding using a Portal. + +## Terraform docs + + +### Requirements + +| Name | Version | +|------|---------| +| terraform | >= 1.3.0 | +| aws | ~> 5.17 | +| github | ~> 5.38 | + +### Providers + +| Name | Version | +|------|---------| +| aws | ~> 5.17 | +| github | ~> 5.38 | + +### Modules + +| Name | Source | Version | +|------|--------|---------| +| iam\_github\_oidc\_provider | terraform-aws-modules/iam/aws//modules/iam-github-oidc-provider | ~> 5.30 | +| iam\_github\_oidc\_role | terraform-aws-modules/iam/aws//modules/iam-github-oidc-role | ~> 5.30 | + +### Resources + +| Name | Type | +|------|------| +| [aws_iam_policy.ecr_push_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource | +| [github_actions_organization_secret.backstage_humanitec_token](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_organization_secret) | resource | +| [github_actions_organization_variable.backstage_aws_region](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_organization_variable) | resource | +| [github_actions_organization_variable.backstage_aws_role_arn](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_organization_variable) | resource | +| [github_actions_organization_variable.backstage_cloud_provider](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_organization_variable) | resource | +| [github_actions_organization_variable.backstage_humanitec_org_id](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_organization_variable) | resource | + +### Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| aws\_region | AWS region | `string` | n/a | yes | +| github\_org\_id | GitHub org id | `string` | n/a | yes | +| humanitec\_ci\_service\_user\_token | Humanitec CI Service User Token | `string` | n/a | yes | +| humanitec\_org\_id | Humanitec Organization ID | `string` | n/a | yes | + diff --git a/examples/with-backstage/aws-github.tf b/modules/github/aws.tf similarity index 96% rename from examples/with-backstage/aws-github.tf rename to modules/github/aws.tf index 92b6923..13e93f7 100644 --- a/examples/with-backstage/aws-github.tf +++ b/modules/github/aws.tf @@ -1,6 +1,5 @@ locals { - name = "gha-ecr-push" - cloud_provider = "aws" + name = "gha-ecr-push" } # Create a role for GitHub Actions to push to ECR using OpenID Connect (OIDC) so we don't need to store AWS credentials in GitHub diff --git a/modules/github/main.tf b/modules/github/main.tf new file mode 100644 index 0000000..fa3f0a0 --- /dev/null +++ b/modules/github/main.tf @@ -0,0 +1,35 @@ +locals { + cloud_provider = "aws" +} + +# Configure GitHub variables & secrets for all scaffolded apps + +resource "github_actions_organization_variable" "backstage_cloud_provider" { + variable_name = "CLOUD_PROVIDER" + visibility = "all" + value = local.cloud_provider +} + +resource "github_actions_organization_variable" "backstage_aws_region" { + variable_name = "AWS_REGION" + visibility = "all" + value = var.aws_region +} + +resource "github_actions_organization_variable" "backstage_aws_role_arn" { + variable_name = "AWS_ROLE_ARN" + visibility = "all" + value = module.iam_github_oidc_role.arn +} + +resource "github_actions_organization_variable" "backstage_humanitec_org_id" { + variable_name = "HUMANITEC_ORG_ID" + visibility = "all" + value = var.humanitec_org_id +} + +resource "github_actions_organization_secret" "backstage_humanitec_token" { + secret_name = "HUMANITEC_TOKEN" + visibility = "all" + plaintext_value = var.humanitec_ci_service_user_token +} diff --git a/modules/github/providers.tf b/modules/github/providers.tf new file mode 100644 index 0000000..506adda --- /dev/null +++ b/modules/github/providers.tf @@ -0,0 +1,13 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.17" + } + github = { + source = "integrations/github" + version = "~> 5.38" + } + } + required_version = ">= 1.3.0" +} diff --git a/modules/github/terraform.tfvars.example b/modules/github/terraform.tfvars.example new file mode 100644 index 0000000..b757f21 --- /dev/null +++ b/modules/github/terraform.tfvars.example @@ -0,0 +1,12 @@ + +# AWS region +aws_region = "" + +# GitHub org id +github_org_id = "" + +# Humanitec CI Service User Token +humanitec_ci_service_user_token = "" + +# Humanitec Organization ID +humanitec_org_id = "" \ No newline at end of file diff --git a/examples/with-backstage/variables.tf b/modules/github/variables.tf similarity index 50% rename from examples/with-backstage/variables.tf rename to modules/github/variables.tf index 43ac6c5..10041b8 100644 --- a/examples/with-backstage/variables.tf +++ b/modules/github/variables.tf @@ -1,39 +1,35 @@ -variable "aws_account_id" { - description = "AWS Account (ID) to use" - type = string -} - variable "aws_region" { description = "AWS region" type = string } -variable "github_org_id" { - description = "GitHub org id" - type = string -} - variable "humanitec_org_id" { description = "Humanitec Organization ID" type = string + + validation { + condition = var.humanitec_org_id != null + error_message = "Humanitec Organization ID must not be empty" + } } variable "humanitec_ci_service_user_token" { description = "Humanitec CI Service User Token" type = string sensitive = true -} -variable "instance_types" { - description = "List of EC2 instances types to use for EKS nodes" - type = list(string) - default = [ - "t3.large" - ] + validation { + condition = var.humanitec_ci_service_user_token != null + error_message = "Humanitec CI Service User Token must not be empty" + } } -variable "disk_size" { - description = "Disk size in GB to use for EKS nodes" - type = number - default = 20 +variable "github_org_id" { + description = "GitHub org id" + type = string + + validation { + condition = var.github_org_id != null + error_message = "GitHub org id must not be empty" + } } diff --git a/modules/portal-backstage/README.md b/modules/portal-backstage/README.md new file mode 100644 index 0000000..f12ea1d --- /dev/null +++ b/modules/portal-backstage/README.md @@ -0,0 +1,50 @@ +# Portal: Backstage + +This module deploys the [Humanitec Reference Architecture Backstage](https://github.com/humanitec-architecture/backstage) as Application into a specific Humanitec Organization. + +## Terraform docs + + +### Requirements + +| Name | Version | +|------|---------| +| terraform | >= 1.3.0 | +| aws | ~> 5.17 | +| github | ~> 5.38 | +| humanitec | ~> 1.0 | +| random | ~> 3.5 | + +### Providers + +| Name | Version | +|------|---------| +| humanitec | ~> 1.0 | + +### Modules + +| Name | Source | Version | +|------|--------|---------| +| backstage\_postgres | github.com/humanitec-architecture/resource-packs-in-cluster | v2024-06-05//humanitec-resource-defs/postgres/basic | +| portal\_backstage | github.com/humanitec-architecture/shared-terraform-modules | v2024-06-06//modules/portal-backstage | + +### Resources + +| Name | Type | +|------|------| +| [humanitec_application.backstage](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/application) | resource | +| [humanitec_resource_definition_criteria.backstage_postgres](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/resource_definition_criteria) | resource | + +### Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| github\_app\_client\_id | GitHub App Client ID | `string` | n/a | yes | +| github\_app\_client\_secret | GitHub App Client Secret | `string` | n/a | yes | +| github\_app\_id | GitHub App ID | `string` | n/a | yes | +| github\_app\_private\_key | GitHub App Private Key | `string` | n/a | yes | +| github\_org\_id | GitHub org id | `string` | n/a | yes | +| github\_webhook\_secret | GitHub Webhook Secret | `string` | n/a | yes | +| humanitec\_ci\_service\_user\_token | Humanitec CI Service User Token | `string` | n/a | yes | +| humanitec\_org\_id | Humanitec Organization ID | `string` | n/a | yes | + diff --git a/modules/portal-backstage/main.tf b/modules/portal-backstage/main.tf new file mode 100644 index 0000000..95fcc9d --- /dev/null +++ b/modules/portal-backstage/main.tf @@ -0,0 +1,44 @@ +resource "humanitec_application" "backstage" { + id = "backstage" + name = "backstage" +} + +module "portal_backstage" { + # Not pinned as we don't have a release yet + # tflint-ignore: terraform_module_pinned_source + source = "github.com/humanitec-architecture/shared-terraform-modules?ref=v2024-06-06//modules/portal-backstage" + + cloud_provider = "aws" + + humanitec_org_id = var.humanitec_org_id + humanitec_app_id = humanitec_application.backstage.id + humanitec_ci_service_user_token = var.humanitec_ci_service_user_token + + github_org_id = var.github_org_id + github_app_client_id = var.github_app_client_id + github_app_client_secret = var.github_app_client_secret + github_app_id = var.github_app_id + github_app_private_key = var.github_app_private_key + github_webhook_secret = var.github_webhook_secret +} + +# Configure required resources for backstage + +locals { + res_def_prefix = "backstage-" +} + +# in-cluster postgres + +module "backstage_postgres" { + source = "github.com/humanitec-architecture/resource-packs-in-cluster?ref=v2024-06-05//humanitec-resource-defs/postgres/basic" + + prefix = local.res_def_prefix +} + +resource "humanitec_resource_definition_criteria" "backstage_postgres" { + resource_definition_id = module.backstage_postgres.id + app_id = humanitec_application.backstage.id + + force_delete = true +} diff --git a/examples/with-backstage/provider.tf b/modules/portal-backstage/providers.tf similarity index 51% rename from examples/with-backstage/provider.tf rename to modules/portal-backstage/providers.tf index e388034..30a1f60 100644 --- a/examples/with-backstage/provider.tf +++ b/modules/portal-backstage/providers.tf @@ -4,21 +4,13 @@ terraform { source = "hashicorp/aws" version = "~> 5.17" } - github = { - source = "integrations/github" - version = "~> 5.38" - } - helm = { - source = "hashicorp/helm" - version = "~> 2.12" - } humanitec = { source = "humanitec/humanitec" version = "~> 1.0" } - kubernetes = { - source = "hashicorp/kubernetes" - version = "~> 2.25" + github = { + source = "integrations/github" + version = "~> 5.38" } random = { source = "hashicorp/random" @@ -27,16 +19,3 @@ terraform { } required_version = ">= 1.3.0" } - -provider "humanitec" { - org_id = var.humanitec_org_id -} - -provider "github" { - owner = var.github_org_id -} - -provider "aws" { - region = var.aws_region - allowed_account_ids = [var.aws_account_id] -} diff --git a/modules/portal-backstage/terraform.tfvars.example b/modules/portal-backstage/terraform.tfvars.example new file mode 100644 index 0000000..8b207b4 --- /dev/null +++ b/modules/portal-backstage/terraform.tfvars.example @@ -0,0 +1,24 @@ + +# GitHub App Client ID +github_app_client_id = "" + +# GitHub App Client Secret +github_app_client_secret = "" + +# GitHub App ID +github_app_id = "" + +# GitHub App Private Key +github_app_private_key = "" + +# GitHub org id +github_org_id = "" + +# GitHub Webhook Secret +github_webhook_secret = "" + +# Humanitec CI Service User Token +humanitec_ci_service_user_token = "" + +# Humanitec Organization ID +humanitec_org_id = "" \ No newline at end of file diff --git a/modules/portal-backstage/variables.tf b/modules/portal-backstage/variables.tf new file mode 100644 index 0000000..4437cf6 --- /dev/null +++ b/modules/portal-backstage/variables.tf @@ -0,0 +1,40 @@ +variable "humanitec_org_id" { + description = "Humanitec Organization ID" + type = string +} + +variable "humanitec_ci_service_user_token" { + description = "Humanitec CI Service User Token" + type = string + sensitive = true +} + +variable "github_org_id" { + description = "GitHub org id" + type = string +} + +variable "github_app_client_id" { + description = "GitHub App Client ID" + type = string +} + +variable "github_app_client_secret" { + description = "GitHub App Client Secret" + type = string +} + +variable "github_app_id" { + description = "GitHub App ID" + type = string +} + +variable "github_webhook_secret" { + description = "GitHub Webhook Secret" + type = string +} + +variable "github_app_private_key" { + description = "GitHub App Private Key" + type = string +} diff --git a/examples/with-backstage/main.tf b/providers.tf similarity index 53% rename from examples/with-backstage/main.tf rename to providers.tf index 1d1b5cf..322760f 100644 --- a/examples/with-backstage/main.tf +++ b/providers.tf @@ -1,11 +1,44 @@ -# AWS reference architecture +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.17" + } + github = { + source = "integrations/github" + version = "~> 5.38" + } + helm = { + source = "hashicorp/helm" + version = "~> 2.12" + } + humanitec = { + source = "humanitec/humanitec" + version = "~> 1.0" + } + kubernetes = { + source = "hashicorp/kubernetes" + version = "~> 2.25" + } + random = { + source = "hashicorp/random" + version = "~> 3.5" + } + } + required_version = ">= 1.3.0" +} -module "base" { - source = "../../modules/base" +provider "humanitec" { + org_id = var.humanitec_org_id +} + +provider "github" { + owner = var.github_org_id +} - region = var.aws_region - instance_types = var.instance_types - disk_size = var.disk_size +provider "aws" { + region = var.aws_region + allowed_account_ids = [var.aws_account_id] } provider "kubernetes" { diff --git a/terraform.tfvars.example b/terraform.tfvars.example index df144cc..6c3f8ab 100644 --- a/terraform.tfvars.example +++ b/terraform.tfvars.example @@ -2,16 +2,25 @@ # AWS Account (ID) to use aws_account_id = "" -# AWS Region to deploy into +# AWS region aws_region = "" # Disk size in GB to use for EKS nodes disk_size = 20 +# GitHub org id +github_org_id = "" + +# Humanitec CI Service User Token +humanitec_ci_service_user_token = "" + # Humanitec Organization ID humanitec_org_id = "" # List of EC2 instances types to use for EKS nodes instance_types = [ "t3.large" -] \ No newline at end of file +] + +# Deploy Backstage +with_backstage = false \ No newline at end of file diff --git a/variables.tf b/variables.tf index 8e5f173..76731c4 100644 --- a/variables.tf +++ b/variables.tf @@ -1,11 +1,15 @@ +variable "aws_account_id" { + description = "AWS Account (ID) to use" + type = string +} variable "aws_region" { - description = "AWS Region to deploy into" + description = "AWS region" type = string } -variable "aws_account_id" { - description = "AWS Account (ID) to use" +variable "github_org_id" { + description = "GitHub org id" type = string } @@ -14,6 +18,12 @@ variable "humanitec_org_id" { type = string } +variable "humanitec_ci_service_user_token" { + description = "Humanitec CI Service User Token" + type = string + sensitive = true +} + variable "instance_types" { description = "List of EC2 instances types to use for EKS nodes" type = list(string) @@ -27,3 +37,9 @@ variable "disk_size" { type = number default = 20 } + +variable "with_backstage" { + description = "Deploy Backstage" + type = bool + default = false +}