Skip to content

Commit

Permalink
Merge pull request #38087 from DrFaust92/workteam-access
Browse files Browse the repository at this point in the history
r/sagemaker_workteam - add support for `worker_access_configuration`
  • Loading branch information
ewbankkit authored Jun 24, 2024
2 parents c5af20e + fefad02 commit e38d04a
Show file tree
Hide file tree
Showing 5 changed files with 244 additions and 5 deletions.
3 changes: 3 additions & 0 deletions .changelog/38087.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_sagemaker_workteam: Add `worker_access_configuration` attribute
```
11 changes: 6 additions & 5 deletions internal/service/sagemaker/sagemaker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,12 @@ func TestAccSageMaker_serial(t *testing.T) {
"VPC": testAccWorkforce_vpc,
},
"Workteam": {
acctest.CtDisappears: testAccWorkteam_disappears,
"tags": testAccWorkteam_tags,
"CognitoConfig": testAccWorkteam_cognitoConfig,
"NotificationConfig": testAccWorkteam_notificationConfig,
"OidcConfig": testAccWorkteam_oidcConfig,
acctest.CtDisappears: testAccWorkteam_disappears,
"tags": testAccWorkteam_tags,
"CognitoConfig": testAccWorkteam_cognitoConfig,
"NotificationConfig": testAccWorkteam_notificationConfig,
"WorkerAccessConfiguration": testAccWorkteam_workerAccessConfiguration,
"OidcConfig": testAccWorkteam_oidcConfig,
},
"Servicecatalog": {
acctest.CtBasic: testAccServicecatalogPortfolioStatus_basic,
Expand Down
149 changes: 149 additions & 0 deletions internal/service/sagemaker/workteam.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,50 @@ func ResourceWorkteam() *schema.Resource {
},
DiffSuppressFunc: verify.SuppressMissingOptionalConfigurationBlock,
},
"worker_access_configuration": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"s3_presign": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"iam_policy_constraints": {
Type: schema.TypeList,
Optional: true,
Computed: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"source_ip": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice(sagemaker.EnabledOrDisabled_Values(), false),
ExactlyOneOf: []string{"worker_access_configuration.0.s3_presign.0.iam_policy_constraints.0.source_ip", "worker_access_configuration.0.s3_presign.0.iam_policy_constraints.0.vpc_source_ip"},
},
"vpc_source_ip": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ValidateFunc: validation.StringInSlice(sagemaker.EnabledOrDisabled_Values(), false),
ExactlyOneOf: []string{"worker_access_configuration.0.s3_presign.0.iam_policy_constraints.0.source_ip", "worker_access_configuration.0.s3_presign.0.iam_policy_constraints.0.vpc_source_ip"},
},
},
},
},
},
},
},
},
},
},
"subdomain": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -154,6 +198,10 @@ func resourceWorkteamCreate(ctx context.Context, d *schema.ResourceData, meta in
input.NotificationConfiguration = expandWorkteamNotificationConfiguration(v.([]interface{}))
}

if v, ok := d.GetOk("worker_access_configuration"); ok {
input.WorkerAccessConfiguration = expandWorkerAccessConfiguration(v.([]interface{}))
}

log.Printf("[DEBUG] Updating SageMaker Workteam: %s", input)
_, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, 2*time.Minute, func() (interface{}, error) {
return conn.CreateWorkteamWithContext(ctx, input)
Expand Down Expand Up @@ -198,6 +246,10 @@ func resourceWorkteamRead(ctx context.Context, d *schema.ResourceData, meta inte
return sdkdiag.AppendErrorf(diags, "setting notification_configuration: %s", err)
}

if err := d.Set("worker_access_configuration", flattenWorkerAccessConfiguration(workteam.WorkerAccessConfiguration)); err != nil {
return sdkdiag.AppendErrorf(diags, "setting worker_access_configuration: %s", err)
}

return diags
}

Expand All @@ -219,6 +271,10 @@ func resourceWorkteamUpdate(ctx context.Context, d *schema.ResourceData, meta in
input.NotificationConfiguration = expandWorkteamNotificationConfiguration(d.Get("notification_configuration").([]interface{}))
}

if d.HasChange("worker_access_configuration") {
input.WorkerAccessConfiguration = expandWorkerAccessConfiguration(d.Get("worker_access_configuration").([]interface{}))
}

log.Printf("[DEBUG] Updating SageMaker Workteam: %s", input)
_, err := conn.UpdateWorkteamWithContext(ctx, input)

Expand Down Expand Up @@ -380,3 +436,96 @@ func flattenWorkteamNotificationConfiguration(config *sagemaker.NotificationConf

return []map[string]interface{}{m}
}

func expandWorkerAccessConfiguration(l []interface{}) *sagemaker.WorkerAccessConfiguration {
if len(l) == 0 || l[0] == nil {
return nil
}

m := l[0].(map[string]interface{})

config := &sagemaker.WorkerAccessConfiguration{}

if v, ok := m["s3_presign"].([]interface{}); ok && len(v) > 0 && v[0] != nil {
config.S3Presign = expandS3Presign(v)
} else {
return nil
}

return config
}

func flattenWorkerAccessConfiguration(config *sagemaker.WorkerAccessConfiguration) []map[string]interface{} {
if config == nil {
return []map[string]interface{}{}
}

m := map[string]interface{}{
"s3_presign": flattenS3Presign(config.S3Presign),
}

return []map[string]interface{}{m}
}

func expandS3Presign(l []interface{}) *sagemaker.S3Presign {
if len(l) == 0 || l[0] == nil {
return nil
}

m := l[0].(map[string]interface{})

config := &sagemaker.S3Presign{}

if v, ok := m["iam_policy_constraints"].([]interface{}); ok && len(v) > 0 && v[0] != nil {
config.IamPolicyConstraints = expandIAMPolicyConstraints(v)
} else {
return nil
}

return config
}

func flattenS3Presign(config *sagemaker.S3Presign) []map[string]interface{} {
if config == nil {
return []map[string]interface{}{}
}

m := map[string]interface{}{
"iam_policy_constraints": flattenIAMPolicyConstraints(config.IamPolicyConstraints),
}

return []map[string]interface{}{m}
}

func expandIAMPolicyConstraints(l []interface{}) *sagemaker.IamPolicyConstraints {
if len(l) == 0 || l[0] == nil {
return nil
}

m := l[0].(map[string]interface{})

config := &sagemaker.IamPolicyConstraints{}

if v, ok := m["source_ip"].(string); ok && v != "" {
config.SourceIp = aws.String(v)
}

if v, ok := m["vpc_source_ip"].(string); ok && v != "" {
config.VpcSourceIp = aws.String(v)
}

return config
}

func flattenIAMPolicyConstraints(config *sagemaker.IamPolicyConstraints) []map[string]interface{} {
if config == nil {
return []map[string]interface{}{}
}

m := map[string]interface{}{
"source_ip": aws.StringValue(config.SourceIp),
"vpc_source_ip": aws.StringValue(config.VpcSourceIp),
}

return []map[string]interface{}{m}
}
72 changes: 72 additions & 0 deletions internal/service/sagemaker/workteam_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,54 @@ func testAccWorkteam_notificationConfig(t *testing.T) {
})
}

func testAccWorkteam_workerAccessConfiguration(t *testing.T) {
ctx := acctest.Context(t)
var workteam sagemaker.Workteam
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_sagemaker_workteam.test"

resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.SageMakerServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckWorkteamDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccWorkteamConfig_workerAccessConfiguration(rName, "Enabled"),
Check: resource.ComposeTestCheckFunc(
testAccCheckWorkteamExists(ctx, resourceName, &workteam),
resource.TestCheckResourceAttr(resourceName, "workteam_name", rName),
acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "sagemaker", regexache.MustCompile(`workteam/.+`)),
resource.TestCheckResourceAttr(resourceName, names.AttrDescription, rName),
resource.TestCheckResourceAttr(resourceName, "worker_access_configuration.#", acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, "worker_access_configuration.0.s3_presign.#", acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, "worker_access_configuration.0.s3_presign.0.iam_policy_constraints.#", acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, "worker_access_configuration.0.s3_presign.0.iam_policy_constraints.0.source_ip", "Enabled"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"workforce_name"},
},
{
Config: testAccWorkteamConfig_workerAccessConfiguration(rName, "Disabled"),
Check: resource.ComposeTestCheckFunc(
testAccCheckWorkteamExists(ctx, resourceName, &workteam),
resource.TestCheckResourceAttr(resourceName, "workteam_name", rName),
acctest.MatchResourceAttrRegionalARN(resourceName, names.AttrARN, "sagemaker", regexache.MustCompile(`workteam/.+`)),
resource.TestCheckResourceAttr(resourceName, names.AttrDescription, rName),
resource.TestCheckResourceAttr(resourceName, "worker_access_configuration.#", acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, "worker_access_configuration.0.s3_presign.#", acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, "worker_access_configuration.0.s3_presign.0.iam_policy_constraints.#", acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, "worker_access_configuration.0.s3_presign.0.iam_policy_constraints.0.source_ip", "Disabled"),
),
},
},
})
}

func testAccWorkteam_disappears(t *testing.T) {
ctx := acctest.Context(t)
var workteam sagemaker.Workteam
Expand Down Expand Up @@ -505,6 +553,30 @@ resource "aws_sagemaker_workteam" "test" {
`, rName))
}

