Skip to content

Commit

Permalink
New Resource: azuread_application_fallback_public_client
Browse files Browse the repository at this point in the history
  • Loading branch information
manicminer committed Oct 17, 2023
1 parent dc109c8 commit 9b0213c
Show file tree
Hide file tree
Showing 4 changed files with 371 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package applications

import (
"context"
"fmt"
"net/http"
"time"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-sdk/sdk/odata"
"github.com/hashicorp/terraform-provider-azuread/internal/sdk"
"github.com/hashicorp/terraform-provider-azuread/internal/services/applications/parse"
"github.com/hashicorp/terraform-provider-azuread/internal/tf"
"github.com/hashicorp/terraform-provider-azuread/internal/tf/pluginsdk"
)

type ApplicationFallbackPublicClientModel struct {
ApplicationId string `tfschema:"application_id"`
Enabled bool `tfschema:"enabled"`
}

type ApplicationFallbackPublicClientResource struct{}

func (r ApplicationFallbackPublicClientResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return parse.ValidateFallbackPublicClientID
}

var _ sdk.Resource = ApplicationFallbackPublicClientResource{}

func (r ApplicationFallbackPublicClientResource) ResourceType() string {
return "azuread_application_fallback_public_client"
}

func (r ApplicationFallbackPublicClientResource) ModelObject() interface{} {
return &ApplicationFallbackPublicClientModel{}
}

func (r ApplicationFallbackPublicClientResource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"application_id": {
Description: "The resource ID of the application to which the fallback public client setting should be applied",
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: parse.ValidateApplicationID,
},

"enabled": {
Description: "Specifies explicitly whether the application is a public client. Appropriate for apps using token grant flows that don't use a redirect URI",
Type: pluginsdk.TypeBool,
Optional: true,
Default: false,
ForceNew: true,
},
}
}

func (r ApplicationFallbackPublicClientResource) Attributes() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{}
}

func (r ApplicationFallbackPublicClientResource) Create() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 10 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Applications.ApplicationsClient
client.BaseClient.DisableRetries = true
defer func() { client.BaseClient.DisableRetries = false }()

var model ApplicationFallbackPublicClientModel
if err := metadata.Decode(&model); err != nil {
return fmt.Errorf("decoding: %+v", err)
}

applicationId, err := parse.ParseApplicationID(model.ApplicationId)
if err != nil {
return err
}

id := parse.NewFallbackPublicClientID(applicationId.ApplicationId)

tf.LockByName(applicationResourceName, id.ApplicationId)
defer tf.UnlockByName(applicationResourceName, id.ApplicationId)

if _, err = client.SetFallbackPublicClient(ctx, id.ApplicationId, pointer.To(model.Enabled)); err != nil {
return fmt.Errorf("setting %s: %+v", id, err)
}

metadata.SetID(id)
return nil
},
}
}

func (r ApplicationFallbackPublicClientResource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Applications.ApplicationsClient
client.BaseClient.DisableRetries = true
defer func() { client.BaseClient.DisableRetries = false }()

id, err := parse.ParseFallbackPublicClientID(metadata.ResourceData.Id())
if err != nil {
return err
}

applicationId := parse.NewApplicationID(id.ApplicationId)

tf.LockByName(applicationResourceName, id.ApplicationId)
defer tf.UnlockByName(applicationResourceName, id.ApplicationId)

result, status, err := client.Get(ctx, id.ApplicationId, odata.Query{})
if err != nil {
if status == http.StatusNotFound {
return metadata.MarkAsGone(id)
}
return fmt.Errorf("retrieving %s: %+v", id, err)
}
if result == nil {
return fmt.Errorf("retrieving %s: result was nil", id)
}
if result.IsFallbackPublicClient == nil {
return metadata.MarkAsGone(id)
}

state := ApplicationFallbackPublicClientModel{
ApplicationId: applicationId.ID(),
Enabled: pointer.From(result.IsFallbackPublicClient),
}

return metadata.Encode(&state)
},
}
}

func (r ApplicationFallbackPublicClientResource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.Applications.ApplicationsClient
client.BaseClient.DisableRetries = true
defer func() { client.BaseClient.DisableRetries = false }()

id, err := parse.ParseFallbackPublicClientID(metadata.ResourceData.Id())
if err != nil {
return err
}

var model ApplicationFallbackPublicClientModel
if err := metadata.Decode(&model); err != nil {
return fmt.Errorf("decoding: %+v", err)
}

tf.LockByName(applicationResourceName, id.ApplicationId)
defer tf.UnlockByName(applicationResourceName, id.ApplicationId)

_, err = client.SetFallbackPublicClient(ctx, id.ApplicationId, nil)
if err != nil {
return fmt.Errorf("unsetting %s: %+v", id, err)
}

return nil
},
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package applications_test

import (
"context"
"fmt"
"net/http"
"testing"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-sdk/sdk/odata"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/hashicorp/terraform-provider-azuread/internal/acceptance"
"github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check"
"github.com/hashicorp/terraform-provider-azuread/internal/clients"
"github.com/hashicorp/terraform-provider-azuread/internal/services/applications/parse"
)

type ApplicationFallbackPublicClientResource struct{}

