Skip to content

Commit

Permalink
Add Terraform configuration files for Azure AD and management groups
Browse files Browse the repository at this point in the history
  • Loading branch information
adamrushuk committed Mar 17, 2024
1 parent 6e10ce3 commit dfd9252
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 138 deletions.
139 changes: 1 addition & 138 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ env:
TF_PLAN: "tfplan"
# https://github.com/hashicorp/terraform/releases
TF_VERSION: "1.7.3"
TF_WORKING_DIR: ./terraform
TF_WORKING_DIR: ./terraform-vf
# azurerm provider oidc
# https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/service_principal_oidc
# https://developer.hashicorp.com/terraform/language/settings/backends/azurerm#oidc_request_token
Expand Down Expand Up @@ -123,20 +123,10 @@ jobs:
subscription-id: ${{ secrets.ARM_SUBSCRIPTION_ID }}
enable-AzPSSession: true

# This is required when developing after the initial build, and the AKS cluster may have been stopped
# Ensure AKS cluster is running, else timeouts will occur on k8s Terraform apply tasks
- name: Start AKS Cluster
continue-on-error: true
run: ./scripts/start_aks_cluster.sh

# Prereqs
- name: Create Storage Account for Terraform state
run: ./scripts/storage_create.sh

# TODO remove this step
# - name: Lookup Storage Key
# run: ./scripts/storage_key.sh

- name: Replace tokens in Terraform config files
run: pwsh -command "./scripts/Replace-Tokens.ps1"
env:
Expand Down Expand Up @@ -199,137 +189,10 @@ jobs:
env:
TF_PLAN: ${{ env.TF_PLAN }}

# Kubernetes
- name: Deploy kubernetes manifests
run: ./scripts/k8s_manifests_apply.sh

- name: Wait for resources to be "Ready"
run: ./scripts/wait.sh

# Ansible
- name: Run Ansible playbook
run: ./scripts/ansible.sh
env:
NEXUS_ADMIN_PASSWORD: ${{ secrets.NEXUS_ADMIN_PASSWORD }}
NEXUS_USER_PASSWORD: ${{ secrets.NEXUS_USER_PASSWORD }}

# Docker
# https://github.com/Azure/docker-login
- name: Docker repo login
uses: Azure/docker-login@v1
with:
login-server: ${{ env.DOCKER_FQDN }}
username: ${{ env.NEXUS_USER_USERNAME }}
password: ${{ secrets.NEXUS_USER_PASSWORD }}

- name: Push images to Docker repo
run: ./scripts/push_docker_images.sh

# TODO: Remove once issue has been fixed
- name: Fix Function App version
run: pwsh -command "./scripts/Fix-FunctionApp.ps1"
env:
FUNCTION_APP_NAME: "${{ env.PREFIX }}-funcapp"
FUNCTION_APP_RG: "${{ env.PREFIX }}-rg-aks-dev-001"

# Pester tests
- name: 🧪 Run Pester tests
continue-on-error: true
run: pwsh -command "./scripts/Start-Test.ps1"

# https://github.com/actions/upload-artifact
- name: Archive test artifacts
uses: actions/upload-artifact@v3
with:
name: test-results
path: test/pester-test-results.xml
if: always()

# remove NuGet proxy repo so pester report step doesnt fail
- name: Unregister NuGet proxy repo
run: pwsh -command "Unregister-PSRepository -Name nuget.org-proxy -Verbose"

# Shows at the bottom of a run: https://github.com/adamrushuk/devops-lab/runs/1035347513?check_suite_focus=true
# https://github.com/zyborg/pester-tests-report
# - name: Pester report
# uses: zyborg/pester-tests-report@v1
# with:
# test_results_path: test/pester-test-results.xml
# report_name: pester_tests
# report_title: Pester Tests
# github_token: ${{ secrets.GITHUB_TOKEN }}

# - uses: dorny/[email protected]
# with:
# # artifact: test-results # artifact name
# name: Pester Tests Results # Name of the check run which will be created
# path: 'test/pester-test-results.xml' # Path to test results (inside artifact .zip)
# reporter: java-junit # Format of test results

# Notify
- name: Notify slack
continue-on-error: true
env:
SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
run: ./scripts/send_slack_message.sh "[devops-lab] Build complete"

# used for any windows-only tasks
test-windows:
needs: build-and-deploy

# https://github.com/actions/runner-images?tab=readme-ov-file#available-images
runs-on: windows-2022

# https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idenvironment
environment:
name: dev

# only run if owner triggered action
if: github.actor == github.event.repository.owner.login

