-
Notifications
You must be signed in to change notification settings - Fork 95
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(azure-devops): create projects and respective service connection…
…s to ARM resources
- Loading branch information
Showing
19 changed files
with
445 additions
and
90 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,98 +1,122 @@ | ||
# Note: all variables included here for easier understanding/learning | ||
# Azure AD Groups | ||
# --------------- | ||
|
||
resource "azuread_group" "groups" { | ||
for_each = var.groups | ||
name = "demo-${each.value}-${local.suffix}" | ||
prevent_duplicate_names = true | ||
} | ||
|
||
|
||
# Suffix | ||
# ------ | ||
# Some Azure resources, e.g. storage accounts must have globally | ||
# unique names. Use a suffix to avoid automation errors. | ||
# Azure DevOps | ||
# ------------ | ||
|
||
resource "random_string" "suffix" { | ||
length = 4 | ||
special = false | ||
upper = false | ||
resource "azuredevops_project" "team_projects" { | ||
for_each = var.projects | ||
project_name = each.value.name | ||
description = each.value.description | ||
visibility = "private" | ||
version_control = "Git" | ||
|
||
features = { | ||
repositories = "enabled" | ||
pipelines = "enabled" | ||
artifacts = "disabled" | ||
boards = "disabled" | ||
testplans = "disabled" | ||
} | ||
} | ||
|
||
locals { | ||
suffix = random_string.suffix.result | ||
module "ado_standard_permissions" { | ||
for_each = var.projects | ||
source = "./modules/azure-devops-permissions" | ||
ado_project_id = azuredevops_project.team_projects["proj_${each.value.team}"].id | ||
team_aad_id = azuread_group.groups["${each.value.team}"].id | ||
admin_aad_id = azuread_group.groups["${each.value.team}_admins"].id | ||
} | ||
|
||
# Supermarket Project | ||
|
||
# Azure AD Groups | ||
# --------------- | ||
# Workspaces generally have 2 groups of actors, general | ||
# team members who are granted "Contributor" permissions | ||
# and admins who are granted "Owner" permissions. | ||
|
||
variable "teams" { | ||
type = map(string) | ||
default = { | ||
fruits = "fruits" | ||
fruits_admins = "fruits-admins" | ||
veggies_admins = "veggies-admins" | ||
veggies = "veggies" | ||
infra = "infra" | ||
infra_admins = "infra" | ||
resource "azuredevops_project" "supermarket" { | ||
project_name = "supermarket" | ||
description = "Example: 1 project, many teams, many repos" | ||
visibility = "private" | ||
version_control = "Git" | ||
|
||
features = { | ||
boards = "enabled" | ||
repositories = "enabled" | ||
pipelines = "enabled" | ||
artifacts = "disabled" | ||
testplans = "disabled" | ||
} | ||
} | ||
|
||
resource "azuread_group" "groups" { | ||
for_each = var.teams | ||
name = "demo-${each.value}-${local.suffix}" | ||
prevent_duplicate_names = true | ||
module "supermarket_permissions_fruits" { | ||
source = "./modules/azure-devops-permissions" | ||
ado_project_id = azuredevops_project.supermarket.id | ||
team_aad_id = azuread_group.groups["fruits"].id | ||
admin_aad_id = azuread_group.groups["fruits_admins"].id | ||
} | ||
|
||
module "supermarket_permissions_veggies" { | ||
source = "./modules/azure-devops-permissions" | ||
ado_project_id = azuredevops_project.supermarket.id | ||
team_aad_id = azuread_group.groups["veggies"].id | ||
admin_aad_id = azuread_group.groups["veggies_admins"].id | ||
} | ||
|
||
# Workspaces | ||
# ---------- | ||
# This map defines our workspaces. The keys can be referenced in outputs, | ||
# e.g. module.workspace["gov_shared"]. Suffixes are appended later. | ||
|
||
variable "environments" { | ||
type = map(map(string)) | ||
|
||
default = { | ||
fru_dev = { | ||
env = "dev" | ||
team = "fruits" | ||
} | ||
|
||
fru_prod = { | ||
env = "prod" | ||
team = "fruits" | ||
} | ||
|
||
veg_dev = { | ||
env = "dev" | ||
team = "veggies" | ||
} | ||
|
||
veg_prod = { | ||
env = "prod" | ||
team = "veggies" | ||
} | ||
|
||
shared = { | ||
env = "shared" | ||
team = "infra" | ||
} | ||
# Shared Collaboration | ||
|
||
resource "azuredevops_project" "collaboration" { | ||
project_name = "shared-collaboration" | ||
description = "Example: what if separate teams should talk to each other? (Disadvantage: cannot link external project commits to work items in this project)" | ||
visibility = "private" | ||
version_control = "Git" | ||
|
||
features = { | ||
boards = "enabled" | ||
repositories = "disabled" | ||
pipelines = "disabled" | ||
artifacts = "disabled" | ||
testplans = "disabled" | ||
} | ||
} | ||
|
||
module "collaboration_permissions_fruits" { | ||
source = "./modules/azure-devops-permissions" | ||
ado_project_id = azuredevops_project.collaboration.id | ||
team_aad_id = azuread_group.groups["fruits"].id | ||
admin_aad_id = azuread_group.groups["fruits_admins"].id | ||
} | ||
|
||
module "collaboration_permissions_veggies" { | ||
source = "./modules/azure-devops-permissions" | ||
ado_project_id = azuredevops_project.collaboration.id | ||
team_aad_id = azuread_group.groups["veggies"].id | ||
admin_aad_id = azuread_group.groups["veggies_admins"].id | ||
} | ||
|
||
|
||
# Workspaces | ||
# ---------- | ||
|
||
module "workspace" { | ||
for_each = var.environments | ||
source = "./modules/workspace" | ||
source = "./modules/azure-resources" | ||
name = "${each.value.team}-${each.value.env}-${local.suffix}" | ||
team_group_id = azuread_group.groups["${each.value.team}"].id | ||
admin_group_id = azuread_group.groups["${each.value.team}_admins"].id | ||
} | ||
|
||
|
||
# Outputs | ||
# ------- | ||
# Service Connections for ADO | ||
# --------------------------- | ||
|
||
output "workspaces" { | ||
value = module.workspace[*] | ||
module "service_connections" { | ||
for_each = module.workspace | ||
source = "./modules/azure-devops-service-connection" | ||
service_principal_id = each.value.service_principals[0].application_id | ||
key_vault_name = each.value.key_vault | ||
resource_group_name = each.value.resource_group_name | ||
} | ||
|
||
output "aad_groups" { | ||
value = azuread_group.groups[*] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Azure DevOps Groups | ||
# ------------------- | ||
# To add AAD groups to existing project roles: | ||
# | ||
# 1) Create new ADO group | ||
# 2) Get Reference to existing group, e.g. Contribtors in ADO Project | ||
# 3) Add ADO group (which references AAD group) to "Contributors" from above | ||
|
||
locals { | ||
team_role = "Contributors" | ||
admin_role = "Project Administrators" | ||
} | ||
|
||
# Contributors | ||
|
||
resource "azuredevops_group" "team_group" { | ||
origin_id = var.team_aad_id | ||
} | ||
|
||
data "azuredevops_group" "contributors" { | ||
project_id = var.ado_project_id | ||
name = local.team_role | ||
} | ||
|
||
resource "azuredevops_group_membership" "team" { | ||
group = data.azuredevops_group.contributors.descriptor | ||
members = [ | ||
azuredevops_group.team_group.descriptor | ||
] | ||
} | ||
|
||
# Administrators | ||
|
||
resource "azuredevops_group" "admins_group" { | ||
origin_id = var.admin_aad_id | ||
} | ||
|
||
data "azuredevops_group" "admins" { | ||
project_id = var.ado_project_id | ||
name = local.admin_role | ||
} | ||
|
||
resource "azuredevops_group_membership" "admins" { | ||
group = data.azuredevops_group.admins.descriptor | ||
members = [ | ||
azuredevops_group.team_group.descriptor | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
output "azure_devops_groups" { | ||
value = { | ||
contributors = { | ||
origin = azuredevops_group.team_group.origin | ||
principal_name = azuredevops_group.team_group.principal_name | ||
project_role = data.azuredevops_group.contributors.name | ||
project_id = data.azuredevops_group.contributors.project_id | ||
subject_kind = azuredevops_group.team_group.subject_kind | ||
} | ||
admins = { | ||
origin = azuredevops_group.admins_group.origin | ||
principal_name = azuredevops_group.admins_group.principal_name | ||
project_role = data.azuredevops_group.admins.name | ||
project_id = data.azuredevops_group.admins.project_id | ||
subject_kind = azuredevops_group.admins_group.subject_kind | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
variable "ado_project_id" { | ||
description = "Azure DevOps Project ID" | ||
type = string | ||
} | ||
|
||
variable "team_aad_id" { | ||
description = "AAD Group ID to receive 'Contributor' permissions" | ||
type = string | ||
} | ||
|
||
variable "admin_aad_id" { | ||
description = "AAD Group ID to receive 'Owner' permissions" | ||
type = string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
terraform { | ||
required_providers { | ||
azuredevops = { | ||
source = "terraform-providers/azuredevops" | ||
} | ||
} | ||
required_version = ">= 0.13" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
# 1 - get Service Principal secret from Key Vault | ||
|
||
data "azurerm_key_vault" "workspace" { | ||
name = var.key_vault_name | ||
resource_group_name = var.resource_group_name | ||
} | ||
|
||
data "azurerm_key_vault_secret" "sp_secret" { | ||
name = local.sp_secret_name | ||
key_vault_id = data.azurerm_key_vault.workspace.id | ||
} | ||
|
||
# 2 - get reference to ADO Project | ||
|
||
data "azuredevops_project" "team" { | ||
project_name = local.project_name | ||
} | ||
|
||
# 3 -get Subscription Info | ||
|
||
data "azurerm_subscription" "current" { | ||
} | ||
|
||
data "azurerm_client_config" "current" { | ||
} | ||
|
||
# 4 - finally create Service Connection in ADO project | ||
|
||
resource "azuredevops_serviceendpoint_azurerm" "workspace_endpoint" { | ||
project_id = data.azuredevops_project.team.id | ||
service_endpoint_name = local.connection_name | ||
credentials { | ||
serviceprincipalid = var.service_principal_id | ||
serviceprincipalkey = data.azurerm_key_vault_secret.sp_secret.value | ||
} | ||
azurerm_spn_tenantid = data.azurerm_client_config.current.tenant_id | ||
azurerm_subscription_id = data.azurerm_client_config.current.subscription_id | ||
azurerm_subscription_name = data.azurerm_subscription.current.display_name | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
output "service_connection" { | ||
value = azuredevops_serviceendpoint_azurerm.workspace_endpoint | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
variable "service_principal_id" { | ||
type = string | ||
description = "ID of Service Principal scoped to workspace/environment. The display name of this service principal uses `fruits-dev-XXXX-rg-sp` format, where `X` is a random character." | ||
} | ||
|
||
variable "key_vault_name" { | ||
type = string | ||
description = "Name of Key Vault of this workspace, e.g. `fruits-dev-XXXX-kv`, where `X` is a random character." | ||
} | ||
|
||
variable "resource_group_name" { | ||
type = string | ||
description = "Name of resource group of this workspace, e.g. `fruits-dev-XXXX-rg`, where `X` is a random character." | ||
} | ||
|
||
locals { | ||
sp_secret_name = "workspace-sp-secret" | ||
connection_name = "${var.resource_group_name}-connection" | ||
project_name = split("-", var.resource_group_name)[0] == "infra" ? "central-it" : "project-${split("-", var.resource_group_name)[0]}" | ||
} | ||
|
||
# Note: ADO project names are determined based on Resource Group name patterns: | ||
# | ||
# - fruits-dev-u6t7-rg | ||
# - veggies-prod-u6t7-rg | ||
# - infra-shared-u6t7-rg (breaks convetion) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
terraform { | ||
required_providers { | ||
azuredevops = { | ||
source = "terraform-providers/azuredevops" | ||
} | ||
azurerm = { | ||
source = "hashicorp/azurerm" | ||
version = "~> 2.30.0" | ||
} | ||
} | ||
required_version = ">= 0.13" | ||
} |
Oops, something went wrong.