Skip to content

Commit

Permalink
New resource: azurerm_federated_identity_credential (#19199)
Browse files Browse the repository at this point in the history
Fixes #18617
  • Loading branch information
favoretti authored Nov 10, 2022
1 parent 52ff6b0 commit 529c6b4
Show file tree
Hide file tree
Showing 4 changed files with 413 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
package managedidentity

import (
"context"
"fmt"
"time"

"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonids"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
"github.com/hashicorp/go-azure-sdk/resource-manager/managedidentity/2022-01-31-preview/managedidentities"
"github.com/hashicorp/terraform-provider-azurerm/internal/sdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
)

var _ sdk.Resource = FederatedIdentityCredentialResource{}

type FederatedIdentityCredentialResource struct{}

func (r FederatedIdentityCredentialResource) ModelObject() interface{} {
return &FederatedIdentityCredentialResourceSchema{}
}

type FederatedIdentityCredentialResourceSchema struct {
Audience []string `tfschema:"audience"`
Issuer string `tfschema:"issuer"`
Name string `tfschema:"name"`
ResourceGroupName string `tfschema:"resource_group_name"`
ResourceName string `tfschema:"parent_id"`
Subject string `tfschema:"subject"`
}

func (r FederatedIdentityCredentialResource) IDValidationFunc() pluginsdk.SchemaValidateFunc {
return managedidentities.ValidateFederatedIdentityCredentialID
}
func (r FederatedIdentityCredentialResource) ResourceType() string {
return "azurerm_federated_identity_credential"
}
func (r FederatedIdentityCredentialResource) Arguments() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{
"audience": {
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
ForceNew: true,
Required: true,
Type: pluginsdk.TypeList,
MaxItems: 1,
},
"issuer": {
ForceNew: true,
Required: true,
Type: pluginsdk.TypeString,
},
"name": {
ForceNew: true,
Required: true,
Type: pluginsdk.TypeString,
},
"resource_group_name": commonschema.ResourceGroupName(),
"parent_id": {
ForceNew: true,
Required: true,
Type: pluginsdk.TypeString,
},
"subject": {
ForceNew: true,
Required: true,
Type: pluginsdk.TypeString,
},
}
}
func (r FederatedIdentityCredentialResource) Attributes() map[string]*pluginsdk.Schema {
return map[string]*pluginsdk.Schema{}
}
func (r FederatedIdentityCredentialResource) Create() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.ManagedIdentity.ManagedIdentities

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

subscriptionId := metadata.Client.Account.SubscriptionId
parentId, err := commonids.ParseUserAssignedIdentityID(config.ResourceName)
if err != nil {
return fmt.Errorf("parsing parent resource ID: %+v", err)
}
id := managedidentities.NewFederatedIdentityCredentialID(subscriptionId, config.ResourceGroupName, parentId.ResourceName, config.Name)

existing, err := client.FederatedIdentityCredentialsGet(ctx, id)
if err != nil {
if !response.WasNotFound(existing.HttpResponse) {
return fmt.Errorf("checking for the presence of an existing %s: %+v", id, err)
}
}
if !response.WasNotFound(existing.HttpResponse) {
return metadata.ResourceRequiresImport(r.ResourceType(), id)
}

var payload managedidentities.FederatedIdentityCredential
if err := r.mapFederatedIdentityCredentialResourceSchemaToFederatedIdentityCredential(config, &payload); err != nil {
return fmt.Errorf("mapping schema model to sdk model: %+v", err)
}

if _, err := client.FederatedIdentityCredentialsCreateOrUpdate(ctx, id, payload); err != nil {
return fmt.Errorf("creating %s: %+v", id, err)
}

metadata.SetID(id)
return nil
},
}
}
func (r FederatedIdentityCredentialResource) Read() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 5 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.ManagedIdentity.ManagedIdentities
schema := FederatedIdentityCredentialResourceSchema{}

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

resp, err := client.FederatedIdentityCredentialsGet(ctx, *id)
if err != nil {
if response.WasNotFound(resp.HttpResponse) {
return metadata.MarkAsGone(*id)
}
return fmt.Errorf("retrieving %s: %+v", *id, err)
}

if model := resp.Model; model != nil {
schema.Name = id.FederatedIdentityCredentialResourceName
schema.ResourceGroupName = id.ResourceGroupName
parentId := commonids.NewUserAssignedIdentityID(id.SubscriptionId, id.ResourceGroupName, id.ResourceName)
schema.ResourceName = parentId.ID()
if err := r.mapFederatedIdentityCredentialToFederatedIdentityCredentialResourceSchema(*model, &schema); err != nil {
return fmt.Errorf("flattening model: %+v", err)
}
}

return metadata.Encode(&schema)
},
}
}
func (r FederatedIdentityCredentialResource) Delete() sdk.ResourceFunc {
return sdk.ResourceFunc{
Timeout: 30 * time.Minute,
Func: func(ctx context.Context, metadata sdk.ResourceMetaData) error {
client := metadata.Client.ManagedIdentity.ManagedIdentities

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

if _, err := client.FederatedIdentityCredentialsDelete(ctx, *id); err != nil {
return fmt.Errorf("deleting %s: %+v", *id, err)
}

return nil
},
}
}

func (r FederatedIdentityCredentialResource) mapFederatedIdentityCredentialResourceSchemaToFederatedIdentityCredentialProperties(input FederatedIdentityCredentialResourceSchema, output *managedidentities.FederatedIdentityCredentialProperties) error {

audiences := make([]string, 0)
for _, v := range input.Audience {
audiences = append(audiences, v)
}
output.Audiences = audiences

output.Issuer = input.Issuer
output.Subject = input.Subject
return nil
}

func (r FederatedIdentityCredentialResource) mapFederatedIdentityCredentialPropertiesToFederatedIdentityCredentialResourceSchema(input managedidentities.FederatedIdentityCredentialProperties, output *FederatedIdentityCredentialResourceSchema) error {

audiences := make([]string, 0)
for _, v := range input.Audiences {
audiences = append(audiences, v)
}
output.Audience = audiences

output.Issuer = input.Issuer
output.Subject = input.Subject
return nil
}

func (r FederatedIdentityCredentialResource) mapFederatedIdentityCredentialResourceSchemaToFederatedIdentityCredential(input FederatedIdentityCredentialResourceSchema, output *managedidentities.FederatedIdentityCredential) error {

if output.Properties == nil {
output.Properties = &managedidentities.FederatedIdentityCredentialProperties{}
}
if err := r.mapFederatedIdentityCredentialResourceSchemaToFederatedIdentityCredentialProperties(input, output.Properties); err != nil {
return fmt.Errorf("mapping Schema to SDK Field %q / Model %q: %+v", "FederatedIdentityCredentialProperties", "Properties", err)
}

return nil
}

func (r FederatedIdentityCredentialResource) mapFederatedIdentityCredentialToFederatedIdentityCredentialResourceSchema(input managedidentities.FederatedIdentityCredential, output *FederatedIdentityCredentialResourceSchema) error {

if input.Properties == nil {
input.Properties = &managedidentities.FederatedIdentityCredentialProperties{}
}
if err := r.mapFederatedIdentityCredentialPropertiesToFederatedIdentityCredentialResourceSchema(*input.Properties, output); err != nil {
return fmt.Errorf("mapping SDK Field %q / Model %q to Schema: %+v", "FederatedIdentityCredentialProperties", "Properties", err)
}

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package managedidentity_test

import (
"context"
"fmt"
"testing"

"github.com/hashicorp/go-azure-sdk/resource-manager/managedidentity/2022-01-31-preview/managedidentities"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/utils"
)

type FederatedIdentityCredentialTestResource struct{}

func TestAccFederatedIdentityCredential_basic(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_federated_identity_credential", "test")
r := FederatedIdentityCredentialTestResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func TestAccFederatedIdentityCredential_requiresImport(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_federated_identity_credential", "test")
r := FederatedIdentityCredentialTestResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.basic(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.RequiresImportErrorStep(r.requiresImport),
})
}

func (r FederatedIdentityCredentialTestResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := managedidentities.ParseFederatedIdentityCredentialID(state.ID)
if err != nil {
return nil, err
}

resp, err := clients.ManagedIdentity.ManagedIdentities.FederatedIdentityCredentialsGet(ctx, *id)
if err != nil {
return nil, fmt.Errorf("reading %s: %+v", *id, err)
}

return utils.Bool(resp.Model != nil), nil
}
func (r FederatedIdentityCredentialTestResource) basic(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
resource "azurerm_federated_identity_credential" "test" {
audience = ["foo"]
issuer = "https://foo"
name = "acctest-${local.random_integer}"
resource_group_name = azurerm_resource_group.test.name
parent_id = azurerm_user_assigned_identity.test.id
subject = "foo"
}
`, r.template(data))
}

func (r FederatedIdentityCredentialTestResource) requiresImport(data acceptance.TestData) string {
return fmt.Sprintf(`
%s
resource "azurerm_federated_identity_credential" "import" {
audience = ["foo"]
issuer = "https://foo"
name = "acctest-${local.random_integer}"
resource_group_name = azurerm_resource_group.test.name
parent_id = azurerm_user_assigned_identity.test.id
subject = "foo"
}
`, r.basic(data))
}

func (r FederatedIdentityCredentialTestResource) template(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
locals {
random_integer = %[1]d
primary_location = %[2]q
}
resource "azurerm_resource_group" "test" {
name = "acctestrg-${local.random_integer}"
location = local.primary_location
}
resource "azurerm_user_assigned_identity" "test" {
location = azurerm_resource_group.test.location
name = "acctestuai-${local.random_integer}"
resource_group_name = azurerm_resource_group.test.name
}
`, data.RandomInteger, data.Locations.Primary)
}
6 changes: 5 additions & 1 deletion internal/services/managedidentity/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ func (r Registration) DataSources() []sdk.DataSource {
}

func (r Registration) Resources() []sdk.Resource {
return r.autoRegistration.Resources()
resources := []sdk.Resource{
FederatedIdentityCredentialResource{},
}
resources = append(resources, r.autoRegistration.Resources()...)
return resources
}

// WebsiteCategories returns a list of categories which can be used for the sidebar
Expand Down
Loading

0 comments on commit 529c6b4

Please sign in to comment.