Skip to content

Commit

Permalink
feat: backstage example
Browse files Browse the repository at this point in the history
  • Loading branch information
johanneswuerbach committed Feb 20, 2024
1 parent b88b431 commit b9613ac
Show file tree
Hide file tree
Showing 15 changed files with 723 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ jobs:
- name: Terraform Format Check
run: make fmt-check

- name: Stub GitHub App credentials (required for validation)
run: cd ./examples/with-backstage && STUB_FILE=1 node create-gh-app/index.js

- name: Terraform Validate
run: make validate

Expand Down
156 changes: 156 additions & 0 deletions examples/with-backstage/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# Azure reference architecture with Backstage

Provisions the Azure 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 <https://github.com/account/organizations/new> 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.

```
export GITHUB_TOKEN="my-github-token"
```

* Set the `GITHUB_ORG_ID` environment variable to your GitHub organization ID.

```
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 using `node create-gh-app/index.js`. 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 [Azure documentation](https://learn.microsoft.com/en-us/azure/aks/learn/quick-kubernetes-deploy-cli#connect-to-the-cluster) or use this command:

```
az aks get-credentials --resource-group ref-arch --name ref-arch-aks
```

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 infrastructure 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

<!-- BEGIN_TF_DOCS -->
### Requirements

| Name | Version |
|------|---------|
| terraform | >= 1.3.0 |
| Azure | ~> 5.17 |
| github | ~> 5.38 |
| humanitec | ~> 0.13 |

### Providers

| Name | Version |
|------|---------|
| Azure | ~> 5.17 |
| github | ~> 5.38 |
| humanitec | ~> 0.13 |

### Modules

| Name | Source | Version |
|------|--------|---------|
| backstage\_ecr | terraform-Azure-modules/ecr/Azure | ~> 1.6 |
| backstage\_iam\_policy\_ecr\_create\_repository | git::<https://github.com/humanitec-architecture/resource-packs-Azure.git//humanitec-resource-defs/iam-policy/ecr-create-repository> | n/a |
| backstage\_iam\_role\_service\_account | git::<https://github.com/humanitec-architecture/resource-packs-Azure.git//humanitec-resource-defs/iam-role/service-account> | n/a |
| backstage\_k8s\_service\_account | git::<https://github.com/humanitec-architecture/resource-packs-Azure.git//humanitec-resource-defs/k8s/service-account> | n/a |
| backstage\_mysql | git::<https://github.com/humanitec-architecture/resource-packs-in-cluster.git//humanitec-resource-defs/mysql/basic> | n/a |
| backstage\_postgres | git::<https://github.com/humanitec-architecture/resource-packs-in-cluster.git//humanitec-resource-defs/postgres/basic> | n/a |
| backstage\_workload | git::<https://github.com/humanitec-architecture/resource-packs-Azure.git//humanitec-resource-defs/workload/service-account> | n/a |
| base | ../../modules/base | n/a |
| iam\_github\_oidc\_provider | terraform-Azure-modules/iam/Azure//modules/iam-github-oidc-provider | ~> 5.30 |
| iam\_github\_oidc\_role | terraform-Azure-modules/iam/Azure//modules/iam-github-oidc-role | ~> 5.30 |

### Resources

| Name | Type |
|------|------|
| [Azure_iam_policy.ecr_push_policy](https://registry.terraform.io/providers/hashicorp/Azure/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_Azure_region](https://registry.terraform.io/providers/integrations/github/latest/docs/resources/actions_organization_variable) | resource |
| [github_actions_organization_variable.backstage_Azure_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_iam_policy_ecr_create_repository](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/resource_definition_criteria) | resource |
| [humanitec_resource_definition_criteria.backstage_iam_role_service_account](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/resource_definition_criteria) | resource |
| [humanitec_resource_definition_criteria.backstage_k8s_service_account](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/resource_definition_criteria) | 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_resource_definition_criteria.backstage_workload](https://registry.terraform.io/providers/humanitec/humanitec/latest/docs/resources/resource_definition_criteria) | resource |
| [humanitec_value.Azure_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 |

### Inputs

| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| Azure\_account\_id | Azure Account (ID) to use | `string` | n/a | yes |
| Azure\_region | Azure 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)` | <pre>[<br> "t3.large"<br>]</pre> | no |
| resource\_packs\_Azure\_rev | Revision of the resource-packs-Azure repository to use | `string` | `"refs/heads/main"` | no |
<!-- END_TF_DOCS -->
29 changes: 29 additions & 0 deletions examples/with-backstage/azure-github.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
locals {
name = "gha-acr-push"
cloud_provider = "azure"
github_issuer_url = "https://token.actions.githubusercontent.com"
}

# User for GHA allowed to push images using OpenID Connect (OIDC) so we don't need to store credentials in GitHub
# Reference https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-azure

resource "azurerm_user_assigned_identity" "github_oidc_identity" {
name = local.name
location = module.base.az_resource_group_location
resource_group_name = module.base.az_resource_group_name
}

resource "azurerm_federated_identity_credential" "github_oidc_identity" {
name = "github-action-identity"
resource_group_name = module.base.az_resource_group_name
audience = ["api://AzureADTokenExchange"]
issuer = local.github_issuer_url
subject = "repository_owner:${var.github_org_id}" # configured in github_actions_organization_oidc_subject_claim_customization_template
parent_id = azurerm_user_assigned_identity.github_oidc_identity.id
}

resource "azurerm_role_assignment" "github_oidc_identity_acr" {
scope = module.base.az_container_registry_id
role_definition_name = "AcrPush"
principal_id = azurerm_user_assigned_identity.github_oidc_identity.principal_id
}
86 changes: 86 additions & 0 deletions examples/with-backstage/backstage-github.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# 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_azure_client_id" {
variable_name = "AZURE_CLIENT_ID"
visibility = "all"
value = azurerm_user_assigned_identity.github_oidc_identity.client_id
}

resource "github_actions_organization_variable" "backstage_azure_tenant_id" {
variable_name = "AZURE_TENANT_ID"
visibility = "all"
value = azurerm_user_assigned_identity.github_oidc_identity.tenant_id
}

resource "github_actions_organization_variable" "backstage_azure_subscription_id" {
variable_name = "AZURE_SUBSCRIPTION_ID"
visibility = "all"
value = var.subscription_id
}

resource "github_actions_organization_variable" "backstage_azure_acr_name" {
variable_name = "AZURE_ACR_NAME"
visibility = "all"
value = module.base.az_container_registry_name
}

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,
humanitec_application.backstage,
humanitec_resource_definition_criteria.backstage_postgres,
github_actions_organization_secret.backstage_humanitec_token,
]
}

# Required as Azure doesn't support wildcards in scopes https://github.com/Azure/azure-workload-identity/issues/373
# More details in https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#customizing-the-token-claims
resource "github_actions_repository_oidc_subject_claim_customization_template" "backstage" {
repository = github_repository.backstage.name
use_default = false
include_claim_keys = ["repository_owner"]
}
114 changes: 114 additions & 0 deletions examples/with-backstage/backstage-humanitec.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
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
}

# Configure required resources for backstage

locals {
res_def_prefix = "backstage-"
}

# in-cluster postgres

module "backstage_postgres" {
source = "git::https://github.com/humanitec-architecture/resource-packs-in-cluster.git//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 = "git::https://github.com/humanitec-architecture/resource-packs-in-cluster.git//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
}
Loading

0 comments on commit b9613ac

Please sign in to comment.