func testAccWorkteamConfig_workerAccessConfiguration(rName, status string) string {
return acctest.ConfigCompose(testAccWorkteamOIDCBaseConfig(rName), fmt.Sprintf(`
resource "aws_sagemaker_workteam" "test" {
workteam_name = %[1]q
workforce_name = aws_sagemaker_workforce.test.id
description = %[1]q
member_definition {
oidc_member_definition {
groups = [%[1]q]
}
}
worker_access_configuration {
s3_presign {
iam_policy_constraints {
source_ip = %[2]q
}
}
}
}
`, rName, status))
}

func testAccWorkteamConfig_tags1(rName, tagKey1, tagValue1 string) string {
return acctest.ConfigCompose(testAccWorkteamOIDCBaseConfig(rName), fmt.Sprintf(`
resource "aws_sagemaker_workteam" "test" {
Expand Down
14 changes: 14 additions & 0 deletions website/docs/r/sagemaker_workteam.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ This resource supports the following arguments:
* `workteam_name` - (Required) The name of the workforce.
* `member_definition` - (Required) A list of Member Definitions that contains objects that identify the workers that make up the work team. Workforces can be created using Amazon Cognito or your own OIDC Identity Provider (IdP). For private workforces created using Amazon Cognito use `cognito_member_definition`. For workforces created using your own OIDC identity provider (IdP) use `oidc_member_definition`. Do not provide input for both of these parameters in a single request. see [Member Definition](#member-definition) details below.
* `notification_configuration` - (Optional) Configures notification of workers regarding available or expiring work items. see [Notification Configuration](#notification-configuration) details below.
* `worker_access_configuration` - (Optional) Use this optional parameter to constrain access to an Amazon S3 resource based on the IP address using supported IAM global condition keys. The Amazon S3 resource is accessed in the worker portal using a Amazon S3 presigned URL. see [Worker Access Configuration](#worker-access-configuration) details below.
* `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level.

### Member Definition
Expand All @@ -76,6 +77,19 @@ This resource supports the following arguments:

* `notification_topic_arn` - (Required) The ARN for the SNS topic to which notifications should be published.

### Worker Access Configuration

* `s3_presign` - (Required) Defines any Amazon S3 resource constraints. see [S3 Presign](#s3-presign) details below.

#### S3 Presign

* `iam_policy_constraints` - (Required) Use this parameter to specify the allowed request source. Possible sources are either SourceIp or VpcSourceIp. see [IAM Policy Constraints](#iam-policy-constraints) details below.

##### IAM Policy Constraints

* `source_ip` - (Optional) When SourceIp is Enabled the worker's IP address when a task is rendered in the worker portal is added to the IAM policy as a Condition used to generate the Amazon S3 presigned URL. This IP address is checked by Amazon S3 and must match in order for the Amazon S3 resource to be rendered in the worker portal. Valid values are `Enabled` or `Disabled`
* `vpc_source_ip` - (Optional) When VpcSourceIp is Enabled the worker's IP address when a task is rendered in private worker portal inside the VPC is added to the IAM policy as a Condition used to generate the Amazon S3 presigned URL. To render the task successfully Amazon S3 checks that the presigned URL is being accessed over an Amazon S3 VPC Endpoint, and that the worker's IP address matches the IP address in the IAM policy. To learn more about configuring private worker portal, see [Use Amazon VPC mode from a private worker portal](https://docs.aws.amazon.com/sagemaker/latest/dg/samurai-vpc-worker-portal.html). Valid values are `Enabled` or `Disabled`

## Attribute Reference

This resource exports the following attributes in addition to the arguments above:
Expand Down

0 comments on commit e38d04a

Please sign in to comment.