Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,13 @@ The following arguments are supported:

* `application_id` - (Required) The resource ID of the application for which this federated identity credential should be created. Changing this field forces a new resource to be created.
* `audiences` - (Required) List of audiences that can appear in the external token. This specifies what should be accepted in the `aud` claim of incoming tokens.
* `claims_matching_expression` - (Optional) The expression that subjects will be matched against.
* `description` - (Optional) A description for the federated identity credential.
* `display_name` - (Required) A unique display name for the federated identity credential. Changing this forces a new resource to be created.
* `issuer` - (Required) The URL of the external identity provider, which must match the issuer claim of the external token being exchanged. The combination of the values of issuer and subject must be unique on the app.
* `subject` - (Required) The identifier of the external software workload within the external identity provider. The combination of issuer and subject must be unique on the app.
* `subject` - (Optional) The identifier of the external software workload within the external identity provider. The combination of issuer and subject must be unique on the app.

-> At least one of `subject` or `claims_matching_expression` must be specified.

## Attributes Reference

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ import (

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/applications/stable/application"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/applications/stable/federatedidentitycredential"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/stable"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/applications/beta/application"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/applications/beta/federatedidentitycredential"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta"
"github.com/hashicorp/go-azure-sdk/sdk/nullable"
"github.com/hashicorp/terraform-provider-azuread/internal/clients"
"github.com/hashicorp/terraform-provider-azuread/internal/helpers/consistency"
Expand Down Expand Up @@ -48,7 +48,7 @@ func applicationFederatedIdentityCredentialResource() *pluginsdk.Resource {
Type: pluginsdk.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: stable.ValidateApplicationID,
ValidateFunc: beta.ValidateApplicationID,
},

"audiences": {
Expand Down Expand Up @@ -78,9 +78,17 @@ func applicationFederatedIdentityCredentialResource() *pluginsdk.Resource {
},

"subject": {
Description: "The identifier of the external software workload within the external identity provider. The combination of issuer and subject must be unique on the app.",
Type: pluginsdk.TypeString,
Required: true,
Description: "The identifier of the external software workload within the external identity provider. The combination of issuer and subject must be unique on the app.",
Type: pluginsdk.TypeString,
Optional: true,
ExactlyOneOf: []string{"subject", "claims_matching_expression"},
},

"claims_matching_expression": {
Description: "The expression that subjects will be matched against.",
Type: pluginsdk.TypeString,
Optional: true,
ExactlyOneOf: []string{"subject", "claims_matching_expression"},
},

"description": {
Expand All @@ -99,10 +107,10 @@ func applicationFederatedIdentityCredentialResource() *pluginsdk.Resource {
}

func applicationFederatedIdentityCredentialResourceCreate(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) pluginsdk.Diagnostics { //nolint
client := meta.(*clients.Client).Applications.ApplicationClient
client := meta.(*clients.Client).Applications.ApplicationClientBeta
federatedIdentityCredentialClient := meta.(*clients.Client).Applications.ApplicationFederatedIdentityCredential

applicationId, err := stable.ParseApplicationID(d.Get("application_id").(string))
applicationId, err := beta.ParseApplicationID(d.Get("application_id").(string))
if err != nil {
return tf.ErrorDiagPathF(err, "application_id", "Parsing `application_id`")
}
Expand All @@ -120,12 +128,19 @@ func applicationFederatedIdentityCredentialResourceCreate(ctx context.Context, d
return tf.ErrorDiagF(errors.New("model was nil"), "retrieving %s", applicationId)
}

credential := stable.FederatedIdentityCredential{
credential := beta.FederatedIdentityCredential{
Audiences: tf.ExpandStringSlice(d.Get("audiences").([]interface{})),
Description: nullable.Value(d.Get("description").(string)),
Issuer: d.Get("issuer").(string),
Name: d.Get("display_name").(string),
Subject: d.Get("subject").(string),
Subject: nullable.Value(d.Get("subject").(string)),
}

if v, ok := d.GetOk("claims_matching_expression"); ok {
credential.ClaimsMatchingExpression = pointer.To(beta.FederatedIdentityExpression{
LanguageVersion: 1,
Value: v.(string),
})
}

federatedIdentityCredentialResp, err := federatedIdentityCredentialClient.CreateFederatedIdentityCredential(ctx, *applicationId, credential, federatedidentitycredential.DefaultCreateFederatedIdentityCredentialOperationOptions())
Expand All @@ -141,7 +156,7 @@ func applicationFederatedIdentityCredentialResourceCreate(ctx context.Context, d
return tf.ErrorDiagF(errors.New("nil or empty ID received"), "API error adding federated identity credential for %s", applicationId)
}

id := stable.NewApplicationIdFederatedIdentityCredentialID(applicationId.ApplicationId, *newCredential.Id)
id := beta.NewApplicationIdFederatedIdentityCredentialID(applicationId.ApplicationId, *newCredential.Id)

// Wait for the credential to replicate
timeout, _ := ctx.Deadline()
Expand Down Expand Up @@ -192,18 +207,25 @@ func applicationFederatedIdentityCredentialResourceUpdate(ctx context.Context, d
tf.LockByName(applicationResourceName, id.ObjectId)
defer tf.UnlockByName(applicationResourceName, id.ObjectId)

credential := stable.FederatedIdentityCredential{
credential := beta.FederatedIdentityCredential{
Id: pointer.To(id.KeyId),
Audiences: tf.ExpandStringSlice(d.Get("audiences").([]interface{})),
Description: nullable.Value(d.Get("description").(string)),
Issuer: d.Get("issuer").(string),
Subject: d.Get("subject").(string),
Subject: nullable.Value(d.Get("subject").(string)),

// Name is immutable but must be specified as it is a required field
Name: d.Get("display_name").(string),
}

credentialId := stable.NewApplicationIdFederatedIdentityCredentialID(id.ObjectId, id.KeyId)
if v, ok := d.GetOk("claims_matching_expression"); ok {
credential.ClaimsMatchingExpression = pointer.To(beta.FederatedIdentityExpression{
LanguageVersion: 1,
Value: v.(string),
})
}

credentialId := beta.NewApplicationIdFederatedIdentityCredentialID(id.ObjectId, id.KeyId)

if _, err = federatedIdentityCredentialClient.UpdateFederatedIdentityCredential(ctx, credentialId, credential, federatedidentitycredential.DefaultUpdateFederatedIdentityCredentialOperationOptions()); err != nil {
return tf.ErrorDiagF(err, "Updating federated identity credential with ID %q for application with object ID %q", id.KeyId, id.ObjectId)
Expand All @@ -220,8 +242,8 @@ func applicationFederatedIdentityCredentialResourceRead(ctx context.Context, d *
return tf.ErrorDiagPathF(err, "id", "Parsing federated identity credential with ID %q", d.Id())
}

applicationId := stable.NewApplicationID(id.ObjectId)
credentialId := stable.NewApplicationIdFederatedIdentityCredentialID(id.ObjectId, id.KeyId)
applicationId := beta.NewApplicationID(id.ObjectId)
credentialId := beta.NewApplicationIdFederatedIdentityCredentialID(id.ObjectId, id.KeyId)

resp, err := federatedIdentityCredentialClient.GetFederatedIdentityCredential(ctx, credentialId, federatedidentitycredential.DefaultGetFederatedIdentityCredentialOperationOptions())
if err != nil {
Expand All @@ -245,7 +267,11 @@ func applicationFederatedIdentityCredentialResourceRead(ctx context.Context, d *
tf.Set(d, "description", credential.Description.GetOrZero())
tf.Set(d, "display_name", credential.Name)
tf.Set(d, "issuer", credential.Issuer)
tf.Set(d, "subject", credential.Subject)
tf.Set(d, "subject", credential.Subject.GetOrZero())

if credential.ClaimsMatchingExpression != nil {
tf.Set(d, "claims_matching_expression", credential.ClaimsMatchingExpression.Value)
}

return nil
}
Expand All @@ -258,7 +284,7 @@ func applicationFederatedIdentityCredentialResourceDelete(ctx context.Context, d
return tf.ErrorDiagPathF(err, "id", "Parsing federated identity credential with ID %q", d.Id())
}

credentialId := stable.NewApplicationIdFederatedIdentityCredentialID(id.ObjectId, id.KeyId)
credentialId := beta.NewApplicationIdFederatedIdentityCredentialID(id.ObjectId, id.KeyId)

tf.LockByName(applicationResourceName, id.ObjectId)
defer tf.UnlockByName(applicationResourceName, id.ObjectId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ import (

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/applications/stable/federatedidentitycredential"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/stable"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/applications/beta/federatedidentitycredential"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/common-types/beta"
"github.com/hashicorp/terraform-plugin-testing/terraform"
"github.com/hashicorp/terraform-provider-azuread/internal/acceptance"
"github.com/hashicorp/terraform-provider-azuread/internal/acceptance/check"
Expand Down Expand Up @@ -53,6 +53,22 @@ func TestAccApplicationFederatedIdentityCredential_complete(t *testing.T) {
})
}

func TestAccApplicationFederatedIdentityCredential_flexible(t *testing.T) {
data := acceptance.BuildTestData(t, "azuread_application_federated_identity_credential", "test")
r := ApplicationFederatedIdentityCredentialResource{}

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

func TestAccApplicationFederatedIdentityCredential_update(t *testing.T) {
data := acceptance.BuildTestData(t, "azuread_application_federated_identity_credential", "test")
r := ApplicationFederatedIdentityCredentialResource{}
Expand Down Expand Up @@ -93,7 +109,7 @@ func (r ApplicationFederatedIdentityCredentialResource) Exists(ctx context.Conte
return nil, fmt.Errorf("parsing Application Federated Identity Credential ID: %v", err)
}

credentialId := stable.NewApplicationIdFederatedIdentityCredentialID(id.ObjectId, id.KeyId)
credentialId := beta.NewApplicationIdFederatedIdentityCredentialID(id.ObjectId, id.KeyId)

resp, err := client.GetFederatedIdentityCredential(ctx, credentialId, federatedidentitycredential.DefaultGetFederatedIdentityCredentialOperationOptions())
if err != nil {
Expand Down Expand Up @@ -142,3 +158,18 @@ resource "azuread_application_federated_identity_credential" "test" {
}
`, r.template(data), data.RandomString, data.UUID())
}

func (r ApplicationFederatedIdentityCredentialResource) flexible(data acceptance.TestData) string {
return fmt.Sprintf(`
%[1]s

resource "azuread_application_federated_identity_credential" "test" {
application_id = azuread_application.test.id
display_name = "hashitown.example.com-%[2]s"
description = "Funtime tokens for HashiTown"
audiences = ["api://AzureADTokenExchange"]
issuer = "https://token.actions.githubusercontent.com"
claims_matching_expression = "claims['sub'] matches 'repo:contoso/contoso-repo:ref:refs/heads/*'"
}
`, r.template(data), data.RandomString)
}
2 changes: 1 addition & 1 deletion internal/services/applications/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ package client

import (
applicationBeta "github.com/hashicorp/go-azure-sdk/microsoft-graph/applications/beta/application"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/applications/beta/federatedidentitycredential"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/applications/stable/application"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/applications/stable/federatedidentitycredential"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/applications/stable/logo"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/applications/stable/owner"
"github.com/hashicorp/go-azure-sdk/microsoft-graph/applicationtemplates/stable/applicationtemplate"
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading