From b5ac97f03682c234d0cb8b879744162810baeb45 Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Fri, 6 Sep 2019 12:58:16 -0700 Subject: [PATCH 1/4] Fix crash in ExpandStringSlice --- azurerm/resource_arm_key_vault_certificate.go | 3 +- ...resource_arm_key_vault_certificate_test.go | 114 ++++++++++++++++++ azurerm/utils/common_marshal.go | 6 +- 3 files changed, 121 insertions(+), 2 deletions(-) diff --git a/azurerm/resource_arm_key_vault_certificate.go b/azurerm/resource_arm_key_vault_certificate.go index 94c8c2c92579..0d1e39c63c6e 100644 --- a/azurerm/resource_arm_key_vault_certificate.go +++ b/azurerm/resource_arm_key_vault_certificate.go @@ -236,7 +236,8 @@ func resourceArmKeyVaultCertificate() *schema.Resource { Computed: true, ForceNew: true, Elem: &schema.Schema{ - Type: schema.TypeString, + Type: schema.TypeString, + ValidateFunc: validate.NoEmptyStrings, }, }, "key_usage": { diff --git a/azurerm/resource_arm_key_vault_certificate_test.go b/azurerm/resource_arm_key_vault_certificate_test.go index 4d85e5f23b60..7c368d91db5a 100644 --- a/azurerm/resource_arm_key_vault_certificate_test.go +++ b/azurerm/resource_arm_key_vault_certificate_test.go @@ -245,6 +245,28 @@ func TestAccAzureRMKeyVaultCertificate_basicExtendedKeyUsage(t *testing.T) { }) } +func TestAccAzureRMKeyVaultCertificate_emptyExtendedKeyUsage(t *testing.T) { + resourceName := "azurerm_key_vault_certificate.test" + rs := acctest.RandString(6) + config := testAccAzureRMKeyVaultCertificate_emptyExtendedKeyUsage(rs, testLocation()) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKeyVaultCertificateExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "certificate_data"), + resource.TestCheckResourceAttr(resourceName, "certificate_policy.0.x509_certificate_properties.0.extended_key_usage.#", "0"), + ), + }, + }, + }) +} + func testCheckAzureRMKeyVaultCertificateDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*ArmClient).keyvault.ManagementClient ctx := testAccProvider.Meta().(*ArmClient).StopContext @@ -896,3 +918,95 @@ resource "azurerm_key_vault_certificate" "test" { } `, rString, location, rString, rString) } + +func testAccAzureRMKeyVaultCertificate_emptyExtendedKeyUsage(rString string, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%s" + location = "%s" +} + +resource "azurerm_key_vault" "test" { + name = "acctestkeyvault%s" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + + sku_name = "standard" + + access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${data.azurerm_client_config.current.service_principal_object_id}" + + certificate_permissions = [ + "create", + "delete", + "get", + "update", + ] + + key_permissions = [ + "create", + ] + + secret_permissions = [ + "set", + ] + + storage_permissions = [ + "set", + ] + } +} + +resource "azurerm_key_vault_certificate" "test" { + name = "acctestcert%s" + vault_uri = "${azurerm_key_vault.test.vault_uri}" + + certificate_policy { + issuer_parameters { + name = "Self" + } + + key_properties { + exportable = true + key_size = 2048 + key_type = "RSA" + reuse_key = true + } + + lifetime_action { + action { + action_type = "AutoRenew" + } + + trigger { + days_before_expiry = 30 + } + } + + secret_properties { + content_type = "application/x-pkcs12" + } + + x509_certificate_properties { + extended_key_usage = [] + + key_usage = [ + "cRLSign", + "dataEncipherment", + "digitalSignature", + "keyAgreement", + "keyCertSign", + "keyEncipherment", + ] + + subject = "CN=hello-world" + validity_in_months = 12 + } + } +} +`, rString, location, rString, rString) +} diff --git a/azurerm/utils/common_marshal.go b/azurerm/utils/common_marshal.go index 9d3b98bdad3d..33bdc24c4fbf 100644 --- a/azurerm/utils/common_marshal.go +++ b/azurerm/utils/common_marshal.go @@ -3,7 +3,11 @@ package utils func ExpandStringSlice(input []interface{}) *[]string { result := make([]string, 0) for _, item := range input { - result = append(result, item.(string)) + if item != nil { + result = append(result, item.(string)) + } else { + result = append(result, "") + } } return &result } From b95aa6a9454e51639ea070530a1969750f23ef64 Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Fri, 6 Sep 2019 12:59:05 -0700 Subject: [PATCH 2/4] Revert "Fix crash in ExpandStringSlice" This reverts commit b5ac97f03682c234d0cb8b879744162810baeb45. --- azurerm/resource_arm_key_vault_certificate.go | 3 +- ...resource_arm_key_vault_certificate_test.go | 114 ------------------ azurerm/utils/common_marshal.go | 6 +- 3 files changed, 2 insertions(+), 121 deletions(-) diff --git a/azurerm/resource_arm_key_vault_certificate.go b/azurerm/resource_arm_key_vault_certificate.go index 0d1e39c63c6e..94c8c2c92579 100644 --- a/azurerm/resource_arm_key_vault_certificate.go +++ b/azurerm/resource_arm_key_vault_certificate.go @@ -236,8 +236,7 @@ func resourceArmKeyVaultCertificate() *schema.Resource { Computed: true, ForceNew: true, Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validate.NoEmptyStrings, + Type: schema.TypeString, }, }, "key_usage": { diff --git a/azurerm/resource_arm_key_vault_certificate_test.go b/azurerm/resource_arm_key_vault_certificate_test.go index 7c368d91db5a..4d85e5f23b60 100644 --- a/azurerm/resource_arm_key_vault_certificate_test.go +++ b/azurerm/resource_arm_key_vault_certificate_test.go @@ -245,28 +245,6 @@ func TestAccAzureRMKeyVaultCertificate_basicExtendedKeyUsage(t *testing.T) { }) } -func TestAccAzureRMKeyVaultCertificate_emptyExtendedKeyUsage(t *testing.T) { - resourceName := "azurerm_key_vault_certificate.test" - rs := acctest.RandString(6) - config := testAccAzureRMKeyVaultCertificate_emptyExtendedKeyUsage(rs, testLocation()) - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - Providers: testAccProviders, - CheckDestroy: testCheckAzureRMKeyVaultCertificateDestroy, - Steps: []resource.TestStep{ - { - Config: config, - Check: resource.ComposeTestCheckFunc( - testCheckAzureRMKeyVaultCertificateExists(resourceName), - resource.TestCheckResourceAttrSet(resourceName, "certificate_data"), - resource.TestCheckResourceAttr(resourceName, "certificate_policy.0.x509_certificate_properties.0.extended_key_usage.#", "0"), - ), - }, - }, - }) -} - func testCheckAzureRMKeyVaultCertificateDestroy(s *terraform.State) error { client := testAccProvider.Meta().(*ArmClient).keyvault.ManagementClient ctx := testAccProvider.Meta().(*ArmClient).StopContext @@ -918,95 +896,3 @@ resource "azurerm_key_vault_certificate" "test" { } `, rString, location, rString, rString) } - -func testAccAzureRMKeyVaultCertificate_emptyExtendedKeyUsage(rString string, location string) string { - return fmt.Sprintf(` -data "azurerm_client_config" "current" {} - -resource "azurerm_resource_group" "test" { - name = "acctestRG-%s" - location = "%s" -} - -resource "azurerm_key_vault" "test" { - name = "acctestkeyvault%s" - location = "${azurerm_resource_group.test.location}" - resource_group_name = "${azurerm_resource_group.test.name}" - tenant_id = "${data.azurerm_client_config.current.tenant_id}" - - sku_name = "standard" - - access_policy { - tenant_id = "${data.azurerm_client_config.current.tenant_id}" - object_id = "${data.azurerm_client_config.current.service_principal_object_id}" - - certificate_permissions = [ - "create", - "delete", - "get", - "update", - ] - - key_permissions = [ - "create", - ] - - secret_permissions = [ - "set", - ] - - storage_permissions = [ - "set", - ] - } -} - -resource "azurerm_key_vault_certificate" "test" { - name = "acctestcert%s" - vault_uri = "${azurerm_key_vault.test.vault_uri}" - - certificate_policy { - issuer_parameters { - name = "Self" - } - - key_properties { - exportable = true - key_size = 2048 - key_type = "RSA" - reuse_key = true - } - - lifetime_action { - action { - action_type = "AutoRenew" - } - - trigger { - days_before_expiry = 30 - } - } - - secret_properties { - content_type = "application/x-pkcs12" - } - - x509_certificate_properties { - extended_key_usage = [] - - key_usage = [ - "cRLSign", - "dataEncipherment", - "digitalSignature", - "keyAgreement", - "keyCertSign", - "keyEncipherment", - ] - - subject = "CN=hello-world" - validity_in_months = 12 - } - } -} -`, rString, location, rString, rString) -} diff --git a/azurerm/utils/common_marshal.go b/azurerm/utils/common_marshal.go index 33bdc24c4fbf..9d3b98bdad3d 100644 --- a/azurerm/utils/common_marshal.go +++ b/azurerm/utils/common_marshal.go @@ -3,11 +3,7 @@ package utils func ExpandStringSlice(input []interface{}) *[]string { result := make([]string, 0) for _, item := range input { - if item != nil { - result = append(result, item.(string)) - } else { - result = append(result, "") - } + result = append(result, item.(string)) } return &result } From 3b17647e083abcec239f76a2c300f6daac0eea45 Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Thu, 12 Sep 2019 13:13:51 -0700 Subject: [PATCH 3/4] Adding bot connection and fixing bot channels registration --- azurerm/internal/services/bot/client.go | 13 +- azurerm/provider.go | 1 + .../resource_arm_bot_channels_registration.go | 17 +- ...urce_arm_bot_channels_registration_test.go | 28 +- azurerm/resource_arm_bot_connection.go | 262 ++++++++++++++++++ azurerm/resource_arm_bot_connection_test.go | 205 ++++++++++++++ website/azurerm.erb | 4 + website/docs/r/bot_connection.markdown | 79 ++++++ 8 files changed, 585 insertions(+), 24 deletions(-) create mode 100644 azurerm/resource_arm_bot_connection.go create mode 100644 azurerm/resource_arm_bot_connection_test.go create mode 100644 website/docs/r/bot_connection.markdown diff --git a/azurerm/internal/services/bot/client.go b/azurerm/internal/services/bot/client.go index 9bff0fc5eb1f..49aff3685b54 100644 --- a/azurerm/internal/services/bot/client.go +++ b/azurerm/internal/services/bot/client.go @@ -6,15 +6,20 @@ import ( ) type Client struct { - BotClient *botservice.BotsClient + BotClient *botservice.BotsClient + ConnectionClient *botservice.BotConnectionClient } func BuildClient(o *common.ClientOptions) *Client { - BotClient := botservice.NewBotsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) - o.ConfigureClient(&BotClient.Client, o.ResourceManagerAuthorizer) + botClient := botservice.NewBotsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&botClient.Client, o.ResourceManagerAuthorizer) + + connectionClient := botservice.NewBotConnectionClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&connectionClient.Client, o.ResourceManagerAuthorizer) return &Client{ - BotClient: &BotClient, + BotClient: &botClient, + ConnectionClient: &connectionClient, } } diff --git a/azurerm/provider.go b/azurerm/provider.go index 1da694a717e5..e7a0e53b7c88 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -175,6 +175,7 @@ func Provider() terraform.ResourceProvider { "azurerm_batch_application": resourceArmBatchApplication(), "azurerm_batch_certificate": resourceArmBatchCertificate(), "azurerm_bot_channels_registration": resourceArmBotChannelsRegistration(), + "azurerm_bot_connection": resourceArmBotConnection(), "azurerm_batch_pool": resourceArmBatchPool(), "azurerm_cdn_endpoint": resourceArmCdnEndpoint(), "azurerm_cdn_profile": resourceArmCdnProfile(), diff --git a/azurerm/resource_arm_bot_channels_registration.go b/azurerm/resource_arm_bot_channels_registration.go index 2722ba7fc835..31acc61a6c99 100644 --- a/azurerm/resource_arm_bot_channels_registration.go +++ b/azurerm/resource_arm_bot_channels_registration.go @@ -32,7 +32,7 @@ func resourceArmBotChannelsRegistration() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validate.CosmosEntityName, + ValidateFunc: validate.NoEmptyStrings, }, "resource_group_name": azure.SchemaResourceGroupName(), @@ -53,7 +53,7 @@ func resourceArmBotChannelsRegistration() *schema.Resource { Type: schema.TypeString, ForceNew: true, Required: true, - ValidateFunc: azure.ValidateResourceID, + ValidateFunc: validate.UUID, }, "display_name": { @@ -109,17 +109,12 @@ func resourceArmBotChannelsRegistrationCreate(d *schema.ResourceData, meta inter if !utils.ResponseWasNotFound(existing.Response) { return fmt.Errorf("Error checking for presence of creating Bot Channels Registration %q (Resource Group %q): %+v", name, resourceGroup, err) } - } else { - id, err := azure.CosmosGetIDFromResponse(existing.Response) - if err != nil { - return fmt.Errorf("Error generating import ID for Bot Channels Registration %q (Resource Group %q): %+v", name, resourceGroup, err) - } - - return tf.ImportAsExistsError("azurerm_bot", id) + } + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_bot_channels_registration", *existing.ID) } } - t := d.Get("tags").(map[string]interface{}) displayName := d.Get("display_name").(string) if displayName == "" { displayName = name @@ -139,7 +134,7 @@ func resourceArmBotChannelsRegistrationCreate(d *schema.ResourceData, meta inter Name: botservice.SkuName(d.Get("sku").(string)), }, Kind: botservice.KindBot, - Tags: tags.Expand(t), + Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } if _, err := client.Create(ctx, resourceGroup, name, bot); err != nil { diff --git a/azurerm/resource_arm_bot_channels_registration_test.go b/azurerm/resource_arm_bot_channels_registration_test.go index c50abb158d86..455f974e705f 100644 --- a/azurerm/resource_arm_bot_channels_registration_test.go +++ b/azurerm/resource_arm_bot_channels_registration_test.go @@ -14,19 +14,29 @@ import ( func TestAccAzureRMBotChannelsRegistration(t *testing.T) { // NOTE: this is a combined test rather than separate split out tests due to // Azure only being able provision against one app id at a time - testCases := map[string]func(t *testing.T){ - "basic": testAccAzureRMBotChannelsRegistration_basic, - "update": testAccAzureRMBotChannelsRegistration_update, - "complete": testAccAzureRMBotChannelsRegistration_complete, + testCases := map[string]map[string]func(t *testing.T){ + "basic": { + "basic": testAccAzureRMBotChannelsRegistration_basic, + "update": testAccAzureRMBotChannelsRegistration_update, + "complete": testAccAzureRMBotChannelsRegistration_complete, + }, + "coonection": { + "basic": testAccAzureRMBotConnection_basic, + "complete": testAccAzureRMBotConnection_complete, + }, } - for name, tc := range testCases { - tc := tc - t.Run(name, func(t *testing.T) { - tc(t) + for group, m := range testCases { + m := m + t.Run(group, func(t *testing.T) { + for name, tc := range m { + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } }) } - } func testAccAzureRMBotChannelsRegistration_basic(t *testing.T) { diff --git a/azurerm/resource_arm_bot_connection.go b/azurerm/resource_arm_bot_connection.go new file mode 100644 index 000000000000..0cf218b7d2b5 --- /dev/null +++ b/azurerm/resource_arm_bot_connection.go @@ -0,0 +1,262 @@ +package azurerm + +import ( + "fmt" + "github.com/Azure/azure-sdk-for-go/services/preview/botservice/mgmt/2018-07-12/botservice" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" + "log" + + "github.com/hashicorp/terraform/helper/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmBotConnection() *schema.Resource { + return &schema.Resource{ + Create: resourceArmBotConnectionCreate, + Read: resourceArmBotConnectionRead, + Update: resourceArmBotConnectionUpdate, + Delete: resourceArmBotConnectionDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "location": azure.SchemaLocation(), + + "resource_group_name": azure.SchemaResourceGroupName(), + + "bot_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "service_provider_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "client_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "client_secret": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "scopes": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "parameters": { + Type: schema.TypeMap, + Optional: true, + }, + + "tags": tags.Schema(), + }, + } +} + +func resourceArmBotConnectionCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).bot.ConnectionClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + botName := d.Get("bot_name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, resourceGroup, name, botName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of creating Bot Connection %q (Resource Group %q / Bot %q): %+v", name, resourceGroup, botName, err) + } + } + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_bot_connection", *existing.ID) + } + } + + connection := botservice.ConnectionSetting{ + Properties: &botservice.ConnectionSettingProperties{ + ServiceProviderDisplayName: utils.String(d.Get("service_provider_name").(string)), + ClientID: utils.String(d.Get("client_id").(string)), + ClientSecret: utils.String(d.Get("client_secret").(string)), + Scopes: utils.String(d.Get("scopes").(string)), + Parameters: expandAzureRMBotConnectionParameters(d.Get("parameters").(map[string]interface{})), + }, + Kind: botservice.KindBot, + Location: utils.String(d.Get("location").(string)), + Tags: tags.Expand(d.Get("tags").(map[string]interface{})), + } + + if _, err := client.Create(ctx, resourceGroup, botName, name, connection); err != nil { + return fmt.Errorf("Error issuing create request for creating Bot Connection %q (Resource Group %q / Bot %q): %+v", name, resourceGroup, botName, err) + } + + resp, err := client.Get(ctx, resourceGroup, botName, name) + if err != nil { + return fmt.Errorf("Error making get request for Bot Connection %q (Resource Group %q / Bot %q): %+v", name, resourceGroup, botName, err) + } + + if resp.ID == nil { + return fmt.Errorf("Cannot read Bot Connection %q (Resource Group %q / Bot %q): %+v", name, resourceGroup, botName, err) + } + + d.SetId(*resp.ID) + + return resourceArmBotConnectionRead(d, meta) +} + +func resourceArmBotConnectionRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).bot.ConnectionClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + botName := id.Path["botServices"] + name := id.Path["connections"] + + resp, err := client.Get(ctx, id.ResourceGroup, botName, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Error reading Bot Connection %q (Resource Group %q / Bot %q)", name, id.ResourceGroup, botName) + d.SetId("") + return nil + } + + return fmt.Errorf("Error reading Bot Connection %q (Resource Group %q / Bot %q): %+v", name, id.ResourceGroup, botName, err) + } + + d.Set("resource_group_name", id.ResourceGroup) + d.Set("name", name) + d.Set("bot_name", botName) + d.Set("location", resp.Location) + + if props := resp.Properties; props != nil { + d.Set("client_id", props.ClientID) + d.Set("scopes", props.Scopes) + if parameters := props.Parameters; parameters != nil { + if err := d.Set("parameters", flattenAzureRMBotConnectionParameters(parameters)); err != nil { + return fmt.Errorf("Error setting `parameters`: %+v", err) + } + } + } + + return tags.FlattenAndSet(d, resp.Tags) +} + +func resourceArmBotConnectionUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).bot.ConnectionClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + resourceGroup := d.Get("resource_group_name").(string) + botName := d.Get("bot_name").(string) + + connection := botservice.ConnectionSetting{ + Properties: &botservice.ConnectionSettingProperties{ + ServiceProviderDisplayName: utils.String(d.Get("service_provider_name").(string)), + ClientID: utils.String(d.Get("client_id").(string)), + ClientSecret: utils.String(d.Get("client_secret").(string)), + Scopes: utils.String(d.Get("scopes").(string)), + Parameters: expandAzureRMBotConnectionParameters(d.Get("parameters").(map[string]interface{})), + }, + Kind: botservice.KindBot, + Location: utils.String(d.Get("location").(string)), + Tags: tags.Expand(d.Get("tags").(map[string]interface{})), + } + + if _, err := client.Update(ctx, resourceGroup, botName, name, connection); err != nil { + return fmt.Errorf("Error issuing update request for creating Bot Connection %q (Resource Group %q / Bot %q): %+v", name, resourceGroup, botName, err) + } + + resp, err := client.Get(ctx, resourceGroup, botName, name) + if err != nil { + return fmt.Errorf("Error making get request for Bot Connection %q (Resource Group %q / Bot %q): %+v", name, resourceGroup, botName, err) + } + + if resp.ID == nil { + return fmt.Errorf("Cannot read Bot Connection %q (Resource Group %q / Bot %q): %+v", name, resourceGroup, botName, err) + } + + d.SetId(*resp.ID) + + return resourceArmBotConnectionRead(d, meta) + +} + +func resourceArmBotConnectionDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).bot.ConnectionClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + botName := id.Path["botServices"] + name := id.Path["connections"] + + resp, err := client.Delete(ctx, id.ResourceGroup, botName, name) + if err != nil { + if !response.WasNotFound(resp.Response) { + return fmt.Errorf("Error deleting Bot Connection %q (Resource Group %q / Bot %q): %+v", name, id.ResourceGroup, botName, err) + } + } + + return nil +} + +func expandAzureRMBotConnectionParameters(input map[string]interface{}) *[]botservice.ConnectionSettingParameter { + output := make([]botservice.ConnectionSettingParameter, 0) + + for k, v := range input { + output = append(output, botservice.ConnectionSettingParameter{ + Key: utils.String(k), + Value: utils.String(v.(string)), + }) + } + return &output +} + +func flattenAzureRMBotConnectionParameters(input *[]botservice.ConnectionSettingParameter) map[string]interface{} { + output := make(map[string]interface{}) + + for _, parameter := range *input { + if key := parameter.Key; key != nil { + // We disregard the clientSecret and clientId as one is sensitive and the other is returned in the ClientId attribute. + if *key != "clientSecret" && *key != "clientId" && *key != "scopes" { + if value := parameter.Value; value != nil { + output[*key] = *value + } + } + } + } + + return output +} diff --git a/azurerm/resource_arm_bot_connection_test.go b/azurerm/resource_arm_bot_connection_test.go new file mode 100644 index 000000000000..c9b957bcf93c --- /dev/null +++ b/azurerm/resource_arm_bot_connection_test.go @@ -0,0 +1,205 @@ +package azurerm + +import ( + "fmt" + "net/http" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func testAccAzureRMBotConnection_basic(t *testing.T) { + ri := tf.AccRandTimeInt() + config := testAccAzureRMBotConnection_basicConfig(ri, testLocation()) + resourceName := "azurerm_bot_connection.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMBotConnectionDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMBotConnectionExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "client_secret", + "service_provider_name", + }, + }, + }, + }) +} + +func testAccAzureRMBotConnection_complete(t *testing.T) { + ri := tf.AccRandTimeInt() + config := testAccAzureRMBotConnection_completeConfig(ri, testLocation()) + config2 := testAccAzureRMBotConnection_completeUpdateConfig(ri, testLocation()) + resourceName := "azurerm_bot_connection.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMBotConnectionDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMBotConnectionExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "client_secret", + "service_provider_name", + }, + }, + { + Config: config2, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMBotConnectionExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "client_secret", + "service_provider_name", + }, + }, + }, + }) +} + +func testCheckAzureRMBotConnectionExists(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Ensure we have enough information in state to look up in API + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + name := rs.Primary.Attributes["name"] + botName := rs.Primary.Attributes["bot_name"] + resourceGroup, hasResourceGroup := rs.Primary.Attributes["resource_group_name"] + if !hasResourceGroup { + return fmt.Errorf("Bad: no resource group found in state for Bot Channels Registration: %s", name) + } + + client := testAccProvider.Meta().(*ArmClient).bot.ConnectionClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, botName, name) + if err != nil { + return fmt.Errorf("Bad: Get on botConnectionClient: %+v", err) + } + + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Bot Connection %q (resource group: %q / bot: %q) does not exist", name, resourceGroup, botName) + } + + return nil + } +} + +func testCheckAzureRMBotConnectionDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).bot.ConnectionClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_bot" { + continue + } + + name := rs.Primary.Attributes["name"] + botName := rs.Primary.Attributes["bot_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := client.Get(ctx, resourceGroup, botName, name) + + if err != nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("Bot Connection still exists:\n%#v", resp.Properties) + } + } + + return nil +} + +func testAccAzureRMBotConnection_basicConfig(rInt int, location string) string { + template := testAccAzureRMBotChannelsRegistration_basicConfig(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_bot_connection" "test" { + name = "acctestBc%d" + bot_name = "${azurerm_bot_channels_registration.test.name}" + location = "${azurerm_bot_channels_registration.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + service_provider_name = "box" + client_id = "test" + client_secret = "secret" +} +`, template, rInt) +} + +func testAccAzureRMBotConnection_completeConfig(rInt int, location string) string { + template := testAccAzureRMBotChannelsRegistration_basicConfig(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_bot_connection" "test" { + name = "acctestBc%d" + bot_name = "${azurerm_bot_channels_registration.test.name}" + location = "${azurerm_bot_channels_registration.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + service_provider_name = "Salesforce" + client_id = "test" + client_secret = "secret" + scopes = "testscope" + + parameters = { + loginUri = "www.example.com" + } +} +`, template, rInt) +} + +func testAccAzureRMBotConnection_completeUpdateConfig(rInt int, location string) string { + template := testAccAzureRMBotChannelsRegistration_basicConfig(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_bot_connection" "test" { + name = "acctestBc%d" + bot_name = "${azurerm_bot_channels_registration.test.name}" + location = "${azurerm_bot_channels_registration.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + service_provider_name = "Salesforce" + client_id = "test2" + client_secret = "secret2" + scopes = "testscope2" + + parameters = { + loginUri = "www.example2.com" + } +} +`, template, rInt) +} diff --git a/website/azurerm.erb b/website/azurerm.erb index 0cda4aee99b8..1d229046fa28 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -647,6 +647,10 @@
  • bot_channels_registration
  • + +
  • + bot_connection +
  • diff --git a/website/docs/r/bot_connection.markdown b/website/docs/r/bot_connection.markdown new file mode 100644 index 000000000000..6a6baf0f7f46 --- /dev/null +++ b/website/docs/r/bot_connection.markdown @@ -0,0 +1,79 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: bot_connection" +sidebar_current: "docs-azurerm-resource-bot-connection" +description: |- + Manages a Bot Connection. +--- + +# azurerm_bot_connection + +Manages a Bot Connection. + +## Example Usage + +```hcl +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "example" { + name = "example" + location = "northeurope" +} + +resource "azurerm_bot_channels_registration" "example" { + name = "example" + location = "global" + resource_group_name = "${azurerm_resource_group.example.name}" + sku = "F0" + microsoft_app_id = "${data.azurerm_client_config.current.service_principal_application_id}" +} + +resource "azurerm_bot_connection" "example" { + name = "example" + bot_name = "${azurerm_bot_channels_registration.example.name}" + location = "${azurerm_bot_channels_registration.example.location}" + resource_group_name = "${azurerm_resource_group.example.name}" + service_provider_name = "box" + client_id = "exampleId" + client_secret = "exampleSecrete" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name of the Bot Connection. Changing this forces a new resource to be created. Must be globally unique. + +* `resource_group_name` - (Required) The name of the resource group in which to create the Bot Connection. Changing this forces a new resource to be created. + +* `location` - (Required) The supported Azure location where the resource exists. Changing this forces a new resource to be created. + +* `bot_name` - (Required) The name of the Bot Resource this connection will be associated with. Changing this forces a new resource to be created. + +* `service_provider_name` - (Required) The name of the service provider that will be associated with this connection. Changing this forces a new resource to be created. + +* `client_id` - (Required) The Client ID that will be used to authenticate with the service provider. + +* `client_secret` - (Required) The Client Secret that will be used to authenticate with the service provider. + +* `scopes` - (Optional) The Scopes at which the connection should be applied. + +* `parameters` - (Optional) A map of additional parameters to apply to the connection. + +* `tags` - (Optional) A mapping of tags to assign to the resource. + + +## Attributes Reference + +The following attributes are exported: + +* `id` - The Bot Connection ID. + +## Import + +Bot Connection can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_bot_connection.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/example/providers/Microsoft.BotService/botServices/example/connections/example +``` From b7918410c0c847a9977a2d6464e7ef09e1113e42 Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Thu, 12 Sep 2019 13:48:23 -0700 Subject: [PATCH 4/4] Fmting --- azurerm/resource_arm_bot_connection.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/azurerm/resource_arm_bot_connection.go b/azurerm/resource_arm_bot_connection.go index 0cf218b7d2b5..c0858c1b7951 100644 --- a/azurerm/resource_arm_bot_connection.go +++ b/azurerm/resource_arm_bot_connection.go @@ -2,16 +2,16 @@ package azurerm import ( "fmt" - "github.com/Azure/azure-sdk-for-go/services/preview/botservice/mgmt/2018-07-12/botservice" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "log" + "github.com/Azure/azure-sdk-for-go/services/preview/botservice/mgmt/2018-07-12/botservice" "github.com/hashicorp/terraform/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/response" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" )