diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index f8cad11..39e8382 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -12,7 +12,7 @@ on: schedule: - cron: '16 22 * * 2' push: - branches: [ "main" ] + branches: ["main"] # Declare default permissions as read only. permissions: read-all diff --git a/alz/azuredevops/locals.files.terraform.tf b/alz/azuredevops/locals.files.terraform.tf index 6eb6111..3da7326 100644 --- a/alz/azuredevops/locals.files.terraform.tf +++ b/alz/azuredevops/locals.files.terraform.tf @@ -39,7 +39,7 @@ locals { module_files = { for key, value in module.files.files : key => { - content = replace((file(value.path)), "# backend \"azurerm\" {}", "backend \"azurerm\" {\n use_oidc = true\n use_azuread_auth = true\n }") + content = replace((file(value.path)), "# backend \"azurerm\" {}", "backend \"azurerm\" {}") } } repository_files = merge(local.cicd_files, local.module_files, var.use_separate_repository_for_pipeline_templates ? {} : local.cicd_template_files) diff --git a/alz/azuredevops/locals.tf b/alz/azuredevops/locals.tf index 34a8ea6..dedcec4 100644 --- a/alz/azuredevops/locals.tf +++ b/alz/azuredevops/locals.tf @@ -87,3 +87,7 @@ locals { locals { starter_module_folder_path = var.module_folder_path_relative ? ("${path.module}/${var.module_folder_path}") : var.module_folder_path } + +locals { + agent_container_instance_dockerfile_url = "${var.agent_container_image_repository}#${var.agent_container_image_tag}:${var.agent_container_image_folder}" +} diff --git a/alz/azuredevops/main.tf b/alz/azuredevops/main.tf index 0f68ce0..d02d373 100644 --- a/alz/azuredevops/main.tf +++ b/alz/azuredevops/main.tf @@ -27,7 +27,7 @@ module "azure" { user_assigned_managed_identities = local.managed_identities federated_credentials = local.federated_credentials agent_container_instances = local.agent_container_instances - agent_container_instance_image = var.agent_container_image + agent_container_instance_managed_identity_name = local.resource_names.container_instance_managed_identity agent_organization_url = module.azure_devops.organization_url agent_token = var.azure_devops_agents_personal_access_token agent_organization_environment_variable = var.agent_organization_environment_variable @@ -39,16 +39,23 @@ module "azure" { root_parent_management_group_id = local.root_parent_management_group_id virtual_network_name = local.resource_names.virtual_network virtual_network_subnet_name_container_instances = local.resource_names.subnet_container_instances - virtual_network_subnet_name_storage = local.resource_names.subnet_storage - private_endpoint_name = local.resource_names.private_endpoint + virtual_network_subnet_name_private_endpoints = local.resource_names.subnet_private_endpoints + storage_account_private_endpoint_name = local.resource_names.storage_account_private_endpoint use_private_networking = var.use_private_networking allow_storage_access_from_my_ip = var.allow_storage_access_from_my_ip virtual_network_address_space = var.virtual_network_address_space virtual_network_subnet_address_prefix_container_instances = var.virtual_network_subnet_address_prefix_container_instances - virtual_network_subnet_address_prefix_storage = var.virtual_network_subnet_address_prefix_storage + virtual_network_subnet_address_prefix_private_endpoints = var.virtual_network_subnet_address_prefix_private_endpoints storage_account_replication_type = var.storage_account_replication_type public_ip_name = local.resource_names.public_ip nat_gateway_name = local.resource_names.nat_gateway + use_self_hosted_agents = var.use_self_hosted_agents + container_registry_name = local.resource_names.container_registry + container_registry_private_endpoint_name = local.resource_names.container_registry_private_endpoint + container_registry_image_name = local.resource_names.container_image_name + container_registry_image_tag = var.agent_container_image_tag + container_registry_dockerfile_name = var.agent_container_image_dockerfile + container_registry_dockerfile_repository_folder_url = local.agent_container_instance_dockerfile_url } module "azure_devops" { diff --git a/alz/azuredevops/pipelines/terraform/templates/cd.yaml b/alz/azuredevops/pipelines/terraform/templates/cd.yaml index a446a3a..83ece47 100644 --- a/alz/azuredevops/pipelines/terraform/templates/cd.yaml +++ b/alz/azuredevops/pipelines/terraform/templates/cd.yaml @@ -23,7 +23,6 @@ stages: steps: - checkout: self displayName: Checkout Terraform Module - - template: helpers/terraform-block-msi-endpoint.yaml - template: helpers/terraform-installer.yaml parameters: terraformVersion: 'latest' @@ -82,7 +81,6 @@ stages: buildType: 'current' artifactName: 'module' targetPath: '$(Build.SourcesDirectory)' - - template: helpers/terraform-block-msi-endpoint.yaml - template: helpers/terraform-installer.yaml parameters: terraformVersion: 'latest' diff --git a/alz/azuredevops/pipelines/terraform/templates/ci.yaml b/alz/azuredevops/pipelines/terraform/templates/ci.yaml index 9eb630a..fdcdf46 100644 --- a/alz/azuredevops/pipelines/terraform/templates/ci.yaml +++ b/alz/azuredevops/pipelines/terraform/templates/ci.yaml @@ -34,7 +34,6 @@ stages: steps: - checkout: self displayName: Checkout Terraform Module - - template: helpers/terraform-block-msi-endpoint.yaml - template: helpers/terraform-installer.yaml parameters: terraformVersion: 'latest' diff --git a/alz/azuredevops/pipelines/terraform/templates/helpers/terraform-apply.yaml b/alz/azuredevops/pipelines/terraform/templates/helpers/terraform-apply.yaml index e5b21bb..4f31e91 100644 --- a/alz/azuredevops/pipelines/terraform/templates/helpers/terraform-apply.yaml +++ b/alz/azuredevops/pipelines/terraform/templates/helpers/terraform-apply.yaml @@ -22,12 +22,10 @@ steps: $env:ARM_TENANT_ID = $account.tenantId $env:ARM_SUBSCRIPTION_ID = $account.id - - # Note: We are using CLI auth for the provider as it caches the access token for us, which helps with edge cases like terraform test. - # The backend is hard coded to use OIDC auth as it does not support CLI auth yet. - $env:ARM_USE_CLI = 'true' $env:ARM_OIDC_TOKEN = $oidcToken + $env:ARM_USE_OIDC = "true" $env:ARM_CLIENT_ID = $clientId + $env:ARM_USE_AZUREAD = "true" # Run Terraform Apply $command = "terraform" diff --git a/alz/azuredevops/pipelines/terraform/templates/helpers/terraform-block-msi-endpoint.yaml b/alz/azuredevops/pipelines/terraform/templates/helpers/terraform-block-msi-endpoint.yaml deleted file mode 100644 index a8891b5..0000000 --- a/alz/azuredevops/pipelines/terraform/templates/helpers/terraform-block-msi-endpoint.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -steps: - # This hack allows MSI to be tried for authentication. As setting the CLIENT_ID causes AzAPI to check MSI auth prior to CLI auth and it fails on Microsoft Hosted agents. - # This only works on Linux based runners - - pwsh: | - sudo iptables -I OUTPUT --destination 169.254.169.254 -j REJECT - displayName: Block MSI endpoint - condition: and(succeeded(), eq(variables['selfHostedAgent'], 'false')) diff --git a/alz/azuredevops/pipelines/terraform/templates/helpers/terraform-init.yaml b/alz/azuredevops/pipelines/terraform/templates/helpers/terraform-init.yaml index 2507b52..84b61e1 100644 --- a/alz/azuredevops/pipelines/terraform/templates/helpers/terraform-init.yaml +++ b/alz/azuredevops/pipelines/terraform/templates/helpers/terraform-init.yaml @@ -23,6 +23,13 @@ steps: $subscriptionId = $account.id $tenantId = $account.tenantId + $env:ARM_TENANT_ID = $tenantId + $env:ARM_SUBSCRIPTION_ID = $subscriptionId + $env:ARM_OIDC_TOKEN = $oidcToken + $env:ARM_USE_OIDC = "true" + $env:ARM_CLIENT_ID = $clientId + $env:ARM_USE_AZUREAD = "true" + $arguments = @() $arguments += "init" $arguments += "-backend-config=storage_account_name=$($env:BACKEND_AZURE_STORAGE_ACCOUNT_NAME)" @@ -30,13 +37,6 @@ steps: $arguments += "-backend-config=key=$($env:BACKEND_AZURE_STORAGE_ACCOUNT_CONTAINER_KEY_NAME)" $arguments += "-backend-config=resource_group_name=$($env:BACKEND_AZURE_RESOURCE_GROUP_NAME)" - $env:ARM_SUBSCRIPTION_ID = $subscriptionId - $env:ARM_TENANT_ID = $tenantId - - # Note: The backend is hardcoded to use oidc auth as we want to use a different auth type for the provider during plan and apply. - $env:ARM_OIDC_TOKEN = $oidcToken - $env:ARM_CLIENT_ID = $clientId - # Run terraform init $command = "terraform" Write-Host "Running: $command $arguments" diff --git a/alz/azuredevops/pipelines/terraform/templates/helpers/terraform-plan.yaml b/alz/azuredevops/pipelines/terraform/templates/helpers/terraform-plan.yaml index 4510a44..a93f248 100644 --- a/alz/azuredevops/pipelines/terraform/templates/helpers/terraform-plan.yaml +++ b/alz/azuredevops/pipelines/terraform/templates/helpers/terraform-plan.yaml @@ -22,12 +22,10 @@ steps: $env:ARM_TENANT_ID = $account.tenantId $env:ARM_SUBSCRIPTION_ID = $account.id - - # Note: We are using CLI auth for the provider as it caches the access token for us, which helps with edge cases like terraform test. - # The backend is hard coded to use OIDC auth as it does not support CLI auth yet. - $env:ARM_USE_CLI = 'true' $env:ARM_OIDC_TOKEN = $oidcToken + $env:ARM_USE_OIDC = "true" $env:ARM_CLIENT_ID = $clientId + $env:ARM_USE_AZUREAD = "true" # Run Terraform Plan $command = "terraform" diff --git a/alz/azuredevops/terraform.tfvars b/alz/azuredevops/terraform.tfvars index 404d6cf..c883b43 100644 --- a/alz/azuredevops/terraform.tfvars +++ b/alz/azuredevops/terraform.tfvars @@ -1,5 +1,8 @@ # Azure Variables -agent_container_image = "microsoftavm/azure-devops-agent:1.1.0" +agent_container_image_repository = "https://github.com/Azure/terraform-azurerm-avm-ptn-cicd-agents-and-runners" +agent_container_image_tag = "8ff4b85" # NOTE: Container registry task does not support tag ref, so we are using the commit hash of the release instead +agent_container_image_folder = "container-images/azure-devops-agent" +agent_container_image_dockerfile = "dockerfile" # Names resource_names = { @@ -15,12 +18,9 @@ resource_names = { storage_container = "{{environment_name}}-tfstate" container_instance_01 = "aci-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}" container_instance_02 = "aci-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number_plus_1}}" - container_instance_03 = "aci-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number_plus_2}}" - container_instance_04 = "aci-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number_plus_3}}" + container_instance_managed_identity = "id-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}-aci" agent_01 = "agent-{{service_name}}-{{environment_name}}-{{postfix_number}}" agent_02 = "agent-{{service_name}}-{{environment_name}}-{{postfix_number_plus_1}}" - agent_03 = "agent-{{service_name}}-{{environment_name}}-{{postfix_number_plus_2}}" - agent_04 = "agent-{{service_name}}-{{environment_name}}-{{postfix_number_plus_3}}" version_control_system_repository = "{{service_name}}-{{environment_name}}" version_control_system_repository_templates = "{{service_name}}-{{environment_name}}-templates" version_control_system_service_connection_plan = "sc-{{service_name}}-{{environment_name}}-plan" @@ -36,6 +36,9 @@ resource_names = { public_ip = "pip-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}" nat_gateway = "nat-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}" subnet_container_instances = "subnet-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}-aci" - subnet_storage = "subnet-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}-sto" - private_endpoint = "pe-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}" + subnet_private_endpoints = "subnet-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}-pe" + storage_account_private_endpoint = "pe-{{service_name}}-{{environment_name}}-{{azure_location}}-sto-{{postfix_number}}" + container_registry = "acr{{service_name}}{{environment_name}}{{azure_location_short}}{{postfix_number}}{{random_string}}" + container_registry_private_endpoint = "pe-{{service_name}}-{{environment_name}}-{{azure_location}}-acr-{{postfix_number}}" + container_image_name = "azure-devops-agent" } diff --git a/alz/azuredevops/variables.hidden.tf b/alz/azuredevops/variables.hidden.tf index a63f793..1928801 100644 --- a/alz/azuredevops/variables.hidden.tf +++ b/alz/azuredevops/variables.hidden.tf @@ -4,8 +4,23 @@ variable "additional_files" { default = [] } -variable "agent_container_image" { - description = "The container image to use for Azure DevOps Agents" +variable "agent_container_image_repository" { + description = "The container image repository to use for Azure DevOps Agents" + type = string +} + +variable "agent_container_image_tag" { + description = "The container image tag to use for Azure DevOps Agents" + type = string +} + +variable "agent_container_image_folder" { + description = "The folder containing the Dockerfile for the container image" + type = string +} + +variable "agent_container_image_dockerfile" { + description = "The Dockerfile to use for the container image" type = string } @@ -86,7 +101,7 @@ variable "virtual_network_subnet_address_prefix_container_instances" { default = "10.0.0.0/26" } -variable "virtual_network_subnet_address_prefix_storage" { +variable "virtual_network_subnet_address_prefix_private_endpoints" { type = string description = "Address prefix for the virtual network subnet" default = "10.0.0.64/26" diff --git a/alz/github/locals.tf b/alz/github/locals.tf index 357c198..56082ce 100644 --- a/alz/github/locals.tf +++ b/alz/github/locals.tf @@ -77,3 +77,7 @@ locals { locals { starter_module_folder_path = var.module_folder_path_relative ? ("${path.module}/${var.module_folder_path}") : var.module_folder_path } + +locals { + runner_container_instance_dockerfile_url = "${var.runner_container_image_repository}#${var.runner_container_image_tag}:${var.runner_container_image_folder}" +} diff --git a/alz/github/main.tf b/alz/github/main.tf index b2321e7..e65a24c 100644 --- a/alz/github/main.tf +++ b/alz/github/main.tf @@ -29,7 +29,7 @@ module "azure" { target_subscriptions = local.target_subscriptions root_parent_management_group_id = local.root_parent_management_group_id agent_container_instances = local.runner_container_instances - agent_container_instance_image = var.runner_container_image + agent_container_instance_managed_identity_name = local.resource_names.container_instance_managed_identity agent_organization_url = local.runner_organization_repository_url agent_token = var.github_runners_personal_access_token agent_organization_environment_variable = var.runner_organization_environment_variable @@ -40,16 +40,23 @@ module "azure" { agent_token_environment_variable = var.runner_token_environment_variable virtual_network_name = local.resource_names.virtual_network virtual_network_subnet_name_container_instances = local.resource_names.subnet_container_instances - virtual_network_subnet_name_storage = local.resource_names.subnet_storage - private_endpoint_name = local.resource_names.private_endpoint + virtual_network_subnet_name_private_endpoints = local.resource_names.subnet_private_endpoints + storage_account_private_endpoint_name = local.resource_names.storage_account_private_endpoint use_private_networking = var.use_private_networking allow_storage_access_from_my_ip = var.allow_storage_access_from_my_ip virtual_network_address_space = var.virtual_network_address_space virtual_network_subnet_address_prefix_container_instances = var.virtual_network_subnet_address_prefix_container_instances - virtual_network_subnet_address_prefix_storage = var.virtual_network_subnet_address_prefix_storage + virtual_network_subnet_address_prefix_private_endpoints = var.virtual_network_subnet_address_prefix_private_endpoints storage_account_replication_type = var.storage_account_replication_type public_ip_name = local.resource_names.public_ip nat_gateway_name = local.resource_names.nat_gateway + use_self_hosted_agents = var.use_self_hosted_runners + container_registry_name = local.resource_names.container_registry + container_registry_private_endpoint_name = local.resource_names.container_registry_private_endpoint + container_registry_image_name = local.resource_names.container_image_name + container_registry_image_tag = var.runner_container_image_tag + container_registry_dockerfile_name = var.runner_container_image_dockerfile + container_registry_dockerfile_repository_folder_url = local.runner_container_instance_dockerfile_url } module "github" { diff --git a/alz/github/terraform.tfvars b/alz/github/terraform.tfvars index 5386aa5..0f9ed46 100644 --- a/alz/github/terraform.tfvars +++ b/alz/github/terraform.tfvars @@ -1,5 +1,8 @@ # Azure Variables -runner_container_image = "microsoftavm/github-runner:1.0.1" +runner_container_image_repository = "https://github.com/Azure/terraform-azurerm-avm-ptn-cicd-agents-and-runners" +runner_container_image_tag = "8ff4b85" # NOTE: Container registry task does not support tag ref, so we are using the commit hash of the release instead +runner_container_image_folder = "container-images/github-runner" +runner_container_image_dockerfile = "dockerfile" # Naming resource_names = { @@ -14,6 +17,7 @@ resource_names = { storage_container = "{{environment_name}}-tfstate" container_instance_01 = "aci-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}" container_instance_02 = "aci-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number_plus_1}}" + container_instance_managed_identity = "id-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}-aci" runner_01 = "runner-{{service_name}}-{{environment_name}}-{{postfix_number}}" runner_02 = "runner-{{service_name}}-{{environment_name}}-{{postfix_number_plus_1}}" version_control_system_repository = "{{service_name}}-{{environment_name}}" @@ -26,6 +30,9 @@ resource_names = { public_ip = "pip-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}" nat_gateway = "nat-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}" subnet_container_instances = "subnet-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}-aci" - subnet_storage = "subnet-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}-sto" - private_endpoint = "pe-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}" + subnet_private_endpoints = "subnet-{{service_name}}-{{environment_name}}-{{azure_location}}-{{postfix_number}}-pe" + storage_account_private_endpoint = "pe-{{service_name}}-{{environment_name}}-{{azure_location}}-sto-{{postfix_number}}" + container_registry = "acr{{service_name}}{{environment_name}}{{azure_location_short}}{{postfix_number}}{{random_string}}" + container_registry_private_endpoint = "pe-{{service_name}}-{{environment_name}}-{{azure_location}}-acr-{{postfix_number}}" + container_image_name = "github-runner" } diff --git a/alz/github/variables.hidden.tf b/alz/github/variables.hidden.tf index 9cb6dab..2ebb344 100644 --- a/alz/github/variables.hidden.tf +++ b/alz/github/variables.hidden.tf @@ -15,8 +15,23 @@ variable "resource_names" { description = "Overrides for resource names" } -variable "runner_container_image" { - description = "The container image to use for GitHub Runners" +variable "runner_container_image_repository" { + description = "The container image repository to use for GitHub Runner" + type = string +} + +variable "runner_container_image_tag" { + description = "The container image tag to use for GitHub Runner" + type = string +} + +variable "runner_container_image_folder" { + description = "The folder containing the Dockerfile for the container image" + type = string +} + +variable "runner_container_image_dockerfile" { + description = "The Dockerfile to use for the container image" type = string } @@ -86,7 +101,7 @@ variable "virtual_network_subnet_address_prefix_container_instances" { default = "10.0.0.0/26" } -variable "virtual_network_subnet_address_prefix_storage" { +variable "virtual_network_subnet_address_prefix_private_endpoints" { type = string description = "Address prefix for the virtual network subnet" default = "10.0.0.64/26" diff --git a/alz/local/main.tf b/alz/local/main.tf index d768502..d195bf7 100644 --- a/alz/local/main.tf +++ b/alz/local/main.tf @@ -28,6 +28,8 @@ module "azure" { target_subscriptions = local.target_subscriptions root_parent_management_group_id = local.root_parent_management_group_id storage_account_replication_type = var.storage_account_replication_type + use_self_hosted_agents = false + use_private_networking = false } resource "local_file" "alz" { diff --git a/modules/azure/container_instances.tf b/modules/azure/container_instances.tf index 73c8641..30314ec 100644 --- a/modules/azure/container_instances.tf +++ b/modules/azure/container_instances.tf @@ -1,5 +1,5 @@ resource "azurerm_container_group" "alz" { - for_each = var.agent_container_instances + for_each = var.use_self_hosted_agents ? var.agent_container_instances : {} name = each.value.container_instance_name location = var.azure_location resource_group_name = azurerm_resource_group.agents[0].name @@ -8,9 +8,21 @@ resource "azurerm_container_group" "alz" { subnet_ids = var.use_private_networking ? [azurerm_subnet.container_instances[0].id] : [] zones = each.value.zones + identity { + type = "UserAssigned" + identity_ids = [azurerm_user_assigned_identity.container_instances[0].id] + } + + image_registry_credential { + server = azurerm_container_registry.alz[0].login_server + user_assigned_identity_id = azurerm_user_assigned_identity.container_instances[0].id + } + container { - name = each.value.container_instance_name - image = var.agent_container_instance_image + name = each.value.container_instance_name + image = "${azurerm_container_registry.alz[0].login_server}/${var.container_registry_image_name}:${var.container_registry_image_tag}" + + cpu = each.value.cpu memory = each.value.memory cpu_limit = each.value.cpu_max @@ -32,4 +44,13 @@ resource "azurerm_container_group" "alz" { (var.agent_token_environment_variable) = var.agent_token } } + + depends_on = [azurerm_container_registry_task_schedule_run_now.alz] +} + +resource "azurerm_user_assigned_identity" "container_instances" { + count = var.use_self_hosted_agents ? 1 : 0 + location = var.azure_location + name = var.agent_container_instance_managed_identity_name + resource_group_name = azurerm_resource_group.agents[0].name } diff --git a/modules/azure/container_registry.tf b/modules/azure/container_registry.tf new file mode 100644 index 0000000..f7b2776 --- /dev/null +++ b/modules/azure/container_registry.tf @@ -0,0 +1,57 @@ +resource "azurerm_container_registry" "alz" { + count = var.use_self_hosted_agents ? 1 : 0 + name = var.container_registry_name + resource_group_name = azurerm_resource_group.agents[0].name + location = var.azure_location + sku = var.use_private_networking ? "Premium" : "Basic" + public_network_access_enabled = !var.use_private_networking + zone_redundancy_enabled = var.use_private_networking + network_rule_bypass_option = var.use_private_networking ? "AzureServices" : "None" +} + +resource "azurerm_container_registry_task" "alz" { + count = var.use_self_hosted_agents ? 1 : 0 + name = "image-build-task" + container_registry_id = azurerm_container_registry.alz[0].id + platform { + os = "Linux" + } + docker_step { + dockerfile_path = var.container_registry_dockerfile_name + context_path = var.container_registry_dockerfile_repository_folder_url + context_access_token = "a" # This is a dummy value becuase the context_access_token should not be required in the provider + image_names = ["${var.container_registry_image_name}:${var.container_registry_image_tag}"] + } + identity { + type = "SystemAssigned" # Note this has to be a System Assigned Identity to work with private networking and `network_rule_bypass_option` set to `AzureServices` + } + registry_credential { + custom { + login_server = azurerm_container_registry.alz[0].login_server + identity = "[system]" + } + } +} + +resource "azurerm_container_registry_task_schedule_run_now" "alz" { + count = var.use_self_hosted_agents ? 1 : 0 + container_registry_task_id = azurerm_container_registry_task.alz[0].id + lifecycle { + replace_triggered_by = [azurerm_container_registry_task.alz] + } + depends_on = [azurerm_role_assignment.container_registry_push_for_task] +} + +resource "azurerm_role_assignment" "container_registry_pull_for_container_instance" { + count = var.use_self_hosted_agents ? 1 : 0 + scope = azurerm_container_registry.alz[0].id + role_definition_name = "AcrPull" + principal_id = azurerm_user_assigned_identity.container_instances[0].principal_id +} + +resource "azurerm_role_assignment" "container_registry_push_for_task" { + count = var.use_self_hosted_agents ? 1 : 0 + scope = azurerm_container_registry.alz[0].id + role_definition_name = "AcrPush" + principal_id = azurerm_container_registry_task.alz[0].identity[0].principal_id +} diff --git a/modules/azure/data.tf b/modules/azure/data.tf index 59eb2e7..6279fc7 100644 --- a/modules/azure/data.tf +++ b/modules/azure/data.tf @@ -8,7 +8,7 @@ data "azurerm_management_group" "alz" { } data "http" "ip" { - count = local.use_private_networking && var.allow_storage_access_from_my_ip ? 1 : 0 + count = var.use_private_networking && var.use_self_hosted_agents && var.allow_storage_access_from_my_ip ? 1 : 0 url = "https://api.ipify.org/" retry { attempts = 5 diff --git a/modules/azure/locals.tf b/modules/azure/locals.tf index 3195495..d1b95f7 100644 --- a/modules/azure/locals.tf +++ b/modules/azure/locals.tf @@ -7,8 +7,3 @@ locals { locals { subscription_ids = { for subscription_id in distinct(var.target_subscriptions) : subscription_id => subscription_id } } - -locals { - has_agent_container_instances = length(var.agent_container_instances) > 0 - use_private_networking = var.use_private_networking && local.has_agent_container_instances -} diff --git a/modules/azure/networking.tf b/modules/azure/networking.tf index 5033ede..d2b5d10 100644 --- a/modules/azure/networking.tf +++ b/modules/azure/networking.tf @@ -1,5 +1,5 @@ resource "azurerm_virtual_network" "alz" { - count = local.use_private_networking ? 1 : 0 + count = var.use_private_networking && var.use_self_hosted_agents ? 1 : 0 name = var.virtual_network_name location = var.azure_location resource_group_name = azurerm_resource_group.network[0].name @@ -7,7 +7,7 @@ resource "azurerm_virtual_network" "alz" { } resource "azurerm_public_ip" "alz" { - count = local.use_private_networking ? 1 : 0 + count = var.use_private_networking && var.use_self_hosted_agents ? 1 : 0 name = var.public_ip_name location = var.azure_location resource_group_name = azurerm_resource_group.network[0].name @@ -16,7 +16,7 @@ resource "azurerm_public_ip" "alz" { } resource "azurerm_nat_gateway" "alz" { - count = local.use_private_networking ? 1 : 0 + count = var.use_private_networking && var.use_self_hosted_agents ? 1 : 0 name = var.nat_gateway_name location = var.azure_location resource_group_name = azurerm_resource_group.network[0].name @@ -24,13 +24,13 @@ resource "azurerm_nat_gateway" "alz" { } resource "azurerm_nat_gateway_public_ip_association" "alz" { - count = local.use_private_networking ? 1 : 0 + count = var.use_private_networking && var.use_self_hosted_agents ? 1 : 0 nat_gateway_id = azurerm_nat_gateway.alz[0].id public_ip_address_id = azurerm_public_ip.alz[0].id } resource "azurerm_subnet" "container_instances" { - count = local.use_private_networking ? 1 : 0 + count = var.use_private_networking && var.use_self_hosted_agents ? 1 : 0 name = var.virtual_network_subnet_name_container_instances resource_group_name = azurerm_resource_group.network[0].name virtual_network_name = azurerm_virtual_network.alz[0].name @@ -46,50 +46,16 @@ resource "azurerm_subnet" "container_instances" { } resource "azurerm_subnet_nat_gateway_association" "container_instances" { - count = local.use_private_networking ? 1 : 0 + count = var.use_private_networking && var.use_self_hosted_agents ? 1 : 0 subnet_id = azurerm_subnet.container_instances[0].id nat_gateway_id = azurerm_nat_gateway.alz[0].id } -resource "azurerm_subnet" "storage" { - count = local.use_private_networking ? 1 : 0 - name = var.virtual_network_subnet_name_storage +resource "azurerm_subnet" "private_endpoints" { + count = var.use_private_networking && var.use_self_hosted_agents ? 1 : 0 + name = var.virtual_network_subnet_name_private_endpoints resource_group_name = azurerm_resource_group.network[0].name virtual_network_name = azurerm_virtual_network.alz[0].name - address_prefixes = [var.virtual_network_subnet_address_prefix_storage] + address_prefixes = [var.virtual_network_subnet_address_prefix_private_endpoints] private_endpoint_network_policies = "Enabled" } - -resource "azurerm_private_dns_zone" "alz" { - count = local.use_private_networking ? 1 : 0 - name = "privatelink.blob.core.windows.net" - resource_group_name = azurerm_resource_group.network[0].name -} - -resource "azurerm_private_dns_zone_virtual_network_link" "alz" { - count = local.use_private_networking ? 1 : 0 - name = var.private_endpoint_name - resource_group_name = azurerm_resource_group.network[0].name - private_dns_zone_name = azurerm_private_dns_zone.alz[0].name - virtual_network_id = azurerm_virtual_network.alz[0].id -} - -resource "azurerm_private_endpoint" "alz" { - count = local.use_private_networking ? 1 : 0 - name = var.private_endpoint_name - location = var.azure_location - resource_group_name = azurerm_resource_group.network[0].name - subnet_id = azurerm_subnet.storage[0].id - - private_service_connection { - name = var.private_endpoint_name - private_connection_resource_id = azurerm_storage_account.alz.id - subresource_names = ["blob"] - is_manual_connection = false - } - - private_dns_zone_group { - name = var.private_endpoint_name - private_dns_zone_ids = [azurerm_private_dns_zone.alz[0].id] - } -} diff --git a/modules/azure/private_endpoints.tf b/modules/azure/private_endpoints.tf new file mode 100644 index 0000000..57fccdd --- /dev/null +++ b/modules/azure/private_endpoints.tf @@ -0,0 +1,50 @@ +locals { + private_endpoints = var.use_private_networking && var.use_self_hosted_agents ? { + storage_account = { + name = var.storage_account_private_endpoint_name + resource_id = azurerm_storage_account.alz.id + dns_record = "privatelink.blob.core.windows.net" + sub_resource = "blob" + } + container_registry = { + name = var.container_registry_private_endpoint_name + resource_id = azurerm_container_registry.alz[0].id + dns_record = "privatelink.azurecr.io" + sub_resource = "registry" + } + } : {} +} + +resource "azurerm_private_dns_zone" "alz" { + for_each = local.private_endpoints + name = each.value.dns_record + resource_group_name = azurerm_resource_group.network[0].name +} + +resource "azurerm_private_dns_zone_virtual_network_link" "alz" { + for_each = local.private_endpoints + name = each.value.name + resource_group_name = azurerm_resource_group.network[0].name + private_dns_zone_name = azurerm_private_dns_zone.alz[each.key].name + virtual_network_id = azurerm_virtual_network.alz[0].id +} + +resource "azurerm_private_endpoint" "alz" { + for_each = local.private_endpoints + name = each.value.name + location = var.azure_location + resource_group_name = azurerm_resource_group.network[0].name + subnet_id = azurerm_subnet.private_endpoints[0].id + + private_service_connection { + name = each.value.name + private_connection_resource_id = each.value.resource_id + subresource_names = [each.value.sub_resource] + is_manual_connection = false + } + + private_dns_zone_group { + name = each.value.name + private_dns_zone_ids = [azurerm_private_dns_zone.alz[each.key].id] + } +} diff --git a/modules/azure/resource_groups.tf b/modules/azure/resource_groups.tf index 52fb417..4d5fbea 100644 --- a/modules/azure/resource_groups.tf +++ b/modules/azure/resource_groups.tf @@ -9,13 +9,13 @@ resource "azurerm_resource_group" "identity" { } resource "azurerm_resource_group" "agents" { - count = local.has_agent_container_instances ? 1 : 0 + count = var.use_self_hosted_agents ? 1 : 0 name = var.resource_group_agents_name location = var.azure_location } resource "azurerm_resource_group" "network" { - count = local.use_private_networking ? 1 : 0 + count = var.use_private_networking ? 1 : 0 name = var.resource_group_network_name location = var.azure_location } diff --git a/modules/azure/storage.tf b/modules/azure/storage.tf index 8e2b049..6f97478 100644 --- a/modules/azure/storage.tf +++ b/modules/azure/storage.tf @@ -6,11 +6,11 @@ resource "azurerm_storage_account" "alz" { account_replication_type = var.storage_account_replication_type allow_nested_items_to_be_public = false shared_access_key_enabled = false - public_network_access_enabled = local.use_private_networking && !var.allow_storage_access_from_my_ip ? false : true + public_network_access_enabled = var.use_private_networking && var.use_self_hosted_agents && !var.allow_storage_access_from_my_ip ? false : true } resource "azurerm_storage_account_network_rules" "alz" { - count = local.use_private_networking ? 1 : 0 + count = var.use_private_networking ? 1 : 0 storage_account_id = azurerm_storage_account.alz.id default_action = "Deny" ip_rules = var.allow_storage_access_from_my_ip ? [data.http.ip[0].response_body] : [] diff --git a/modules/azure/variables.tf b/modules/azure/variables.tf index 7cf21f6..4d90a57 100644 --- a/modules/azure/variables.tf +++ b/modules/azure/variables.tf @@ -64,11 +64,6 @@ variable "agent_container_instances" { default = {} } -variable "agent_container_instance_image" { - type = string - default = "" -} - variable "agent_organization_url" { type = string default = "" @@ -222,7 +217,7 @@ variable "virtual_network_subnet_name_container_instances" { default = "" } -variable "virtual_network_subnet_name_storage" { +variable "virtual_network_subnet_name_private_endpoints" { type = string description = "Name of the virtual network subnet" default = "" @@ -234,19 +229,19 @@ variable "virtual_network_subnet_address_prefix_container_instances" { default = "10.0.0.0/26" } -variable "virtual_network_subnet_address_prefix_storage" { +variable "virtual_network_subnet_address_prefix_private_endpoints" { type = string description = "Address prefix for the virtual network subnet" default = "10.0.0.64/26" } -variable "private_endpoint_name" { +variable "storage_account_private_endpoint_name" { type = string default = "" } variable "use_private_networking" { - description = "Controls whether to use private networking for the runner to storage account communication" + description = "Controls whether to use private networking for the runner to storage account and runner to container registry communication" type = bool default = true } @@ -255,3 +250,48 @@ variable "allow_storage_access_from_my_ip" { type = bool default = false } + +variable "container_registry_name" { + type = string + description = "The name of the container registry" + default = "" +} + +variable "container_registry_private_endpoint_name" { + type = string + default = "" +} + +variable "container_registry_dockerfile_repository_folder_url" { + type = string + description = "The branch and folder of the repository containing the Dockerfile" + default = "" +} + +variable "container_registry_dockerfile_name" { + type = string + description = "The dockerfile to build" + default = "dockerfile" +} + +variable "container_registry_image_name" { + type = string + description = "The name of the image to build" + default = "" +} + +variable "container_registry_image_tag" { + type = string + description = "The pattern for the image tag" + default = "{{.Run.ID}}" +} + +variable "use_self_hosted_agents" { + type = bool + default = true +} + +variable "agent_container_instance_managed_identity_name" { + type = string + default = "" +}