Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changelog/48723.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_bedrockagentcore_oauth2_credential_provider: Add `atlassian_oauth2_provider_config` and `linkedin_oauth2_provider_config` provider configuration blocks
```

```release-note:enhancement
resource/aws_bedrockagentcore_oauth2_credential_provider: Add `client_secret_source` and `client_secret_config` arguments to all OAuth2 provider configuration blocks
```
136 changes: 129 additions & 7 deletions internal/service/bedrockagentcore/oauth2_credential_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ type oauth2CredentialProviderResource struct {
framework.ResourceWithModel[oauth2CredentialProviderResourceModel]
}

func oauth2ClientCredentialsAttributes(context.Context) map[string]schema.Attribute {
func oauth2ClientCredentialsAttributes(ctx context.Context) map[string]schema.Attribute {
return map[string]schema.Attribute{
"client_credentials_wo_version": schema.Int64Attribute{
Optional: true,
Expand All @@ -65,6 +65,10 @@ func oauth2ClientCredentialsAttributes(context.Context) map[string]schema.Attrib
}...),
},
},
"client_secret_source": schema.StringAttribute{
CustomType: fwtypes.StringEnumType[awstypes.SecretSourceType](),
Optional: true,
},
names.AttrClientID: schema.StringAttribute{
Optional: true,
Sensitive: true,
Expand Down Expand Up @@ -126,6 +130,25 @@ func oauth2ClientCredentialsAttributes(context.Context) map[string]schema.Attrib
}
}

func oauth2ClientSecretConfigBlock(ctx context.Context) schema.ListNestedBlock {
return schema.ListNestedBlock{
CustomType: fwtypes.NewListNestedObjectTypeOf[oauth2SecretReferenceModel](ctx),
Validators: []validator.List{
listvalidator.SizeAtMost(1),
},
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
"json_key": schema.StringAttribute{
Required: true,
},
"secret_id": schema.StringAttribute{
Required: true,
},
},
},
}
}

func basicOAuth2ProviderConfigBlock[T any](ctx context.Context) schema.ListNestedBlock {
attrs := oauth2ClientCredentialsAttributes(ctx)
attrs["oauth_discovery"] = framework.ResourceComputedListOfObjectsAttribute[oauth2DiscoveryModel](ctx)
Expand All @@ -137,6 +160,9 @@ func basicOAuth2ProviderConfigBlock[T any](ctx context.Context) schema.ListNeste
},
NestedObject: schema.NestedBlockObject{
Attributes: attrs,
Blocks: map[string]schema.Block{
"client_secret_config": oauth2ClientSecretConfigBlock(ctx),
},
},
}
}
Expand Down Expand Up @@ -183,6 +209,7 @@ func (r *oauth2CredentialProviderResource) Schema(ctx context.Context, request r
NestedObject: schema.NestedBlockObject{
Attributes: oauth2ClientCredentialsAttributes(ctx),
Blocks: map[string]schema.Block{
"client_secret_config": oauth2ClientSecretConfigBlock(ctx),
"oauth_discovery": schema.ListNestedBlock{
CustomType: fwtypes.NewListNestedObjectTypeOf[oauth2DiscoveryModel](ctx),
Validators: []validator.List{
Expand Down Expand Up @@ -225,8 +252,10 @@ func (r *oauth2CredentialProviderResource) Schema(ctx context.Context, request r
},
},
},
"atlassian_oauth2_provider_config": basicOAuth2ProviderConfigBlock[atlassianOAuth2ProviderConfigModel](ctx),
"github_oauth2_provider_config": basicOAuth2ProviderConfigBlock[githubOAuth2ProviderConfigModel](ctx),
"google_oauth2_provider_config": basicOAuth2ProviderConfigBlock[googleOAuth2ProviderConfigModel](ctx),
"linkedin_oauth2_provider_config": basicOAuth2ProviderConfigBlock[linkedinOAuth2ProviderConfigModel](ctx),
"microsoft_oauth2_provider_config": basicOAuth2ProviderConfigBlock[microsoftOAuth2ProviderConfigModel](ctx),
"salesforce_oauth2_provider_config": basicOAuth2ProviderConfigBlock[salesforceOAuth2ProviderConfigModel](ctx),
"slack_oauth2_provider_config": basicOAuth2ProviderConfigBlock[slackOAuth2ProviderConfigModel](ctx),
Expand Down Expand Up @@ -481,9 +510,11 @@ type oauth2CredentialProviderResourceModel struct {
}

type oauth2ProviderConfigModel struct {
AtlassianOAuth2ProviderConfig fwtypes.ListNestedObjectValueOf[atlassianOAuth2ProviderConfigModel] `tfsdk:"atlassian_oauth2_provider_config"`
CustomOAuth2ProviderConfig fwtypes.ListNestedObjectValueOf[customOAuth2ProviderConfigModel] `tfsdk:"custom_oauth2_provider_config"`
GithubOAuth2ProviderConfig fwtypes.ListNestedObjectValueOf[githubOAuth2ProviderConfigModel] `tfsdk:"github_oauth2_provider_config"`
GoogleOAuth2ProviderConfig fwtypes.ListNestedObjectValueOf[googleOAuth2ProviderConfigModel] `tfsdk:"google_oauth2_provider_config"`
LinkedinOAuth2ProviderConfig fwtypes.ListNestedObjectValueOf[linkedinOAuth2ProviderConfigModel] `tfsdk:"linkedin_oauth2_provider_config"`
MicrosoftOAuth2ProviderConfig fwtypes.ListNestedObjectValueOf[microsoftOAuth2ProviderConfigModel] `tfsdk:"microsoft_oauth2_provider_config"`
SalesforceOAuth2ProviderConfig fwtypes.ListNestedObjectValueOf[salesforceOAuth2ProviderConfigModel] `tfsdk:"salesforce_oauth2_provider_config"`
SlackOAuth2ProviderConfig fwtypes.ListNestedObjectValueOf[slackOAuth2ProviderConfigModel] `tfsdk:"slack_oauth2_provider_config"`
Expand All @@ -495,7 +526,7 @@ func (m *oauth2CredentialProviderResourceModel) clientCredentials(ctx context.Co
data, d := m.OAuth2ProviderConfig.ToPtr(ctx)
diags.Append(d...)
if diags.HasError() || data == nil {
return inttypes.Zero[oauth2ClientCredentialsModel](), diags
return newOAuth2ClientCredentials(ctx), diags
}

v, d := data.clientCredentials(ctx)
Expand All @@ -504,6 +535,15 @@ func (m *oauth2CredentialProviderResourceModel) clientCredentials(ctx context.Co
return v, diags
}

// newOAuth2ClientCredentials returns a client credentials model with the nested
// object fields initialized to null. The zero value of fwtypes.ListNestedObjectValueOf
// has no element type and panics when serialized, so it must be explicitly nulled.
func newOAuth2ClientCredentials(ctx context.Context) oauth2ClientCredentialsModel {
return oauth2ClientCredentialsModel{
ClientSecretConfig: fwtypes.NewListNestedObjectValueOfNull[oauth2SecretReferenceModel](ctx),
}
}

var (
_ fwflex.Flattener = &oauth2ProviderConfigModel{}
_ fwflex.Expander = &oauth2ProviderConfigModel{}
Expand All @@ -514,8 +554,21 @@ func (m *oauth2ProviderConfigModel) Flatten(ctx context.Context, v any) diag.Dia

// Propagate client credentials from State.
clientCredentials := oauth2ClientCredentialsCtxKey.FromContext(ctx)
// The zero value of the nested-object field panics when serialized; ensure it is valid null.
if clientCredentials.ClientSecretConfig.IsNull() && clientCredentials.ClientSecretConfig.ElementType(ctx) == nil {
clientCredentials.ClientSecretConfig = fwtypes.NewListNestedObjectValueOfNull[oauth2SecretReferenceModel](ctx)
}

switch t := v.(type) {
case awstypes.Oauth2ProviderConfigOutputMemberAtlassianOauth2ProviderConfig:
var model atlassianOAuth2ProviderConfigModel
smerr.AddEnrich(ctx, &diags, fwflex.Flatten(ctx, t.Value, &model))
if diags.HasError() {
return diags
}
model.oauth2ClientCredentialsModel = clientCredentials
m.AtlassianOAuth2ProviderConfig = fwtypes.NewListNestedObjectValueOfPtrMust(ctx, &model)

case awstypes.Oauth2ProviderConfigOutputMemberCustomOauth2ProviderConfig:
var model customOAuth2ProviderConfigModel
smerr.AddEnrich(ctx, &diags, fwflex.Flatten(ctx, t.Value, &model))
Expand Down Expand Up @@ -543,6 +596,15 @@ func (m *oauth2ProviderConfigModel) Flatten(ctx context.Context, v any) diag.Dia
model.oauth2ClientCredentialsModel = clientCredentials
m.GoogleOAuth2ProviderConfig = fwtypes.NewListNestedObjectValueOfPtrMust(ctx, &model)

case awstypes.Oauth2ProviderConfigOutputMemberLinkedinOauth2ProviderConfig:
var model linkedinOAuth2ProviderConfigModel
smerr.AddEnrich(ctx, &diags, fwflex.Flatten(ctx, t.Value, &model))
if diags.HasError() {
return diags
}
model.oauth2ClientCredentialsModel = clientCredentials
m.LinkedinOAuth2ProviderConfig = fwtypes.NewListNestedObjectValueOfPtrMust(ctx, &model)

case awstypes.Oauth2ProviderConfigOutputMemberMicrosoftOauth2ProviderConfig:
var model microsoftOAuth2ProviderConfigModel
smerr.AddEnrich(ctx, &diags, fwflex.Flatten(ctx, t.Value, &model))
Expand Down Expand Up @@ -588,6 +650,19 @@ func (m oauth2ProviderConfigModel) Expand(ctx context.Context) (any, diag.Diagno
}

switch {
case !m.AtlassianOAuth2ProviderConfig.IsNull():
data, d := m.AtlassianOAuth2ProviderConfig.ToPtr(ctx)
smerr.AddEnrich(ctx, &diags, d)
if diags.HasError() {
return nil, diags
}
data.oauth2ClientCredentialsModel = clientCredentials
var r awstypes.Oauth2ProviderConfigInputMemberAtlassianOauth2ProviderConfig
smerr.AddEnrich(ctx, &diags, fwflex.Expand(ctx, data, &r.Value))
if diags.HasError() {
return nil, diags
}
return &r, diags
case !m.CustomOAuth2ProviderConfig.IsNull():
data, d := m.CustomOAuth2ProviderConfig.ToPtr(ctx)
smerr.AddEnrich(ctx, &diags, d)
Expand Down Expand Up @@ -629,6 +704,20 @@ func (m oauth2ProviderConfigModel) Expand(ctx context.Context) (any, diag.Diagno
}
return &r, diags

case !m.LinkedinOAuth2ProviderConfig.IsNull():
data, d := m.LinkedinOAuth2ProviderConfig.ToPtr(ctx)
smerr.AddEnrich(ctx, &diags, d)
if diags.HasError() {
return nil, diags
}
data.oauth2ClientCredentialsModel = clientCredentials
var r awstypes.Oauth2ProviderConfigInputMemberLinkedinOauth2ProviderConfig
smerr.AddEnrich(ctx, &diags, fwflex.Expand(ctx, data, &r.Value))
if diags.HasError() {
return nil, diags
}
return &r, diags

case !m.MicrosoftOAuth2ProviderConfig.IsNull():
data, d := m.MicrosoftOAuth2ProviderConfig.ToPtr(ctx)
smerr.AddEnrich(ctx, &diags, d)
Expand Down Expand Up @@ -678,6 +767,14 @@ func (m *oauth2ProviderConfigModel) clientCredentials(ctx context.Context) (oaut
var diags diag.Diagnostics

switch {
case !m.AtlassianOAuth2ProviderConfig.IsNull():
v, d := m.AtlassianOAuth2ProviderConfig.ToPtr(ctx)
diags.Append(d...)
if diags.HasError() {
return inttypes.Zero[oauth2ClientCredentialsModel](), diags
}
return v.oauth2ClientCredentialsModel, diags

case !m.CustomOAuth2ProviderConfig.IsNull():
v, d := m.CustomOAuth2ProviderConfig.ToPtr(ctx)
diags.Append(d...)
Expand All @@ -702,6 +799,14 @@ func (m *oauth2ProviderConfigModel) clientCredentials(ctx context.Context) (oaut
}
return v.oauth2ClientCredentialsModel, diags

case !m.LinkedinOAuth2ProviderConfig.IsNull():
v, d := m.LinkedinOAuth2ProviderConfig.ToPtr(ctx)
diags.Append(d...)
if diags.HasError() {
return inttypes.Zero[oauth2ClientCredentialsModel](), diags
}
return v.oauth2ClientCredentialsModel, diags

case !m.MicrosoftOAuth2ProviderConfig.IsNull():
v, d := m.MicrosoftOAuth2ProviderConfig.ToPtr(ctx)
diags.Append(d...)
Expand Down Expand Up @@ -731,11 +836,18 @@ func (m *oauth2ProviderConfigModel) clientCredentials(ctx context.Context) (oaut
}

type oauth2ClientCredentialsModel struct {
ClientCredentialsWOVersion types.Int64 `tfsdk:"client_credentials_wo_version"`
ClientID types.String `tfsdk:"client_id"`
ClientIDWO types.String `tfsdk:"client_id_wo"`
ClientSecret types.String `tfsdk:"client_secret"`
ClientSecretWO types.String `tfsdk:"client_secret_wo"`
ClientCredentialsWOVersion types.Int64 `tfsdk:"client_credentials_wo_version"`
ClientID types.String `tfsdk:"client_id"`
ClientIDWO types.String `tfsdk:"client_id_wo"`
ClientSecret types.String `tfsdk:"client_secret"`
ClientSecretConfig fwtypes.ListNestedObjectValueOf[oauth2SecretReferenceModel] `tfsdk:"client_secret_config"`
ClientSecretSource fwtypes.StringEnum[awstypes.SecretSourceType] `tfsdk:"client_secret_source"`
ClientSecretWO types.String `tfsdk:"client_secret_wo"`
}

type oauth2SecretReferenceModel struct {
JSONKey types.String `tfsdk:"json_key"`
SecretID types.String `tfsdk:"secret_id"`
}

type oauth2DiscoveryModel struct {
Expand All @@ -748,11 +860,21 @@ type customOAuth2ProviderConfigModel struct {
OAuthDiscovery fwtypes.ListNestedObjectValueOf[oauth2DiscoveryModel] `tfsdk:"oauth_discovery"`
}

type atlassianOAuth2ProviderConfigModel struct {
oauth2ClientCredentialsModel
OAuthDiscovery fwtypes.ListNestedObjectValueOf[oauth2DiscoveryModel] `tfsdk:"oauth_discovery"`
}

type githubOAuth2ProviderConfigModel struct {
oauth2ClientCredentialsModel
OAuthDiscovery fwtypes.ListNestedObjectValueOf[oauth2DiscoveryModel] `tfsdk:"oauth_discovery"`
}

type linkedinOAuth2ProviderConfigModel struct {
oauth2ClientCredentialsModel
OAuthDiscovery fwtypes.ListNestedObjectValueOf[oauth2DiscoveryModel] `tfsdk:"oauth_discovery"`
}

type googleOAuth2ProviderConfigModel struct {
oauth2ClientCredentialsModel
OAuthDiscovery fwtypes.ListNestedObjectValueOf[oauth2DiscoveryModel] `tfsdk:"oauth_discovery"`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,79 @@ func TestAccBedrockAgentCoreOAuth2CredentialProvider_full(t *testing.T) {
})
}

func TestAccBedrockAgentCoreOAuth2CredentialProvider_atlassian(t *testing.T) {
ctx := acctest.Context(t)
var oauth2credentialprovider bedrockagentcorecontrol.GetOauth2CredentialProviderOutput
rName := acctest.RandomWithPrefix(t, acctest.ResourcePrefix)
resourceName := "aws_bedrockagentcore_oauth2_credential_provider.test"

acctest.ParallelTest(ctx, t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(ctx, t)
acctest.PreCheckPartitionHasService(t, names.BedrockEndpointID)
testAccPreCheckOAuth2CredentialProviders(ctx, t)
},
ErrorCheck: acctest.ErrorCheck(t, names.BedrockAgentCoreServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckOAuth2CredentialProviderDestroy(ctx, t),
Steps: []resource.TestStep{
{
Config: testAccOAuth2CredentialProviderConfig_predefined(rName, "atlassian_oauth2_provider_config", "AtlassianOauth2"),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckOAuth2CredentialProviderExists(ctx, t, resourceName, &oauth2credentialprovider),
),
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate),
},
},
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateIdFunc: acctest.AttrImportStateIdFunc(resourceName, names.AttrName),
ImportStateVerify: true,
ImportStateVerifyIdentifierAttribute: names.AttrName,
ImportStateVerifyIgnore: []string{
"oauth2_provider_config.0.atlassian_oauth2_provider_config.0.client_secret",
"oauth2_provider_config.0.atlassian_oauth2_provider_config.0.client_id",
},
},
},
})
}

func TestAccBedrockAgentCoreOAuth2CredentialProvider_linkedin(t *testing.T) {
ctx := acctest.Context(t)
var oauth2credentialprovider bedrockagentcorecontrol.GetOauth2CredentialProviderOutput
rName := acctest.RandomWithPrefix(t, acctest.ResourcePrefix)
resourceName := "aws_bedrockagentcore_oauth2_credential_provider.test"

acctest.ParallelTest(ctx, t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(ctx, t)
acctest.PreCheckPartitionHasService(t, names.BedrockEndpointID)
testAccPreCheckOAuth2CredentialProviders(ctx, t)
},
ErrorCheck: acctest.ErrorCheck(t, names.BedrockAgentCoreServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckOAuth2CredentialProviderDestroy(ctx, t),
Steps: []resource.TestStep{
{
Config: testAccOAuth2CredentialProviderConfig_predefined(rName, "linkedin_oauth2_provider_config", "LinkedinOauth2"),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckOAuth2CredentialProviderExists(ctx, t, resourceName, &oauth2credentialprovider),
),
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectResourceAction(resourceName, plancheck.ResourceActionCreate),
},
},
},
},
})
}

func TestAccBedrockAgentCoreOAuth2CredentialProvider_tags(t *testing.T) {
ctx := acctest.Context(t)
var oauth2credentialprovider bedrockagentcorecontrol.GetOauth2CredentialProviderOutput
Expand Down Expand Up @@ -399,6 +472,22 @@ resource "aws_bedrockagentcore_oauth2_credential_provider" "test" {
`, rName)
}

func testAccOAuth2CredentialProviderConfig_predefined(rName, blockName, vendor string) string {
return fmt.Sprintf(`
resource "aws_bedrockagentcore_oauth2_credential_provider" "test" {
name = %[1]q

credential_provider_vendor = %[3]q
oauth2_provider_config {
%[2]s {
client_id = "test-client-id"
client_secret = "test-client-secret"
}
}
}
`, rName, blockName, vendor)
}

func testAccOAuth2CredentialProviderConfig_customWithDiscoveryURL(rName, clientId, clientSecret string, version int, discoveryURL string) string {
return fmt.Sprintf(`
resource "aws_bedrockagentcore_oauth2_credential_provider" "test" {
Expand Down
Loading
Loading