Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Resources for Claims Mapping Policy Management #766

Merged
merged 4 commits into from
Apr 8, 2022
Merged
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
1 change: 1 addition & 0 deletions .teamcity/components/project.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var services = mapOf(
"domains" to "Domains",
"groups" to "Groups",
"invitations" to "Invitations",
"policies" to "Policies",
"serviceprincipals" to "Service Principals",
"users" to "Users"
)
Expand Down
70 changes: 70 additions & 0 deletions docs/resources/claims_mapping_policy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
subcategory: "Policies"
---

# Resource: azuread_claims_mapping_policy

Manages a Claims Mapping Policy 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" "my_policy" {
definition = [
jsonencode(
{
ClaimsMappingPolicy = {
ClaimsSchema = [
{
ID = "employeeid"
JwtClaimType = "name"
SamlClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
Source = "user"
},
{
ID = "tenantcountry"
JwtClaimType = "country"
SamlClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country"
Source = "company"
}
]
IncludeBasicClaimSet = "true"
Version = 1
}
}
),
]
description = "Policy created with Terraform"
display_name = "My Policy"
}
```

## Argument Reference

The following arguments are supported:

* `definition` - (Required) The claims mapping policy. This is a JSON formatted string, for which the [`jsonencode()`](https://www.terraform.io/language/functions/jsonencode) function can be used.
* `description` - (Required) The description for this Claims Mapping Policy.
* `display_name` - (Required) The display name for this Claims Mapping Policy.

## Attributes Reference

In addition to all arguments above, the following attributes are exported:

* `id` - The ID of the Claims Mapping Policy.

## Import

Claims Mapping Policy can be imported using the `id`, e.g.

```shell
terraform import azuread_claims_mapping_policy.my_policy 00000000-0000-0000-0000-000000000000
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@

---
subcategory: "Service Principals"
---

# Resource: azuread_service_principal_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_service_principal_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 object 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/claimsMappingPolicy/claims-mapping-policy-uuid`, e.g:

```shell
terraform import azuread_service_principal_claims_mapping_policy_assignment.app 00000000-0000-0000-0000-000000000000/claimsMappingPolicy/11111111-0000-0000-0000-000000000000
```
3 changes: 3 additions & 0 deletions internal/clients/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
domains "github.com/hashicorp/terraform-provider-azuread/internal/services/domains/client"
groups "github.com/hashicorp/terraform-provider-azuread/internal/services/groups/client"
invitations "github.com/hashicorp/terraform-provider-azuread/internal/services/invitations/client"
policies "github.com/hashicorp/terraform-provider-azuread/internal/services/policies/client"
serviceprincipals "github.com/hashicorp/terraform-provider-azuread/internal/services/serviceprincipals/client"
users "github.com/hashicorp/terraform-provider-azuread/internal/services/users/client"
)
Expand All @@ -41,6 +42,7 @@ type Client struct {
Domains *domains.Client
Groups *groups.Client
Invitations *invitations.Client
Policies *policies.Client
ServicePrincipals *serviceprincipals.Client
Users *users.Client
}
Expand All @@ -56,6 +58,7 @@ func (client *Client) build(ctx context.Context, o *common.ClientOptions) error
client.DirectoryRoles = directoryroles.NewClient(o)
client.Groups = groups.NewClient(o)
client.Invitations = invitations.NewClient(o)
client.Policies = policies.NewClient(o)
client.ServicePrincipals = serviceprincipals.NewClient(o)
client.Users = users.NewClient(o)

Expand Down
2 changes: 2 additions & 0 deletions internal/provider/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/hashicorp/terraform-provider-azuread/internal/services/domains"
"github.com/hashicorp/terraform-provider-azuread/internal/services/groups"
"github.com/hashicorp/terraform-provider-azuread/internal/services/invitations"
"github.com/hashicorp/terraform-provider-azuread/internal/services/policies"
"github.com/hashicorp/terraform-provider-azuread/internal/services/serviceprincipals"
"github.com/hashicorp/terraform-provider-azuread/internal/services/users"
)
Expand All @@ -23,6 +24,7 @@ func SupportedServices() []ServiceRegistration {
domains.Registration{},
groups.Registration{},
invitations.Registration{},
policies.Registration{},
serviceprincipals.Registration{},
users.Registration{},
}
Expand Down
132 changes: 132 additions & 0 deletions internal/services/policies/claims_mapping_policy_resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package policies

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

"github.com/hashicorp/go-uuid"
"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/tf"
"github.com/hashicorp/terraform-provider-azuread/internal/utils"
"github.com/manicminer/hamilton/msgraph"
"github.com/manicminer/hamilton/odata"
)

