forked from hashicorp/terraform-provider-azuread
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ Add Claims Mapping Policy Assignment Resource
Adds support for the claims mapping policy assignment resource so claims mapping policies can be assigned to a service principle with Terraform. Related to: - manicminer/hamilton#147 - hashicorp#644 - https://docs.microsoft.com/en-us/graph/api/serviceprincipal-post-claimsmappingpolicies?view=graph-rest-1.0&tabs=http
- Loading branch information
1 parent
6298318
commit d5154d1
Showing
5 changed files
with
346 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
|
||
--- | ||
subcategory: "Policies" | ||
--- | ||
|
||
# Resource: claims_mapping_policy_assignment | ||
|
||
Manages a Claims Mapping Policy Assignment within Azure Active Directory. | ||
|
||
## API Permissions | ||
|
||
The following API permissions are required in order to use this resource. | ||
|
||
When authenticated with a service principal, this resource requires the following application roles: `Policy.ReadWrite.ApplicationConfiguration` | ||
|
||
When authenticated with a user principal, this resource requires one of the following directory roles: `Application Administrator` or `Global Administrator` | ||
|
||
## Example Usage | ||
|
||
```terraform | ||
resource "azuread_claims_mapping_policy_assignment" "app" { | ||
claims_mapping_policy_id = azuread_claims_mapping_policy.my_policy.id | ||
service_principal_id = azuread_service_principal.my_principal.id | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `claims_mapping_policy_id` - (Required) The `id` of the claims mapping policy to assign. | ||
* `service_principal_id` - (Required) The `id` of the service principal for the policy assignment. | ||
|
||
## Attributes Reference | ||
|
||
In addition to all arguments above, the following attributes are exported: | ||
|
||
* `id` - The ID of the Claims Mapping Policy Assignment. | ||
|
||
## Import | ||
|
||
Claims Mapping Policy can be imported using the `id`, in the form `service-principal-uuid/azuread_claims_mapping_policy/claims-mapping-policy-uuid`, e.g: | ||
|
||
```shell | ||
terraform import azuread_claims_mapping_policy_assignment.app 00000000-0000-0000-0000-000000000000/azuread_claims_mapping_policy/00000000-0000-0000-0000-000000000000 | ||
``` |
30 changes: 30 additions & 0 deletions
30
internal/services/serviceprincipals/parse/claims_mapping_policy_assignment.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
package parse | ||
|
||
import "fmt" | ||
|
||
type ClaimsMappingPolicyAssignmentId struct { | ||
ObjectSubResourceId | ||
ServicePolicyId string | ||
ClaimsMappingPolicyId string | ||
} | ||
|
||
func NewClaimsMappingPolicyAssignmentID(ServicePolicyId, ClaimsMappingPolicyId string) ClaimsMappingPolicyAssignmentId { | ||
return ClaimsMappingPolicyAssignmentId{ | ||
ObjectSubResourceId: NewObjectSubResourceID(ServicePolicyId, "azuread_claims_mapping_policy", ClaimsMappingPolicyId), | ||
ServicePolicyId: ServicePolicyId, | ||
ClaimsMappingPolicyId: ClaimsMappingPolicyId, | ||
} | ||
} | ||
|
||
func ClaimsMappingPolicyAssignmentID(idString string) (*ClaimsMappingPolicyAssignmentId, error) { | ||
id, err := ObjectSubResourceID(idString, "azuread_claims_mapping_policy") | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to parse azuread_claims_mapping_policy ID: %v", err) | ||
} | ||
|
||
return &ClaimsMappingPolicyAssignmentId{ | ||
ObjectSubResourceId: *id, | ||
ServicePolicyId: id.objectId, | ||
ClaimsMappingPolicyId: id.subId, | ||
}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
155 changes: 155 additions & 0 deletions
155
internal/services/serviceprincipals/service_principal_claims_mapping_policy_assignment.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
package serviceprincipals | ||
|
||
import ( | ||
"context" | ||
"log" | ||
"net/http" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/diag" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/hashicorp/terraform-provider-azuread/internal/clients" | ||
"github.com/hashicorp/terraform-provider-azuread/internal/services/serviceprincipals/parse" | ||
"github.com/hashicorp/terraform-provider-azuread/internal/tf" | ||
"github.com/hashicorp/terraform-provider-azuread/internal/utils" | ||
"github.com/manicminer/hamilton/msgraph" | ||
"github.com/manicminer/hamilton/odata" | ||
) | ||
|
||
func servicePrincipalClaimsMappingPolicyAssignment() *schema.Resource { | ||
return &schema.Resource{ | ||
CreateContext: servicePrincipalClaimsMappingPolicyAssignmentResourceCreate, | ||
ReadContext: servicePrincipalClaimsMappingPolicyAssignmentResourceRead, | ||
DeleteContext: servicePrincipalClaimsMappingPolicyAssignmentResourceDelete, | ||
|
||
Importer: tf.ValidateResourceIDPriorToImport(func(id string) error { | ||
_, err := parse.ObjectSubResourceID(id, "azuread_claims_mapping_policy") | ||
return err | ||
}), | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"claims_mapping_policy_id": { | ||
Description: "ID of the claims mapping policy to assign", | ||
ForceNew: true, | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
|
||
"service_principal_id": { | ||
Description: "ID of the service principal to assign the policy to", | ||
ForceNew: true, | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func servicePrincipalClaimsMappingPolicyAssignmentResourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
spClient := meta.(*clients.Client).ServicePrincipals.ServicePrincipalsClient | ||
policyClient := meta.(*clients.Client).ServicePrincipals.ClaimsMappingPolicyClient | ||
|
||
policyID := d.Get("claims_mapping_policy_id").(string) | ||
policy, _, err := policyClient.Get(ctx, policyID, odata.Query{}) | ||
if err != nil { | ||
return tf.ErrorDiagF( | ||
err, | ||
"Could not find ClaimsMappingPolicy, claims_mapping_policy_id: %q", | ||
policyID, | ||
) | ||
} | ||
|
||
properties := msgraph.ServicePrincipal{ | ||
DirectoryObject: msgraph.DirectoryObject{ | ||
ID: utils.String(d.Get("service_principal_id").(string)), | ||
}, | ||
ClaimsMappingPolicies: &[]msgraph.ClaimsMappingPolicy{ | ||
*policy, | ||
}, | ||
} | ||
_, err = spClient.AssignClaimsMappingPolicy(ctx, &properties) | ||
if err != nil { | ||
return tf.ErrorDiagF( | ||
err, | ||
"Could not create ClaimsMappingPolicyAssignment, service_principal_id: %q, claims_mapping_policy_id: %q", | ||
*properties.DirectoryObject.ID, | ||
*(*properties.ClaimsMappingPolicies)[0].DirectoryObject.ID, | ||
) | ||
} | ||
|
||
resourceID := parse.NewClaimsMappingPolicyAssignmentID( | ||
*properties.DirectoryObject.ID, | ||
*(*properties.ClaimsMappingPolicies)[0].DirectoryObject.ID, | ||
) | ||
|
||
d.SetId(resourceID.String()) | ||
|
||
return servicePrincipalClaimsMappingPolicyAssignmentResourceRead(ctx, d, meta) | ||
} | ||
|
||
func servicePrincipalClaimsMappingPolicyAssignmentResourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client := meta.(*clients.Client).ServicePrincipals.ServicePrincipalsClient | ||
|
||
id, err := parse.ClaimsMappingPolicyAssignmentID(d.Id()) | ||
if err != nil { | ||
return tf.ErrorDiagPathF(err, "id", "Parsing Claims Mapping Policy Assignment ID %q", d.Id()) | ||
} | ||
|
||
spID := id.ServicePolicyId | ||
|
||
policyList, status, err := client.ListClaimsMappingPolicy(ctx, spID) | ||
if err != nil { | ||
if status == http.StatusNotFound { | ||
log.Printf("[DEBUG] Service Principal with Object ID %q was not found - removing claims mapping policy assignment from state!", spID) | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
return tf.ErrorDiagF(err, "listing Claims Mapping Policy Assignments for Service Principal with object ID: %q", d.Id()) | ||
} | ||
|
||
policyID := id.ClaimsMappingPolicyId | ||
var foundPolicy *msgraph.ClaimsMappingPolicy | ||
|
||
// Check the assignment is found in the currently assigned policies | ||
for _, policy := range *policyList { | ||
if *policy.ID == policyID { | ||
foundPolicy = &policy | ||
break | ||
} | ||
} | ||
if foundPolicy == nil { | ||
d.SetId("") | ||
log.Printf("[DEBUG] Claims Mapping Policy with Object ID %q was not found - removing assignment from state!", policyID) | ||
return nil | ||
} | ||
|
||
tf.Set(d, "service_principal_id", spID) | ||
tf.Set(d, "claims_mapping_policy_id", policyID) | ||
|
||
return nil | ||
} | ||
|
||
func servicePrincipalClaimsMappingPolicyAssignmentResourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { | ||
client := meta.(*clients.Client).ServicePrincipals.ServicePrincipalsClient | ||
|
||
id, err := parse.ClaimsMappingPolicyAssignmentID(d.Id()) | ||
if err != nil { | ||
return tf.ErrorDiagPathF(err, "id", "Parsing Claims Mapping Policy Assignment ID %q", d.Id()) | ||
} | ||
|
||
claimIDs := []string{id.ClaimsMappingPolicyId} | ||
|
||
spID := id.ServicePolicyId | ||
|
||
sp := msgraph.ServicePrincipal{ | ||
DirectoryObject: msgraph.DirectoryObject{ | ||
ID: &spID, | ||
}, | ||
} | ||
_, err = client.RemoveClaimsMappingPolicy(ctx, &sp, &claimIDs) | ||
if err != nil { | ||
return tf.ErrorDiagF(err, "Could not Remove ClaimsMappingPolicyAssignment, service_principal_id: %q, claims_mapping_policy_ids: %q", spID, claimIDs) | ||
} | ||
|
||
return servicePrincipalClaimsMappingPolicyAssignmentResourceRead(ctx, d, meta) | ||
} |
114 changes: 114 additions & 0 deletions
114
...nal/services/serviceprincipals/service_principal_claims_mapping_policy_assignment_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package serviceprincipals_test | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/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/utils" | ||
) | ||
|
||
type ServicePrincipalClaimsMappingPolicyAssignment struct{} | ||
|
||
func TestClaimsMappingPolicyAssignment_basic(t *testing.T) { | ||
data := acceptance.BuildTestData(t, "azuread_claims_mapping_policy_assignment", "test") | ||
mappingPolicy := acceptance.TestData{ | ||
ResourceName: "azuread_claims_mapping_policy.test2", | ||
} | ||
r := ServicePrincipalClaimsMappingPolicyAssignment{} | ||
|
||
data.ResourceTest(t, r, []resource.TestStep{ | ||
{ | ||
Config: r.basicClaimsMappingPolicyAssignment(data), | ||
Check: resource.ComposeTestCheckFunc( | ||
check.That(data.ResourceName).ExistsInAzure(r), | ||
), | ||
}, | ||
data.ImportStep(), | ||
{ | ||
Config: r.updateClaimsMappingPolicyAssignment(data), | ||
Check: resource.ComposeTestCheckFunc( | ||
check.That(data.ResourceName).ExistsInAzure(r), | ||
check.That(data.ResourceName).Key( | ||
"claims_mapping_policy_id", | ||
).MatchesOtherKey( | ||
check.That(mappingPolicy.ResourceName).Key( | ||
"id", | ||
), | ||
), | ||
), | ||
}, | ||
}) | ||
} | ||
|
||
func (ServicePrincipalClaimsMappingPolicyAssignment) basicClaimsMappingPolicyAssignment(data acceptance.TestData) string { | ||
c := ServicePrincipalClaimsMappingPolicy{} | ||
return c.basicClaimsMappingPolicy(data) + ` | ||
data "azuread_application_published_app_ids" "well_known" {} | ||
resource "azuread_service_principal" "msgraph" { | ||
application_id = data.azuread_application_published_app_ids.well_known.result.MicrosoftGraph | ||
use_existing = true | ||
} | ||
resource "azuread_claims_mapping_policy_assignment" "test" { | ||
service_principal_id = azuread_service_principal.msgraph.id | ||
claims_mapping_policy_id = azuread_claims_mapping_policy.test.id | ||
} | ||
` | ||
} | ||
|
||
func (ServicePrincipalClaimsMappingPolicyAssignment) updateClaimsMappingPolicyAssignment(data acceptance.TestData) string { | ||
return fmt.Sprintf(` | ||
provider "azuread" {} | ||
data "azuread_application_published_app_ids" "well_known" {} | ||
resource "azuread_service_principal" "msgraph" { | ||
application_id = data.azuread_application_published_app_ids.well_known.result.MicrosoftGraph | ||
use_existing = true | ||
} | ||
resource "azuread_claims_mapping_policy" "test2" { | ||
definition = [ | ||
"{\"ClaimsMappingPolicy\":{\"Version\":1,\"IncludeBasicClaimSet\":\"false\",\"ClaimsSchema\": [{\"Source\":\"user\",\"ID\":\"employeeid\",\"SamlClaimType\":\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name\",\"JwtClaimType\":\"name\"},{\"Source\":\"company\",\"ID\":\"tenantcountry\",\"SamlClaimType\":\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country\",\"JwtClaimType\":\"country\"}]}}" | ||
] | ||
description = "%[1]s" | ||
display_name = "integration-%[1]s" | ||
} | ||
resource "azuread_claims_mapping_policy_assignment" "test" { | ||
service_principal_id = azuread_service_principal.msgraph.id | ||
claims_mapping_policy_id = azuread_claims_mapping_policy.test2.id | ||
} | ||
`, data.RandomString) | ||
} | ||
|
||
func (r ServicePrincipalClaimsMappingPolicyAssignment) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) { | ||
client := clients.ServicePrincipals.ServicePrincipalsClient | ||
client.BaseClient.DisableRetries = true | ||
|
||
spID := state.Attributes["service_principal_id"] | ||
policyList, status, err := client.ListClaimsMappingPolicy(ctx, spID) | ||
if err != nil { | ||
if status == http.StatusNotFound { | ||
return utils.Bool(false), fmt.Errorf("Service Policy with object ID %q does not exist", spID) | ||
} | ||
return utils.Bool(false), fmt.Errorf("failed to retrieve claims mapping policy assignments with service policy ID %q: %+v", spID, err) | ||
} | ||
|
||
policyID := state.Attributes["claims_mapping_policy_id"] | ||
|
||
// Check the assignment is found in the currently assigned policies | ||
for _, policy := range *policyList { | ||
if *policy.ID == policyID { | ||
return utils.Bool(true), nil | ||
} | ||
} | ||
return utils.Bool(false), nil | ||
} |