diff --git a/.changelog/38084.txt b/.changelog/38084.txt new file mode 100644 index 000000000000..bd603d840b95 --- /dev/null +++ b/.changelog/38084.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +aws_appfabric_app_authorization_connection +``` \ No newline at end of file diff --git a/internal/service/appfabric/app_authorization.go b/internal/service/appfabric/app_authorization.go index 8b8f2091ec37..537aa747c034 100644 --- a/internal/service/appfabric/app_authorization.go +++ b/internal/service/appfabric/app_authorization.go @@ -25,7 +25,6 @@ import ( "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" - "github.com/hashicorp/terraform-provider-aws/internal/create" "github.com/hashicorp/terraform-provider-aws/internal/enum" "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" @@ -50,17 +49,13 @@ func newAppAuthorizationResource(_ context.Context) (resource.ResourceWithConfig return r, nil } -const ( - ResNameAppAuthorization = "App Authorization" -) - type appAuthorizationResource struct { framework.ResourceWithConfigure framework.WithTimeouts framework.WithImportByID } -func (r *appAuthorizationResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { +func (*appAuthorizationResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { response.TypeName = "aws_appfabric_app_authorization" } @@ -93,9 +88,6 @@ func (r *appAuthorizationResource) Schema(ctx context.Context, request resource. }, "auth_url": schema.StringAttribute{ Computed: true, - PlanModifiers: []planmodifier.String{ - stringplanmodifier.UseStateForUnknown(), - }, }, names.AttrCreatedAt: schema.StringAttribute{ CustomType: timetypes.RFC3339Type{}, @@ -229,36 +221,35 @@ func (r *appAuthorizationResource) Create(ctx context.Context, request resource. input.Tags = getTagsIn(ctx) output, err := conn.CreateAppAuthorization(ctx, input) + if err != nil { - response.Diagnostics.AddError( - create.ProblemStandardMessage(names.AppFabric, create.ErrActionCreating, ResNameAppAuthorization, data.ID.String(), err), - err.Error(), - ) + response.Diagnostics.AddError(fmt.Sprintf("creating AppFabric App (%s) Authorization", data.App.ValueString()), err.Error()) + return } // Set values for unknowns. - appAuth := output.AppAuthorization - data.AppAuthorizationARN = fwflex.StringToFramework(ctx, appAuth.AppAuthorizationArn) + data.AppAuthorizationARN = fwflex.StringToFramework(ctx, output.AppAuthorization.AppAuthorizationArn) data.setID() - aAuth, err := waitAppAuthorizationCreated(ctx, conn, data.AppAuthorizationARN.ValueString(), data.AppBundleARN.ValueString(), r.CreateTimeout(ctx, data.Timeouts)) + appAuthorization, err := waitAppAuthorizationCreated(ctx, conn, data.AppAuthorizationARN.ValueString(), data.AppBundleARN.ValueString(), r.CreateTimeout(ctx, data.Timeouts)) + if err != nil { - response.Diagnostics.AddError(fmt.Sprintf("waiting for App Fabric App Authorization (%s) to be created", data.AppAuthorizationARN.ValueString()), err.Error()) + response.Diagnostics.AddError(fmt.Sprintf("waiting for AppFabric App Authorization (%s) create", data.ID.ValueString()), err.Error()) return } - // Set values for unknowns after creation is complete.I - data.Persona = fwflex.StringValueToFramework(ctx, aAuth.Persona) - data.AuthUrl = fwflex.StringToFramework(ctx, aAuth.AuthUrl) + // Set values for unknowns after creation is complete. + data.AuthURL = fwflex.StringToFramework(ctx, appAuthorization.AuthUrl) if err := data.parseAuthURL(); err != nil { response.Diagnostics.AddError("parsing Auth URL", err.Error()) return } - data.CreatedAt = fwflex.TimeToFramework(ctx, aAuth.CreatedAt) - data.UpdatedAt = fwflex.TimeToFramework(ctx, aAuth.UpdatedAt) + data.CreatedAt = fwflex.TimeToFramework(ctx, appAuthorization.CreatedAt) + data.Persona = fwflex.StringValueToFramework(ctx, appAuthorization.Persona) + data.UpdatedAt = fwflex.TimeToFramework(ctx, appAuthorization.UpdatedAt) response.Diagnostics.Append(response.State.Set(ctx, data)...) } @@ -288,7 +279,7 @@ func (r *appAuthorizationResource) Read(ctx context.Context, request resource.Re } if err != nil { - response.Diagnostics.AddError(fmt.Sprintf("reading App Fabric AppAuthorization ID (%s)", data.AppAuthorizationARN.ValueString()), err.Error()) + response.Diagnostics.AddError(fmt.Sprintf("reading AppFabric App Authorization (%s)", data.ID.ValueString()), err.Error()) return } @@ -298,8 +289,8 @@ func (r *appAuthorizationResource) Read(ctx context.Context, request resource.Re return } - //Seting it because of the dynamic nature of Auth Url - data.AuthUrl = fwflex.StringToFramework(ctx, output.AuthUrl) + // Setting it because of the dynamic nature of Auth URL. + data.AuthURL = fwflex.StringToFramework(ctx, output.AuthUrl) if err := data.parseAuthURL(); err != nil { response.Diagnostics.AddError("parsing Auth URL", err.Error()) @@ -323,7 +314,7 @@ func (r *appAuthorizationResource) Update(ctx context.Context, request resource. conn := r.Meta().AppFabricClient(ctx) // Check if updates are necessary based on the changed attributes - if !old.Credential.Equal(new.Credential) || !old.Tenant.Equal(new.Tenant) || !new.Tags.Equal(old.Tags) { + if !old.Credential.Equal(new.Credential) || !old.Tenant.Equal(new.Tenant) { var credentialsData []credentialModel response.Diagnostics.Append(new.Credential.ElementsAs(ctx, &credentialsData, false)...) if response.Diagnostics.HasError() { @@ -336,45 +327,42 @@ func (r *appAuthorizationResource) Update(ctx context.Context, request resource. return } - input := &appfabric.UpdateAppAuthorizationInput{ - AppAuthorizationIdentifier: aws.String(new.AppAuthorizationARN.ValueString()), - AppBundleIdentifier: aws.String(new.AppBundleARN.ValueString()), - } + input := &appfabric.UpdateAppAuthorizationInput{} response.Diagnostics.Append(fwflex.Expand(ctx, new, input)...) if response.Diagnostics.HasError() { return } + input.AppAuthorizationIdentifier = fwflex.StringFromFramework(ctx, new.AppAuthorizationARN) + input.AppBundleIdentifier = fwflex.StringFromFramework(ctx, new.AppBundleARN) input.Credential = credential _, err := conn.UpdateAppAuthorization(ctx, input) + if err != nil { - response.Diagnostics.AddError( - "Failed to update App Fabric App Authorization", - fmt.Sprintf("Error updating AppAuthorization with ID %s: %s", new.AppAuthorizationARN.String(), err.Error()), - ) + response.Diagnostics.AddError(fmt.Sprintf("updating AppFabric App Authorization (%s)", new.ID.ValueString()), err.Error()) + return } - appAuth, err := waitAppAuthorizationUpdated(ctx, conn, new.AppAuthorizationARN.ValueString(), new.AppBundleARN.ValueString(), r.UpdateTimeout(ctx, new.Timeouts)) + appAuthorization, err := waitAppAuthorizationUpdated(ctx, conn, new.AppAuthorizationARN.ValueString(), new.AppBundleARN.ValueString(), r.UpdateTimeout(ctx, new.Timeouts)) + if err != nil { - response.Diagnostics.AddError( - "Failed to fetch App Fabric App Authorization after update", - fmt.Sprintf("Error reading AppAuthorization with ARN %s post update: %s", new.AppAuthorizationARN.ValueString(), err.Error()), - ) + response.Diagnostics.AddError(fmt.Sprintf("waiting for AppFabric App Authorization (%s) update", new.ID.ValueString()), err.Error()) + return } - // Set values for unknowns after creation is complete. - new.UpdatedAt = fwflex.TimeToFramework(ctx, appAuth.UpdatedAt) - new.Persona = fwflex.StringValueToFramework(ctx, appAuth.Persona) - new.AuthUrl = fwflex.StringToFramework(ctx, appAuth.AuthUrl) + // Set values for unknowns. + new.AuthURL = fwflex.StringToFramework(ctx, appAuthorization.AuthUrl) if err := new.parseAuthURL(); err != nil { response.Diagnostics.AddError("parsing Auth URL", err.Error()) return } + new.UpdatedAt = fwflex.TimeToFramework(ctx, appAuthorization.UpdatedAt) } else { + new.AuthURL = old.AuthURL new.UpdatedAt = old.UpdatedAt } @@ -391,8 +379,8 @@ func (r *appAuthorizationResource) Delete(ctx context.Context, request resource. conn := r.Meta().AppFabricClient(ctx) _, err := conn.DeleteAppAuthorization(ctx, &appfabric.DeleteAppAuthorizationInput{ - AppAuthorizationIdentifier: aws.String(data.AppAuthorizationARN.ValueString()), - AppBundleIdentifier: aws.String(data.AppBundleARN.ValueString()), + AppAuthorizationIdentifier: fwflex.StringFromFramework(ctx, data.AppAuthorizationARN), + AppBundleIdentifier: fwflex.StringFromFramework(ctx, data.AppBundleARN), }) if errs.IsA[*awstypes.ResourceNotFoundException](err) { @@ -400,20 +388,62 @@ func (r *appAuthorizationResource) Delete(ctx context.Context, request resource. } if err != nil { - response.Diagnostics.AddError(fmt.Sprintf("deleting App Fabric AppAuthorizations (%s)", data.AppAuthorizationARN.ValueString()), err.Error()) + response.Diagnostics.AddError(fmt.Sprintf("deleting AppFabric App Authorization (%s)", data.ID.ValueString()), err.Error()) return } if _, err = waitAppAuthorizationDeleted(ctx, conn, data.AppAuthorizationARN.ValueString(), data.AppBundleARN.ValueString(), r.DeleteTimeout(ctx, data.Timeouts)); err != nil { - response.Diagnostics.AddError(fmt.Sprintf("waiting for App Fabric AppAuthenticator (%s) delete", data.AppAuthorizationARN.ValueString()), err.Error()) + response.Diagnostics.AddError(fmt.Sprintf("waiting for AppFabric AppAuthenticator (%s) delete", data.ID.ValueString()), err.Error()) return } } -func (r *appAuthorizationResource) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { - r.SetTagsAll(ctx, request, resp) +func (r *appAuthorizationResource) ModifyPlan(ctx context.Context, request resource.ModifyPlanRequest, response *resource.ModifyPlanResponse) { + r.SetTagsAll(ctx, request, response) +} + +func findAppAuthorizationByTwoPartKey(ctx context.Context, conn *appfabric.Client, appAuthorizationARN, appBundleIdentifier string) (*awstypes.AppAuthorization, error) { + in := &appfabric.GetAppAuthorizationInput{ + AppAuthorizationIdentifier: aws.String(appAuthorizationARN), + AppBundleIdentifier: aws.String(appBundleIdentifier), + } + + output, err := conn.GetAppAuthorization(ctx, in) + + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: in, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.AppAuthorization == nil { + return nil, tfresource.NewEmptyResultError(in) + } + + return output.AppAuthorization, nil +} + +func statusAppAuthorization(ctx context.Context, conn *appfabric.Client, appAuthorizationARN, appBundleIdentifier string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findAppAuthorizationByTwoPartKey(ctx, conn, appAuthorizationARN, appBundleIdentifier) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.Status), nil + } } func waitAppAuthorizationCreated(ctx context.Context, conn *appfabric.Client, appAuthorizationARN, appBundleIdentifier string, timeout time.Duration) (*awstypes.AppAuthorization, error) { @@ -467,48 +497,6 @@ func waitAppAuthorizationDeleted(ctx context.Context, conn *appfabric.Client, ap return nil, err } -func statusAppAuthorization(ctx context.Context, conn *appfabric.Client, appAuthorizationARN, appBundleIdentifier string) retry.StateRefreshFunc { - return func() (interface{}, string, error) { - output, err := findAppAuthorizationByTwoPartKey(ctx, conn, appAuthorizationARN, appBundleIdentifier) - - if tfresource.NotFound(err) { - return nil, "", nil - } - - if err != nil { - return nil, "", err - } - - return output, string(output.Status), nil - } -} - -func findAppAuthorizationByTwoPartKey(ctx context.Context, conn *appfabric.Client, appAuthorizationARN, appBundleIdentifier string) (*awstypes.AppAuthorization, error) { - in := &appfabric.GetAppAuthorizationInput{ - AppAuthorizationIdentifier: aws.String(appAuthorizationARN), - AppBundleIdentifier: aws.String(appBundleIdentifier), - } - - output, err := conn.GetAppAuthorization(ctx, in) - - if errs.IsA[*awstypes.ResourceNotFoundException](err) { - return nil, &retry.NotFoundError{ - LastError: err, - LastRequest: in, - } - } - - if err != nil { - return nil, err - } - - if output == nil || output.AppAuthorization == nil { - return nil, tfresource.NewEmptyResultError(in) - } - - return output.AppAuthorization, nil -} - func expandCredentialsValue(ctx context.Context, credentialModels []credentialModel) (awstypes.Credential, diag.Diagnostics) { credentials := []awstypes.Credential{} var diags diag.Diagnostics @@ -561,7 +549,7 @@ type appAuthorizationResourceModel struct { AppAuthorizationARN types.String `tfsdk:"arn"` AppBundleARN fwtypes.ARN `tfsdk:"app_bundle_arn"` AuthType fwtypes.StringEnum[awstypes.AuthType] `tfsdk:"auth_type"` - AuthUrl types.String `tfsdk:"auth_url"` + AuthURL types.String `tfsdk:"auth_url"` CreatedAt timetypes.RFC3339 `tfsdk:"created_at"` Credential fwtypes.ListNestedObjectValueOf[credentialModel] `tfsdk:"credential"` ID types.String `tfsdk:"id"` @@ -613,11 +601,11 @@ type tenantModel struct { } func (m *appAuthorizationResourceModel) parseAuthURL() error { - if m.AuthUrl.IsNull() { + if m.AuthURL.IsNull() { return nil } - fullURL := m.AuthUrl.ValueString() + fullURL := m.AuthURL.ValueString() index := strings.Index(fullURL, "oauth2") if index == -1 { @@ -625,7 +613,7 @@ func (m *appAuthorizationResourceModel) parseAuthURL() error { } baseURL := fullURL[:index+len("oauth2")] - m.AuthUrl = types.StringValue(baseURL) + m.AuthURL = types.StringValue(baseURL) return nil } diff --git a/internal/service/appfabric/app_authorization_connection.go b/internal/service/appfabric/app_authorization_connection.go new file mode 100644 index 000000000000..6f04fcccf650 --- /dev/null +++ b/internal/service/appfabric/app_authorization_connection.go @@ -0,0 +1,291 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package appfabric + +import ( + "context" + "fmt" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/service/appfabric" + awstypes "github.com/aws/aws-sdk-go-v2/service/appfabric/types" + "github.com/hashicorp/terraform-plugin-framework-timeouts/resource/timeouts" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" + "github.com/hashicorp/terraform-provider-aws/internal/enum" + "github.com/hashicorp/terraform-provider-aws/internal/errs" + "github.com/hashicorp/terraform-provider-aws/internal/errs/fwdiag" + "github.com/hashicorp/terraform-provider-aws/internal/flex" + "github.com/hashicorp/terraform-provider-aws/internal/framework" + fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex" + fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types" + "github.com/hashicorp/terraform-provider-aws/internal/tfresource" + "github.com/hashicorp/terraform-provider-aws/names" +) + +// @FrameworkResource(name="App Authorization Connection") +func newAppAuthorizationConnectionResource(_ context.Context) (resource.ResourceWithConfigure, error) { + r := &appAuthorizationConnectionResource{} + + r.SetDefaultCreateTimeout(30 * time.Minute) + + return r, nil +} + +type appAuthorizationConnectionResource struct { + framework.ResourceWithConfigure + framework.WithNoUpdate + framework.WithNoOpDelete + framework.WithTimeouts +} + +func (*appAuthorizationConnectionResource) Metadata(_ context.Context, request resource.MetadataRequest, response *resource.MetadataResponse) { + response.TypeName = "aws_appfabric_app_authorization_connection" +} + +func (r *appAuthorizationConnectionResource) Schema(ctx context.Context, request resource.SchemaRequest, response *resource.SchemaResponse) { + response.Schema = schema.Schema{ + Attributes: map[string]schema.Attribute{ + "app": schema.StringAttribute{ + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "app_authorization_arn": schema.StringAttribute{ + CustomType: fwtypes.ARNType, + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "app_bundle_arn": schema.StringAttribute{ + CustomType: fwtypes.ARNType, + Required: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.RequiresReplace(), + }, + }, + "tenant": schema.ListAttribute{ + CustomType: fwtypes.NewListNestedObjectTypeOf[tenantModel](ctx), + Computed: true, + ElementType: types.ObjectType{ + AttrTypes: fwtypes.AttributeTypesMust[tenantModel](ctx), + }, + }, + names.AttrID: framework.IDAttribute(), + }, + Blocks: map[string]schema.Block{ + "auth_request": schema.ListNestedBlock{ + CustomType: fwtypes.NewListNestedObjectTypeOf[authRequestModel](ctx), + Validators: []validator.List{ + listvalidator.SizeAtMost(1), + }, + NestedObject: schema.NestedBlockObject{ + Attributes: map[string]schema.Attribute{ + "code": schema.StringAttribute{ + Required: true, + }, + "redirect_uri": schema.StringAttribute{ + Required: true, + }, + }, + }, + }, + names.AttrTimeouts: timeouts.Block(ctx, timeouts.Opts{ + Create: true, + }), + }, + } +} + +func (r *appAuthorizationConnectionResource) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) { + var data appAuthorizationConnectionResourceModel + response.Diagnostics.Append(request.Plan.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + + conn := r.Meta().AppFabricClient(ctx) + + input := &appfabric.ConnectAppAuthorizationInput{} + response.Diagnostics.Append(fwflex.Expand(ctx, data, input)...) + if response.Diagnostics.HasError() { + return + } + + input.AppBundleIdentifier = fwflex.StringFromFramework(ctx, data.AppBundleARN) + input.AppAuthorizationIdentifier = fwflex.StringFromFramework(ctx, data.AppAuthorizationARN) + + _, err := conn.ConnectAppAuthorization(ctx, input) + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("creating AppFabric App Authorization (%s) Connection", data.AppAuthorizationARN.ValueString()), err.Error()) + + return + } + + // Set values for unknowns. + data.setID() + + appAuthorization, err := waitConnectAppAuthorizationCreated(ctx, conn, data.AppAuthorizationARN.ValueString(), data.AppBundleARN.ValueString(), r.CreateTimeout(ctx, data.Timeouts)) + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("waiting for AppFabric App Authorization Connection (%s) create", data.ID.ValueString()), err.Error()) + + return + } + + // Set values for unknowns. + data.App = fwflex.StringToFramework(ctx, appAuthorization.App) + + var tenant tenantModel + response.Diagnostics.Append(fwflex.Flatten(ctx, appAuthorization.Tenant, &tenant)...) + if response.Diagnostics.HasError() { + return + } + data.Tenant = fwtypes.NewListNestedObjectValueOfPtrMust(ctx, &tenant) + + response.Diagnostics.Append(response.State.Set(ctx, data)...) +} + +func (r *appAuthorizationConnectionResource) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) { + var data appAuthorizationConnectionResourceModel + response.Diagnostics.Append(request.State.Get(ctx, &data)...) + if response.Diagnostics.HasError() { + return + } + + if err := data.InitFromID(); err != nil { + response.Diagnostics.AddError("parsing resource ID", err.Error()) + + return + } + + conn := r.Meta().AppFabricClient(ctx) + + output, err := findAppAuthorizationConnectionByTwoPartKey(ctx, conn, data.AppAuthorizationARN.ValueString(), data.AppBundleARN.ValueString()) + + if tfresource.NotFound(err) { + response.Diagnostics.Append(fwdiag.NewResourceNotFoundWarningDiagnostic(err)) + response.State.RemoveResource(ctx) + + return + } + + if err != nil { + response.Diagnostics.AddError(fmt.Sprintf("reading AppFabric App Authorization Connection (%s)", data.ID.ValueString()), err.Error()) + + return + } + + // Set values for unknowns. + response.Diagnostics.Append(fwflex.Flatten(ctx, output, &data)...) + if response.Diagnostics.HasError() { + return + } + + response.Diagnostics.Append(response.State.Set(ctx, &data)...) +} + +func findAppAuthorizationConnectionByTwoPartKey(ctx context.Context, conn *appfabric.Client, appAuthorizationARN, appBundleIdentifier string) (*awstypes.AppAuthorization, error) { + input := &appfabric.GetAppAuthorizationInput{ + AppAuthorizationIdentifier: aws.String(appAuthorizationARN), + AppBundleIdentifier: aws.String(appBundleIdentifier), + } + + output, err := conn.GetAppAuthorization(ctx, input) + + if errs.IsA[*awstypes.ResourceNotFoundException](err) { + return nil, &retry.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.AppAuthorization == nil { + return nil, tfresource.NewEmptyResultError(input) + } + + return output.AppAuthorization, nil +} + +func statusConnectAppAuthorization(ctx context.Context, conn *appfabric.Client, appAuthorizationARN, appBundleArn string) retry.StateRefreshFunc { + return func() (interface{}, string, error) { + output, err := findAppAuthorizationConnectionByTwoPartKey(ctx, conn, appAuthorizationARN, appBundleArn) + + if tfresource.NotFound(err) { + return nil, "", nil + } + + if err != nil { + return nil, "", err + } + + return output, string(output.Status), nil + } +} + +func waitConnectAppAuthorizationCreated(ctx context.Context, conn *appfabric.Client, appAuthorizationARN, appBundleArn string, timeout time.Duration) (*awstypes.AppAuthorization, error) { + stateConf := &retry.StateChangeConf{ + Pending: enum.Slice(awstypes.AppAuthorizationStatusPendingConnect), + Target: enum.Slice(awstypes.AppAuthorizationStatusConnected), + Refresh: statusConnectAppAuthorization(ctx, conn, appAuthorizationARN, appBundleArn), + Timeout: timeout, + } + + outputRaw, err := stateConf.WaitForStateContext(ctx) + + if out, ok := outputRaw.(*awstypes.AppAuthorization); ok { + return out, err + } + + return nil, err +} + +type appAuthorizationConnectionResourceModel struct { + App types.String `tfsdk:"app"` + AppAuthorizationARN fwtypes.ARN `tfsdk:"app_authorization_arn"` + AppBundleARN fwtypes.ARN `tfsdk:"app_bundle_arn"` + AuthRequest fwtypes.ListNestedObjectValueOf[authRequestModel] `tfsdk:"auth_request"` + ID types.String `tfsdk:"id"` + Tenant fwtypes.ListNestedObjectValueOf[tenantModel] `tfsdk:"tenant"` + Timeouts timeouts.Value `tfsdk:"timeouts"` +} + +const ( + appAuthorizationConnectionResourceIDPartCount = 2 +) + +func (m *appAuthorizationConnectionResourceModel) InitFromID() error { + parts, err := flex.ExpandResourceId(m.ID.ValueString(), appAuthorizationConnectionResourceIDPartCount, false) + if err != nil { + return err + } + + m.AppAuthorizationARN = fwtypes.ARNValue(parts[0]) + m.AppBundleARN = fwtypes.ARNValue(parts[1]) + + return nil +} + +func (m *appAuthorizationConnectionResourceModel) setID() { + m.ID = types.StringValue(errs.Must(flex.FlattenResourceId([]string{m.AppAuthorizationARN.ValueString(), m.AppBundleARN.ValueString()}, appAuthorizationConnectionResourceIDPartCount, false))) +} + +type authRequestModel struct { + Code types.String `tfsdk:"code"` + RedirectURI types.String `tfsdk:"redirect_uri"` +} diff --git a/internal/service/appfabric/app_authorization_connection_test.go b/internal/service/appfabric/app_authorization_connection_test.go new file mode 100644 index 000000000000..9a92ab134314 --- /dev/null +++ b/internal/service/appfabric/app_authorization_connection_test.go @@ -0,0 +1,171 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package appfabric_test + +import ( + "context" + "fmt" + "testing" + + sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-provider-aws/internal/acctest" + "github.com/hashicorp/terraform-provider-aws/internal/conns" + tfappfabric "github.com/hashicorp/terraform-provider-aws/internal/service/appfabric" + "github.com/hashicorp/terraform-provider-aws/names" +) + +func testAccAppAuthorizationConnection_basic(t *testing.T) { + ctx := acctest.Context(t) + resourceName := "aws_appfabric_app_authorization_connection.test" + appBudleResourceName := "aws_appfabric_app_bundle.test" + appAuthorization := "aws_appfabric_app_authorization.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + // See https://docs.aws.amazon.com/appfabric/latest/adminguide/terraform.html#terraform-appfabric-connecting. + tenantID := acctest.SkipIfEnvVarNotSet(t, "AWS_APPFABRIC_TERRAFORMCLOUD_TENANT_ID") + serviceAccountToken := acctest.SkipIfEnvVarNotSet(t, "AWS_APPFABRIC_TERRAFORMCLOUD_SERVICE_ACCOUNT_TOKEN") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckRegion(t, names.USEast1RegionID, names.APNortheast1RegionID, names.EUWest1RegionID) + }, + ErrorCheck: acctest.ErrorCheck(t, names.AppFabricServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: acctest.CheckDestroyNoop, + Steps: []resource.TestStep{ + { + Config: testAccAppAuthorizationConnectionConfig_basic(rName, tenantID, serviceAccountToken), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckAppAuthorizationConnectionExists(ctx, resourceName), + resource.TestCheckResourceAttrSet(resourceName, "app"), + resource.TestCheckResourceAttrPair(resourceName, "app_bundle_arn", appBudleResourceName, names.AttrARN), + resource.TestCheckResourceAttrPair(resourceName, "app_authorization_arn", appAuthorization, names.AttrARN), + resource.TestCheckResourceAttr(resourceName, "auth_request.#", acctest.Ct0), + resource.TestCheckResourceAttr(resourceName, "tenant.#", acctest.Ct1), + resource.TestCheckResourceAttr(resourceName, "tenant.0.tenant_display_name", rName), + resource.TestCheckResourceAttr(resourceName, "tenant.0.tenant_identifier", tenantID), + ), + }, + }, + }) +} +func testAccAppAuthorizationConnection_OAuth2(t *testing.T) { + acctest.Skip(t, "Currently not able to test") + + ctx := acctest.Context(t) + resourceName := "aws_appfabric_app_authorization_connection.test" + appBudleResourceName := "aws_appfabric_app_bundle.test" + appAuthorization := "aws_appfabric_app_authorization.test" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + acctest.PreCheck(ctx, t) + acctest.PreCheckRegion(t, names.USEast1RegionID, names.APNortheast1RegionID, names.EUWest1RegionID) + }, + ErrorCheck: acctest.ErrorCheck(t, names.AppFabricServiceID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: acctest.CheckDestroyNoop, + Steps: []resource.TestStep{ + { + Config: testAccAppAuthorizationConnectionConfig_OAuth2(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAppAuthorizationConnectionExists(ctx, resourceName), + resource.TestCheckResourceAttrPair(resourceName, "app_bundle_arn", appBudleResourceName, names.AttrARN), + resource.TestCheckResourceAttrPair(resourceName, "app_authorization_arn", appAuthorization, names.AttrARN), + ), + }, + }, + }) +} + +func testAccCheckAppAuthorizationConnectionExists(ctx context.Context, n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + conn := acctest.Provider.Meta().(*conns.AWSClient).AppFabricClient(ctx) + + _, err := tfappfabric.FindAppAuthorizationConnectionByTwoPartKey(ctx, conn, rs.Primary.Attributes["app_authorization_arn"], rs.Primary.Attributes["app_bundle_arn"]) + + return err + } +} + +func testAccAppAuthorizationConnectionConfig_basic(rName, tenantID, serviceAccountToken string) string { + return fmt.Sprintf(` +resource "aws_appfabric_app_bundle" "test" { + tags = { + Name = %[1]q + } +} + +resource "aws_appfabric_app_authorization" "test" { + app_bundle_arn = aws_appfabric_app_bundle.test.arn + app = "TERRAFORMCLOUD" + auth_type = "apiKey" + + credential { + api_key_credential { + api_key = %[3]q + } + } + + tenant { + tenant_display_name = %[1]q + tenant_identifier = %[2]q + } + + tags = { + Name = %[1]q + } +} + +resource "aws_appfabric_app_authorization_connection" "test" { + app_bundle_arn = aws_appfabric_app_bundle.test.arn + app_authorization_arn = aws_appfabric_app_authorization.test.arn +} +`, rName, tenantID, serviceAccountToken) +} + +func testAccAppAuthorizationConnectionConfig_OAuth2(rName string) string { + return fmt.Sprintf(` +resource "aws_appfabric_app_bundle" "test" { + tags = { + Name = %[1]q + } +} + +resource "aws_appfabric_app_authorization" "test" { + app_bundle_arn = aws_appfabric_app_bundle.test.arn + app = "DROPBOX" + auth_type = "oauth2" + + credential { + oauth2_credential { + client_id = "newClinentID" + client_secret = "newSecretforOath2" + } + } + tenant { + tenant_display_name = "test" + tenant_identifier = "test" + } +} + +resource "aws_appfabric_app_authorization_connection" "test" { + app_bundle_arn = aws_appfabric_app_bundle.test.arn + app_authorization_arn = aws_appfabric_app_authorization.test.arn + auth_request { + code = "testcode" + redirect_uri = aws_appfabric_app_authorization.test.auth_url + } + +} +`, rName) +} diff --git a/internal/service/appfabric/appfabric_test.go b/internal/service/appfabric/appfabric_test.go index 86cad1a8d7d8..b2c2d5096768 100644 --- a/internal/service/appfabric/appfabric_test.go +++ b/internal/service/appfabric/appfabric_test.go @@ -30,6 +30,10 @@ func TestAccAppFabric_serial(t *testing.T) { "oath2Update": testAccAppAuthorization_oath2Update, "tags": testAccAppAuthorization_tags, }, + "AppAuthorizationConnection": { + acctest.CtBasic: testAccAppAuthorizationConnection_basic, + "oath2Connect": testAccAppAuthorizationConnection_OAuth2, + }, } acctest.RunSerialTests2Levels(t, testCases, serializeDelay) diff --git a/internal/service/appfabric/exports_test.go b/internal/service/appfabric/exports_test.go index ff2fa01a037f..f8da47e994ed 100644 --- a/internal/service/appfabric/exports_test.go +++ b/internal/service/appfabric/exports_test.go @@ -5,9 +5,11 @@ package appfabric // Exports for use in tests only. var ( - ResourceAppAuthorization = newAppAuthorizationResource - ResourceAppBundle = newAppBundleResource + ResourceAppAuthorization = newAppAuthorizationResource + ResourceConnectAppAuthorization = newAppAuthorizationConnectionResource + ResourceAppBundle = newAppBundleResource - FindAppAuthorizationByTwoPartKey = findAppAuthorizationByTwoPartKey - FindAppBundleByID = findAppBundleByID + FindAppAuthorizationByTwoPartKey = findAppAuthorizationByTwoPartKey + FindAppAuthorizationConnectionByTwoPartKey = findAppAuthorizationConnectionByTwoPartKey + FindAppBundleByID = findAppBundleByID ) diff --git a/internal/service/appfabric/service_package_gen.go b/internal/service/appfabric/service_package_gen.go index d83176c47f42..7c96474456c0 100644 --- a/internal/service/appfabric/service_package_gen.go +++ b/internal/service/appfabric/service_package_gen.go @@ -20,6 +20,10 @@ func (p *servicePackage) FrameworkDataSources(ctx context.Context) []*types.Serv func (p *servicePackage) FrameworkResources(ctx context.Context) []*types.ServicePackageFrameworkResource { return []*types.ServicePackageFrameworkResource{ + { + Factory: newAppAuthorizationConnectionResource, + Name: "App Authorization Connection", + }, { Factory: newAppAuthorizationResource, Name: "App Authorization", diff --git a/website/docs/r/appfabric_app_authorization_connection.html.markdown b/website/docs/r/appfabric_app_authorization_connection.html.markdown new file mode 100644 index 000000000000..6a5932af3302 --- /dev/null +++ b/website/docs/r/appfabric_app_authorization_connection.html.markdown @@ -0,0 +1,48 @@ +--- +subcategory: "AppFabric" +layout: "aws" +page_title: "AWS: aws_appfabric_app_authorization_connection" +description: |- + Terraform resource for managing an AWS AppFabric App Authorization Connection. +--- + +# Resource: aws_appfabric_app_authorization_connection + +Terraform resource for managing an AWS AppFabric App Authorization Connection. + +## Example Usage + +### Basic Usage + +```terraform +resource "aws_appfabric_app_authorization_connection" "example" { + app_authorization_arn = aws_appfabric_app_authorization.test.arn + app_bundle_arn = aws_appfabric_app_bundle.arn +} +``` + +## Argument Reference + +This resource supports the following arguments: + +* `app_bundle_arn` - (Required) The Amazon Resource Name (ARN) of the app bundle to use for the request. +* `app_authorization_arn` - (Required) The Amazon Resource Name (ARN) or Universal Unique Identifier (UUID) of the app authorization to use for the request. +* `auth_request` - (Optional) Contains OAuth2 authorization information.This is required if the app authorization for the request is configured with an OAuth2 (oauth2) authorization type. + +Auth Request support the following: + +* `code` - (Required) The authorization code returned by the application after permission is granted in the application OAuth page (after clicking on the AuthURL).. +* `redirect_uri` - (Optional) The redirect URL that is specified in the AuthURL and the application client. + +## Attribute Reference + +This resource exports the following attributes in addition to the arguments above: + +* `app` - The name of the application. +* `tenant` - Contains information about an application tenant, such as the application display name and identifier. + +## Timeouts + +[Configuration options](https://developer.hashicorp.com/terraform/language/resources/syntax#operation-timeouts): + +* `create` - (Default `30m`)