diff --git a/azurerm/provider.go b/azurerm/provider.go index bd4d279ca778..e1adc4062e7f 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -195,6 +195,7 @@ func Provider() terraform.ResourceProvider { "azurerm_batch_account": resourceArmBatchAccount(), "azurerm_batch_application": resourceArmBatchApplication(), "azurerm_batch_certificate": resourceArmBatchCertificate(), + "azurerm_bot_channel_email": resourceArmBotChannelEmail(), "azurerm_bot_channel_slack": resourceArmBotChannelSlack(), "azurerm_bot_channels_registration": resourceArmBotChannelsRegistration(), "azurerm_bot_connection": resourceArmBotConnection(), diff --git a/azurerm/resource_arm_bot_channel_email.go b/azurerm/resource_arm_bot_channel_email.go new file mode 100644 index 000000000000..34ecece8e884 --- /dev/null +++ b/azurerm/resource_arm_bot_channel_email.go @@ -0,0 +1,203 @@ +package azurerm + +import ( + "fmt" + "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/utils" +) + +func resourceArmBotChannelEmail() *schema.Resource { + return &schema.Resource{ + Create: resourceArmBotChannelEmailCreate, + Read: resourceArmBotChannelEmailRead, + Delete: resourceArmBotChannelEmailDelete, + Update: resourceArmBotChannelEmailUpdate, + + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "resource_group_name": azure.SchemaResourceGroupName(), + + "location": azure.SchemaLocation(), + + "bot_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "email_address": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "email_password": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + } +} + +func resourceArmBotChannelEmailCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).bot.ChannelClient + ctx := meta.(*ArmClient).StopContext + + resourceGroup := d.Get("resource_group_name").(string) + botName := d.Get("bot_name").(string) + + if features.ShouldResourcesBeImported() && d.IsNewResource() { + existing, err := client.Get(ctx, resourceGroup, string(botservice.ChannelNameEmailChannel), botName) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of creating Channel Email for Bot %q (Resource Group %q): %+v", resourceGroup, botName, err) + } + } + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_bot_channel_email", *existing.ID) + } + } + + channel := botservice.BotChannel{ + Properties: botservice.EmailChannel{ + Properties: &botservice.EmailChannelProperties{ + EmailAddress: utils.String(d.Get("email_address").(string)), + Password: utils.String(d.Get("email_password").(string)), + IsEnabled: utils.Bool(true), + }, + ChannelName: botservice.ChannelNameEmailChannel1, + }, + Location: utils.String(azure.NormalizeLocation(d.Get("location").(string))), + Kind: botservice.KindBot, + } + + if _, err := client.Create(ctx, resourceGroup, botName, botservice.ChannelNameEmailChannel, channel); err != nil { + return fmt.Errorf("Error issuing create request for Channel Email for Bot %q (Resource Group %q): %+v", resourceGroup, botName, err) + } + + resp, err := client.Get(ctx, resourceGroup, botName, string(botservice.ChannelNameEmailChannel)) + if err != nil { + return fmt.Errorf("Error making get request for Channel Email for Bot %q (Resource Group %q): %+v", resourceGroup, botName, err) + } + + if resp.ID == nil { + return fmt.Errorf("Cannot read Channel Email for Bot %q (Resource Group %q): %+v", resourceGroup, botName, err) + } + + d.SetId(*resp.ID) + + return resourceArmBotChannelEmailRead(d, meta) +} + +func resourceArmBotChannelEmailRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).bot.ChannelClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + botName := id.Path["botServices"] + resp, err := client.Get(ctx, id.ResourceGroup, botName, string(botservice.ChannelNameEmailChannel)) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Channel Email for Bot %q (Resource Group %q) was not found - removing from state!", id.ResourceGroup, botName) + d.SetId("") + return nil + } + + return fmt.Errorf("Error reading Channel Email for Bot %q (Resource Group %q): %+v", id.ResourceGroup, botName, err) + } + + d.Set("resource_group_name", id.ResourceGroup) + d.Set("location", resp.Location) + d.Set("bot_name", botName) + + if props := resp.Properties; props != nil { + if channel, ok := props.AsEmailChannel(); ok { + if channelProps := channel.Properties; channelProps != nil { + d.Set("email_address", channelProps.EmailAddress) + } + } + } + + return nil +} + +func resourceArmBotChannelEmailUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).bot.ChannelClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + botName := id.Path["botServices"] + + channel := botservice.BotChannel{ + Properties: botservice.EmailChannel{ + Properties: &botservice.EmailChannelProperties{ + EmailAddress: utils.String(d.Get("email_address").(string)), + Password: utils.String(d.Get("email_password").(string)), + IsEnabled: utils.Bool(true), + }, + ChannelName: botservice.ChannelNameEmailChannel1, + }, + Location: utils.String(azure.NormalizeLocation(d.Get("location").(string))), + Kind: botservice.KindBot, + } + + if _, err := client.Update(ctx, id.ResourceGroup, botName, botservice.ChannelNameEmailChannel, channel); err != nil { + return fmt.Errorf("Error issuing create request for Channel Email for Bot %q (Resource Group %q): %+v", id.ResourceGroup, botName, err) + } + + resp, err := client.Get(ctx, id.ResourceGroup, botName, string(botservice.ChannelNameEmailChannel)) + if err != nil { + return fmt.Errorf("Error making get request for Channel Email for Bot %q (Resource Group %q): %+v", id.ResourceGroup, botName, err) + } + + if resp.ID == nil { + return fmt.Errorf("Cannot read Channel Email for Bot %q (Resource Group %q): %+v", id.ResourceGroup, botName, err) + } + + d.SetId(*resp.ID) + + return resourceArmBotChannelEmailRead(d, meta) +} + +func resourceArmBotChannelEmailDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).bot.ChannelClient + ctx := meta.(*ArmClient).StopContext + + id, err := azure.ParseAzureResourceID(d.Id()) + if err != nil { + return err + } + + botName := id.Path["botServices"] + + resp, err := client.Delete(ctx, id.ResourceGroup, botName, string(botservice.ChannelNameEmailChannel)) + if err != nil { + if !response.WasNotFound(resp.Response) { + return fmt.Errorf("Error deleting Channel Email for Bot %q (Resource Group %q): %+v", id.ResourceGroup, botName, err) + } + } + + return nil +} diff --git a/azurerm/resource_arm_bot_channel_email_test.go b/azurerm/resource_arm_bot_channel_email_test.go new file mode 100644 index 000000000000..9a3eea5fa672 --- /dev/null +++ b/azurerm/resource_arm_bot_channel_email_test.go @@ -0,0 +1,188 @@ +package azurerm + +import ( + "fmt" + "net/http" + "os" + "testing" + + "github.com/Azure/azure-sdk-for-go/services/preview/botservice/mgmt/2018-07-12/botservice" + "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 TestAccAzureRMBotChannelEmail_basic(t *testing.T) { + if ok := skipEmailChannel(); ok { + t.Skip("Skipping as one of `ARM_TEST_EMAIL`, AND `ARM_TEST_EMAIL_PASSWORD` was not specified") + } + ri := tf.AccRandTimeInt() + config := testAccAzureRMBotChannelEmail_basicConfig(ri, testLocation()) + resourceName := "azurerm_bot_channel_email.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMBotChannelEmailDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMBotChannelEmailExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "email_password", + }, + }, + }, + }) +} + +func TestAccAzureRMBotChannelEmail_update(t *testing.T) { + if ok := skipEmailChannel(); ok { + t.Skip("Skipping as one of `ARM_TEST_EMAIL`, AND `ARM_TEST_EMAIL_PASSWORD` was not specified") + } + if ok := skipSlackChannel(); ok { + t.Skip("Skipping as one of `ARM_TEST_SLACK_CLIENT_ID`, `ARM_TEST_SLACK_CLIENT_SECRET`, or `ARM_TEST_SLACK_VERIFICATION_TOKEN` was not specified") + } + ri := tf.AccRandTimeInt() + config := testAccAzureRMBotChannelEmail_basicConfig(ri, testLocation()) + config2 := testAccAzureRMBotChannelEmail_basicUpdate(ri, testLocation()) + resourceName := "azurerm_bot_channel_email.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMBotChannelEmailDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMBotChannelEmailExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "email_password", + }, + }, + { + Config: config2, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMBotChannelEmailExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{ + "email_password", + }, + }, + }, + }) +} + +func testCheckAzureRMBotChannelEmailExists(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) + } + + 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 Channel Email") + } + + client := testAccProvider.Meta().(*ArmClient).bot.ChannelClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + resp, err := client.Get(ctx, resourceGroup, botName, string(botservice.ChannelNameEmailChannel)) + if err != nil { + return fmt.Errorf("Bad: Get on botChannelClient: %+v", err) + } + + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Bot Channel Email %q (resource group: %q / bot: %q) does not exist", name, resourceGroup, botName) + } + + return nil + } +} + +func testCheckAzureRMBotChannelEmailDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).bot.ChannelClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_bot_channel_email" { + continue + } + + botName := rs.Primary.Attributes["bot_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + resp, err := client.Get(ctx, resourceGroup, botName, string(botservice.ChannelNameEmailChannel)) + + if err != nil { + return nil + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("Bot Channel Email still exists:\n%#v", resp.Properties) + } + } + + return nil +} + +func testAccAzureRMBotChannelEmail_basicConfig(rInt int, location string) string { + template := testAccAzureRMBotChannelsRegistration_basicConfig(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_bot_channel_email" "test" { + bot_name = "${azurerm_bot_channels_registration.test.name}" + location = "${azurerm_bot_channels_registration.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + email_address = "%s" + email_password = "%s" +} +`, template, os.Getenv("ARM_TEST_EMAIL"), os.Getenv("ARM_TEST_EMAIL_PASSWORD")) +} + +func testAccAzureRMBotChannelEmail_basicUpdate(rInt int, location string) string { + template := testAccAzureRMBotChannelsRegistration_basicConfig(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_bot_channel_email" "test" { + bot_name = "${azurerm_bot_channels_registration.test.name}" + location = "${azurerm_bot_channels_registration.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + email_address = "%s" + email_password = "%s" +} +`, template, os.Getenv("ARM_TEST_EMAIL"), os.Getenv("ARM_TEST_EMAIL_PASSWORD")) +} + +func skipEmailChannel() bool { + if os.Getenv("ARM_TEST_EMAIL") == "" || os.Getenv("ARM_TEST_EMAIL_PASSWORD") == "" { + return true + } + + return false +} diff --git a/website/azurerm.erb b/website/azurerm.erb index 38542d1b1858..4437a8efeec5 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -648,9 +648,14 @@