Skip to content

Commit

Permalink
azurerm_lighthouse_definition - support the `eligible_authorization…
Browse files Browse the repository at this point in the history
…` property (hashicorp#19569)
  • Loading branch information
neil-yechenwei authored and favoretti committed Jan 12, 2023
1 parent 2612d84 commit 9fa48ac
Show file tree
Hide file tree
Showing 3 changed files with 340 additions and 0 deletions.
238 changes: 238 additions & 0 deletions internal/services/lighthouse/lighthouse_definition_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/hashicorp/go-azure-sdk/resource-manager/managedservices/2022-10-01/registrationdefinitions"
"github.com/hashicorp/go-uuid"
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
azValidate "github.com/hashicorp/terraform-provider-azurerm/helpers/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
Expand Down Expand Up @@ -99,6 +100,76 @@ func resourceLighthouseDefinition() *pluginsdk.Resource {
Optional: true,
},

"eligible_authorization": {
Type: pluginsdk.TypeSet,
Optional: true,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"principal_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.IsUUID,
},

"role_definition_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.IsUUID,
},

"just_in_time_access_policy": {
Type: pluginsdk.TypeList,
Optional: true,
MaxItems: 1,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"multi_factor_auth_provider": {
Type: pluginsdk.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{
string(registrationdefinitions.MultiFactorAuthProviderAzure),
}, false),
},

"maximum_activation_duration": {
Type: pluginsdk.TypeString,
Optional: true,
Default: "PT8H",
ValidateFunc: azValidate.ISO8601Duration,
},

"approver": {
Type: pluginsdk.TypeSet,
Optional: true,
Elem: &pluginsdk.Resource{
Schema: map[string]*pluginsdk.Schema{
"principal_id": {
Type: pluginsdk.TypeString,
Required: true,
ValidateFunc: validation.IsUUID,
},

"principal_display_name": {
Type: pluginsdk.TypeString,
Optional: true,
ValidateFunc: validation.StringIsNotEmpty,
},
},
},
},
},
},
},

"principal_display_name": {
Type: pluginsdk.TypeString,
Optional: true,
ValidateFunc: validation.StringIsNotEmpty,
},
},
},
},

"lighthouse_definition_id": {
Type: pluginsdk.TypeString,
Optional: true,
Expand Down Expand Up @@ -182,6 +253,14 @@ func resourceLighthouseDefinitionCreateUpdate(d *pluginsdk.ResourceData, meta in
},
}

if v, ok := d.GetOk("eligible_authorization"); ok {
eligibleAuthorization, err := expandLighthouseDefinitionEligibleAuthorization(v.(*pluginsdk.Set).List())
if err != nil {
return err
}
parameters.Properties.EligibleAuthorizations = eligibleAuthorization
}

// NOTE: this API call uses DefinitionId then Scope - check in the future
if err := client.CreateOrUpdateThenPoll(ctx, id, parameters); err != nil {
return fmt.Errorf("creating/updating %s: %+v", id, err)
Expand Down Expand Up @@ -224,6 +303,9 @@ func resourceLighthouseDefinitionRead(d *pluginsdk.ResourceData, meta interface{
if err := d.Set("authorization", flattenLighthouseDefinitionAuthorization(props.Authorizations)); err != nil {
return fmt.Errorf("setting `authorization`: %+v", err)
}
if err := d.Set("eligible_authorization", flattenLighthouseDefinitionEligibleAuthorization(props.EligibleAuthorizations)); err != nil {
return fmt.Errorf("setting `eligible_authorization`: %+v", err)
}
d.Set("description", props.Description)
d.Set("name", props.RegistrationDefinitionName)
d.Set("managing_tenant_id", props.ManagedByTenantId)
Expand Down Expand Up @@ -313,3 +395,159 @@ func flattenLighthouseDefinitionPlan(input *registrationdefinitions.Plan) []inte
},
}
}