steps:
# Checkout
- uses: actions/checkout@v4

# Init tasks - inc Env var concatenation
# https://docs.github.com/en/free-pro-team@latest/actions/reference/workflow-commands-for-github-actions#environment-files
- name: Init tasks - inc Env var concatenation (Workaround)
# * NOTE: different syntax required for Windows agents
run: |
echo "AKS_RG_NAME=${{ env.PREFIX }}-rg-aks-dev-001" | Out-File -Append -Encoding utf8 -FilePath "$env:GITHUB_ENV"
echo "AKS_CLUSTER_NAME=${{ env.PREFIX }}-aks-001" | Out-File -Append -Encoding utf8 -FilePath "$env:GITHUB_ENV"
echo "DNS_DOMAIN_NAME=nexus.${{ env.ROOT_DOMAIN_NAME }}" | Out-File -Append -Encoding utf8 -FilePath "$env:GITHUB_ENV"
# Login
# https://github.com/Azure/login
- name: Login via OIDC to Azure Public Cloud (az cli and az powershell)
uses: azure/login@v1
with:
client-id: ${{ secrets.ARM_CLIENT_ID }}
tenant-id: ${{ secrets.ARM_TENANT_ID }}
subscription-id: ${{ secrets.ARM_SUBSCRIPTION_ID }}
enable-AzPSSession: true

# Chocolatey
- name: Test Nexus Chocolatey proxy repo
run: |
choco install velero --source "https://${{ env.DNS_DOMAIN_NAME }}/repository/chocolatey-proxy/"
# Velero CLI
- name: Test Velero CLI
# NOTE: Some functions cast values to a string to perform comparisons
# https://docs.github.com/en/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions#functions
# ! WARNING: only single quotes work for boolean comparison
if: env.VELERO_ENABLED == 'true'
run: |
az aks get-credentials --resource-group "${{ env.AKS_RG_NAME }}" --name "${{ env.AKS_CLUSTER_NAME }}" --overwrite-existing --admin
echo "`nVelero version info:"
velero version || true
echo "`nVelero backup location info:"
velero backup-location get
echo "`nVelero backup info:"
velero backup get
118 changes: 118 additions & 0 deletions terraform-vf/cpam_sso.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Configures EntraID App Registration Auth using OIDC

locals {
cpam_fqdn = "vgpwa1vw.eito-dublin.local"
cpam_owner_ids = [
"cc9375df-8e64-4e24-8ab2-45e02c01a111", # Adam Rush
]
}

# CPAM groups
resource "azuread_group" "cpam_admins" {
display_name = "CPAM-Admins"
security_enabled = true
prevent_duplicate_names = true
owners = concat([data.azuread_client_config.current.object_id], local.cpam_owner_ids)
}

resource "azuread_group" "cpam_readonly" {
display_name = "CPAM-ReadOnly"
security_enabled = true
prevent_duplicate_names = true
owners = concat([data.azuread_client_config.current.object_id], local.cpam_owner_ids)
}

resource "azuread_service_principal" "msgraph" {
client_id = data.azuread_application_published_app_ids.well_known.result.MicrosoftGraph
use_existing = true
}

# https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/application
resource "azuread_application" "cpam" {
display_name = "cpam"
description = "Used for Single Sign On to CPAM"
owners = local.cpam_owner_ids
sign_in_audience = "AzureADMyOrg"
group_membership_claims = ["All"]
prevent_duplicate_names = true

web {
redirect_uris = ["https://vgpwa1vw.eito-dublin.local/PasswordVault/api/Auth/OIDC/TitanicusOIDC/Token"]
logout_url = "https://${local.cpam_fqdn}/PasswordVault/v10/logon"

implicit_grant {
access_token_issuance_enabled = false
}
}

required_resource_access {
# Microsoft Graph
resource_app_id = data.azuread_application_published_app_ids.well_known.result.MicrosoftGraph

# Oauth2Permissions are delegated permissions, type=Scope
resource_access {
id = azuread_service_principal.msgraph.oauth2_permission_scope_ids["User.Read"]
type = "Scope"
}
}

app_role {
allowed_member_types = ["User"]
description = "Reader Access to the PAM"
display_name = "PAM Read Only"
enabled = true
id = random_uuid.reader.id
value = "reader"
}

app_role {
allowed_member_types = ["User"]
description = "Administrative Access to the PAM"
display_name = "PAM Administrators"
enabled = true
id = random_uuid.admin.id
value = "admin"
}

optional_claims {
access_token {
name = "groups"
source = null
essential = false
additional_properties = []
}

id_token {
name = "groups"
source = null
essential = false
additional_properties = []
}
}
}

