diff --git a/README.md b/README.md index e6c1936..e820700 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ When the Amazon ECS task container instance transitions to the `RUNNING` state, The authentication procedure for enrolling the Amazon ECS container instance into the ADO agent pool is accomplished by using a personal access token (PAT). There is no need to configure AWS credentials because the access to AWS resources is handled via the Amazon ECS task and task execution [Identity and Access Management (IAM)](https://aws.amazon.com/iam) roles, thus eliminating the need to configure AWS credentials in ADO. +Alternative Authentication in Azure DevOps includes usage of Entra ID Service Principal. Using the Service Principal information, request Entra ID OAuth 2.0 Token which can then be used instead of the PAT. + ### Prerequisites Here are the prerequisites to use this solution for your Azure Pipelines agents: diff --git a/ado_agent_repo/start.sh b/ado_agent_repo/start.sh index be8b9ab..4a0e341 100644 --- a/ado_agent_repo/start.sh +++ b/ado_agent_repo/start.sh @@ -13,7 +13,22 @@ if [ -z "$AZP_TOKEN_FILE" ]; then fi AZP_TOKEN_FILE="/azp/.token" + +# Instead of storing the PAT in the AWS Secret, you can store the Service Principal Credential +# $SP_APP_ID Replace with your Service Principal Application ID in terraform.tfvars +# $SP_APP_TENANT_ID Replace with your Tenant ID + + if [ -z "$SP_APP_ID" ]; then echo -n $AZP_TOKEN > "$AZP_TOKEN_FILE" + else + # If using Service Principal, use the Secret to and Service Principal to request an Entra ID Token + curl -s -X POST -H 'Content-Type: application/x-www-form-urlencoded' \ + "https://login.microsoftonline.com/$SP_APP_TENANT_ID/oauth2/v2.0/token" \ + -d "client_id=$SP_APP_ID" \ + -d 'grant_type=client_credentials' \ + -d 'scope=https%3A%2F%2Fmanagement.core.windows.net%2F%2F.default' \ + -d "client_secret=$AZP_TOKEN" | jq -r '.access_token' > "$AZP_TOKEN_FILE" + fi fi unset AZP_TOKEN diff --git a/data.tf b/data.tf index cb2f9ef..68c8169 100644 --- a/data.tf +++ b/data.tf @@ -6,6 +6,7 @@ data "aws_caller_identity" "current" {} data "aws_region" "current" {} +data "aws_partition" "current" {} data "aws_iam_policy_document" "ecs-assume-role-policy" { statement { @@ -71,7 +72,7 @@ data "aws_iam_policy_document" "lambda_create_task_role_policy" { "logs:CreateLogStream", "logs:PutLogEvents" ] - resources = ["arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*"] + resources = ["arn:${data.aws_partition.current.partition}:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*"] } statement { @@ -85,7 +86,9 @@ data "aws_iam_policy_document" "lambda_create_task_role_policy" { actions = [ "ecs:RunTask" ] - resources = [module.ecs.ecs_task_def_arn] + resources = [ + "${module.ecs.ecs_task_def_arn}*" + ] } statement { @@ -118,7 +121,7 @@ data "aws_iam_policy_document" "lambda_get_task_role_policy" { "logs:GetLogEvents", "logs:FilterLogEvents" ] - resources = ["arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*"] + resources = ["arn:${data.aws_partition.current.partition}:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*"] } statement { @@ -139,7 +142,7 @@ data "aws_iam_policy_document" "lambda_get_task_role_policy" { actions = [ "ecs:DescribeTasks" ] - resources = ["arn:aws:ecs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:task/${local.prefix}-ecs-cluster-${var.environment}/*"] + resources = ["arn:${data.aws_partition.current.partition}:ecs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:task/${local.prefix}-ecs-cluster-${var.environment}/*"] } statement { actions = [ @@ -151,4 +154,4 @@ data "aws_iam_policy_document" "lambda_get_task_role_policy" { ] } -} \ No newline at end of file +} diff --git a/main.tf b/main.tf index 8d1aa86..c7bec6c 100644 --- a/main.tf +++ b/main.tf @@ -254,7 +254,7 @@ module "ecs_ado_api" { api_path_part = "create-task" api_stage_name = "dev" api_stage_description = "ECS Ado API Deployment" - apigw_lambda_arn = "arn:aws:apigateway:${data.aws_region.current.name}:lambda:path/2015-03-31/functions/${module.create_task_lambda.lambda_function_arn}/invocations" + apigw_lambda_arn = "arn:${data.aws_partition.current.partition}:apigateway:${data.aws_region.current.name}:lambda:path/2015-03-31/functions/${module.create_task_lambda.lambda_function_arn}/invocations" function_name = module.create_task_lambda.lambda_function_name tags = local.resource_tags } \ No newline at end of file diff --git a/modules/apigw/main.tf b/modules/apigw/main.tf index 1e4f2a3..892fb23 100644 --- a/modules/apigw/main.tf +++ b/modules/apigw/main.tf @@ -53,7 +53,7 @@ resource "aws_lambda_permission" "api_lambda" { action = "lambda:InvokeFunction" function_name = var.function_name principal = "apigateway.amazonaws.com" - source_arn = "arn:aws:execute-api:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:${aws_api_gateway_rest_api.api.id}/*/*/*" + source_arn = "arn:${data.aws_partition.current.partition}:execute-api:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:${aws_api_gateway_rest_api.api.id}/*/*/*" } resource "aws_api_gateway_deployment" "api_deployment" { diff --git a/modules/codebuild/main.tf b/modules/codebuild/main.tf index bb867f3..9ea1bb0 100644 --- a/modules/codebuild/main.tf +++ b/modules/codebuild/main.tf @@ -47,4 +47,7 @@ resource "aws_codebuild_project" "terraform_codebuild_project" { type = var.build_project_source buildspec = var.build_spec } + + lifecycle { ignore_changes = [project_visibility] } + } \ No newline at end of file diff --git a/modules/ecr/main.tf b/modules/ecr/main.tf index dd71f92..62e2144 100644 --- a/modules/ecr/main.tf +++ b/modules/ecr/main.tf @@ -45,7 +45,7 @@ resource "aws_ecr_repository_policy" "terraform_ecr_repository_policy" { "Effect" : "Allow", "Principal" : { "AWS" : [ - "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" + "arn:${data.aws_partition.current.partition}:iam::${data.aws_caller_identity.current.account_id}:root" ] }, "Action" : [ diff --git a/modules/ecs/main.tf b/modules/ecs/main.tf index 96a4b40..f24daee 100644 --- a/modules/ecs/main.tf +++ b/modules/ecs/main.tf @@ -34,6 +34,13 @@ resource "aws_ecs_task_definition" "ecs_task_def" { containerPort = var.container_port hostPort = var.container_host_port }] + healthCheck = { + retries = 3 + command = [ "CMD-SHELL", "pgrep -u agent Agent.Listener || exit 1" ] + timeout: 10 + interval: 60 + startPeriod: 5 + } logConfiguration = { logDriver = "awslogs" options = { diff --git a/modules/iam-role/main.tf b/modules/iam-role/main.tf index 7dd4416..924865d 100644 --- a/modules/iam-role/main.tf +++ b/modules/iam-role/main.tf @@ -86,7 +86,7 @@ resource "aws_iam_policy" "codepipeline_policy" { "codecommit:ListBranches", "codecommit:UploadArchive" ], - "Resource": "arn:aws:codecommit:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:${var.source_repository_name}" + "Resource": "arn:${data.aws_partition.current.partition}:codecommit:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:${var.source_repository_name}" }, { "Effect": "Allow", @@ -95,7 +95,7 @@ resource "aws_iam_policy" "codepipeline_policy" { "codebuild:StartBuild", "codebuild:BatchGetProjects" ], - "Resource": "arn:aws:codebuild:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:project/${var.project_name}*" + "Resource": "arn:${data.aws_partition.current.partition}:codebuild:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:project/${var.project_name}*" }, { "Effect": "Allow", @@ -105,7 +105,7 @@ resource "aws_iam_policy" "codepipeline_policy" { "codebuild:UpdateReport", "codebuild:BatchPutTestCases" ], - "Resource": "arn:aws:codebuild:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:report-group/${var.project_name}*" + "Resource": "arn:${data.aws_partition.current.partition}:codebuild:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:report-group/${var.project_name}*" }, { "Effect": "Allow", @@ -119,7 +119,7 @@ resource "aws_iam_policy" "codepipeline_policy" { "ecr:PutImage", "ecr:UploadLayerPart" ], - "Resource": "arn:aws:ecr:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:repository/ado-ecs-ecr" + "Resource": "arn:${data.aws_partition.current.partition}:ecr:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:repository/ado-ecs-ecr" }, { "Effect": "Allow", @@ -167,7 +167,7 @@ data "aws_iam_policy_document" "assume_role_policy" { resource "aws_iam_role_policy_attachment" "ecsTaskExecutionRole_policy" { role = aws_iam_role.ecsTaskExecutionRole.name - policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" + policy_arn = "arn:${data.aws_partition.current.partition}:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role" } resource "aws_iam_role_policy_attachment" "ecsTaskExecutionRole_assume_policy" { @@ -191,7 +191,7 @@ resource "aws_iam_policy" "ecs_task_role_policy" { "logs:CreateLogStream", "logs:PutLogEvents" ], - "Resource": "arn:aws:iam:::role/${var.infrastructure_deployer_role_name}" + "Resource": "arn:${data.aws_partition.current.partition}:iam:::role/${var.infrastructure_deployer_role_name}" } ] } diff --git a/modules/kms/data.tf b/modules/kms/data.tf index c2bde40..0adde8e 100644 --- a/modules/kms/data.tf +++ b/modules/kms/data.tf @@ -5,4 +5,5 @@ #Amazon Web Services, Inc. or Amazon Web Services EMEA SARL or both. data "aws_caller_identity" "current" {} -data "aws_region" "current" {} \ No newline at end of file +data "aws_region" "current" {} +data "aws_partition" "current" {} \ No newline at end of file diff --git a/modules/kms/main.tf b/modules/kms/main.tf index c5f643c..7a87555 100644 --- a/modules/kms/main.tf +++ b/modules/kms/main.tf @@ -27,7 +27,7 @@ data "aws_iam_policy_document" "kms_key_policy_doc" { principals { type = "AWS" - identifiers = ["arn:aws:iam::${local.account_id}:root"] + identifiers = ["arn:${data.aws_partition.current.partition}:iam::${local.account_id}:root"] } } diff --git a/modules/lambda/iam.tf b/modules/lambda/iam.tf index c5dab3f..54f9170 100644 --- a/modules/lambda/iam.tf +++ b/modules/lambda/iam.tf @@ -34,7 +34,7 @@ resource "aws_iam_policy" "lambda_execution_role_policy" { "logs:PutLogEvents" ] Effect = "Allow" - Resource = "arn:aws:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*" + Resource = "arn:${data.aws_partition.current.partition}:logs:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:*" }, { Action = [ diff --git a/terraform.tfvars b/terraform.tfvars index ff21b68..529e4bc 100644 --- a/terraform.tfvars +++ b/terraform.tfvars @@ -17,6 +17,8 @@ container_env_vars = [ { name = "PORT", value = "80" }, { name = "AZP_URL", value = "https://dev.azure.com/change-me/" },// Replace with your ADO Org URL { name = "AZP_POOL", value = "ecscluster" }, // Replace with your ADO Agent Pool name + #{ name = "SP_APP_ID", value = ""}, // Replace with your Service Principal Application ID + #{ name = "SP_APP_TENANT_ID", value = ""}, // Replace with your Tenant ID ] container_port = 80 container_host_port = 80 @@ -34,4 +36,4 @@ lambda_timeout = "90" subnet_ids = "subnet-change-me" // Replace with subnet-id from your account security_groups = "sg-change-me" // Replace with security-group-id from your account -ado_org = "change-me" // Replace with your ADO Org ID +ado_org = "change-me" // Replace with your ADO Org Name