diff --git a/internal/services/lighthouse/lighthouse_definition_resource.go b/internal/services/lighthouse/lighthouse_definition_resource.go index 7bd6f9fea821..9edf9eb714e6 100644 --- a/internal/services/lighthouse/lighthouse_definition_resource.go +++ b/internal/services/lighthouse/lighthouse_definition_resource.go @@ -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" @@ -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, @@ -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) @@ -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) @@ -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 +} diff --git a/internal/services/lighthouse/lighthouse_definition_resource_test.go b/internal/services/lighthouse/lighthouse_definition_resource_test.go index 467120cc33d3..041c0a3760e5 100644 --- a/internal/services/lighthouse/lighthouse_definition_resource_test.go +++ b/internal/services/lighthouse/lighthouse_definition_resource_test.go @@ -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 { @@ -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) +} diff --git a/website/docs/r/lighthouse_definition.html.markdown b/website/docs/r/lighthouse_definition.html.markdown index 471be90e4446..202a8470cbb7 100644 --- a/website/docs/r/lighthouse_definition.html.markdown +++ b/website/docs/r/lighthouse_definition.html.markdown @@ -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. --- @@ -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.