func TestAccApplicationFallbackPublicClient_basic(t *testing.T) {
data := acceptance.BuildTestData(t, "azuread_application_fallback_public_client", "test")
r := ApplicationFallbackPublicClientResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.enabled(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("application_id").Exists(),
check.That(data.ResourceName).Key("enabled").HasValue("true"),
),
},
data.ImportStep(),
})
}

func TestAccApplicationFallbackPublicClient_update(t *testing.T) {
data := acceptance.BuildTestData(t, "azuread_application_fallback_public_client", "test")
r := ApplicationFallbackPublicClientResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.disabled(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("application_id").Exists(),
check.That(data.ResourceName).Key("enabled").HasValue("false"),
),
},
data.ImportStep(),
{
Config: r.enabled(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("application_id").Exists(),
check.That(data.ResourceName).Key("enabled").HasValue("true"),
),
},
data.ImportStep(),
{
Config: r.disabled(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("application_id").Exists(),
check.That(data.ResourceName).Key("enabled").HasValue("false"),
),
},
data.ImportStep(),
})
}

func (r ApplicationFallbackPublicClientResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) {
client := clients.Applications.ApplicationsClient
client.BaseClient.DisableRetries = true
defer func() { client.BaseClient.DisableRetries = false }()

id, err := parse.ParseFallbackPublicClientID(state.ID)
if err != nil {
return nil, err
}

result, status, err := client.Get(ctx, id.ApplicationId, odata.Query{})
if err != nil {
if status == http.StatusNotFound {
return pointer.To(false), nil
}
return nil, fmt.Errorf("retrieving %s: %+v", id, err)
}
if result == nil {
return nil, fmt.Errorf("retrieving %s: result was nil", id)
}

if result.IsFallbackPublicClient == nil {
return pointer.To(false), nil
}

return pointer.To(true), nil
}

func (ApplicationFallbackPublicClientResource) enabled(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azuread" {}
resource "azuread_application_registration" "test" {
display_name = "acctest-FallbackPublicClient-%[1]d"
}
resource "azuread_application_fallback_public_client" "test" {
application_id = azuread_application_registration.test.id
enabled = true
}
`, data.RandomInteger, data.RandomID)
}

func (ApplicationFallbackPublicClientResource) disabled(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azuread" {}
resource "azuread_application_registration" "test" {
display_name = "acctest-FallbackPublicClient-%[1]d"
}
resource "azuread_application_fallback_public_client" "test" {
application_id = azuread_application_registration.test.id
enabled = false
}
`, data.RandomInteger)
}
70 changes: 70 additions & 0 deletions internal/services/applications/parse/fallback_public_client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package parse

import (
"fmt"

"github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids"
"github.com/hashicorp/terraform-provider-azuread/internal/tf/validation"
)

type FallbackPublicClientId struct {
ApplicationId string
}

func NewFallbackPublicClientID(applicationId string) FallbackPublicClientId {
return FallbackPublicClientId{
ApplicationId: applicationId,
}
}

// ParseFallbackPublicClientID parses 'input' into an FallbackPublicClientId
func ParseFallbackPublicClientID(input string) (*FallbackPublicClientId, error) {
parser := resourceids.NewParserFromResourceIdType(FallbackPublicClientId{})
parsed, err := parser.Parse(input, false)
if err != nil {
return nil, fmt.Errorf("parsing %q: %+v", input, err)
}

var ok bool
id := FallbackPublicClientId{}

if id.ApplicationId, ok = parsed.Parsed["applicationId"]; !ok {
return nil, resourceids.NewSegmentNotSpecifiedError(id, "applicationId", *parsed)
}

return &id, nil
}

// ValidateFallbackPublicClientID checks that 'input' can be parsed as an Application ID
func ValidateFallbackPublicClientID(input interface{}, key string) (warnings []string, errors []error) {
v, ok := input.(string)
if !ok {
errors = append(errors, fmt.Errorf("expected %q to be a string", key))
return
}

id, err := ParseFallbackPublicClientID(v)
if err != nil {
errors = append(errors, err)
}

return validation.IsUUID(id.ApplicationId, "ID")
}

func (id FallbackPublicClientId) ID() string {
fmtString := "/applications/%s/fallbackPublicClient"
return fmt.Sprintf(fmtString, id.ApplicationId)
}

// Segments returns a slice of Resource ID Segments which comprise this B 2 C Directory ID
func (id FallbackPublicClientId) Segments() []resourceids.Segment {
return []resourceids.Segment{
resourceids.StaticSegment("applications", "applications", "applications"),
resourceids.UserSpecifiedSegment("applicationId", "00000000-0000-0000-0000-000000000000"),
resourceids.StaticSegment("fallbackPublicClient", "fallbackPublicClient", "fallbackPublicClient"),
}
}

func (id FallbackPublicClientId) String() string {
return fmt.Sprintf("Fallback Public Client (Application ID: %q)", id.ApplicationId)
}
1 change: 1 addition & 0 deletions internal/services/applications/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func (r Registration) DataSources() []sdk.DataSource {
func (r Registration) Resources() []sdk.Resource {
return []sdk.Resource{
ApplicationAppRoleResource{},
ApplicationFallbackPublicClientResource{},
ApplicationPermissionScopeResource{},
ApplicationRegistrationResource{},
}
Expand Down

0 comments on commit 9b0213c

Please sign in to comment.