resource "azuread_service_principal" "cpam" {
client_id = azuread_application.cpam.client_id
owners = concat([data.azuread_client_config.current.object_id], local.cpam_owner_ids)
description = "Argo CD Service Principle"
notes = "Operational notes can go here"
preferred_single_sign_on_mode = "oidc"
}

# Random IDs for the App Roles
resource "random_uuid" "reader" {}

resource "random_uuid" "admin" {}

# App Role Assignments
resource "azuread_app_role_assignment" "cpam_readonly" {
app_role_id = azuread_service_principal.cpam.app_role_ids["reader"]
principal_object_id = azuread_group.cpam_readonly.object_id
resource_object_id = azuread_service_principal.cpam.object_id
}

resource "azuread_app_role_assignment" "cpam_admins" {
app_role_id = azuread_service_principal.cpam.app_role_ids["admin"]
principal_object_id = azuread_group.cpam_admins.object_id
resource_object_id = azuread_service_principal.cpam.object_id
}
4 changes: 4 additions & 0 deletions terraform-vf/data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
data "azuread_client_config" "current" {}

# https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/data-sources/application_published_app_ids
data "azuread_application_published_app_ids" "well_known" {}
61 changes: 61 additions & 0 deletions terraform-vf/local_market_1.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
resource "azuread_users" "lm1_users" {
user_principal_name = ["[email protected]"]
display_name = "localmarket1user"
}

resource "azuread_users" "lm1_admins" {
user_principal_name = ["[email protected]"]
display_name = "localmarket1user"
}

resource "azuread_group" "lm1_users" {
display_name = "LM1 Users"
owners = [data.azuread_client_config.current.object_id]
prevent_duplicate_names = true
security_enabled = true
members = data.azuread_users.lm1_users.object_ids
}

resource "azuread_group" "lm1_admins" {
display_name = "LM1 Admins"
owners = [data.azuread_client_config.current.object_id]
prevent_duplicate_names = true
security_enabled = true
assignable_to_role = true
members = data.azuread_users.lm1_admins.object_ids
}

resource "azuread_administrative_unit" "lm1" {
display_name = "Local Market 1"
description = "Local Market 1"
hidden_membership_enabled = false
}

resource "azuread_administrative_unit_member" "lm1_users" {
for_each = toset(data.azuread_users.lm1_users.object_ids)
administrative_unit_object_id = azuread_administrative_unit.lm1.id
member_object_id = each.value
}

resource "azuread_administrative_unit_member" "lm1_user_group" {
administrative_unit_object_id = azuread_administrative_unit.lm1.id
member_object_id = azuread_group.lm1_users.id
}

# List of roles can be found here
# https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference
resource "azuread_directory_role" "lm1_user_administrator" {
display_name = "User Administrator"
}

resource "azuread_administrative_unit_role_member" "lm1_user_administrator" {
role_object_id = azuread_directory_role.lm1_user_administrator.object_id
administrative_unit_object_id = azuread_administrative_unit.lm1.id
member_object_id = azuread_group.lm1_admins.object_id
}

resource "azurerm_role_assignment" "lm1_reader_sub_access" {
scope = "/subscriptions/d57a223f-3332-42ff-84a0-85afb8f11c8b" # Hardcoded to vf-grp-tsa-prd-devops-01
role_definition_name = "Reader"
principal_id = azuread_group.lm1_users.object_id
}
15 changes: 15 additions & 0 deletions terraform-vf/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
resource "azurerm_management_group" "intermediary" {
display_name = "intermediary"
# 'Tenant Root Group' Management Group
parent_management_group_id = "/providers/Microsoft.Management/managementGroups/d963d62c-d864-49fb-b3ba-6911db326ad2"
}

resource "azurerm_management_group" "local_market_1" {
display_name = "Local Market 1"
parent_management_group_id = azurerm_management_group.intermediary.id
}

resource "azurerm_management_group" "local_market_2" {
display_name = "Local Market 2"
parent_management_group_id = azurerm_management_group.intermediary.id
}
20 changes: 20 additions & 0 deletions terraform-vf/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
terraform {
backend "azurerm" {
key = "terraform.tfstate"
}
required_providers {
azuread = {
source = "hashicorp/azuread"
version = "~> 2.47.0"
}
azurerm = {
source = "hashicorp/azurerm"
version = "~> 3.96.0"
}
}
required_version = ">= 1.0"
}

provider "azurerm" {
features {}
}

0 comments on commit dfd9252

Please sign in to comment.