func expandLighthouseDefinitionEligibleAuthorization(input []interface{}) (*[]registrationdefinitions.EligibleAuthorization, error) {
if len(input) == 0 || input[0] == nil {
return nil, nil
}

var results []registrationdefinitions.EligibleAuthorization

for _, item := range input {
v := item.(map[string]interface{})

result := registrationdefinitions.EligibleAuthorization{
PrincipalId: v["principal_id"].(string),
RoleDefinitionId: v["role_definition_id"].(string),
}

justInTimeAccessPolicy, err := expandLighthouseDefinitionJustInTimeAccessPolicy(v["just_in_time_access_policy"].([]interface{}))
if err != nil {
return nil, err
}
result.JustInTimeAccessPolicy = justInTimeAccessPolicy

if principalDisplayName := v["principal_display_name"].(string); principalDisplayName != "" {
result.PrincipalIdDisplayName = utils.String(principalDisplayName)
}

results = append(results, result)
}

return &results, nil
}

func expandLighthouseDefinitionJustInTimeAccessPolicy(input []interface{}) (*registrationdefinitions.JustInTimeAccessPolicy, error) {
if len(input) == 0 || input[0] == nil {
return nil, nil
}

justInTimeAccessPolicy := input[0].(map[string]interface{})

result := registrationdefinitions.JustInTimeAccessPolicy{
MaximumActivationDuration: utils.String(justInTimeAccessPolicy["maximum_activation_duration"].(string)),
}

multiFactorAuthProvider := registrationdefinitions.MultiFactorAuthProviderNone
if v := justInTimeAccessPolicy["multi_factor_auth_provider"].(string); v != "" {
multiFactorAuthProvider = registrationdefinitions.MultiFactorAuthProvider(v)
}
result.MultiFactorAuthProvider = multiFactorAuthProvider

approvers, err := expandLighthouseDefinitionApprover(justInTimeAccessPolicy["approver"].(*pluginsdk.Set).List())
if err != nil {
return nil, err
}
result.ManagedByTenantApprovers = approvers

return &result, nil
}

func expandLighthouseDefinitionApprover(input []interface{}) (*[]registrationdefinitions.EligibleApprover, error) {
if len(input) == 0 || input[0] == nil {
return nil, nil
}

var results []registrationdefinitions.EligibleApprover

for _, v := range input {
eligibleApprover := v.(map[string]interface{})

result := registrationdefinitions.EligibleApprover{
PrincipalId: eligibleApprover["principal_id"].(string),
}

if principalDisplayName := eligibleApprover["principal_display_name"].(string); principalDisplayName != "" {
result.PrincipalIdDisplayName = utils.String(principalDisplayName)
}

results = append(results, result)
}

return &results, nil
}

func flattenLighthouseDefinitionEligibleAuthorization(input *[]registrationdefinitions.EligibleAuthorization) []interface{} {
if input == nil {
return nil
}

var results []interface{}

for _, item := range *input {
result := map[string]interface{}{
"principal_id": item.PrincipalId,
"role_definition_id": item.RoleDefinitionId,
}

if item.JustInTimeAccessPolicy != nil {
result["just_in_time_access_policy"] = flattenLighthouseDefinitionJustInTimeAccessPolicy(item.JustInTimeAccessPolicy)
}

if item.PrincipalIdDisplayName != nil {
result["principal_display_name"] = *item.PrincipalIdDisplayName
}

results = append(results, result)
}

return results
}

func flattenLighthouseDefinitionJustInTimeAccessPolicy(input *registrationdefinitions.JustInTimeAccessPolicy) []interface{} {
if input == nil {
return nil
}

var results []interface{}

result := map[string]interface{}{}

if v := input.MultiFactorAuthProvider; v != registrationdefinitions.MultiFactorAuthProviderNone {
result["multi_factor_auth_provider"] = string(v)
}

if input.ManagedByTenantApprovers != nil {
result["approver"] = flattenLighthouseDefinitionApprover(input.ManagedByTenantApprovers)
}

maximumActivationDuration := "PT8H"
if input.MaximumActivationDuration != nil {
maximumActivationDuration = *input.MaximumActivationDuration
}
result["maximum_activation_duration"] = maximumActivationDuration

return append(results, result)
}

func flattenLighthouseDefinitionApprover(input *[]registrationdefinitions.EligibleApprover) []interface{} {
if input == nil {
return nil
}

var results []interface{}

for _, item := range *input {
result := map[string]interface{}{
"principal_id": item.PrincipalId,
}

if item.PrincipalIdDisplayName != nil {
result["principal_display_name"] = *item.PrincipalIdDisplayName
}

results = append(results, result)
}

return results
}
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,27 @@ func TestAccLighthouseDefinition_plan(t *testing.T) {
})
}

