Skip to content

Commit

Permalink
Add an example using password authentication
Browse files Browse the repository at this point in the history
- Add an example using password authentication
- Increase AzureRM provider version in examples
  • Loading branch information
adamconnelly authored Jun 21, 2023
1 parent 8e66781 commit 2b67708
Show file tree
Hide file tree
Showing 13 changed files with 246 additions and 4 deletions.
5 changes: 4 additions & 1 deletion .spacelift/config.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
version: 1
module_version: 0.0.6
module_version: 0.0.7

tests:
- name: System-assigned identity
project_root: examples/system-assigned-identity

- name: Password authentication
project_root: examples/password-authentication
2 changes: 1 addition & 1 deletion examples/bastion/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=2.68.0"
version = "=3.61.0"
}

random = {
Expand Down
38 changes: 38 additions & 0 deletions examples/password-authentication/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Private Worker with a User Assigned Identity

This example shows how to provision a Spacelift private worker using an Azure VMSS, using a
User-Assigned Managed Identity to grant the workers permission to manage your Azure subscription.
It also creates a KeyVault to store the worker pool credentials in, and uses the user-assigned
identity to grant the VMSS permission to those secrets.

Using a user-assigned managed identity gives you more control than using a [system-assigned identity](../system-assigned-identity/README.md),
allowing you to do things like provision secrets in KeyVault and grant the correct permissions
before your scale set is created.

**NOTES:**

- When using a user-assigned identity, you need to pass the identity's `client_id`
to the Terraform provider via the `ARM_CLIENT_ID` environment variable when following the
[authentication instructions](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/managed_service_identity).
- Although we're using KeyVault to store the secrets, please note that the Azure RM provider
stores the secret values in plain text in your state, so the usual warnings about treating
your state as a secret apply.

## Usage

To run the example, create a tfvars file with the following variables specified:

```hcl
admin_public_key = "<your-base64-encoded-ssh-public-key>"
worker_pool_config = "<your-worker-pool-config>"
worker_pool_private_key = "<your-worker-pool-private-key>"
worker_pool_id = "<your-worker-pool-id>"
location = "West Europe"
```

Now just initialise Terraform, and run an apply:

```shell
terraform init
terraform apply -var-file myvars.tfvars
```
2 changes: 2 additions & 0 deletions examples/password-authentication/data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
data "azurerm_client_config" "current" {}
data "azurerm_subscription" "primary" {}
8 changes: 8 additions & 0 deletions examples/password-authentication/group.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
resource "random_pet" "this" {}

resource "azurerm_resource_group" "this" {
name = "rg-${var.application}-${random_pet.this.id}-${var.env}"
location = var.location

tags = local.tags
}
14 changes: 14 additions & 0 deletions examples/password-authentication/identity.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Create a user-assigned identity for the VMSS. This allows us to grant it permissions
# over our subscription, as well as configure its access to KeyVault secrets.
resource "azurerm_user_assigned_identity" "vmss" {
resource_group_name = azurerm_resource_group.this.name
location = azurerm_resource_group.this.location
name = "sp5ft-${var.worker_pool_id}"
}

# Uncomment the following resource to grant the VMSS instances access to your current subscription.
# resource "azurerm_role_assignment" "vmss_contributor" {
# scope = data.azurerm_subscription.primary.id
# role_definition_name = "Contributor"
# principal_id = azurerm_user_assigned_identity.vmss.principal_id
# }
57 changes: 57 additions & 0 deletions examples/password-authentication/keyvault.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# KeyVault names need to be globally unique, so we'll just generate a random ID with a specified prefix.
resource "random_id" "keyvault" {
byte_length = 9
prefix = "sp5ft"
}

resource "azurerm_key_vault" "this" {
name = random_id.keyvault.hex
location = azurerm_resource_group.this.location
resource_group_name = azurerm_resource_group.this.name
tenant_id = data.azurerm_client_config.current.tenant_id
sku_name = "premium"
soft_delete_retention_days = 7

# Allow the user running the apply access to KeyVault. You may want to configure
# policies for other users/groups in your organization.
access_policy {
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = data.azurerm_client_config.current.object_id

key_permissions = [
"Create",
"Get",
]

secret_permissions = [
"Set",
"Get",
"List",
"Delete",
"Purge",
"Recover"
]
}

# Grant the VMSS identity permission to download secrets.
access_policy {
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = azurerm_user_assigned_identity.vmss.principal_id

secret_permissions = [
"Get"
]
}
}

resource "azurerm_key_vault_secret" "worker_pool_config" {
name = "worker-pool-config"
value = base64encode(var.worker_pool_config)
key_vault_id = azurerm_key_vault.this.id
}

resource "azurerm_key_vault_secret" "worker_pool_private_key" {
name = "worker-pool-private-key"
value = base64encode(var.worker_pool_private_key)
key_vault_id = azurerm_key_vault.this.id
}
59 changes: 59 additions & 0 deletions examples/password-authentication/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=3.61.0"
}

