From b6a08e46be296c088903107cb17160b29b67ea91 Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Fri, 29 Apr 2022 14:26:56 +0200 Subject: [PATCH 01/21] =?UTF-8?q?chore:=20bump=20all=20provider=20versions?= =?UTF-8?q?,=20major=20versions=20=F0=9F=98=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .terraform.lock.hcl | 116 +++++++++--------- modules/azure-devops-permissions/provider.tf | 2 +- .../provider.tf | 10 +- modules/service-principal/provider.tf | 13 -- provider.tf | 6 +- 5 files changed, 68 insertions(+), 79 deletions(-) delete mode 100644 modules/service-principal/provider.tf diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl index dbb4c7c..0ab0fc0 100644 --- a/.terraform.lock.hcl +++ b/.terraform.lock.hcl @@ -2,79 +2,81 @@ # Manual edits may be lost in future updates. provider "registry.terraform.io/hashicorp/azuread" { - version = "2.14.0" - constraints = ">= 1.4.0, >= 1.5.1" + version = "2.22.0" + constraints = ">= 2.2.0" hashes = [ - "h1:llPDf1aQZeZo9a0YxqJjJYBVByfTmV98GzGS4qH4BUw=", - "zh:0e5fad817e949070fc367808346a8fe22545ff2eaff58ae799f5620e0f3b5d74", - "zh:6480f651297c3db7acaa8d66aa496768543de8d37806d5b3139a5971b65cd57c", - "zh:70f9beafd2f87d9779be087938ba574d8746a0134854d194cf61f48f7ed1409f", - "zh:7be844cc5bc148af429a45a38fbf0875fb17833e43996a2df2ad400a6e699c3d", - "zh:91ad6dd5482a3947512ee17566df255903edeb081b4214cb32292b0b62d1e25a", - "zh:979aabdc49b779f382c09894914bc583a58a81dbac8d079e398088ef95e8d5eb", - "zh:a7abd1cb9b111e40ad7756cb3b49fffef9fbcd750f2a06816d0f6704f2cffe48", - "zh:b8b56f2cab1e853f2da754580a6d0986c69cc37c11a035cedcf1732053911cf6", - "zh:c4685b19e99bb39c6ba6d19796c37566ced97c61dd14f5c33e70e7a1276b0319", - "zh:f4e5b190f228e4b6f3408cf7f04d083bf24b0ddda8992c804f7fc34abf6a60fb", - "zh:fb6f5bc43a39d916f1b050adda14e1b2cbb940776e8fa91d1d24acf28201ded9", + "h1:8AfMLW7SHmCPDODLKpx4QY0kNV4IZBrwDvfS/yAzpTI=", + "zh:062d84c514cd5015af60693ca4f3aece80d358fd7172951546eaba8093065c5b", + "zh:13749654ccd901408c74de2e1d7de43157044c4e739edcc0a66012a6cc6bba7a", + "zh:138c107f6aa554924a241806bca69248af1b7ce79ec93c6eef369886f33eef0a", + "zh:1c3e89cf19118fc07d7b04257251fc9897e722c16e0a0df7b07fcd261f8c12e7", + "zh:33c656e07492808da0584717a3cd52377dff15ae0f1f5f411321b8de08a7693e", + "zh:4e08570e51742e717a914db5dd15c0a73cd1686e0c1f1a07123d3aa70cc00718", + "zh:4fef3aca24238cead0798d29196c9e2270622091897dba040c21500c2ddb4095", + "zh:614c60e3dfdd17b7d93b9355e057c825bb36e61f5bc25ccbc6550ff7bd726b65", + "zh:65d8789b8b088322d4e27ea6cd9935749980fe0a1b94e8e56f0cca35c34c394e", + "zh:823abd9bbd9f42bc4c5769be033bf734bb81bb20152b7e1c009a6234b849e5b6", + "zh:9c7ece6b3c65253bfef6ee29acc0cac033ec061bd6755c5496a7e5c17997c918", + "zh:fc0ff3e3104ee6e89c2fa3bf6c83ba698062e64165b60acfe7ad00f2161d1250", ] } provider "registry.terraform.io/hashicorp/azurerm" { - version = "2.91.0" - constraints = ">= 2.50.0, >= 2.63.0" + version = "3.4.0" + constraints = ">= 3.4.0" hashes = [ - "h1:Lpey6sJtmgK33lBZQjDeWO5wh4uh/u6oYx9n6yQcOD4=", - "zh:17102231bc42ac91260489377fb0344408185f9233f126b825c0d0bdc873c8ec", - "zh:2454e0683fd8b230c7f30da2afc26bb0e9d699b85409d175a25cd094e4bf7089", - "zh:276fdae42310057bc7847ff4af6bb441408153af2ad72f8931145da21072ba8a", - "zh:29fd177efd83807acaadc788cbf151e0ed19275b00c7600e1b72316f00e0f1ea", - "zh:5d5ec15bbd38fa4d50074ff530e8851b06eeb08048666cde5096d44eeb495e9c", - "zh:7974ae42bdb7f9104c1477760d7227243a34087526b4d3eea138f3110b10fd58", - "zh:a9fd00320e15c53061556e0dd5818d7e0ca4af0713554dced1ede819350edd9a", - "zh:d8a1a3294faabfe0722ed5e553f054a92b2dc03b7f479ca58d67d36621289ce4", - "zh:ec4e798182bde6a9d89869c458d36b02d3acdd7ce118c91e8af2b86f082bf5e0", - "zh:fb1b3f126f823cb4b6e9018136562e9c28f65732ef0d0f11c18d04117c7ae7a3", - "zh:ff0ead2fe3c4c5d597fdc3f2183407ab971f2f435a887ff7af7dac9ae3fa6e86", + "h1:xhN9eEt1oP9me06v9VsrqpbImlZV16NkHpHcKgpZ66k=", + "zh:4e9913fc3378436d19150c334e5906eafb83a4af3a270423cb7cdda94b27371f", + "zh:5b3d0cec2a600dc1f6633baa8fc36368c5c330fd7654861edcfa76f760a8f6a9", + "zh:5e0e1f899027bc182f31d996c9611e5ba27a034c848d7b0519b39e559fc4f38d", + "zh:66e3a1383ed6a0370989f6fd6abcfa63ccf6918ae535108595af57b9c20a9257", + "zh:688493baf6a116a399b737d74c11080051aca1ab087e5cddd14cc683b7e45c76", + "zh:9e471d85d52343e3ba778f3a94626d820fbec97bb589a3ac7a6a0939b9387770", + "zh:be1e85635daca1768f26962a4cbbadbf7fd13d9da8f9f188e938beca542c2ad5", + "zh:c00e14b6aa566eb9995cb0e1611a18fb8650d9f35c7636a7643a1b6e22660226", + "zh:c40711e5021838fd879da4c9e6b8f7e72104ada2adf0f3ba22e1cc32c3c54086", + "zh:cc62f8541de8d79577e57664e4f03c1fca893d455e5fb238d20668389c0f09ee", + "zh:cd9cbb5c6e5ceb5fcc7c4d0cab516ff209667d1b539b8c7436bd5e452c6aba8f", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", ] } provider "registry.terraform.io/hashicorp/random" { - version = "3.1.0" + version = "3.1.3" constraints = "~> 3.1.0" hashes = [ - "h1:9cCiLO/Cqr6IUvMDSApCkQItooiYNatZpEXmcu0nnng=", - "h1:rKYu5ZUbXwrLG1w81k7H3nce/Ys6yAxXhWcbtk36HjY=", - "zh:2bbb3339f0643b5daa07480ef4397bd23a79963cc364cdfbb4e86354cb7725bc", - "zh:3cd456047805bf639fbf2c761b1848880ea703a054f76db51852008b11008626", - "zh:4f251b0eda5bb5e3dc26ea4400dba200018213654b69b4a5f96abee815b4f5ff", - "zh:7011332745ea061e517fe1319bd6c75054a314155cb2c1199a5b01fe1889a7e2", - "zh:738ed82858317ccc246691c8b85995bc125ac3b4143043219bd0437adc56c992", - "zh:7dbe52fac7bb21227acd7529b487511c91f4107db9cc4414f50d04ffc3cab427", - "zh:a3a9251fb15f93e4cfc1789800fc2d7414bbc18944ad4c5c98f466e6477c42bc", - "zh:a543ec1a3a8c20635cf374110bd2f87c07374cf2c50617eee2c669b3ceeeaa9f", - "zh:d9ab41d556a48bd7059f0810cf020500635bfc696c9fc3adab5ea8915c1d886b", - "zh:d9e13427a7d011dbd654e591b0337e6074eef8c3b9bb11b2e39eaaf257044fd7", - "zh:f7605bd1437752114baf601bdf6931debe6dc6bfe3006eb7e9bb9080931dca8a", + "h1:LPSVX+oXKGaZmxgtaPf2USxoEsWK/pnhmm/5FKw+PtU=", + "zh:26e07aa32e403303fc212a4367b4d67188ac965c37a9812e07acee1470687a73", + "zh:27386f48e9c9d849fbb5a8828d461fde35e71f6b6c9fc235bc4ae8403eb9c92d", + "zh:5f4edda4c94240297bbd9b83618fd362348cadf6bf24ea65ea0e1844d7ccedc0", + "zh:646313a907126cd5e69f6a9fafe816e9154fccdc04541e06fed02bb3a8fa2d2e", + "zh:7349692932a5d462f8dee1500ab60401594dddb94e9aa6bf6c4c0bd53e91bbb8", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:9034daba8d9b32b35930d168f363af04cecb153d5849a7e4a5966c97c5dc956e", + "zh:bb81dfca59ef5f949ef39f19ea4f4de25479907abc28cdaa36d12ecd7c0a9699", + "zh:bcf7806b99b4c248439ae02c8e21f77aff9fadbc019ce619b929eef09d1221bb", + "zh:d708e14d169e61f326535dd08eecd3811cd4942555a6f8efabc37dbff9c6fc61", + "zh:dc294e19a46e1cefb9e557a7b789c8dd8f319beca99b8c265181bc633dc434cc", + "zh:f9d758ee53c55dc016dd736427b6b0c3c8eb4d0dbbc785b6a3579b0ffedd9e42", ] } provider "registry.terraform.io/microsoft/azuredevops" { - version = "0.1.8" - constraints = ">= 0.1.0, >= 0.1.8" + version = "0.2.1" + constraints = ">= 0.2.0" hashes = [ - "h1:Rz3VcD+7BjK0krTlHzQdDheu2cNG8OUs5HuMsJJh3v8=", - "zh:15086305cb0312624f8391945f0bcc0e77bc25b770a93e8ddc89dd9d94e4dc5f", - "zh:231c80b5cbfc1c73e9a2aefd7c5e21b5e4e58398375cf9e8052fd11e1e656f7b", - "zh:25742514a15b4212839c1276a66c380054dd990ee0452beb3ae8062b23427913", - "zh:42c3c6ef2ba96a7121869ee13f3e351f6b300f31b415a1aaac1bf6ef018c5461", - "zh:451390e74b77b1049d0412eb6b2df7a23979ebae0c0fb1f96ef64f76689f8d69", - "zh:58c6b6ec9b51950c3f7a20b9a5ec6d862da614a34eab3a7e6a80601209da6fdd", - "zh:6715cfdafc1f4704b05a7aa33daac7be320d6a4e9c5fe0400b194e6cf90639a8", - "zh:96a5d90681048bffe593678f3a1ba2a8a542e72d566c37238408fd27a6619c47", - "zh:a18b54e3da609d00d68e5c837bd805834671ce0e3709b9050b3f8a2641bc7259", - "zh:bdf19514cfd1dc8c8d42fdddc432f58ed8b3589da77aa355d3bdf94f7a18d8e1", - "zh:cb0e717394503ccd4dc367a96bd8afdb7bb73dcd350fc708e2ddcc4d5a788508", - "zh:eaf0dc2f51eb8fbe6e220bac9616af52e9f37ff731d23636d03ee314d6d064aa", + "h1:ivb/yTf7VTsMzIBCg+/NH7qYzq48KPgbtv0E7ZLf+2g=", + "zh:230d28f656a66edeed9028ddc6420833dbb81d3ed6327b160cb6590a3301e52e", + "zh:3dae0d821a9e52a210288ba43994354a70a8a8d9ef273d8935979ce562eaa5e0", + "zh:53784786ccf61790f10d2d3bc4ccd1b91f5cb38302ef6a63c92be7f3509abf70", + "zh:763d1c2e7d0c443e6bad18cf94a035658e841c6e536c46d71b20b0dfa9ad8040", + "zh:81c2f1ad1d9392870111dcdf0274e65dbff202dee5abc0985066a266cb0c9365", + "zh:9f2168271c2407c513205d9e2001c44339cfeb93376245236cb80e0ab62b9b67", + "zh:a38a90c94ac3c01f9f49da5fa7c0a61ccfdc3a976347cf8ed2984b3d29aa2b77", + "zh:cb09b26758f5fa6367b6f9ff1f3c33cc2e9d5f61d3b84550d75405ba1617acfa", + "zh:d8076306289d6e1cf8a34598e49015b9e95a362409c9276dd640c22d1c8b39b1", + "zh:dfa9fb0ca886db7d1addabb1c25128ecf5f0ce65749c0ac32b2e36ee965f5dee", + "zh:e020300eac7b560936687caafebfd616b937fdb4888c2246e4d9749bc3cfc968", + "zh:f12de8c46a0bf557d0a11e26b4253d3bc73e63a19d0caa24e1bafd1c8bb4e09c", ] } diff --git a/modules/azure-devops-permissions/provider.tf b/modules/azure-devops-permissions/provider.tf index 6ae5e62..0453d73 100644 --- a/modules/azure-devops-permissions/provider.tf +++ b/modules/azure-devops-permissions/provider.tf @@ -2,7 +2,7 @@ terraform { required_providers { azuredevops = { source = "microsoft/azuredevops" - version = ">=0.1.0" + version = ">=0.2.0" } } } diff --git a/modules/azure-devops-service-connection/provider.tf b/modules/azure-devops-service-connection/provider.tf index 8242239..ab608e4 100644 --- a/modules/azure-devops-service-connection/provider.tf +++ b/modules/azure-devops-service-connection/provider.tf @@ -2,11 +2,11 @@ terraform { required_providers { azuredevops = { source = "microsoft/azuredevops" - version = ">=0.1.0" - } - azurerm = { - source = "hashicorp/azurerm" - version = ">= 2.50.0" + version = ">=0.2.0" } + # azurerm = { + # source = "hashicorp/azurerm" + # version = ">= 2.50.0" + # } } } diff --git a/modules/service-principal/provider.tf b/modules/service-principal/provider.tf deleted file mode 100644 index 5495ee4..0000000 --- a/modules/service-principal/provider.tf +++ /dev/null @@ -1,13 +0,0 @@ -terraform { - required_providers { - azuread = { - source = "hashicorp/azuread" - version = ">=1.4.0" - } - - random = { - source = "hashicorp/random" - version = "~> 3.1.0" - } - } -} diff --git a/provider.tf b/provider.tf index e4d190d..427a37c 100644 --- a/provider.tf +++ b/provider.tf @@ -4,15 +4,15 @@ terraform { required_providers { azuread = { source = "hashicorp/azuread" - version = ">=1.5.1" + version = ">=2.2.0" } azuredevops = { source = "microsoft/azuredevops" - version = ">=0.1.8" + version = ">=0.2.0" } azurerm = { source = "hashicorp/azurerm" - version = ">=2.63.0" + version = ">=3.4.0" } random = { source = "hashicorp/random" From 76c94558a45f87c43fda792ab45fc935ec86d126 Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 09:14:27 +0200 Subject: [PATCH 02/21] provider(storage): remove deprecated property --- modules/azure-resources/main.tf | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/azure-resources/main.tf b/modules/azure-resources/main.tf index 2e206bf..2f18f6a 100644 --- a/modules/azure-resources/main.tf +++ b/modules/azure-resources/main.tf @@ -18,7 +18,6 @@ resource "azurerm_storage_account" "storage" { location = azurerm_resource_group.workspace.location account_tier = "Standard" account_replication_type = "LRS" - allow_blob_public_access = false tags = var.tags } @@ -109,4 +108,4 @@ resource "azurerm_role_assignment" "kv_team_devs" { # } # Why does it take up to 10 minutes for Key Vault RBAC to propagate? -# See https://docs.microsoft.com/en-us/azure/key-vault/general/rbac-guide?tabs=azure-cli#known-limits-and-performance \ No newline at end of file +# See https://docs.microsoft.com/en-us/azure/key-vault/general/rbac-guide?tabs=azure-cli#known-limits-and-performance From f9573e64cb17a4f59c217300a72259eb26e5f27e Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 09:19:41 +0200 Subject: [PATCH 03/21] provider(aad): since v2 azure creates service principal passwords --- modules/service-principal/main.tf | 5 +++-- modules/service-principal/outputs.tf | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/service-principal/main.tf b/modules/service-principal/main.tf index 6abfc17..3662c77 100644 --- a/modules/service-principal/main.tf +++ b/modules/service-principal/main.tf @@ -7,10 +7,11 @@ resource "azuread_application" "app" { owners = var.owners } -resource "azuread_application_password" "workspace_sp_secret" { - application_object_id = azuread_application.app.object_id +resource "azuread_service_principal_password" "workspace_sp_secret" { + service_principal_id = azuread_service_principal.sp.object_id } resource "azuread_service_principal" "sp" { application_id = azuread_application.app.application_id + owners = var.owners } diff --git a/modules/service-principal/outputs.tf b/modules/service-principal/outputs.tf index 59f46b0..777ba66 100644 --- a/modules/service-principal/outputs.tf +++ b/modules/service-principal/outputs.tf @@ -20,7 +20,7 @@ output "client_id" { } output "client_secret" { - value = azuread_application_password.workspace_sp_secret.value + value = azuread_service_principal_password.workspace_sp_secret.value description = "Client Secret for Service Principal to be imported into Key Vault" sensitive = true } From d606615ed57e1f1dcbce34cfb39d2c6f4d8b8642 Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 09:23:27 +0200 Subject: [PATCH 04/21] chore: remove unused code --- modules/azure-resources/main.tf | 9 --------- 1 file changed, 9 deletions(-) diff --git a/modules/azure-resources/main.tf b/modules/azure-resources/main.tf index 2f18f6a..ce2276c 100644 --- a/modules/azure-resources/main.tf +++ b/modules/azure-resources/main.tf @@ -38,15 +38,6 @@ resource "azurerm_key_vault" "kv" { enable_rbac_authorization = true } -# ------------------ -# Service Principals -# ------------------ - -# module "workspace_sp" { -# source = "./../service-principal" -# name = "${local.name}-sp" -# } - # ----------------------- # RBAC - Role Assignments # ----------------------- From 4fc3809c0495fb2868b0e42cbae45ee857bb4209 Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 09:23:46 +0200 Subject: [PATCH 05/21] main-proj: specify dependency relationship --- main.tf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/main.tf b/main.tf index 2e16b35..2ae985f 100644 --- a/main.tf +++ b/main.tf @@ -54,6 +54,11 @@ module "arm_environments" { admins_group_id = azuread_group.groups["${each.value.team}_admins"].id superadmins_group_id = local.superadmins_aad_object_id service_principal_id = module.service_principals["${each.value.team}_${each.value.env}"].principal_id + + depends_on = [ + azuread_group.groups, + module.service_principals + ] } # ------------ From b36c3b179a98ec33446bbf89b68db6beaf37af37 Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 11:10:48 +0200 Subject: [PATCH 06/21] feat(initial-setup): bootstrap aad owners group, sp with terraform --- modules/cicd-setup/.terraform.lock.hcl | 42 +++++++++++ modules/cicd-setup/README.md | 52 ++++++++++++++ modules/cicd-setup/main.tf | 96 ++++++++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 modules/cicd-setup/.terraform.lock.hcl create mode 100644 modules/cicd-setup/README.md create mode 100644 modules/cicd-setup/main.tf diff --git a/modules/cicd-setup/.terraform.lock.hcl b/modules/cicd-setup/.terraform.lock.hcl new file mode 100644 index 0000000..26083c5 --- /dev/null +++ b/modules/cicd-setup/.terraform.lock.hcl @@ -0,0 +1,42 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/azuread" { + version = "2.22.0" + constraints = ">= 2.2.0" + hashes = [ + "h1:8AfMLW7SHmCPDODLKpx4QY0kNV4IZBrwDvfS/yAzpTI=", + "zh:062d84c514cd5015af60693ca4f3aece80d358fd7172951546eaba8093065c5b", + "zh:13749654ccd901408c74de2e1d7de43157044c4e739edcc0a66012a6cc6bba7a", + "zh:138c107f6aa554924a241806bca69248af1b7ce79ec93c6eef369886f33eef0a", + "zh:1c3e89cf19118fc07d7b04257251fc9897e722c16e0a0df7b07fcd261f8c12e7", + "zh:33c656e07492808da0584717a3cd52377dff15ae0f1f5f411321b8de08a7693e", + "zh:4e08570e51742e717a914db5dd15c0a73cd1686e0c1f1a07123d3aa70cc00718", + "zh:4fef3aca24238cead0798d29196c9e2270622091897dba040c21500c2ddb4095", + "zh:614c60e3dfdd17b7d93b9355e057c825bb36e61f5bc25ccbc6550ff7bd726b65", + "zh:65d8789b8b088322d4e27ea6cd9935749980fe0a1b94e8e56f0cca35c34c394e", + "zh:823abd9bbd9f42bc4c5769be033bf734bb81bb20152b7e1c009a6234b849e5b6", + "zh:9c7ece6b3c65253bfef6ee29acc0cac033ec061bd6755c5496a7e5c17997c918", + "zh:fc0ff3e3104ee6e89c2fa3bf6c83ba698062e64165b60acfe7ad00f2161d1250", + ] +} + +provider "registry.terraform.io/hashicorp/azurerm" { + version = "3.4.0" + constraints = ">= 3.4.0" + hashes = [ + "h1:xhN9eEt1oP9me06v9VsrqpbImlZV16NkHpHcKgpZ66k=", + "zh:4e9913fc3378436d19150c334e5906eafb83a4af3a270423cb7cdda94b27371f", + "zh:5b3d0cec2a600dc1f6633baa8fc36368c5c330fd7654861edcfa76f760a8f6a9", + "zh:5e0e1f899027bc182f31d996c9611e5ba27a034c848d7b0519b39e559fc4f38d", + "zh:66e3a1383ed6a0370989f6fd6abcfa63ccf6918ae535108595af57b9c20a9257", + "zh:688493baf6a116a399b737d74c11080051aca1ab087e5cddd14cc683b7e45c76", + "zh:9e471d85d52343e3ba778f3a94626d820fbec97bb589a3ac7a6a0939b9387770", + "zh:be1e85635daca1768f26962a4cbbadbf7fd13d9da8f9f188e938beca542c2ad5", + "zh:c00e14b6aa566eb9995cb0e1611a18fb8650d9f35c7636a7643a1b6e22660226", + "zh:c40711e5021838fd879da4c9e6b8f7e72104ada2adf0f3ba22e1cc32c3c54086", + "zh:cc62f8541de8d79577e57664e4f03c1fca893d455e5fb238d20668389c0f09ee", + "zh:cd9cbb5c6e5ceb5fcc7c4d0cab516ff209667d1b539b8c7436bd5e452c6aba8f", + "zh:f569b65999264a9416862bca5cd2a6177d94ccb0424f3a4ef424428912b9cb3c", + ] +} diff --git a/modules/cicd-setup/README.md b/modules/cicd-setup/README.md new file mode 100644 index 0000000..59eeb79 --- /dev/null +++ b/modules/cicd-setup/README.md @@ -0,0 +1,52 @@ +# Initial Setup for CI/CD + +Many Azure Active Directory Objects including service principals require owners. To prevent errors in running future Infrastructure as Code and Azure Portal use, this script will bootstrap this initial group for you. + +❗️ This is not part of the main project. Thus you must manually navigate `cd` into this directory and run. Save the `aad_superowners_group_id` output for [deploying the main project demo](https://github.com/Azure/devops-governance/blob/main/DEPLOY.md). + +### Security Concerns + +- Do NOT use a production subcription because this code automates Azure AD objects, which are security concerns if not managed properly. +- Be aware that "Owner" assignments are a security risk. This demo uses owner because custom roles requires an [Azure AD Premium P1 or P2 license](https://docs.microsoft.com/en-us/azure/active-directory/roles/custom-create). +- For production scenarios, please read this project's accompanying Azure Architecture Center article about [best practices for custom "headless owner" roles](https://docs.microsoft.com/en-us/azure/architecture/example-scenario/governance/end-to-end-governance-in-azure#3-create-a-custom-role-for-the-service-principal-used-to-access-production). + +### Confirm you have required Azure AD Permissions + +Most code in this project will fail without proper permissions. Per [AAD Provider for Terraform Docs](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/service_principal#api-permissions)… + - A **Service Principal** needs one of the following *application roles* + - `Application.ReadWrite.All` + - or `Directory.ReadWrite.All` + - A **User Principal** needs one of the following *directory roles* + - `Application Administrator` + - or `Global Administrator` + +### Resources created + +When this Infrastructure as Code is deployed successfully… + + +…the following resources will be created: + +- **Service Principal** named `governance-demo-github-cicd` +- **Role Assignment** of `Owner` role to service principal at current subscription scope +- New **Azure AD group** named `governance-demo-subscription-owners` with memberships + - the current logged-in user + - service principal created above + +#### Example Terraform Output + +``` +aad_superowners_group_id = "73c74b2f-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +headless_owner_sp = { + "application_id" = "89b93e8b-xxxx-xxxx-xxxx-xxxxxxxxxxxx" + "display_name" = "governance-demo-github-cicd" + "object_id" = "2c05b567-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +} +``` + +👉 Note the **aad_superowners_group_id** value `73c74b2f-xxxx-xxxx-xxxx-xxxxxxxxxxxx` which you need for this project. + +## References + + +- Terraform Docs - [Azure AD Service Principal - Required API Permissions](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/service_principal#api-permissions) \ No newline at end of file diff --git a/modules/cicd-setup/main.tf b/modules/cicd-setup/main.tf new file mode 100644 index 0000000..e33f186 --- /dev/null +++ b/modules/cicd-setup/main.tf @@ -0,0 +1,96 @@ +terraform { + required_providers { + azuread = { + source = "hashicorp/azuread" + version = ">=2.2.0" + } + azurerm = { + source = "hashicorp/azurerm" + version = ">=3.4.0" + } + } + required_version = ">= 0.15" +} + +provider "azurerm" { + features {} +} + +# ======== +# Config +# ======== + +locals { + headless_owner_name = "governance-demo-github-cicd" + owners_group_name = "governance-demo-subscription-owners" +} + +data "azurerm_subscription" "visual_studio" {} +data "azuread_client_config" "current" {} +data "azurerm_client_config" "current" {} + + +# ================================== +# Headless Owner Service Principal +# ================================== + +resource "azuread_application" "headless_owner" { + display_name = local.headless_owner_name + owners = [data.azuread_client_config.current.object_id] +} + +resource "azuread_service_principal" "headless_owner" { + application_id = azuread_application.headless_owner.application_id + app_role_assignment_required = false + owners = [data.azuread_client_config.current.object_id] + tags = [ + "ci-managed:false", + "demo:true" + ] +} + +resource "azuread_service_principal_password" "headless_owner" { + service_principal_id = azuread_service_principal.headless_owner.object_id +} + +# "Owner" Role Assignment + +resource "azurerm_role_assignment" "headless_owner" { + scope = data.azurerm_subscription.visual_studio.id + role_definition_name = "Owner" + principal_id = azuread_service_principal.headless_owner.id +} + + +# ================== +# Owners AAD Group +# ================== + +resource "azuread_group" "superowners" { + display_name = local.owners_group_name + security_enabled = true + prevent_duplicate_names = true + owners = [data.azuread_client_config.current.object_id] + members = [ + data.azuread_client_config.current.object_id, + # azuread_application.headless_owner.object_id + azuread_service_principal.headless_owner.id + ] +} + + +# ========= +# Outputs +# ========= + +output "aad_superowners_group_id" { + value = azuread_group.superowners.object_id +} + +output "headless_owner_sp" { + value = { + display_name = azuread_application.headless_owner.display_name + object_id = azuread_application.headless_owner.object_id + application_id = azuread_application.headless_owner.application_id + } +} From fb11def7f6872b3e890189b1bfc973c3b6ff3bb0 Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 11:30:12 +0200 Subject: [PATCH 07/21] style(comments)): prefer '===' to separate sections --- main.tf | 45 ++++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/main.tf b/main.tf index 2ae985f..5db6448 100644 --- a/main.tf +++ b/main.tf @@ -1,6 +1,6 @@ -# ------ -# Config -# ------ +# ======== +# Config +# ======== data "azurerm_client_config" "current" {} @@ -18,9 +18,9 @@ locals { superadmins_aad_object_id = var.superadmins_aad_object_id == "" ? data.azurerm_client_config.current.object_id : var.superadmins_aad_object_id # Default to current ARM client } -# --------------- -# Azure AD Groups -# --------------- +# ================= +# Azure AD Groups +# ================= resource "azuread_group" "groups" { for_each = var.groups @@ -29,9 +29,9 @@ resource "azuread_group" "groups" { security_enabled = true } -# ------------------ -# Service Principals -# ------------------ +# ==================== +# Service Principals +# ==================== # TODO: document use for CI only. Apps should use diff. SP per PILP @@ -42,9 +42,9 @@ module "service_principals" { owners = local.application_owners_ids } -# ------------------------------ -# Resource Groups ("Workspaces") -# ------------------------------ +# ================================ +# Resource Groups ("Workspaces") +# ================================ module "arm_environments" { for_each = var.environments @@ -61,17 +61,18 @@ module "arm_environments" { ] } -# ------------ -# Azure DevOps -# ------------ +# ============== +# Azure DevOps +# ============== # The following section Bootstraps: # - Projects: Team silos and shared projects # - Security Group Assignments: like Role Assignments in ARM # - Service Connections: service principal credentials created in code above -# Projects -# -------- +# ============== +# ADO Projects +# ============== # Team Projects @@ -125,8 +126,9 @@ resource "azuredevops_project" "collaboration" { } } -# Security Group Assignments -# -------------------------- +# ================================ +# ADO Security Group Assignments +# ================================ # Teams Silo Projects - Security Group Assignments @@ -196,8 +198,9 @@ module "ado_collaboration_permissions_veggies" { ] } -# Service Connections -# ------------------- +# ========================= +# ADO Service Connections +# ========================= module "service_connections" { for_each = module.arm_environments From 48ae651e64428a608581ddec3a178175839e5ec7 Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 11:34:28 +0200 Subject: [PATCH 08/21] module(service-principal): rename owners to owners_list to indicate type --- main.tf | 8 ++++---- modules/service-principal/main.tf | 4 ++-- modules/service-principal/variables.tf | 4 ++-- variables.tf | 1 + 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/main.tf b/main.tf index 5db6448..3532049 100644 --- a/main.tf +++ b/main.tf @@ -36,10 +36,10 @@ resource "azuread_group" "groups" { # TODO: document use for CI only. Apps should use diff. SP per PILP module "service_principals" { - for_each = var.environments - source = "./modules/service-principal" - name = "${each.value.team}-${each.value.env}-${local.suffix}-ci-sp" - owners = local.application_owners_ids + for_each = var.environments + source = "./modules/service-principal" + name = "${each.value.team}-${each.value.env}-${local.suffix}-ci-sp" + owners_list = local.application_owners_ids } # ================================ diff --git a/modules/service-principal/main.tf b/modules/service-principal/main.tf index 3662c77..0046218 100644 --- a/modules/service-principal/main.tf +++ b/modules/service-principal/main.tf @@ -4,7 +4,7 @@ resource "azuread_application" "app" { display_name = local.name - owners = var.owners + owners = var.owners_list } resource "azuread_service_principal_password" "workspace_sp_secret" { @@ -13,5 +13,5 @@ resource "azuread_service_principal_password" "workspace_sp_secret" { resource "azuread_service_principal" "sp" { application_id = azuread_application.app.application_id - owners = var.owners + owners = var.owners_list } diff --git a/modules/service-principal/variables.tf b/modules/service-principal/variables.tf index c9b6b1b..bb0bfec 100644 --- a/modules/service-principal/variables.tf +++ b/modules/service-principal/variables.tf @@ -23,11 +23,11 @@ variable "password_lifetime" { default = "4380h" } -variable "owners" { +variable "owners_list" { type = list(string) description = "A set of object IDs of principals that will be granted ownership of the application (service principal)." validation { - condition = length(var.owners) > 0 + condition = length(var.owners_list) > 0 error_message = "Every Application must have an owner. Owners cannot be empty." } } diff --git a/variables.tf b/variables.tf index 8ecdf7a..7e42ffa 100644 --- a/variables.tf +++ b/variables.tf @@ -5,6 +5,7 @@ variable "superadmins_aad_object_id" { default = "" } +# Service Principal Owners variable "application_owners_ids" { type = list(string) description = "A set of object IDs of principals that will be granted ownership of the application (service principal). Supported object types are users or service principals. It is best practice to specify one or more owners, incl. the principal used to execute Terraform" From 82938d8d591583b1b9b1d5b065f22fb53a0c9325 Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 11:43:47 +0200 Subject: [PATCH 09/21] style: comments headings use '===' --- terraform.tfvars | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/terraform.tfvars b/terraform.tfvars index baf2e3c..358ec87 100644 --- a/terraform.tfvars +++ b/terraform.tfvars @@ -1,6 +1,6 @@ -# --------------- -# Azure AD Groups -# --------------- +# ================= +# 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. @@ -17,9 +17,9 @@ groups = { infra_admins = "infra-admins" } -# --------------------- -# Azure DevOps Projects -# --------------------- +# ======================= +# Azure DevOps Projects +# ======================= projects = { proj_fruits = { @@ -41,9 +41,9 @@ projects = { } } -# ---------------- -# ARM Environments -# ---------------- +# ================== +# ARM Environments +# ================== # The keys can be referenced in outputs, # e.g. module.workspace["shared"]. Suffixes are appended later. From 9b289ff1bca40ee6c6944c564e44c0d4d73897d3 Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 11:51:03 +0200 Subject: [PATCH 10/21] chore(comments): rename RGs to ARM resources --- main.tf | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/main.tf b/main.tf index 3532049..22004a5 100644 --- a/main.tf +++ b/main.tf @@ -42,9 +42,11 @@ module "service_principals" { owners_list = local.application_owners_ids } -# ================================ -# Resource Groups ("Workspaces") -# ================================ +# ============================== +# ARM Resources ("Workspaces") +# ============================== + +# Resource Group, Storage Account, and Key Vault module "arm_environments" { for_each = var.environments From dd086021162c699b1906ab1febe3023e1b95583d Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 11:57:18 +0200 Subject: [PATCH 11/21] feat(environments): can specify dev or prod vars --- environments/README.md | 14 ++++++++++++++ environments/dev.tfvars | 7 +++++++ environments/prod.tfvars | 7 +++++++ main.tf | 2 +- modules/azure-resources/variables.tf | 6 ------ variables.tf | 10 ++++++++++ 6 files changed, 39 insertions(+), 7 deletions(-) create mode 100644 environments/README.md create mode 100644 environments/dev.tfvars create mode 100644 environments/prod.tfvars diff --git a/environments/README.md b/environments/README.md new file mode 100644 index 0000000..4990fb4 --- /dev/null +++ b/environments/README.md @@ -0,0 +1,14 @@ +# Environments Configuration (Optional) + +Define specific variables per environment. Currently used for Azure Resource tags, e.g. `env=dev` vs `env=prod`. + +### Usage example + +``` +terraform plan -var-file=environments/dev.tfvars +terraform apply -var-file=environments/dev.tfvars +``` + +### Default Values + +Project should without without these custom values. See default values defined in [`/variables.tf`](./../variables.tf) \ No newline at end of file diff --git a/environments/dev.tfvars b/environments/dev.tfvars new file mode 100644 index 0000000..b177393 --- /dev/null +++ b/environments/dev.tfvars @@ -0,0 +1,7 @@ +tags = { + public = "true" + demo = "e2e-governance" + demo-version = "v0.5.0" + env = "dev" + iac = "terraform" +} diff --git a/environments/prod.tfvars b/environments/prod.tfvars new file mode 100644 index 0000000..426cfc1 --- /dev/null +++ b/environments/prod.tfvars @@ -0,0 +1,7 @@ +tags = { + public = "true" + demo = "e2e-governance" + demo-version = "v0.5.0" + env = "production" + iac = "terraform" +} diff --git a/main.tf b/main.tf index 22004a5..43f1c63 100644 --- a/main.tf +++ b/main.tf @@ -56,7 +56,7 @@ module "arm_environments" { admins_group_id = azuread_group.groups["${each.value.team}_admins"].id superadmins_group_id = local.superadmins_aad_object_id service_principal_id = module.service_principals["${each.value.team}_${each.value.env}"].principal_id - + tags = var.tags depends_on = [ azuread_group.groups, module.service_principals diff --git a/modules/azure-resources/variables.tf b/modules/azure-resources/variables.tf index dc205e9..4aa82dc 100644 --- a/modules/azure-resources/variables.tf +++ b/modules/azure-resources/variables.tf @@ -49,12 +49,6 @@ variable "client_object_id" { variable "tags" { description = "Tags to apply to Azure Resources" type = map(string) - default = { - demo = "governance" - devops = "true" - oss = "terraform" - public = "true" - } } data "azurerm_client_config" "current" {} diff --git a/variables.tf b/variables.tf index 7e42ffa..1f29cb4 100644 --- a/variables.tf +++ b/variables.tf @@ -26,3 +26,13 @@ variable "projects" { variable "environments" { type = map(map(string)) } + +variable "tags" { + description = "Tags to apply to Azure Resources" + type = map(string) + default = { + public = "true" + demo = "e2e-governance" + iac = "terraform" + } +} From 62b7d64c6963beb577a8c3085051f60e9374fb58 Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 12:00:55 +0200 Subject: [PATCH 12/21] chore(_override): improve doc --- _override.tf.sample | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_override.tf.sample b/_override.tf.sample index 356fdae..43a8bdb 100644 --- a/_override.tf.sample +++ b/_override.tf.sample @@ -1,5 +1,5 @@ -# To deploy this project from a local machine, first remove -# the `.sample` extension from this file before running the +# To deploy this project from a local machine (without a Terraform backend), +# first remove the `.sample` extension from this file before running the # `terraform init` command. # # For details see: From 74c0691d4b87e22695a3f3e537be93b7f84b4d0c Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 13:01:21 +0200 Subject: [PATCH 13/21] feat: diff vars per env --- environments/README.md | 8 ++++---- environments/dev.tfvars | 7 +++---- environments/prod.tfvars | 7 +++---- main.tf | 3 ++- variables.tf | 9 ++++++++- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/environments/README.md b/environments/README.md index 4990fb4..7eab897 100644 --- a/environments/README.md +++ b/environments/README.md @@ -2,13 +2,13 @@ Define specific variables per environment. Currently used for Azure Resource tags, e.g. `env=dev` vs `env=prod`. +These custom tags are merged into defaults defined in [`/variables.tf`](./../variables.tf) + ### Usage example +These values need to be explicitly specified via `-var-file` flag. + ``` terraform plan -var-file=environments/dev.tfvars terraform apply -var-file=environments/dev.tfvars ``` - -### Default Values - -Project should without without these custom values. See default values defined in [`/variables.tf`](./../variables.tf) \ No newline at end of file diff --git a/environments/dev.tfvars b/environments/dev.tfvars index b177393..49e1816 100644 --- a/environments/dev.tfvars +++ b/environments/dev.tfvars @@ -1,7 +1,6 @@ -tags = { - public = "true" - demo = "e2e-governance" +custom_tags = { demo-version = "v0.5.0" env = "dev" - iac = "terraform" + devops-org = "julie-msft" + github = "azure/devops-governance" } diff --git a/environments/prod.tfvars b/environments/prod.tfvars index 426cfc1..b18b591 100644 --- a/environments/prod.tfvars +++ b/environments/prod.tfvars @@ -1,7 +1,6 @@ -tags = { - public = "true" - demo = "e2e-governance" +custom_tags = { demo-version = "v0.5.0" env = "production" - iac = "terraform" + devops-org = "julie-msft" + github = "azure/devops-governance" } diff --git a/main.tf b/main.tf index 43f1c63..276c56a 100644 --- a/main.tf +++ b/main.tf @@ -16,6 +16,7 @@ locals { suffix = random_string.suffix.result application_owners_ids = length(var.application_owners_ids) == 0 ? [data.azurerm_client_config.current.object_id] : var.application_owners_ids superadmins_aad_object_id = var.superadmins_aad_object_id == "" ? data.azurerm_client_config.current.object_id : var.superadmins_aad_object_id # Default to current ARM client + tags = merge(var.default_tags, var.custom_tags) } # ================= @@ -56,7 +57,7 @@ module "arm_environments" { admins_group_id = azuread_group.groups["${each.value.team}_admins"].id superadmins_group_id = local.superadmins_aad_object_id service_principal_id = module.service_principals["${each.value.team}_${each.value.env}"].principal_id - tags = var.tags + tags = local.tags depends_on = [ azuread_group.groups, module.service_principals diff --git a/variables.tf b/variables.tf index 1f29cb4..0899c43 100644 --- a/variables.tf +++ b/variables.tf @@ -27,12 +27,19 @@ variable "environments" { type = map(map(string)) } -variable "tags" { +variable "default_tags" { description = "Tags to apply to Azure Resources" type = map(string) default = { public = "true" demo = "e2e-governance" iac = "terraform" + ci = "azure-pipelines" } } + +variable "custom_tags" { + description = "Extra Tags to apply to Azure Resources" + type = map(string) + default = {} +} From fa871b552d8c33e5a56f30d06f2b37fdd960b08f Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 13:01:51 +0200 Subject: [PATCH 14/21] ci-cd-bootstrap: assign owner role to GROUP not sp --- modules/cicd-setup/main.tf | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/modules/cicd-setup/main.tf b/modules/cicd-setup/main.tf index e33f186..7f9a525 100644 --- a/modules/cicd-setup/main.tf +++ b/modules/cicd-setup/main.tf @@ -16,6 +16,7 @@ provider "azurerm" { features {} } + # ======== # Config # ======== @@ -53,15 +54,6 @@ resource "azuread_service_principal_password" "headless_owner" { service_principal_id = azuread_service_principal.headless_owner.object_id } -# "Owner" Role Assignment - -resource "azurerm_role_assignment" "headless_owner" { - scope = data.azurerm_subscription.visual_studio.id - role_definition_name = "Owner" - principal_id = azuread_service_principal.headless_owner.id -} - - # ================== # Owners AAD Group # ================== @@ -73,12 +65,22 @@ resource "azuread_group" "superowners" { owners = [data.azuread_client_config.current.object_id] members = [ data.azuread_client_config.current.object_id, - # azuread_application.headless_owner.object_id azuread_service_principal.headless_owner.id ] } +# ========================= +# "Owner" Role Assignment +# ========================= + +resource "azurerm_role_assignment" "gov_demo_owners_group" { + scope = data.azurerm_subscription.visual_studio.id + role_definition_name = "Owner" + principal_id = azuread_group.superowners.object_id +} + + # ========= # Outputs # ========= From b4041a6624985eed0e3f77009415c96f7266a4ac Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 13:02:38 +0200 Subject: [PATCH 15/21] backend-sample: improve comment --- backends/backend.hcl.sample | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backends/backend.hcl.sample b/backends/backend.hcl.sample index e38e57a..5c5b97b 100644 --- a/backends/backend.hcl.sample +++ b/backends/backend.hcl.sample @@ -1,6 +1,6 @@ storage_account_name="STORAGE_ACCOUNT_NAME" container_name="STORAGE_CONTAINER_NAME" key="FILENAME.tfstate" -# To authenticate to the Storage account, pick and uncomment one of the options below: -# sas_token="?sv=2019-12-12…" # or account key -# access_key="…" # or SAS token \ No newline at end of file +# To authenticate to the Storage account, pick and uncomment *one* of the options below: +# sas_token="?sv=2019-12-12…" # use SAS token +# access_key="…" # use Storage Account Access Key \ No newline at end of file From 0cc400d88e022ad2d60e85da63c409f85fdb8f90 Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 13:44:19 +0200 Subject: [PATCH 16/21] ci-cd-setup: add missing app role assignments, warning in readme --- modules/cicd-setup/.terraform.lock.hcl | 2 +- modules/cicd-setup/README.md | 9 ++++- modules/cicd-setup/main.tf | 47 +++++++++++++++++++++----- 3 files changed, 48 insertions(+), 10 deletions(-) diff --git a/modules/cicd-setup/.terraform.lock.hcl b/modules/cicd-setup/.terraform.lock.hcl index 26083c5..a04e304 100644 --- a/modules/cicd-setup/.terraform.lock.hcl +++ b/modules/cicd-setup/.terraform.lock.hcl @@ -3,7 +3,7 @@ provider "registry.terraform.io/hashicorp/azuread" { version = "2.22.0" - constraints = ">= 2.2.0" + constraints = ">= 2.22.0" hashes = [ "h1:8AfMLW7SHmCPDODLKpx4QY0kNV4IZBrwDvfS/yAzpTI=", "zh:062d84c514cd5015af60693ca4f3aece80d358fd7172951546eaba8093065c5b", diff --git a/modules/cicd-setup/README.md b/modules/cicd-setup/README.md index 59eeb79..528caf9 100644 --- a/modules/cicd-setup/README.md +++ b/modules/cicd-setup/README.md @@ -46,7 +46,14 @@ headless_owner_sp = { 👉 Note the **aad_superowners_group_id** value `73c74b2f-xxxx-xxxx-xxxx-xxxxxxxxxxxx` which you need for this project. +## ❗️ Last Step - Grant Admin Consent + +The headless owner service principal will not work until you [*manually* grant "Admin content" via the Azure Portal](https://docs.microsoft.com/en-us/azure/active-directory/manage-apps/grant-admin-consent#grant-admin-consent-in-app-registrations +). + +This step is manual and not automated because you should read the docs, warnings, etc. before clicking that button and accepting the security risks. + ## References +- Terraform Docs - [Azure AD Service Principal - Required API Permissions](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/service_principal#api-permissions) -- Terraform Docs - [Azure AD Service Principal - Required API Permissions](https://registry.terraform.io/providers/hashicorp/azuread/latest/docs/resources/service_principal#api-permissions) \ No newline at end of file diff --git a/modules/cicd-setup/main.tf b/modules/cicd-setup/main.tf index 7f9a525..b09f510 100644 --- a/modules/cicd-setup/main.tf +++ b/modules/cicd-setup/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { azuread = { source = "hashicorp/azuread" - version = ">=2.2.0" + version = ">=2.22.0" } azurerm = { source = "hashicorp/azurerm" @@ -24,6 +24,10 @@ provider "azurerm" { locals { headless_owner_name = "governance-demo-github-cicd" owners_group_name = "governance-demo-subscription-owners" + tags = [ + "ci-managed:false", + "demo:true" + ] } data "azurerm_subscription" "visual_studio" {} @@ -34,20 +38,47 @@ data "azurerm_client_config" "current" {} # ================================== # Headless Owner Service Principal # ================================== +# For App Role IDs, see +# https://docs.microsoft.com/en-us/graph/permissions-reference#all-permissions-and-ids resource "azuread_application" "headless_owner" { display_name = local.headless_owner_name owners = [data.azuread_client_config.current.object_id] + tags = local.tags + + required_resource_access { + resource_app_id = "00000003-0000-0000-c000-000000000000" # Microsoft Graph + + resource_access { + id = "1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9" # Application.ReadWrite.All + type = "Role" + } + + resource_access { + id = "19dbc75e-c2e2-444c-a770-ec69d8559fc7" # Directory.ReadWrite.All + type = "Role" + } + } + + lifecycle { + ignore_changes = [ + owners + ] + } } resource "azuread_service_principal" "headless_owner" { application_id = azuread_application.headless_owner.application_id app_role_assignment_required = false owners = [data.azuread_client_config.current.object_id] - tags = [ - "ci-managed:false", - "demo:true" - ] + tags = local.tags + + lifecycle { + ignore_changes = [ + owners, + tags + ] + } } resource "azuread_service_principal_password" "headless_owner" { @@ -70,9 +101,9 @@ resource "azuread_group" "superowners" { } -# ========================= -# "Owner" Role Assignment -# ========================= +# ============================= +# "Owner" ARM Role Assignment +# ============================= resource "azurerm_role_assignment" "gov_demo_owners_group" { scope = data.azurerm_subscription.visual_studio.id From 2c8778a26bdb7c6f429f8572338e7f1946ea9fe9 Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 14:02:07 +0200 Subject: [PATCH 17/21] providers: update aad provider --- .terraform.lock.hcl | 2 +- provider.tf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl index 0ab0fc0..05be96c 100644 --- a/.terraform.lock.hcl +++ b/.terraform.lock.hcl @@ -3,7 +3,7 @@ provider "registry.terraform.io/hashicorp/azuread" { version = "2.22.0" - constraints = ">= 2.2.0" + constraints = ">= 2.22.0" hashes = [ "h1:8AfMLW7SHmCPDODLKpx4QY0kNV4IZBrwDvfS/yAzpTI=", "zh:062d84c514cd5015af60693ca4f3aece80d358fd7172951546eaba8093065c5b", diff --git a/provider.tf b/provider.tf index 427a37c..6279ebb 100644 --- a/provider.tf +++ b/provider.tf @@ -4,7 +4,7 @@ terraform { required_providers { azuread = { source = "hashicorp/azuread" - version = ">=2.2.0" + version = ">=2.22.0" } azuredevops = { source = "microsoft/azuredevops" From 6b8fa501cb1785a1a3c1821bb2e5aba24fec722d Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 14:14:59 +0200 Subject: [PATCH 18/21] fix: missing dependency --- main.tf | 1 + 1 file changed, 1 insertion(+) diff --git a/main.tf b/main.tf index 276c56a..c9e4dff 100644 --- a/main.tf +++ b/main.tf @@ -214,6 +214,7 @@ module "service_connections" { depends_on = [ azuread_group.groups, + azuredevops_project.team_projects, module.arm_environments, module.service_principals ] From 11c7f8d65525a27bcebb46f764fb67d3ff3ce288 Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 14:15:19 +0200 Subject: [PATCH 19/21] style: space --- modules/azure-devops-service-connection/main.tf | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/azure-devops-service-connection/main.tf b/modules/azure-devops-service-connection/main.tf index 3751cd7..e9b47f4 100644 --- a/modules/azure-devops-service-connection/main.tf +++ b/modules/azure-devops-service-connection/main.tf @@ -26,6 +26,7 @@ resource "azuredevops_serviceendpoint_azurerm" "workspace_endpoint" { 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 + credentials { serviceprincipalid = var.service_principal_id serviceprincipalkey = var.service_principal_secret From 12dd35e4f0baaf8884978f4f225898aa80c486d8 Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 14:16:44 +0200 Subject: [PATCH 20/21] module(sp): remove no longer needed variable --- modules/service-principal/variables.tf | 6 ------ 1 file changed, 6 deletions(-) diff --git a/modules/service-principal/variables.tf b/modules/service-principal/variables.tf index bb0bfec..ce50752 100644 --- a/modules/service-principal/variables.tf +++ b/modules/service-principal/variables.tf @@ -17,12 +17,6 @@ variable "tenant_id" { default = "Current Client Tenant ID" } -variable "password_lifetime" { - type = string - description = "Lifetime of password in hours, e.g. '720h'. Defaults to 6 months. After password expires, credential needs to be refreshed (but not deleted)." - default = "4380h" -} - variable "owners_list" { type = list(string) description = "A set of object IDs of principals that will be granted ownership of the application (service principal)." From 197f94e50ec7b76eeaee69a5290fb0832021b31e Mon Sep 17 00:00:00 2001 From: Julie Ng Date: Sun, 1 May 2022 14:25:04 +0200 Subject: [PATCH 21/21] cicd-setup: fix object id exports --- modules/cicd-setup/README.md | 12 +++++++++--- modules/cicd-setup/main.tf | 7 +++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/modules/cicd-setup/README.md b/modules/cicd-setup/README.md index 528caf9..c7e83f0 100644 --- a/modules/cicd-setup/README.md +++ b/modules/cicd-setup/README.md @@ -37,14 +37,20 @@ When this Infrastructure as Code is deployed successfully… ``` aad_superowners_group_id = "73c74b2f-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -headless_owner_sp = { - "application_id" = "89b93e8b-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +headless_owner_service_principal = { "display_name" = "governance-demo-github-cicd" "object_id" = "2c05b567-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } ``` -👉 Note the **aad_superowners_group_id** value `73c74b2f-xxxx-xxxx-xxxx-xxxxxxxxxxxx` which you need for this project. +### Required Object IDs for Main Project + +These values can be set locally. See [`local.auto.tfvars`](./../../../devops-governance/local.auto.tfvars.example) for details. + +👉 Note the **aad_superowners_group_id** value `73c74b2f-xxxx-xxxx-xxxx-xxxxxxxxxxxx` which you need for the `superadmins_aad_object_id` variable in the main project. + +👉 Note the **headless_owner_service_principal.object_id** value `2c05b567-xxxx-xxxx-xxxx-xxxxxxxxxxxx` which you need for the `application_owners_ids` variable in the main project. + ## ❗️ Last Step - Grant Admin Consent diff --git a/modules/cicd-setup/main.tf b/modules/cicd-setup/main.tf index b09f510..9c8db61 100644 --- a/modules/cicd-setup/main.tf +++ b/modules/cicd-setup/main.tf @@ -120,10 +120,9 @@ output "aad_superowners_group_id" { value = azuread_group.superowners.object_id } -output "headless_owner_sp" { +output "headless_owner_service_principal" { value = { - display_name = azuread_application.headless_owner.display_name - object_id = azuread_application.headless_owner.object_id - application_id = azuread_application.headless_owner.application_id + display_name = azuread_service_principal.headless_owner.display_name + object_id = azuread_service_principal.headless_owner.object_id } }