func TestAccLighthouseDefinition_eligibleAuthorization(t *testing.T) {
secondTenantID := os.Getenv("ARM_TENANT_ID_ALT")
principalID := os.Getenv("ARM_PRINCIPAL_ID_ALT_TENANT")
if secondTenantID == "" || principalID == "" {
t.Skip("Skipping as ARM_TENANT_ID_ALT and/or ARM_PRINCIPAL_ID_ALT_TENANT are not specified")
}

data := acceptance.BuildTestData(t, "azurerm_lighthouse_definition", "test")
r := LighthouseDefinitionResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.eligibleAuthorization(uuid.New().String(), secondTenantID, principalID, data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep("lighthouse_definition_id"),
})
}

func (LighthouseDefinitionResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := registrationdefinitions.ParseScopedRegistrationDefinitionID(state.ID)
if err != nil {
Expand Down Expand Up @@ -383,3 +404,50 @@ resource "azurerm_lighthouse_definition" "test" {
}
`, data.RandomInteger, secondTenantID, principalID, planName, planPublisher, planProduct, planVersion)
}

func (LighthouseDefinitionResource) eligibleAuthorization(id string, secondTenantID string, principalID string, data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}
data "azurerm_role_definition" "contributor" {
role_definition_id = "b24988ac-6180-42a0-ab88-20f7382dd24c" // Contributor role
}
data "azurerm_role_definition" "reader" {
role_definition_id = "acdd72a7-3385-48ef-bd42-f606fba81ae7"
}
data "azurerm_subscription" "test" {}
resource "azurerm_lighthouse_definition" "test" {
lighthouse_definition_id = "%s"
name = "acctest-LD-%d"
managing_tenant_id = "%s"
scope = data.azurerm_subscription.test.id
authorization {
principal_id = "%s"
role_definition_id = data.azurerm_role_definition.reader.role_definition_id
principal_display_name = "Reader"
}
eligible_authorization {
principal_id = "%s"
role_definition_id = data.azurerm_role_definition.contributor.role_definition_id
principal_display_name = "Tier 1 Support"
just_in_time_access_policy {
multi_factor_auth_provider = "Azure"
maximum_activation_duration = "PT7H"
approver {
principal_id = "%s"
principal_display_name = "Tier 2 Support"
}
}
}
}
`, id, data.RandomInteger, secondTenantID, principalID, principalID, principalID)
}
34 changes: 34 additions & 0 deletions website/docs/r/lighthouse_definition.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ The following arguments are supported:

* `description` - (Optional) A description of the Lighthouse Definition.

* `eligible_authorization` - (Optional) An `eligible_authorization` block as defined below.

* `plan` - (Optional) A `plan` block as defined below.

---
Expand All @@ -64,6 +66,38 @@ An `authorization` block supports the following:

---

An `eligible_authorization` block supports the following:

* `principal_id` - (Required) The Principal ID of the Azure Active Directory.

* `role_definition_id` - (Required) The Principal ID of the Azure built-in role that defines the permissions that the Azure Active Directory will have on the projected scope.

* `just_in_time_access_policy` - (Optional) A `just_in_time_access_policy` block as defined below.

* `principal_display_name` - (Optional) The display name of the Azure Active Directory Principal.

---

A `just_in_time_access_policy` block supports the following:

* `multi_factor_auth_provider` - (Optional) The multi-factor authorization provider to be used for just-in-time access requests. Possible value is `Azure`.

~> **Note:** When this property isn't set, it would be set to `None`.

* `maximum_activation_duration` - (Optional) The maximum access duration in ISO 8601 format for just-in-time access requests. Defaults to `PT8H`.

* `approver` - (Optional) An `approver` block as defined below.

---

An `approver` block supports the following:

* `principal_id` - (Required) The Principal ID of the Azure Active Directory principal for the approver.

* `principal_display_name` - (Optional) The display name of the Azure Active Directory Principal for the approver.

---

A `plan` block supports the following:

* `name` - (Required) The plan name of the marketplace offer.
Expand Down

0 comments on commit 9fa48ac

Please sign in to comment.