func claimsMappingPolicyResource() *schema.Resource {
return &schema.Resource{
CreateContext: claimsMappingPolicyResourceCreate,
ReadContext: claimsMappingPolicyResourceRead,
UpdateContext: claimsMappingPolicyResourceUpdate,
DeleteContext: claimsMappingPolicyResourceDelete,

Importer: tf.ValidateResourceIDPriorToImport(func(id string) error {
if _, err := uuid.ParseUUID(id); err != nil {
return fmt.Errorf("specified ID (%q) is not valid: %s", id, err)
}
return nil
}),

Schema: map[string]*schema.Schema{
"definition": {
Description: "A string collection containing a JSON string that defines the rules and settings for this policy",
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},

"display_name": {
Description: "Display name for this policy",
Type: schema.TypeString,
Required: true,
},
},
}
}

func claimsMappingPolicyResourceCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*clients.Client).Policies.ClaimsMappingPolicyClient

claimsMappingPolicy := msgraph.ClaimsMappingPolicy{
Definition: tf.ExpandStringSlicePtr(d.Get("definition").([]interface{})),
DisplayName: utils.String(d.Get("display_name").(string)),
}
policy, _, err := client.Create(ctx, claimsMappingPolicy)
if err != nil {
return tf.ErrorDiagF(err, "Could not create Claims Mapping Policy")
}

if policy.ID == nil || *policy.ID == "" {
return tf.ErrorDiagF(fmt.Errorf("Object ID returned for Claims Mapping Policy is nil"), "Bad API response")
}

d.SetId(*policy.ID)

return claimsMappingPolicyResourceRead(ctx, d, meta)
}

func claimsMappingPolicyResourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*clients.Client).Policies.ClaimsMappingPolicyClient
objectId := d.Id()

policy, status, err := client.Get(ctx, objectId, odata.Query{})
if err != nil {
if status == http.StatusNotFound {
log.Printf("[DEBUG] Claims Mapping Policy with Object ID %q was not found - removing from state!", objectId)
d.SetId("")
return nil
}

return tf.ErrorDiagF(err, "retrieving Claims Mapping Policy with object ID: %q", d.Id())
}

tf.Set(d, "definition", policy.Definition)
tf.Set(d, "display_name", policy.DisplayName)

return nil
}

func claimsMappingPolicyResourceUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*clients.Client).Policies.ClaimsMappingPolicyClient
objectId := d.Id()

claimsMappingPolicy := msgraph.ClaimsMappingPolicy{
DirectoryObject: msgraph.DirectoryObject{
ID: &objectId,
},
Definition: tf.ExpandStringSlicePtr(d.Get("definition").([]interface{})),
DisplayName: utils.String(d.Get("display_name").(string)),
}
_, err := client.Update(ctx, claimsMappingPolicy)
if err != nil {
return tf.ErrorDiagF(err, "Could not update Claims Mapping Policy with object ID %q", objectId)
}

return claimsMappingPolicyResourceRead(ctx, d, meta)
}

func claimsMappingPolicyResourceDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*clients.Client).Policies.ClaimsMappingPolicyClient
objectId := d.Id()

_, status, err := client.Get(ctx, objectId, odata.Query{})
if err != nil {
if status == http.StatusNotFound {
return tf.ErrorDiagPathF(fmt.Errorf("Claims Mapping Policy was not found"), "id", "Retrieving Claims Mapping Policy with object ID %q", objectId)
}

return tf.ErrorDiagPathF(err, "id", "Retrieving Claims Mapping Policy with object ID %q", objectId)
}

status, err = client.Delete(ctx, objectId)
if err != nil {
return tf.ErrorDiagF(err, "Deleting Claims Mapping Policy with object ID %q, received status %d", objectId, status)
}

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package policies_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/manicminer/hamilton/odata"
)

type ClaimsMappingPolicyResource struct{}

func TestClaimsMappingPolicy_basic(t *testing.T) {
data := acceptance.BuildTestData(t, "azuread_claims_mapping_policy", "test")
r := ClaimsMappingPolicyResource{}

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

func (ClaimsMappingPolicyResource) basic(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azuread" {}

resource "azuread_claims_mapping_policy" "test" {
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\"}]}}"
]
display_name = "acctest-%[1]s"
}
`, data.RandomString)
}

func (ClaimsMappingPolicyResource) update(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azuread" {}

resource "azuread_claims_mapping_policy" "test" {
definition = [
"{\"ClaimsMappingPolicy\":{\"Version\":1,\"IncludeBasicClaimSet\":\"true\",\"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\"}]}}"
]
display_name = "acctest-%[1]s-updated"
}
`, data.RandomString)
}

func (r ClaimsMappingPolicyResource) Exists(ctx context.Context, clients *clients.Client, state *terraform.InstanceState) (*bool, error) {
client := clients.Policies.ClaimsMappingPolicyClient
client.BaseClient.DisableRetries = true

exists := false
_, status, err := client.Get(ctx, state.ID, odata.Query{})
if err != nil {
if status == http.StatusNotFound {
return nil, fmt.Errorf("Claims mapping policy with object ID %q does not exist", state.ID)
}
return &exists, fmt.Errorf("failed to retrieve claims mapping policy with object ID %q: %+v", state.ID, err)
}

exists = true
return &exists, nil
}
Loading