Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
176 changes: 140 additions & 36 deletions internal/services/servicebus/servicebus_namespace_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 {
Expand Down Expand Up @@ -119,7 +118,7 @@ func resourceServiceBusNamespace() *pluginsdk.Resource {

"identity_id": {
Type: pluginsdk.TypeString,
Required: true,
Optional: true,
ValidateFunc: commonids.ValidateUserAssignedIdentityID,
},

Expand Down Expand Up @@ -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)
ctx, cancel := timeouts.ForCreate(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)
Copy link
Copy Markdown
Contributor

@stephybun stephybun Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't comment further down, but I don't think we need the d.IsNewResource() check down on line 287 anymore

Expand Down Expand Up @@ -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),
Expand All @@ -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 {
Expand All @@ -346,11 +345,113 @@ 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") {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Without any context here, why do we need to wrap this update on the network rule set ind.HasChange in the Create?

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.ForUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()

log.Printf("[INFO] preparing arguments for ServiceBus Namespace update")

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)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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)
if existing.Model == nil {
return fmt.Errorf("retrieving %s: `model` was nil", *id)
}
if existing.Model.Properties == nil {
return fmt.Errorf("retrieving %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(d.Get("tags").(map[string]interface{}))
}

if d.HasChange("minimum_tls_version") {
if tlsValue := d.Get("minimum_tls_version").(string); tlsValue != "" {
minimumTls := namespaces.TlsVersion(tlsValue)
payload.Properties.MinimumTlsVersion = &minimumTls
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
minimumTls := namespaces.TlsVersion(tlsValue)
payload.Properties.MinimumTlsVersion = &minimumTls
payload.Properties.MinimumTlsVersion = pointer.To(namespaces.TlsVersion(tlsValue))

}
}

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 {
Comment on lines +433 to +436
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect there's a typo here and one of these conditions should be comparing against a different sku

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't write this, just copied from the create, but I believe it is correct. The first check is that it is NOT premium and greater than 0, because basic and standard skus can only have a capacity of 0. The second check returns an error if it is premium and set to 0 because if the sku is premium the sku needs to be 1,2,4,8 or 16.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I missed the not at the front of the first condition.

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())
Expand All @@ -360,16 +461,16 @@ 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 {
log.Printf("[DEBUG] Creating/updating the Network Rule Set associated with %s..", id)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this just be

Suggested change
log.Printf("[DEBUG] Creating/updating the Network Rule Set associated with %s..", id)
log.Printf("[DEBUG] Updating the Network Rule Set associated with %s..", id)

if err = createNetworkRuleSetForNamespace(ctx, client, *id, newNetworkRuleSet.([]interface{})); err != nil {
return err
}
log.Printf("[DEBUG] Created the Network Rule Set associated with %s", id)
log.Printf("[DEBUG] Created/updated the Network Rule Set associated with %s", id)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise

Suggested change
log.Printf("[DEBUG] Created/updated the Network Rule Set associated with %s", id)
log.Printf("[DEBUG] Updated the Network Rule Set associated with %s", id)

}
}

Expand Down Expand Up @@ -507,20 +608,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: pointer.To(keyId.Name),
KeyVersion: pointer.To(keyId.Version),
KeyVaultUri: pointer.To(keyId.KeyVaultBaseUrl),
},
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we allow identity_id to be Optional, can users actually create a servicebus namespace with CMK using SystemAssigned identity on first go?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

they can't, so I've reverted this to how it was before 👍

} else {
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),
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) {
Expand Down Expand Up @@ -631,7 +746,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)),
},
}

Expand Down Expand Up @@ -675,17 +790,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,
Expand Down
2 changes: 1 addition & 1 deletion website/docs/r/servicebus_namespace.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* `identity_id` - (Optional) The ID of the User Assigned Identity that has access to the key.
* `identity_id` - (Required) The ID of the User Assigned Identity that has access to the key.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed! thank you


* `infrastructure_encryption_enabled` - (Optional) Used to specify whether enable Infrastructure Encryption (Double Encryption). Changing this forces a new resource to be created.

Expand Down