random = {
source = "hashicorp/random"
version = "=3.1.0"
}
}
}

# Configure the Microsoft Azure Provider
provider "azurerm" {
features {}
}

module "azure-worker" {
source = "../../"

admin_username = var.admin_username
admin_password = var.admin_password

# This custom configuration block logs into Azure, downloads the worker pool credentials
# from KeyVault, and then configures the environment variables the Spacelift worker will
# read them from.
configuration = <<-EOT
az login --identity
echo "Downloading worker pool credentials from KeyVault" >> /var/log/spacelift/info.log
az keyvault secret download --name "${azurerm_key_vault_secret.worker_pool_config.name}" \
--vault-name "${azurerm_key_vault.this.name}" \
--file "/tmp/worker-pool-config" 1>>/var/log/spacelift/info.log 2>>/var/log/spacelift/error.log
az keyvault secret download --name "${azurerm_key_vault_secret.worker_pool_private_key.name}" \
--vault-name "${azurerm_key_vault.this.name}" \
--file "/tmp/worker-pool-private-key" 1>>/var/log/spacelift/info.log 2>>/var/log/spacelift/error.log
export SPACELIFT_TOKEN=$(cat /tmp/worker-pool-config | base64 --decode)
export SPACELIFT_POOL_PRIVATE_KEY=$(cat /tmp/worker-pool-private-key | base64 --decode)
rm /tmp/worker-pool-config
rm /tmp/worker-pool-private-key
echo "Worker pool credentials configured" >> /var/log/spacelift/info.log
EOT

resource_group = azurerm_resource_group.this
subnet_id = azurerm_subnet.worker.id

identity_type = "UserAssigned"
identity_ids = [azurerm_user_assigned_identity.vmss.id]

worker_pool_id = var.worker_pool_id
name_prefix = "sp5ft-user-identity"
tags = local.tags
}
15 changes: 15 additions & 0 deletions examples/password-authentication/network.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
resource "azurerm_virtual_network" "this" {
name = "vnet"
address_space = ["10.0.0.0/16"]
location = azurerm_resource_group.this.location
resource_group_name = azurerm_resource_group.this.name
tags = local.tags
}

# Create a subnet for our workers
resource "azurerm_subnet" "worker" {
name = "worker"
resource_group_name = azurerm_resource_group.this.name
virtual_network_name = azurerm_virtual_network.this.name
address_prefixes = ["10.0.2.0/24"]
}
3 changes: 3 additions & 0 deletions examples/password-authentication/outputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output "identity_client_id" {
value = azurerm_user_assigned_identity.vmss.client_id
}
43 changes: 43 additions & 0 deletions examples/password-authentication/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
locals {
tags = {
application = var.application
env = var.env
region = var.location
}
}

variable "admin_username" {
type = string
default = "spacelift"
}

variable "application" {
type = string
default = "sp5ft-user-identity"
}

variable "admin_password" {
type = string
}

variable "env" {
type = string
default = "test"
}

variable "location" {
type = string
default = "westeurope"
}

variable "worker_pool_config" {
type = string
}

variable "worker_pool_private_key" {
type = string
}

variable "worker_pool_id" {
type = string
}
2 changes: 1 addition & 1 deletion examples/system-assigned-identity/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=2.68.0"
version = "=3.61.0"
}

random = {
Expand Down
2 changes: 1 addition & 1 deletion examples/user-assigned-identity/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ terraform {
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "=2.68.0"
version = "=3.61.0"
}

random = {
Expand Down

0 comments on commit 2b67708

Please sign in to comment.