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

azurerm_api_connection - display_name and parameter_values are no longer ForceNew #28721

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
77 changes: 51 additions & 26 deletions internal/services/connections/api_connection_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,26 +62,17 @@ func resourceConnection() *pluginsdk.Resource {
"display_name": {
Type: pluginsdk.TypeString,
Optional: true,
Default: "Service Bus",
// @tombuildsstuff: this can't be patched in API version 2016-06-01 and there isn't Swagger for
// API version 2018-07-01-preview, so I guess this is ForceNew for now
//
// > Status=400 Code="PatchApiConnectionPropertiesNotSupported"
// > Message="The request to patch API connection 'acctestconn-220307135205093274' is not supported.
// > None of the fields inside the properties object can be patched."
ForceNew: true,
// Note: O+C because Azure sets a default when `display_name` is not defined but the value depends on which managed API is provided.
// For example:
// - Managed API `servicebus` defaults to `Service Bus`
// - Managed API `sftpwithssh` defaults to `SFTP - SSH`
Computed: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"parameter_values": {
Type: pluginsdk.TypeMap,
Optional: true,
// @tombuildsstuff: this can't be patched in API version 2016-06-01 and there isn't Swagger for
// API version 2018-07-01-preview, so I guess this is ForceNew for now
//
// > Status=400 Code="PatchApiConnectionPropertiesNotSupported"
// > Message="The request to patch API connection 'acctestconn-220307135205093274' is not supported.
// > None of the fields inside the properties object can be patched."
ForceNew: true,
Elem: &pluginsdk.Schema{
Type: pluginsdk.TypeString,
},
Expand Down Expand Up @@ -171,7 +162,9 @@ func resourceConnectionRead(d *schema.ResourceData, meta interface{}) error {
}
d.Set("managed_api_id", apiId)

if err := d.Set("parameter_values", props.ParameterValues); err != nil {
// In version 2016-06-01 the API doesn't return `ParameterValues`.
// The non-secret parameters are returned in `NonSecretParameterValues` instead.
if err := d.Set("parameter_values", flattenParameterValues(pointer.From(props.NonSecretParameterValues))); err != nil {
return fmt.Errorf("setting `parameter_values`: %+v", err)
}
}
Expand All @@ -194,17 +187,37 @@ func resourceConnectionUpdate(d *schema.ResourceData, meta interface{}) error {
return err
}

model := connections.ApiConnectionDefinition{
// @tombuildsstuff: this can't be patched in API version 2016-06-01 and there isn't Swagger for
// API version 2018-07-01-preview, so I guess this is ForceNew for now. The following error is returned
// for both CreateOrUpdate and Update:
//
// > Status=400 Code="PatchApiConnectionPropertiesNotSupported"
// > Message="The request to patch API connection 'acctestconn-220307135205093274' is not supported.
// > None of the fields inside the properties object can be patched."
Tags: tags.Expand(d.Get("tags").(map[string]interface{})),
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 %s: `model` was nil", id)
}

if existing.Model.Properties == nil {
return fmt.Errorf("retrieving %s: `model.Properties` was nil", id)
}
props := existing.Model.Properties

if d.HasChange("display_name") {
props.DisplayName = pointer.To(d.Get("display_name").(string))
}
if _, err := client.Update(ctx, *id, model); err != nil {

// The GET operation returns `NonSecretParameterValues` but we're making updates through `ParameterValues`
// so we remove `NonSecretParameterValues` from the request to avoid conflicting parameters.
// this is fixed in later (preview) versions of the API but these don't have an API spec available.
props.NonSecretParameterValues = nil
if d.HasChange("parameter_values") {
props.ParameterValues = pointer.To(d.Get("parameter_values").(map[string]interface{}))
}

if d.HasChange("tags") {
existing.Model.Tags = tags.Expand(d.Get("tags").(map[string]interface{}))
}

if _, err := client.CreateOrUpdate(ctx, *id, *existing.Model); err != nil {
return fmt.Errorf("updating %s: %+v", *id, err)
}

Expand All @@ -227,3 +240,15 @@ func resourceConnectionDelete(d *schema.ResourceData, meta interface{}) error {

return nil
}

// Because this API may return other primitive types for `parameter_values`
// we need to ensure each value in the map is a string to prevent panics when setting this into state.
func flattenParameterValues(input map[string]interface{}) map[string]string {
output := make(map[string]string)

for k, v := range input {
output[k] = fmt.Sprintf("%v", v)
}

return output
}
30 changes: 10 additions & 20 deletions internal/services/connections/api_connection_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import (
"fmt"
"testing"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-sdk/resource-manager/web/2016-06-01/connections"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/utils"
)

type ApiConnectionTestResource struct{}
Expand Down Expand Up @@ -78,22 +78,18 @@ func (t ApiConnectionTestResource) Exists(ctx context.Context, client *clients.C
resp, err := client.Connections.ConnectionsClient.Get(ctx, *id)
if err != nil {
if response.WasNotFound(resp.HttpResponse) {
return utils.Bool(false), nil
return pointer.To(false), nil
}

return nil, fmt.Errorf("retrieving %s: %+v", *id, err)
}

return utils.Bool(resp.Model != nil), nil
return pointer.To(resp.Model != nil), nil
}

func (t ApiConnectionTestResource) basic(data acceptance.TestData) string {
template := t.template(data)
return fmt.Sprintf(`
provider "azurerm" {
features {}
}

%[1]s

resource "azurerm_api_connection" "test" {
Expand All @@ -105,7 +101,6 @@ resource "azurerm_api_connection" "test" {
}

func (t ApiConnectionTestResource) requiresImport(data acceptance.TestData) string {
template := t.basic(data)
return fmt.Sprintf(`
%[1]s

Expand All @@ -114,16 +109,11 @@ resource "azurerm_api_connection" "import" {
resource_group_name = azurerm_api_connection.test.resource_group_name
managed_api_id = azurerm_api_connection.test.managed_api_id
}
`, template)
`, t.basic(data))
}

func (t ApiConnectionTestResource) complete(data acceptance.TestData) string {
template := t.template(data)
return fmt.Sprintf(`
provider "azurerm" {
features {}
}

%[1]s

resource "azurerm_api_connection" "test" {
Expand All @@ -144,23 +134,19 @@ resource "azurerm_api_connection" "test" {
ignore_changes = ["parameter_values"] # not returned from the API
}
}
`, template, data.RandomInteger)
`, t.template(data), data.RandomInteger)
}

func (t ApiConnectionTestResource) completeUpdated(data acceptance.TestData) string {
template := t.template(data)
return fmt.Sprintf(`
provider "azurerm" {
features {}
}

%[1]s

resource "azurerm_api_connection" "test" {
name = "acctestconn-%[2]d"
resource_group_name = azurerm_resource_group.test.name
managed_api_id = data.azurerm_managed_api.test.id
display_name = "Example 1"
display_name = "Example 2"

parameter_values = {
connectionString = azurerm_servicebus_namespace.test.default_primary_connection_string
Expand All @@ -180,6 +166,10 @@ resource "azurerm_api_connection" "test" {

func (ApiConnectionTestResource) template(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}

resource "azurerm_resource_group" "test" {
name = "acctestRG-conn-%[1]d"
location = %[2]q
Expand Down
10 changes: 5 additions & 5 deletions website/docs/r/api_connection.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ data "azurerm_managed_api" "example" {
}

resource "azurerm_servicebus_namespace" "example" {
name = "acctestsbn-conn-example"
name = "example-namespace"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
sku = "Basic"
Expand Down Expand Up @@ -59,17 +59,17 @@ resource "azurerm_api_connection" "example" {

The following arguments are supported:

* `managed_api_id` - (Required) The ID of the Managed API which this API Connection is linked to. Changing this forces a new API Connection to be created.

* `name` - (Required) The Name which should be used for this API Connection. Changing this forces a new API Connection to be created.

* `resource_group_name` - (Required) The name of the Resource Group where this API Connection should exist. Changing this forces a new API Connection to be created.

* `managed_api_id` - (Required) The ID of the Managed API which this API Connection is linked to. Changing this forces a new API Connection to be created.

---

* `display_name` - (Optional) A display name for this API Connection. Defaults to `Service Bus`. Changing this forces a new API Connection to be created.
* `display_name` - (Optional) A display name for this API Connection.

* `parameter_values` - (Optional) A map of parameter values associated with this API Connection. Changing this forces a new API Connection to be created.
* `parameter_values` - (Optional) A map of parameter values associated with this API Connection.

-> **Note:** The Azure API doesn't return sensitive parameters in the API response which can lead to a diff, as such you may need to use Terraform's `ignore_changes` functionality on this field as shown in the Example Usage above.

Expand Down
Loading