From 1b3f2c1746847a2f89d2b912bf124165c6cb4f2f Mon Sep 17 00:00:00 2001 From: Catriona Date: Fri, 17 Jan 2025 16:41:41 +0000 Subject: [PATCH] split create/update and make customer_managed_key.identity_id optional --- .../servicebus_namespace_resource.go | 172 ++++++++++++++---- .../docs/r/servicebus_namespace.html.markdown | 2 +- 2 files changed, 140 insertions(+), 34 deletions(-) diff --git a/internal/services/servicebus/servicebus_namespace_resource.go b/internal/services/servicebus/servicebus_namespace_resource.go index 5e674fdb5564..0d900bef57ce 100644 --- a/internal/services/servicebus/servicebus_namespace_resource.go +++ b/internal/services/servicebus/servicebus_namespace_resource.go @@ -32,7 +32,6 @@ import ( "github.com/hashicorp/terraform-provider-azurerm/internal/tf/suppress" "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" - "github.com/hashicorp/terraform-provider-azurerm/utils" ) // Default Authorization Rule/Policy created by Azure, used to populate the @@ -44,9 +43,9 @@ var ( func resourceServiceBusNamespace() *pluginsdk.Resource { resource := &pluginsdk.Resource{ - Create: resourceServiceBusNamespaceCreateUpdate, + Create: resourceServiceBusNamespaceCreate, Read: resourceServiceBusNamespaceRead, - Update: resourceServiceBusNamespaceCreateUpdate, + Update: resourceServiceBusNamespaceUpdate, Delete: resourceServiceBusNamespaceDelete, Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { @@ -119,7 +118,7 @@ func resourceServiceBusNamespace() *pluginsdk.Resource { "identity_id": { Type: pluginsdk.TypeString, - Required: true, + Optional: true, ValidateFunc: commonids.ValidateUserAssignedIdentityID, }, @@ -272,13 +271,13 @@ func resourceServiceBusNamespace() *pluginsdk.Resource { return resource } -func resourceServiceBusNamespaceCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) error { +func resourceServiceBusNamespaceCreate(d *pluginsdk.ResourceData, meta interface{}) error { client := meta.(*clients.Client).ServiceBus.NamespacesClient subscriptionId := meta.(*clients.Client).Account.SubscriptionId ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() - log.Printf("[INFO] preparing arguments for ServiceBus Namespace create/update.") + log.Printf("[INFO] preparing arguments for ServiceBus Namespace create") location := azure.NormalizeLocation(d.Get("location").(string)) sku := d.Get("sku").(string) @@ -318,7 +317,7 @@ func resourceServiceBusNamespaceCreateUpdate(d *pluginsdk.ResourceData, meta int }, Properties: &namespaces.SBNamespaceProperties{ Encryption: expandServiceBusNamespaceEncryption(d.Get("customer_managed_key").([]interface{})), - DisableLocalAuth: utils.Bool(!d.Get("local_auth_enabled").(bool)), + DisableLocalAuth: pointer.To(!d.Get("local_auth_enabled").(bool)), PublicNetworkAccess: &publicNetworkEnabled, }, Tags: expandTags(t), @@ -336,7 +335,7 @@ func resourceServiceBusNamespaceCreateUpdate(d *pluginsdk.ResourceData, meta int if strings.EqualFold(sku, string(namespaces.SkuNamePremium)) && capacity.(int) == 0 { return fmt.Errorf("service bus SKU %q only supports `capacity` of 1, 2, 4, 8 or 16", sku) } - parameters.Sku.Capacity = utils.Int64(int64(capacity.(int))) + parameters.Sku.Capacity = pointer.To(int64(capacity.(int))) } if premiumMessagingUnit := d.Get("premium_messaging_partitions"); premiumMessagingUnit != nil { @@ -346,11 +345,115 @@ func resourceServiceBusNamespaceCreateUpdate(d *pluginsdk.ResourceData, meta int if strings.EqualFold(sku, string(namespaces.SkuNamePremium)) && premiumMessagingUnit.(int) == 0 { return fmt.Errorf("service bus SKU %q only supports `premium_messaging_partitions` of 1, 2, 4", sku) } - parameters.Properties.PremiumMessagingPartitions = utils.Int64(int64(premiumMessagingUnit.(int))) + parameters.Properties.PremiumMessagingPartitions = pointer.To(int64(premiumMessagingUnit.(int))) } if err := client.CreateOrUpdateThenPoll(ctx, id, parameters); err != nil { - return fmt.Errorf("creating/updating %s: %+v", id, err) + return fmt.Errorf("creating %s: %+v", id, err) + } + + d.SetId(id.ID()) + + if d.HasChange("network_rule_set") { + networkRuleSet := d.Get("network_rule_set").([]interface{}) + + log.Printf("[DEBUG] Creating the Network Rule Set associated with %s..", id) + if err = createNetworkRuleSetForNamespace(ctx, client, id, networkRuleSet); err != nil { + return err + } + log.Printf("[DEBUG] Created the Network Rule Set associated with %s", id) + + } + + return resourceServiceBusNamespaceRead(d, meta) +} + +func resourceServiceBusNamespaceUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).ServiceBus.NamespacesClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + log.Printf("[INFO] preparing arguments for ServiceBus Namespace update") + + t := d.Get("tags").(map[string]interface{}) + + id, err := namespaces.ParseNamespaceID(d.Id()) + if err != nil { + return err + } + + existing, err := client.Get(ctx, *id) + if err != nil { + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + if existing.Model == nil { + return fmt.Errorf("retrieving existing %s: `model` was nil", *id) + } + if existing.Model.Properties == nil { + return fmt.Errorf("retrieving existing %s: `model.Properties` was nil", *id) + } + + payload := existing.Model + + if d.HasChange("identity") { + identity, err := expandSystemAndUserAssignedMap(d.Get("identity").([]interface{})) + if err != nil { + return fmt.Errorf("expanding `identity`: %+v", err) + } + payload.Identity = identity + + } + + if d.HasChange("public_network_access_enabled") { + publicNetworkEnabled := namespaces.PublicNetworkAccessEnabled + if !d.Get("public_network_access_enabled").(bool) { + publicNetworkEnabled = namespaces.PublicNetworkAccessDisabled + } + payload.Properties.PublicNetworkAccess = &publicNetworkEnabled + } + + if d.HasChange("sku") { + sku := d.Get("sku").(string) + s := namespaces.SkuTier(sku) + payload.Sku = &namespaces.SBSku{ + Name: namespaces.SkuName(sku), + Tier: &s, + } + } + + if d.HasChange("customer_managed_key") { + payload.Properties.Encryption = expandServiceBusNamespaceEncryption(d.Get("customer_managed_key").([]interface{})) + } + + if d.HasChange("local_auth_enabled") { + payload.Properties.DisableLocalAuth = pointer.To(!d.Get("local_auth_enabled").(bool)) + } + + if d.HasChange("tags") { + payload.Tags = expandTags(t) + } + + if d.HasChange("minimum_tls_version") { + if tlsValue := d.Get("minimum_tls_version").(string); tlsValue != "" { + minimumTls := namespaces.TlsVersion(tlsValue) + payload.Properties.MinimumTlsVersion = &minimumTls + } + } + if d.HasChange("capacity") { + sku := d.Get("sku").(string) + if capacity := d.Get("capacity"); capacity != nil { + if !strings.EqualFold(sku, string(namespaces.SkuNamePremium)) && capacity.(int) > 0 { + return fmt.Errorf("service bus SKU %q only supports `capacity` of 0", sku) + } + if strings.EqualFold(sku, string(namespaces.SkuNamePremium)) && capacity.(int) == 0 { + return fmt.Errorf("service bus SKU %q only supports `capacity` of 1, 2, 4, 8 or 16", sku) + } + payload.Sku.Capacity = pointer.To(int64(capacity.(int))) + } + } + if err := client.CreateOrUpdateThenPoll(ctx, *id, *payload); err != nil { + return fmt.Errorf("updating %s: %+v", id, err) } d.SetId(id.ID()) @@ -360,13 +463,13 @@ func resourceServiceBusNamespaceCreateUpdate(d *pluginsdk.ResourceData, meta int // if the network rule set has been removed from config, reset it instead as there is no way to remove a rule set if len(oldNetworkRuleSet.([]interface{})) == 1 && len(newNetworkRuleSet.([]interface{})) == 0 { log.Printf("[DEBUG] Resetting Network Rule Set associated with %s..", id) - if err = resetNetworkRuleSetForNamespace(ctx, client, id); err != nil { + if err = resetNetworkRuleSetForNamespace(ctx, client, *id); err != nil { return err } log.Printf("[DEBUG] Reset the Existing Network Rule Set associated with %s", id) } else { log.Printf("[DEBUG] Creating the Network Rule Set associated with %s..", id) - if err = createNetworkRuleSetForNamespace(ctx, client, id, newNetworkRuleSet.([]interface{})); err != nil { + if err = createNetworkRuleSetForNamespace(ctx, client, *id, newNetworkRuleSet.([]interface{})); err != nil { return err } log.Printf("[DEBUG] Created the Network Rule Set associated with %s", id) @@ -507,20 +610,34 @@ func expandServiceBusNamespaceEncryption(input []interface{}) *namespaces.Encryp v := input[0].(map[string]interface{}) keyId, _ := keyVaultParse.ParseOptionallyVersionedNestedItemID(v["key_vault_key_id"].(string)) keySource := namespaces.KeySourceMicrosoftPointKeyVault - return &namespaces.Encryption{ - KeyVaultProperties: &[]namespaces.KeyVaultProperties{ + + encryption := namespaces.Encryption{ + KeySource: &keySource, + RequireInfrastructureEncryption: pointer.To(v["infrastructure_encryption_enabled"].(bool)), + } + + if v["identity_id"].(string) == "" { + encryption.KeyVaultProperties = &[]namespaces.KeyVaultProperties{ { - KeyName: utils.String(keyId.Name), - KeyVersion: utils.String(keyId.Version), - KeyVaultUri: utils.String(keyId.KeyVaultBaseUrl), + KeyName: pointer.To(keyId.Name), + KeyVersion: pointer.To(keyId.Version), + KeyVaultUri: pointer.To(keyId.KeyVaultBaseUrl), + }, + } + } else { + encryption.KeyVaultProperties = &[]namespaces.KeyVaultProperties{ + { + KeyName: pointer.To(keyId.Name), + KeyVersion: pointer.To(keyId.Version), + KeyVaultUri: pointer.To(keyId.KeyVaultBaseUrl), Identity: &namespaces.UserAssignedIdentityProperties{ - UserAssignedIdentity: utils.String(v["identity_id"].(string)), + UserAssignedIdentity: pointer.To(v["identity_id"].(string)), }, }, - }, - KeySource: &keySource, - RequireInfrastructureEncryption: utils.Bool(v["infrastructure_encryption_enabled"].(bool)), + } } + + return &encryption } func flattenServiceBusNamespaceEncryption(encryption *namespaces.Encryption) ([]interface{}, error) { @@ -631,7 +748,7 @@ func createNetworkRuleSetForNamespace(ctx context.Context, client *namespaces.Na VirtualNetworkRules: vnetRule, IPRules: ipRule, PublicNetworkAccess: &publicNetworkAccess, - TrustedServiceAccessEnabled: utils.Bool(item["trusted_services_allowed"].(bool)), + TrustedServiceAccessEnabled: pointer.To(item["trusted_services_allowed"].(bool)), }, } @@ -675,17 +792,6 @@ func flattenServiceBusNamespaceNetworkRuleSet(networkRuleSet namespaces.NetworkR networkRules := flattenServiceBusNamespaceVirtualNetworkRules(networkRuleSet.VirtualNetworkRules) ipRules := flattenServiceBusNamespaceIPRules(networkRuleSet.IPRules) - // only set network rule set if the values are different than what they are defaulted to during namespace creation - // this has to wait until 4.0 due to `azurerm_servicebus_namespace_network_rule_set` which forces `network_rule_set` to be Optional/Computed - - if defaultAction == string(namespaces.DefaultActionAllow) && - publicNetworkAccess == namespaces.PublicNetworkAccessFlagEnabled && - !trustedServiceEnabled && - len(networkRules) == 0 && - len(ipRules) == 0 { - return []interface{}{} - } - return []interface{}{map[string]interface{}{ "default_action": defaultAction, "trusted_services_allowed": trustedServiceEnabled, diff --git a/website/docs/r/servicebus_namespace.html.markdown b/website/docs/r/servicebus_namespace.html.markdown index 94d2d20cf3c4..732b0768c042 100644 --- a/website/docs/r/servicebus_namespace.html.markdown +++ b/website/docs/r/servicebus_namespace.html.markdown @@ -87,7 +87,7 @@ A `customer_managed_key` block supports the following: * `key_vault_key_id` - (Required) The ID of the Key Vault Key which should be used to Encrypt the data in this ServiceBus Namespace. -* `identity_id` - (Required) The ID of the User Assigned Identity that has access to the key. +* `identity_id` - (Optional) The ID of the User Assigned Identity that has access to the key. * `infrastructure_encryption_enabled` - (Optional) Used to specify whether enable Infrastructure Encryption (Double Encryption). Changing this forces a new resource to be created.