diff --git a/CHANGELOG.md b/CHANGELOG.md index fd4cff4ced34..ec750ad5c013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,11 @@ FEATURES: * **New Data Source:** `azurerm_network_watcher` [GH-2791] * **New Data Source:** `azurerm_recovery_services_protection_policy_vm` [GH-2974] * **New Resource:** `azurerm_api_management_group` [GH-2809] +* **New Resource:** `azurerm_api_management_group_user` [GH-2972] * **New Resource:** `azurerm_api_management_product` [GH-2953] * **New Resource:** `azurerm_api_management_user` [GH-2954] * **New Resource:** `azurerm_eventgrid_domain` [GH-2884] +* **New Resource:** `azurerm_eventgrid_event_subscription` [GH-2967] * **New Resource:** `azurerm_lb_outbound_rule` [GH-2912] * **New Resource:** `azurerm_media_service_account` [GH-2711] * **New Resource:** `azurerm_network_watcher` [GH-2791] diff --git a/azurerm/config.go b/azurerm/config.go index d6513f8145c9..e1811f539d6e 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -113,11 +113,12 @@ type ArmClient struct { kubernetesClustersClient containerservice.ManagedClustersClient containerGroupsClient containerinstance.ContainerGroupsClient - eventGridDomainsClient eventgrid.DomainsClient - eventGridTopicsClient eventgrid.TopicsClient - eventHubClient eventhub.EventHubsClient - eventHubConsumerGroupClient eventhub.ConsumerGroupsClient - eventHubNamespacesClient eventhub.NamespacesClient + eventGridDomainsClient eventgrid.DomainsClient + eventGridEventSubscriptionsClient eventgrid.EventSubscriptionsClient + eventGridTopicsClient eventgrid.TopicsClient + eventHubClient eventhub.EventHubsClient + eventHubConsumerGroupClient eventhub.ConsumerGroupsClient + eventHubNamespacesClient eventhub.NamespacesClient solutionsClient operationsmanagement.SolutionsClient @@ -126,10 +127,11 @@ type ArmClient struct { redisPatchSchedulesClient redis.PatchSchedulesClient // API Management - apiManagementGroupClient apimanagement.GroupClient - apiManagementProductsClient apimanagement.ProductClient - apiManagementServiceClient apimanagement.ServiceClient - apiManagementUsersClient apimanagement.UserClient + apiManagementGroupClient apimanagement.GroupClient + apiManagementGroupUsersClient apimanagement.GroupUserClient + apiManagementProductsClient apimanagement.ProductClient + apiManagementServiceClient apimanagement.ServiceClient + apiManagementUsersClient apimanagement.UserClient // Application Insights appInsightsClient appinsights.ComponentsClient @@ -494,6 +496,10 @@ func (c *ArmClient) registerApiManagementServiceClients(endpoint, subscriptionId c.configureClient(&groupsClient.Client, auth) c.apiManagementGroupClient = groupsClient + groupUsersClient := apimanagement.NewGroupUserClientWithBaseURI(endpoint, subscriptionId) + c.configureClient(&groupUsersClient.Client, auth) + c.apiManagementGroupUsersClient = groupUsersClient + serviceClient := apimanagement.NewServiceClientWithBaseURI(endpoint, subscriptionId) c.configureClient(&serviceClient.Client, auth) c.apiManagementServiceClient = serviceClient @@ -862,6 +868,10 @@ func (c *ArmClient) registerEventGridClients(endpoint, subscriptionId string, au egdc := eventgrid.NewDomainsClientWithBaseURI(endpoint, subscriptionId) c.configureClient(&egdc.Client, auth) c.eventGridDomainsClient = egdc + + egesc := eventgrid.NewEventSubscriptionsClientWithBaseURI(endpoint, subscriptionId) + c.configureClient(&egesc.Client, auth) + c.eventGridEventSubscriptionsClient = egesc } func (c *ArmClient) registerEventHubClients(endpoint, subscriptionId string, auth autorest.Authorizer) { diff --git a/azurerm/provider.go b/azurerm/provider.go index cd4ca94228d2..4a8696fb73d9 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -169,6 +169,7 @@ func Provider() terraform.ResourceProvider { ResourcesMap: map[string]*schema.Resource{ "azurerm_api_management": resourceArmApiManagementService(), "azurerm_api_management_group": resourceArmApiManagementGroup(), + "azurerm_api_management_group_user": resourceArmApiManagementGroupUser(), "azurerm_api_management_product": resourceArmApiManagementProduct(), "azurerm_api_management_user": resourceArmApiManagementUser(), "azurerm_app_service_active_slot": resourceArmAppServiceActiveSlot(), @@ -226,6 +227,7 @@ func Provider() terraform.ResourceProvider { "azurerm_dns_txt_record": resourceArmDnsTxtRecord(), "azurerm_dns_zone": resourceArmDnsZone(), "azurerm_eventgrid_domain": resourceArmEventGridDomain(), + "azurerm_eventgrid_event_subscription": resourceArmEventGridEventSubscription(), "azurerm_eventgrid_topic": resourceArmEventGridTopic(), "azurerm_eventhub_authorization_rule": resourceArmEventHubAuthorizationRule(), "azurerm_eventhub_consumer_group": resourceArmEventHubConsumerGroup(), diff --git a/azurerm/resource_arm_api_management_group.go b/azurerm/resource_arm_api_management_group.go index 6d483c3baf41..b240956b3aa1 100644 --- a/azurerm/resource_arm_api_management_group.go +++ b/azurerm/resource_arm_api_management_group.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/terraform/helper/schema" "github.com/hashicorp/terraform/helper/validation" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" + "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/utils" ) @@ -73,6 +74,19 @@ func resourceArmApiManagementGroupCreateUpdate(d *schema.ResourceData, meta inte externalID := d.Get("external_id").(string) groupType := d.Get("type").(string) + if requireResourcesToBeImported && d.IsNewResource() { + existing, err := client.Get(ctx, resourceGroup, serviceName, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing Group %q (API Management Service %q / Resource Group %q): %s", name, serviceName, resourceGroup, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_api_management_group", *existing.ID) + } + } + parameters := apimanagement.GroupCreateParameters{ GroupCreateParametersProperties: &apimanagement.GroupCreateParametersProperties{ DisplayName: utils.String(displayName), diff --git a/azurerm/resource_arm_api_management_group_test.go b/azurerm/resource_arm_api_management_group_test.go index fa48a8b5e061..4a3f8f336cb2 100644 --- a/azurerm/resource_arm_api_management_group_test.go +++ b/azurerm/resource_arm_api_management_group_test.go @@ -37,6 +37,37 @@ func TestAccAzureRMAPIManagementGroup_basic(t *testing.T) { }) } +func TestAccAzureRMAPIManagementGroup_requiresImport(t *testing.T) { + if !requireResourcesToBeImported { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_api_management_group.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAPIManagementGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMAPIManagementGroup_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAPIManagementGroupExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "display_name", "Test Group"), + resource.TestCheckResourceAttr(resourceName, "type", "custom"), + ), + }, + { + Config: testAccAzureRMAPIManagementGroup_requiresImport(ri, location), + ExpectError: testRequiresImportError("azurerm_api_management_group"), + }, + }, + }) +} + func TestAccAzureRMAPIManagementGroup_complete(t *testing.T) { resourceName := "azurerm_api_management_group.test" ri := tf.AccRandTimeInt() @@ -186,6 +217,20 @@ resource "azurerm_api_management_group" "test" { `, rInt, location, rInt, rInt) } +func testAccAzureRMAPIManagementGroup_requiresImport(rInt int, location string) string { + template := testAccAzureRMAPIManagementGroup_basic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_api_management_group" "import" { + name = "${azurerm_api_management_group.test.name}" + resource_group_name = "${azurerm_api_management_group.test.resource_group_name}" + api_management_name = "${azurerm_api_management_group.test.api_management_name}" + display_name = "${azurerm_api_management_group.test.display_name}" +} +`, template) +} + func testAccAzureRMAPIManagementGroup_complete(rInt int, location string, displayName, description string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { diff --git a/azurerm/resource_arm_api_management_group_user.go b/azurerm/resource_arm_api_management_group_user.go new file mode 100644 index 000000000000..e509dd9ec369 --- /dev/null +++ b/azurerm/resource_arm_api_management_group_user.go @@ -0,0 +1,121 @@ +package azurerm + +import ( + "fmt" + "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/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceArmApiManagementGroupUser() *schema.Resource { + return &schema.Resource{ + Create: resourceArmApiManagementGroupUserCreate, + Read: resourceArmApiManagementGroupUserRead, + Delete: resourceArmApiManagementGroupUserDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "user_id": azure.SchemaApiManagementChildName(), + + "group_name": azure.SchemaApiManagementChildName(), + + "resource_group_name": resourceGroupNameSchema(), + + "api_management_name": azure.SchemaApiManagementName(), + }, + } +} + +func resourceArmApiManagementGroupUserCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).apiManagementGroupUsersClient + ctx := meta.(*ArmClient).StopContext + + resourceGroup := d.Get("resource_group_name").(string) + serviceName := d.Get("api_management_name").(string) + groupName := d.Get("group_name").(string) + userId := d.Get("user_id").(string) + + if requireResourcesToBeImported { + resp, err := client.CheckEntityExists(ctx, resourceGroup, serviceName, groupName, userId) + if err != nil { + if !utils.ResponseWasNotFound(resp) { + return fmt.Errorf("Error checking for present of existing User %q / Group %q (API Management Service %q / Resource Group %q): %+v", userId, groupName, serviceName, resourceGroup, err) + } + } + + if !utils.ResponseWasNotFound(resp) { + subscriptionId := meta.(*ArmClient).subscriptionId + resourceId := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.ApiManagement/service/%s/groups/%s/users/%s", subscriptionId, resourceGroup, serviceName, groupName, userId) + return tf.ImportAsExistsError("azurerm_api_management_group_user", resourceId) + } + } + + resp, err := client.Create(ctx, resourceGroup, serviceName, groupName, userId) + if err != nil { + return fmt.Errorf("Error adding User %q to Group %q (API Management Service %q / Resource Group %q): %+v", userId, groupName, serviceName, resourceGroup, err) + } + + // there's no Read so this is best-effort + d.SetId(*resp.ID) + + return resourceArmApiManagementGroupUserRead(d, meta) +} + +func resourceArmApiManagementGroupUserRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).apiManagementGroupUsersClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + serviceName := id.Path["service"] + groupName := id.Path["groups"] + userId := id.Path["users"] + + resp, err := client.CheckEntityExists(ctx, resourceGroup, serviceName, groupName, userId) + if err != nil { + if utils.ResponseWasNotFound(resp) { + log.Printf("[DEBUG] User %q was not found in Group %q (API Management Service %q / Resource Group %q) was not found - removing from state!", userId, groupName, serviceName, resourceGroup) + d.SetId("") + return nil + } + + return fmt.Errorf("Error retrieving User %q / Group %q (API Management Service %q / Resource Group %q): %+v", userId, groupName, serviceName, resourceGroup, err) + } + + d.Set("group_name", groupName) + d.Set("user_id", userId) + d.Set("resource_group_name", resourceGroup) + d.Set("api_management_name", serviceName) + + return nil +} + +func resourceArmApiManagementGroupUserDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).apiManagementGroupUsersClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureResourceID(d.Id()) + if err != nil { + return err + } + resourceGroup := id.ResourceGroup + serviceName := id.Path["service"] + groupName := id.Path["groups"] + userId := id.Path["users"] + + if resp, err := client.Delete(ctx, resourceGroup, serviceName, groupName, userId); err != nil { + if !utils.ResponseWasNotFound(resp) { + return fmt.Errorf("Error removing User %q from Group %q (API Management Service %q / Resource Group %q): %+v", userId, groupName, serviceName, resourceGroup, err) + } + } + + return nil +} diff --git a/azurerm/resource_arm_api_management_group_user_test.go b/azurerm/resource_arm_api_management_group_user_test.go new file mode 100644 index 000000000000..9209ad4a0831 --- /dev/null +++ b/azurerm/resource_arm_api_management_group_user_test.go @@ -0,0 +1,175 @@ +package azurerm + +import ( + "fmt" + "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 TestAccAzureRMAPIManagementGroupUser_basic(t *testing.T) { + resourceName := "azurerm_api_management_group_user.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAPIManagementGroupUserDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMAPIManagementGroupUser_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAPIManagementGroupUserExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMAPIManagementGroupUser_requiresImport(t *testing.T) { + if !requireResourcesToBeImported { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_api_management_group_user.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAPIManagementGroupUserDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMAPIManagementGroupUser_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAPIManagementGroupUserExists(resourceName), + ), + }, + { + Config: testAccAzureRMAPIManagementGroupUser_requiresImport(ri, location), + ExpectError: testRequiresImportError("azurerm_api_management_group_user"), + }, + }, + }) +} + +func testCheckAzureRMAPIManagementGroupUserDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).apiManagementGroupUsersClient + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_api_management_group_user" { + continue + } + + userId := rs.Primary.Attributes["user_id"] + groupName := rs.Primary.Attributes["group_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serviceName := rs.Primary.Attributes["api_management_name"] + + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.CheckEntityExists(ctx, resourceGroup, serviceName, groupName, userId) + if err != nil { + if !utils.ResponseWasNotFound(resp) { + return err + } + } + + return nil + } + return nil +} + +func testCheckAzureRMAPIManagementGroupUserExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + userId := rs.Primary.Attributes["user_id"] + groupName := rs.Primary.Attributes["group_name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + serviceName := rs.Primary.Attributes["api_management_name"] + + client := testAccProvider.Meta().(*ArmClient).apiManagementGroupUsersClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.CheckEntityExists(ctx, resourceGroup, serviceName, groupName, userId) + if err != nil { + if utils.ResponseWasNotFound(resp) { + return fmt.Errorf("Bad: User %q / Group %q (API Management Service %q / Resource Group %q) does not exist", userId, groupName, serviceName, resourceGroup) + } + return fmt.Errorf("Bad: Get on apiManagementGroupUsersClient: %+v", err) + } + + return nil + } +} + +func testAccAzureRMAPIManagementGroupUser_basic(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_api_management" "test" { + name = "acctestAM-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + publisher_name = "pub1" + publisher_email = "pub1@email.com" + + sku { + name = "Developer" + capacity = 1 + } +} + +resource "azurerm_api_management_group" "test" { + name = "acctestAMGroup-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + api_management_name = "${azurerm_api_management.test.name}" + display_name = "Test Group" +} + +resource "azurerm_api_management_user" "test" { + user_id = "acctestuser%d" + api_management_name = "${azurerm_api_management.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + first_name = "Acceptance" + last_name = "Test" + email = "azure-acctest%d@example.com" +} + +resource "azurerm_api_management_group_user" "test" { + user_id = "${azurerm_api_management_user.test.user_id}" + group_name = "${azurerm_api_management_group.test.name}" + api_management_name = "${azurerm_api_management.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" +} +`, rInt, location, rInt, rInt, rInt, rInt) +} + +func testAccAzureRMAPIManagementGroupUser_requiresImport(rInt int, location string) string { + template := testAccAzureRMAPIManagementGroupUser_basic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_api_management_group_user" "import" { + user_id = "${azurerm_api_management_group_user.test.user_id}" + group_name = "${azurerm_api_management_group_user.test.group_name}" + api_management_name = "${azurerm_api_management_group_user.test.api_management_name}" + resource_group_name = "${azurerm_api_management_group_user.test.resource_group_name}" +} +`, template) +} diff --git a/azurerm/resource_arm_eventgrid_event_subscription.go b/azurerm/resource_arm_eventgrid_event_subscription.go new file mode 100644 index 000000000000..2855e1b31a3e --- /dev/null +++ b/azurerm/resource_arm_eventgrid_event_subscription.go @@ -0,0 +1,641 @@ +package azurerm + +import ( + "fmt" + "log" + "strings" + + "github.com/Azure/azure-sdk-for-go/services/preview/eventgrid/mgmt/2018-09-15-preview/eventgrid" + "github.com/hashicorp/terraform/helper/schema" + "github.com/hashicorp/terraform/helper/validation" + "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/utils" +) + +func resourceArmEventGridEventSubscription() *schema.Resource { + return &schema.Resource{ + Create: resourceArmEventGridEventSubscriptionCreateUpdate, + Read: resourceArmEventGridEventSubscriptionRead, + Update: resourceArmEventGridEventSubscriptionCreateUpdate, + Delete: resourceArmEventGridEventSubscriptionDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "scope": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.NoEmptyStrings, + }, + + "event_delivery_schema": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: string(eventgrid.EventGridSchema), + ValidateFunc: validation.StringInSlice([]string{ + string(eventgrid.CloudEventV01Schema), + string(eventgrid.CustomInputSchema), + string(eventgrid.EventGridSchema), + }, false), + }, + + "topic_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + + "storage_queue_endpoint": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ConflictsWith: []string{"eventhub_endpoint", "hybrid_connection_endpoint", "webhook_endpoint"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "storage_account_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + "queue_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + + "eventhub_endpoint": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ConflictsWith: []string{"storage_queue_endpoint", "hybrid_connection_endpoint", "webhook_endpoint"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "eventhub_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + }, + }, + }, + + "hybrid_connection_endpoint": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ConflictsWith: []string{"storage_queue_endpoint", "eventhub_endpoint", "webhook_endpoint"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "hybrid_connection_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + }, + }, + }, + + "webhook_endpoint": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ConflictsWith: []string{"storage_queue_endpoint", "eventhub_endpoint", "hybrid_connection_endpoint"}, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "url": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.URLIsHTTPS, + }, + }, + }, + }, + + "included_event_types": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + + "subject_filter": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "subject_begins_with": { + Type: schema.TypeString, + Optional: true, + }, + "subject_ends_with": { + Type: schema.TypeString, + Optional: true, + }, + "case_sensitive": { + Type: schema.TypeBool, + Optional: true, + }, + }, + }, + }, + + "storage_blob_dead_letter_destination": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "storage_account_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, + }, + "storage_blob_container_name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validate.NoEmptyStrings, + }, + }, + }, + }, + + "retry_policy": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "max_delivery_attempts": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 30), + }, + "event_time_to_live": { + Type: schema.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 1440), + }, + }, + }, + }, + + "labels": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func resourceArmEventGridEventSubscriptionCreateUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).eventGridEventSubscriptionsClient + ctx := meta.(*ArmClient).StopContext + + name := d.Get("name").(string) + scope := d.Get("scope").(string) + + if requireResourcesToBeImported && d.IsNewResource() { + existing, err := client.Get(ctx, scope, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("Error checking for presence of existing EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) + } + } + + if existing.ID != nil && *existing.ID != "" { + return tf.ImportAsExistsError("azurerm_eventgrid_event_subscription", *existing.ID) + } + } + + destination := expandEventGridEventSubscriptionDestination(d) + if destination == nil { + return fmt.Errorf("One of `webhook_endpoint`, eventhub_endpoint` `hybrid_connection_endpoint` or `storage_queue_endpoint` must be specificed to create an EventGrid Event Subscription") + } + + eventSubscriptionProperties := eventgrid.EventSubscriptionProperties{ + Destination: destination, + Filter: expandEventGridEventSubscriptionFilter(d), + DeadLetterDestination: expandEventGridEventSubscriptionStorageBlobDeadLetterDestination(d), + RetryPolicy: expandEventGridEventSubscriptionRetryPolicy(d), + Labels: utils.ExpandStringArray(d.Get("labels").([]interface{})), + EventDeliverySchema: eventgrid.EventDeliverySchema(d.Get("event_delivery_schema").(string)), + } + + if v, ok := d.GetOk("topic_name"); ok { + eventSubscriptionProperties.Topic = utils.String(v.(string)) + } + + eventSubscription := eventgrid.EventSubscription{ + EventSubscriptionProperties: &eventSubscriptionProperties, + } + + log.Printf("[INFO] preparing arguments for AzureRM EventGrid Event Subscription creation with Properties: %+v.", eventSubscription) + + future, err := client.CreateOrUpdate(ctx, scope, name, eventSubscription) + if err != nil { + return fmt.Errorf("Error creating/updating EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("Error waiting for EventGrid Event Subscription %q (Scope %q) to become available: %s", name, scope, err) + } + + read, err := client.Get(ctx, scope, name) + if err != nil { + return fmt.Errorf("Error retrieving EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) + } + if read.ID == nil { + return fmt.Errorf("Cannot read EventGrid Event Subscription %s (Scope %s) ID", name, scope) + } + + d.SetId(*read.ID) + + return resourceArmEventGridEventSubscriptionRead(d, meta) +} + +func resourceArmEventGridEventSubscriptionRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).eventGridEventSubscriptionsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureEventGridEventSubscriptionID(d.Id()) + if err != nil { + return err + } + scope := id.Scope + name := id.Name + + resp, err := client.Get(ctx, scope, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[WARN] EventGrid Event Subscription '%s' was not found (resource group '%s')", name, scope) + d.SetId("") + return nil + } + + return fmt.Errorf("Error making Read request on EventGrid Event Subscription '%s': %+v", name, err) + } + + d.Set("name", resp.Name) + d.Set("scope", scope) + + if props := resp.EventSubscriptionProperties; props != nil { + d.Set("event_delivery_schema", string(props.EventDeliverySchema)) + + if props.Topic != nil && *props.Topic != "" { + d.Set("topic_name", *props.Topic) + } + + if storageQueueEndpoint, ok := props.Destination.AsStorageQueueEventSubscriptionDestination(); ok { + if err := d.Set("storage_queue_endpoint", flattenEventGridEventSubscriptionStorageQueueEndpoint(storageQueueEndpoint)); err != nil { + return fmt.Errorf("Error setting `storage_queue_endpoint` for EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) + } + } + if eventHubEndpoint, ok := props.Destination.AsEventHubEventSubscriptionDestination(); ok { + if err := d.Set("eventhub_endpoint", flattenEventGridEventSubscriptionEventHubEndpoint(eventHubEndpoint)); err != nil { + return fmt.Errorf("Error setting `eventhub_endpoint` for EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) + } + } + if hybridConnectionEndpoint, ok := props.Destination.AsHybridConnectionEventSubscriptionDestination(); ok { + if err := d.Set("hybrid_connection_endpoint", flattenEventGridEventSubscriptionHybridConnectionEndpoint(hybridConnectionEndpoint)); err != nil { + return fmt.Errorf("Error setting `hybrid_connection_endpoint` for EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) + } + } + if webhookEndpoint, ok := props.Destination.AsWebHookEventSubscriptionDestination(); ok { + if err := d.Set("webhook_endpoint", flattenEventGridEventSubscriptionWebhookEndpoint(webhookEndpoint)); err != nil { + return fmt.Errorf("Error setting `webhook_endpoint` for EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) + } + } + + if filter := props.Filter; filter != nil { + d.Set("included_event_types", filter.IncludedEventTypes) + if err := d.Set("subject_filter", flattenEventGridEventSubscriptionSubjectFilter(filter)); err != nil { + return fmt.Errorf("Error setting `subject_filter` for EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) + } + } + + if props.DeadLetterDestination != nil { + if storageBlobDeadLetterDestination, ok := props.DeadLetterDestination.AsStorageBlobDeadLetterDestination(); ok { + if err := d.Set("storage_blob_dead_letter_destination", flattenEventGridEventSubscriptionStorageBlobDeadLetterDestination(storageBlobDeadLetterDestination)); err != nil { + return fmt.Errorf("Error setting `storage_blob_dead_letter_destination` for EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) + } + } + } + + if retryPolicy := props.RetryPolicy; retryPolicy != nil { + if err := d.Set("retry_policy", flattenEventGridEventSubscriptionRetryPolicy(retryPolicy)); err != nil { + return fmt.Errorf("Error setting `retry_policy` for EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) + } + } + + if labels := props.Labels; labels != nil { + if err := d.Set("labels", *labels); err != nil { + return fmt.Errorf("Error setting `labels` for EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) + } + } + } + + return nil +} + +func resourceArmEventGridEventSubscriptionDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).eventGridEventSubscriptionsClient + ctx := meta.(*ArmClient).StopContext + + id, err := parseAzureEventGridEventSubscriptionID(d.Id()) + if err != nil { + return err + } + scope := id.Scope + name := id.Name + + future, err := client.Delete(ctx, scope, name) + if err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + return fmt.Errorf("Error deleting Event Grid Event Subscription %q: %+v", name, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + if response.WasNotFound(future.Response()) { + return nil + } + return fmt.Errorf("Error deleting Event Grid Event Subscription %q: %+v", name, err) + } + + return nil +} + +type AzureEventGridEventSubscriptionID struct { + Scope string + Name string +} + +func parseAzureEventGridEventSubscriptionID(id string) (*AzureEventGridEventSubscriptionID, error) { + segments := strings.Split(id, "/providers/Microsoft.EventGrid/eventSubscriptions/") + if len(segments) != 2 { + return nil, fmt.Errorf("Expected ID to be in the format `{scope}/providers/Microsoft.EventGrid/eventSubscriptions/{name} - got %d segments", len(segments)) + } + + scope := segments[0] + name := segments[1] + eventSubscriptionID := AzureEventGridEventSubscriptionID{ + Scope: scope, + Name: name, + } + return &eventSubscriptionID, nil +} + +func expandEventGridEventSubscriptionDestination(d *schema.ResourceData) eventgrid.BasicEventSubscriptionDestination { + if _, ok := d.GetOk("storage_queue_endpoint"); ok { + return expandEventGridEventSubscriptionStorageQueueEndpoint(d) + } + + if _, ok := d.GetOk("eventhub_endpoint"); ok { + return expandEventGridEventSubscriptionEventHubEndpoint(d) + } + + if _, ok := d.GetOk("hybrid_connection_endpoint"); ok { + return expandEventGridEventSubscriptionHybridConnectionEndpoint(d) + } + + if _, ok := d.GetOk("webhook_endpoint"); ok { + return expandEventGridEventSubscriptionWebhookEndpoint(d) + } + + return nil +} + +func expandEventGridEventSubscriptionStorageQueueEndpoint(d *schema.ResourceData) eventgrid.BasicEventSubscriptionDestination { + props := d.Get("storage_queue_endpoint").([]interface{})[0].(map[string]interface{}) + storageAccountID := props["storage_account_id"].(string) + queueName := props["queue_name"].(string) + + storageQueueEndpoint := eventgrid.StorageQueueEventSubscriptionDestination{ + EndpointType: eventgrid.EndpointTypeStorageQueue, + StorageQueueEventSubscriptionDestinationProperties: &eventgrid.StorageQueueEventSubscriptionDestinationProperties{ + ResourceID: &storageAccountID, + QueueName: &queueName, + }, + } + return storageQueueEndpoint +} + +func expandEventGridEventSubscriptionEventHubEndpoint(d *schema.ResourceData) eventgrid.BasicEventSubscriptionDestination { + props := d.Get("eventhub_endpoint").([]interface{})[0].(map[string]interface{}) + eventHubID := props["eventhub_id"].(string) + + eventHubEndpoint := eventgrid.EventHubEventSubscriptionDestination{ + EndpointType: eventgrid.EndpointTypeEventHub, + EventHubEventSubscriptionDestinationProperties: &eventgrid.EventHubEventSubscriptionDestinationProperties{ + ResourceID: &eventHubID, + }, + } + return eventHubEndpoint +} + +func expandEventGridEventSubscriptionHybridConnectionEndpoint(d *schema.ResourceData) eventgrid.BasicEventSubscriptionDestination { + props := d.Get("hybrid_connection_endpoint").([]interface{})[0].(map[string]interface{}) + hybridConnectionID := props["hybrid_connection_id"].(string) + + hybridConnectionEndpoint := eventgrid.HybridConnectionEventSubscriptionDestination{ + EndpointType: eventgrid.EndpointTypeHybridConnection, + HybridConnectionEventSubscriptionDestinationProperties: &eventgrid.HybridConnectionEventSubscriptionDestinationProperties{ + ResourceID: &hybridConnectionID, + }, + } + return hybridConnectionEndpoint +} + +func expandEventGridEventSubscriptionWebhookEndpoint(d *schema.ResourceData) eventgrid.BasicEventSubscriptionDestination { + props := d.Get("webhook_endpoint").([]interface{})[0].(map[string]interface{}) + url := props["url"].(string) + + webhookEndpoint := eventgrid.WebHookEventSubscriptionDestination{ + EndpointType: eventgrid.EndpointTypeWebHook, + WebHookEventSubscriptionDestinationProperties: &eventgrid.WebHookEventSubscriptionDestinationProperties{ + EndpointURL: &url, + }, + } + return webhookEndpoint +} + +func expandEventGridEventSubscriptionFilter(d *schema.ResourceData) *eventgrid.EventSubscriptionFilter { + filter := &eventgrid.EventSubscriptionFilter{} + + if includedEvents, ok := d.GetOk("included_event_types"); ok { + filter.IncludedEventTypes = utils.ExpandStringArray(includedEvents.([]interface{})) + } + + if subjectFilter, ok := d.GetOk("subject_filter"); ok { + config := subjectFilter.([]interface{})[0].(map[string]interface{}) + subjectBeginsWith := config["subject_begins_with"].(string) + subjectEndsWith := config["subject_ends_with"].(string) + caseSensitive := config["case_sensitive"].(bool) + + filter.SubjectBeginsWith = &subjectBeginsWith + filter.SubjectEndsWith = &subjectEndsWith + filter.IsSubjectCaseSensitive = &caseSensitive + } + + return filter +} + +func expandEventGridEventSubscriptionStorageBlobDeadLetterDestination(d *schema.ResourceData) eventgrid.BasicDeadLetterDestination { + if v, ok := d.GetOk("storage_blob_dead_letter_destination"); ok { + dest := v.([]interface{})[0].(map[string]interface{}) + resourceID := dest["storage_account_id"].(string) + blobName := dest["storage_blob_container_name"].(string) + return eventgrid.StorageBlobDeadLetterDestination{ + EndpointType: eventgrid.EndpointTypeStorageBlob, + StorageBlobDeadLetterDestinationProperties: &eventgrid.StorageBlobDeadLetterDestinationProperties{ + ResourceID: &resourceID, + BlobContainerName: &blobName, + }, + } + } + return nil +} + +func expandEventGridEventSubscriptionRetryPolicy(d *schema.ResourceData) *eventgrid.RetryPolicy { + if v, ok := d.GetOk("retry_policy"); ok { + dest := v.([]interface{})[0].(map[string]interface{}) + maxDeliveryAttempts := dest["max_delivery_attempts"].(int) + eventTimeToLive := dest["event_time_to_live"].(int) + return &eventgrid.RetryPolicy{ + MaxDeliveryAttempts: utils.Int32(int32(maxDeliveryAttempts)), + EventTimeToLiveInMinutes: utils.Int32(int32(eventTimeToLive)), + } + } + return nil +} + +func flattenEventGridEventSubscriptionStorageQueueEndpoint(input *eventgrid.StorageQueueEventSubscriptionDestination) []interface{} { + if input == nil { + return nil + } + result := make(map[string]interface{}) + + if input.ResourceID != nil { + result["storage_account_id"] = *input.ResourceID + } + if input.QueueName != nil { + result["queue_name"] = *input.QueueName + } + + return []interface{}{result} +} + +func flattenEventGridEventSubscriptionEventHubEndpoint(input *eventgrid.EventHubEventSubscriptionDestination) []interface{} { + if input == nil { + return nil + } + result := make(map[string]interface{}) + + if input.ResourceID != nil { + result["eventhub_id"] = *input.ResourceID + } + + return []interface{}{result} +} + +func flattenEventGridEventSubscriptionHybridConnectionEndpoint(input *eventgrid.HybridConnectionEventSubscriptionDestination) []interface{} { + if input == nil { + return nil + } + result := make(map[string]interface{}) + + if input.ResourceID != nil { + result["eventhub_id"] = *input.ResourceID + } + + return []interface{}{result} +} + +func flattenEventGridEventSubscriptionWebhookEndpoint(input *eventgrid.WebHookEventSubscriptionDestination) []interface{} { + if input == nil { + return nil + } + result := make(map[string]interface{}) + + if input.EndpointURL != nil { + result["url"] = *input.EndpointURL + } + + return []interface{}{result} +} + +func flattenEventGridEventSubscriptionSubjectFilter(filter *eventgrid.EventSubscriptionFilter) []interface{} { + if (filter.SubjectBeginsWith != nil && *filter.SubjectBeginsWith == "") && (filter.SubjectEndsWith != nil && *filter.SubjectEndsWith == "") { + return nil + } + result := make(map[string]interface{}) + + if filter.SubjectBeginsWith != nil { + result["subject_begins_with"] = *filter.SubjectBeginsWith + } + + if filter.SubjectEndsWith != nil { + result["subject_ends_with"] = *filter.SubjectEndsWith + } + + if filter.IsSubjectCaseSensitive != nil { + result["case_sensitive"] = *filter.IsSubjectCaseSensitive + } + + return []interface{}{result} +} + +func flattenEventGridEventSubscriptionStorageBlobDeadLetterDestination(dest *eventgrid.StorageBlobDeadLetterDestination) []interface{} { + if dest == nil { + return nil + } + result := make(map[string]interface{}) + + if dest.ResourceID != nil { + result["storage_account_id"] = *dest.ResourceID + } + + if dest.BlobContainerName != nil { + result["storage_blob_container_name"] = *dest.BlobContainerName + } + + return []interface{}{result} +} + +func flattenEventGridEventSubscriptionRetryPolicy(retryPolicy *eventgrid.RetryPolicy) []interface{} { + result := make(map[string]interface{}) + + if v := retryPolicy.EventTimeToLiveInMinutes; v != nil { + result["event_time_to_live"] = int(*v) + } + + if v := retryPolicy.MaxDeliveryAttempts; v != nil { + result["max_delivery_attempts"] = int(*v) + } + + return []interface{}{result} +} diff --git a/azurerm/resource_arm_eventgrid_event_subscription_test.go b/azurerm/resource_arm_eventgrid_event_subscription_test.go new file mode 100644 index 000000000000..2843adb32634 --- /dev/null +++ b/azurerm/resource_arm_eventgrid_event_subscription_test.go @@ -0,0 +1,423 @@ +package azurerm + +import ( + "fmt" + "net/http" + "strings" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + "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 TestAccAzureRMEventGridEventSubscription_basic(t *testing.T) { + resourceName := "azurerm_eventgrid_event_subscription.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMEventGridEventSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMEventGridEventSubscription_basic(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventGridEventSubscriptionExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "event_delivery_schema", "EventGridSchema"), + resource.TestCheckResourceAttr(resourceName, "storage_queue_endpoint.#", "1"), + resource.TestCheckResourceAttr(resourceName, "storage_blob_dead_letter_destination.#", "1"), + resource.TestCheckResourceAttr(resourceName, "included_event_types.0", "All"), + resource.TestCheckResourceAttr(resourceName, "retry_policy.0.max_delivery_attempts", "11"), + resource.TestCheckResourceAttr(resourceName, "retry_policy.0.event_time_to_live", "11"), + resource.TestCheckResourceAttr(resourceName, "labels.0", "test"), + resource.TestCheckResourceAttr(resourceName, "labels.2", "test2"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMEventGridEventSubscription_eventhub(t *testing.T) { + resourceName := "azurerm_eventgrid_event_subscription.test" + ri := tf.AccRandTimeInt() + + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMEventGridEventSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMEventGridEventSubscription_eventhub(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventGridEventSubscriptionExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "event_delivery_schema", "CloudEventV01Schema"), + resource.TestCheckResourceAttr(resourceName, "eventhub_endpoint.#", "1"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMEventGridEventSubscription_update(t *testing.T) { + resourceName := "azurerm_eventgrid_event_subscription.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMEventGridEventSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMEventGridEventSubscription_basic(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventGridEventSubscriptionExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "event_delivery_schema", "EventGridSchema"), + resource.TestCheckResourceAttr(resourceName, "storage_queue_endpoint.#", "1"), + resource.TestCheckResourceAttr(resourceName, "storage_blob_dead_letter_destination.#", "1"), + resource.TestCheckResourceAttr(resourceName, "included_event_types.0", "All"), + resource.TestCheckResourceAttr(resourceName, "retry_policy.0.max_delivery_attempts", "11"), + resource.TestCheckResourceAttr(resourceName, "retry_policy.0.event_time_to_live", "11"), + resource.TestCheckResourceAttr(resourceName, "labels.0", "test"), + resource.TestCheckResourceAttr(resourceName, "labels.2", "test2"), + ), + }, + { + Config: testAccAzureRMEventGridEventSubscription_update(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventGridEventSubscriptionExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "included_event_types.0", "Microsoft.Storage.BlobCreated"), + resource.TestCheckResourceAttr(resourceName, "included_event_types.1", "Microsoft.Storage.BlobDeleted"), + resource.TestCheckResourceAttr(resourceName, "subject_filter.0.subject_ends_with", ".jpg"), + resource.TestCheckResourceAttr(resourceName, "subject_filter.0.subject_begins_with", "test/test"), + resource.TestCheckResourceAttr(resourceName, "retry_policy.0.max_delivery_attempts", "10"), + resource.TestCheckResourceAttr(resourceName, "retry_policy.0.event_time_to_live", "12"), + resource.TestCheckResourceAttr(resourceName, "labels.0", "test4"), + resource.TestCheckResourceAttr(resourceName, "labels.2", "test6"), + ), + }, + }, + }) +} + +func TestAccAzureRMEventGridEventSubscription_filter(t *testing.T) { + resourceName := "azurerm_eventgrid_event_subscription.test" + ri := tf.AccRandTimeInt() + rs := strings.ToLower(acctest.RandString(11)) + + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMEventGridEventSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMEventGridEventSubscription_filter(ri, rs, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventGridEventSubscriptionExists(resourceName), + resource.TestCheckResourceAttr(resourceName, "included_event_types.0", "Microsoft.Storage.BlobCreated"), + resource.TestCheckResourceAttr(resourceName, "included_event_types.1", "Microsoft.Storage.BlobDeleted"), + resource.TestCheckResourceAttr(resourceName, "subject_filter.0.subject_ends_with", ".jpg"), + resource.TestCheckResourceAttr(resourceName, "subject_filter.0.subject_begins_with", "test/test"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMEventGridEventSubscriptionDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).eventGridEventSubscriptionsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_eventgrid_event_subscription" { + continue + } + + name := rs.Primary.Attributes["name"] + scope := rs.Primary.Attributes["scope"] + + resp, err := client.Get(ctx, scope, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return nil + } + + return err + } + + if resp.StatusCode != http.StatusNotFound { + return fmt.Errorf("EventGrid Event Subscription still exists:\n%#v", resp) + } + } + + return nil +} + +func testCheckAzureRMEventGridEventSubscriptionExists(resourceName 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[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + name := rs.Primary.Attributes["name"] + scope, hasScope := rs.Primary.Attributes["scope"] + if !hasScope { + return fmt.Errorf("Bad: no scope found in state for EventGrid Event Subscription: %s", name) + } + + client := testAccProvider.Meta().(*ArmClient).eventGridEventSubscriptionsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.Get(ctx, scope, name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: EventGrid Event Subscription %q (scope: %s) does not exist", name, scope) + } + + return fmt.Errorf("Bad: Get on eventGridEventSubscriptionsClient: %s", err) + } + + return nil + } +} + +func testAccAzureRMEventGridEventSubscription_basic(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestacc%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags { + environment = "staging" + } +} + +resource "azurerm_storage_queue" "test" { + name = "mysamplequeue-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} + +resource "azurerm_storage_blob" "test" { + name = "herpderp1.vhd" + + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + + type = "page" + size = 5120 +} + +resource "azurerm_eventgrid_event_subscription" "test" { + name = "acctesteg-%d" + scope = "${azurerm_resource_group.test.id}" + storage_queue_endpoint { + storage_account_id = "${azurerm_storage_account.test.id}" + queue_name = "${azurerm_storage_queue.test.name}" + } + + storage_blob_dead_letter_destination { + storage_account_id = "${azurerm_storage_account.test.id}" + storage_blob_container_name = "${azurerm_storage_container.test.name}" + } + + retry_policy { + event_time_to_live = 11 + max_delivery_attempts = 11 + } + + labels = ["test", "test1", "test2"] +} +`, rInt, location, rString, rInt, rInt) +} + +func testAccAzureRMEventGridEventSubscription_update(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestacc%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags { + environment = "staging" + } +} + +resource "azurerm_storage_queue" "test" { + name = "mysamplequeue-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" +} + +resource "azurerm_storage_container" "test" { + name = "vhds" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + container_access_type = "private" +} + +resource "azurerm_storage_blob" "test" { + name = "herpderp1.vhd" + + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" + storage_container_name = "${azurerm_storage_container.test.name}" + + type = "page" + size = 5120 +} + +resource "azurerm_eventgrid_event_subscription" "test" { + name = "acctesteg-%d" + scope = "${azurerm_resource_group.test.id}" + storage_queue_endpoint { + storage_account_id = "${azurerm_storage_account.test.id}" + queue_name = "${azurerm_storage_queue.test.name}" + } + + storage_blob_dead_letter_destination { + storage_account_id = "${azurerm_storage_account.test.id}" + storage_blob_container_name = "${azurerm_storage_container.test.name}" + } + + retry_policy { + event_time_to_live = 12 + max_delivery_attempts = 10 + } + + subject_filter { + subject_begins_with = "test/test" + subject_ends_with = ".jpg" + } + + included_event_types = ["Microsoft.Storage.BlobCreated", "Microsoft.Storage.BlobDeleted"] + labels = ["test4", "test5", "test6"] +} +`, rInt, location, rString, rInt, rInt) +} + +func testAccAzureRMEventGridEventSubscription_eventhub(rInt int, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_eventhub_namespace" "test" { + name = "acctesteventhubnamespace-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + sku = "Basic" +} + +resource "azurerm_eventhub" "test" { + name = "acctesteventhub-%d" + namespace_name = "${azurerm_eventhub_namespace.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + partition_count = 2 + message_retention = 1 +} + +resource "azurerm_eventgrid_event_subscription" "test" { + name = "acctesteg-%d" + scope = "${azurerm_resource_group.test.id}" + event_delivery_schema = "CloudEventV01Schema" + eventhub_endpoint { + eventhub_id = "${azurerm_eventhub.test.id}" + } +} +`, rInt, location, rInt, rInt, rInt) +} + +func testAccAzureRMEventGridEventSubscription_filter(rInt int, rString string, location string) string { + return fmt.Sprintf(` +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestacc%s" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags { + environment = "staging" + } +} + +resource "azurerm_storage_queue" "test" { + name = "mysamplequeue-%d" + resource_group_name = "${azurerm_resource_group.test.name}" + storage_account_name = "${azurerm_storage_account.test.name}" +} + +resource "azurerm_eventgrid_event_subscription" "test" { + name = "acctesteg-%d" + scope = "${azurerm_resource_group.test.id}" + storage_queue_endpoint { + storage_account_id = "${azurerm_storage_account.test.id}" + queue_name = "${azurerm_storage_queue.test.name}" + } + + included_event_types = ["Microsoft.Storage.BlobCreated", "Microsoft.Storage.BlobDeleted"] + + subject_filter { + subject_begins_with = "test/test" + subject_ends_with = ".jpg" + } +} +`, rInt, location, rString, rInt, rInt) +} diff --git a/website/azurerm.erb b/website/azurerm.erb index 396b72a4004f..7463396c0272 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -330,10 +330,14 @@ azurerm_api_management -