Search code examples
amazon-web-servicesterraformamazon-cognitoterraform-provider-awsamazon-sagemaker

Sagemaker workforce with cognito


i am trying to build the terraform for sagemaker private work force with private cognito

Following : https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sagemaker_workforce

it working fine

main.tf

resource "aws_sagemaker_workforce" "workforce" {
  workforce_name = "workforce"

  cognito_config {
    client_id = aws_cognito_user_pool_client.congnito_client.id
    user_pool = aws_cognito_user_pool_domain.domain.user_pool_id
  }
}

resource "aws_cognito_user_pool" "user_pool" {
  name = "sagemaker-cognito-userpool"
}

resource "aws_cognito_user_pool_client" "congnito_client" {
  name            = "congnito-client"
  generate_secret = true
  user_pool_id    = aws_cognito_user_pool.user_pool.id
}

resource "aws_cognito_user_group" "user_group" {
  name         = "user-group"
  user_pool_id = aws_cognito_user_pool.user_pool.id
}

resource "aws_cognito_user_pool_domain" "domain" {
  domain       = "sagemaker-user-pool-ocr-domain"
  user_pool_id = aws_cognito_user_pool.user_pool.id
}

resource "aws_sagemaker_workteam" "workteam" {
  workteam_name  = "worker-team"
  workforce_name = aws_sagemaker_workforce.workforce.id
  description    = "worker-team"

  member_definition {
    cognito_member_definition {
      client_id  = aws_cognito_user_pool_client.congnito_client.id
      user_pool  = aws_cognito_user_pool_domain.domain.user_pool_id
      user_group = aws_cognito_user_group.user_group.id
    }
  }
}

resource "aws_sagemaker_human_task_ui" "template" {
  human_task_ui_name = "human-task-ui-template"

  ui_template {
    content = file("${path.module}/sagemaker-human-task-ui-template.html")
  }
}

resource "aws_sagemaker_flow_definition" "definition" {
  flow_definition_name = "flow-definition"
  role_arn             = var.aws_iam_role

  human_loop_config {
    human_task_ui_arn                     = aws_sagemaker_human_task_ui.template.arn
    task_availability_lifetime_in_seconds = 1
    task_count                            = 1
    task_description                      = "Task description"
    task_title                            = "Please review the Key Value Pairs in this document"
    workteam_arn                          = aws_sagemaker_workteam.workteam.arn
  }

  output_config {
    s3_output_path = "s3://${var.s3_output_path}"
  }
}

it's creating the cognito user pool with callback urls. These callback urls is coming from aws_sagemaker_workforce.workforce.subdomain and getting set in cognito automatically which is what i want.

But i also want to set config in cognito userpool like

allowed_oauth_flows = ["code", "implicit"]
  allowed_oauth_scopes = ["email", "openid", "profile"]

now when i add above two line we need to add callbackurl also which i dont want.

i tried

allowed_oauth_flows = ["code", "implicit"]
  allowed_oauth_scopes = ["email", "openid", "profile"]
  callback_urls = [aws_sagemaker_workforce.workforce.subdomain]

which is giving error :

Cycle: module.sagemaker.aws_cognito_user_pool_client.congnito_client, module.sagemaker.aws_sagemaker_workforce.workforce

as both resource are dependent on each other, i want to pass those two line but it forces me to add callback url also.

here is the final main.tf which is failing with that three line

resource "aws_sagemaker_workforce" "workforce" {
  workforce_name = "workforce"

  cognito_config {
    client_id = aws_cognito_user_pool_client.congnito_client.id
    user_pool = aws_cognito_user_pool_domain.domain.user_pool_id
  }
}

resource "aws_cognito_user_pool" "user_pool" {
  name = "sagemaker-cognito-userpool"
}

resource "aws_cognito_user_pool_client" "congnito_client" {
  name            = "congnito-client"
  generate_secret = true
  user_pool_id    = aws_cognito_user_pool.user_pool.id

  explicit_auth_flows                  = ["ALLOW_REFRESH_TOKEN_AUTH", "ALLOW_USER_PASSWORD_AUTH", "ALLOW_CUSTOM_AUTH", "ALLOW_USER_SRP_AUTH"]
  allowed_oauth_flows_user_pool_client = true
  supported_identity_providers = ["COGNITO"]

  allowed_oauth_flows = ["code", "implicit"]
  allowed_oauth_scopes = ["email", "openid", "profile"]
  callback_urls = [aws_sagemaker_workforce.workforce.subdomain]
}

resource "aws_cognito_user_group" "user_group" {
  name         = "user-group"
  user_pool_id = aws_cognito_user_pool.user_pool.id
}

resource "aws_cognito_user_pool_domain" "domain" {
  domain       = "sagemaker-user-pool-ocr-domain"
  user_pool_id = aws_cognito_user_pool.user_pool.id
}

resource "aws_sagemaker_workteam" "workteam" {
  workteam_name  = "worker-team"
  workforce_name = aws_sagemaker_workforce.workforce.id
  description    = "worker-team"

  member_definition {
    cognito_member_definition {
      client_id  = aws_cognito_user_pool_client.congnito_client.id
      user_pool  = aws_cognito_user_pool_domain.domain.user_pool_id
      user_group = aws_cognito_user_group.user_group.id
    }
  }
}

resource "aws_sagemaker_human_task_ui" "template" {
  human_task_ui_name = "human-task-ui-template"

  ui_template {
    content = file("${path.module}/sagemaker-human-task-ui-template.html")
  }
}

resource "aws_sagemaker_flow_definition" "definition" {
  flow_definition_name = "flow-definition"
  role_arn             = var.aws_iam_role

  human_loop_config {
    human_task_ui_arn                     = aws_sagemaker_human_task_ui.template.arn
    task_availability_lifetime_in_seconds = 1
    task_count                            = 1
    task_description                      = "Task description"
    task_title                            = "Please review the Key Value Pairs in this document"
    workteam_arn                          = aws_sagemaker_workteam.workteam.arn
  }

  output_config {
    s3_output_path = "s3://${var.s3_output_path}"
  }
}

Solution

  • You do not need to specify the callback URL for the workforce. It is sufficient to specify the following in order to create the aws_cognito_user_pool_client resource:

    callback_urls = [
        "https://${aws_cognito_user_pool_domain.domain>.cloudfront_distribution_arn}",
    ]
    

    Then you reference the user pool client in your workforce definition:

    resource "aws_sagemaker_workforce" "..." {
        workforce_name = "..."
    
        cognito_config {
            client_id = aws_cognito_user_pool_client.<client_name>.id
            user_pool = aws_cognito_user_pool_domain.<domain_name>.user_pool_id
        }
    }
    

    Existence of the callback URLs can be proven after applying the terraform configuration by running aws cognito-idp describe-user-pool-client --user-pool-id <pool_id> --client-id <client_id>:

    "UserPoolClient": {
        ...
        "CallbackURLs": [
            "https://____.cloudfront.net",
            "https://____.labeling.eu-central-1.sagemaker.aws/oauth2/idpresponse"
        ],
        "LogoutURLs": [
            "https://____.labeling.eu-central-1.sagemaker.aws/logout"
        ],
    

    It seems as terraform itself does not do anything special on workforce creation (see https://github.com/hashicorp/terraform-provider-aws/blob/main/internal/service/sagemaker/workforce.go). So the callback urls seem to be added by AWS SageMaker itself.

    This means that you have to instruct terraform to ignore changes on those attributes in the aws_cognito_user_pool_client configuration:

    lifecycle {
        ignore_changes = [
            callback_urls, logout_urls
        ]
    }