From 2468103268d9f4e10bad169d438a02cc684292e7 Mon Sep 17 00:00:00 2001 From: Matthew Frahry Date: Thu, 28 Feb 2019 15:42:17 -0700 Subject: [PATCH 01/13] New Resource: `azurerm_eventgrid_event_subscription` (#2967) --- azurerm/config.go | 15 +- azurerm/provider.go | 1 + ...source_arm_eventgrid_event_subscription.go | 641 ++++++++++++++++++ ...e_arm_eventgrid_event_subscription_test.go | 423 ++++++++++++ ...eventgrid_event_subscription.html.markdown | 145 ++++ 5 files changed, 1220 insertions(+), 5 deletions(-) create mode 100644 azurerm/resource_arm_eventgrid_event_subscription.go create mode 100644 azurerm/resource_arm_eventgrid_event_subscription_test.go create mode 100644 website/docs/r/eventgrid_event_subscription.html.markdown diff --git a/azurerm/config.go b/azurerm/config.go index 3f3a422c6038..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 @@ -867,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 ce0a57c84047..4a8696fb73d9 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -227,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_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/docs/r/eventgrid_event_subscription.html.markdown b/website/docs/r/eventgrid_event_subscription.html.markdown new file mode 100644 index 000000000000..f383071e7e01 --- /dev/null +++ b/website/docs/r/eventgrid_event_subscription.html.markdown @@ -0,0 +1,145 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_eventgrid_event_subscription" +sidebar_current: "docs-azurerm-resource-messaging-eventgrid-event-subscription" +description: |- + Manages an EventGrid Event Subscription + +--- + +# azurerm_eventgrid_event_subscription + +Manages an EventGrid Event Subscription + +## Example Usage + +```hcl +resource "azurerm_resource_group" "default" { + name = "defaultResourceGroup" + location = "West US 2" +} + +resource "azurerm_storage_account" "default" { + name = "defaultStorageAccount" + resource_group_name = "${azurerm_resource_group.default.name}" + location = "${azurerm_resource_group.default.location}" + account_tier = "Standard" + account_replication_type = "LRS" + + tags { + environment = "staging" + } + } + + resource "azurerm_storage_queue" "default" { + name = defaultStorageQueue" + resource_group_name = "${azurerm_resource_group.default.name}" + storage_account_name = "${azurerm_storage_account.default.name}" + } + +resource "azurerm_eventgrid_event_subscription" "default" { + name = "defaultEventSubscription" + scope = "${azurerm_resource_group.default.id}" + storage_queue_endpoint { + storage_account_id = "${azurerm_storage_account.default.id}" + queue_name = "${azurerm_storage_queue.default.name}" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name of the EventGrid Event Subscription resource. Changing this forces a new resource to be created. + +* `scope` - (Required) Specifies the scope at which the EventGrid Event Subscription should be created. Changing this forces a new resource to be created. + +* `event_delivery_schema` - (Optional) Specifies the event delivery schema for the event subscription. Possible values include: `EventGridSchema`, `CloudEventV01Schema`, `CustomInputSchema`. + +* `topic_name` - (Optional) Specifies the name of the topic to associate with the event subscription. + +* `storage_queue_endpoint` - (Optional) A `storage_queue_endpoint` block as defined below. + +* `eventhub_endpoint` - (Optional) A `eventhub_endpoint` block as defined below. + +* `hybrid_connection_endpoint` - (Optional) A `hybrid_connection_endpoint` block as defined below. + +* `webhook_endpoint` - (Optional) A `webhook_endpoint` block as defined below. + +~> **NOTE:** One of `storage_queue_endpoint`, `eventhub_endpoint`, `hybrid_connection_endpoint` or `webhook_endpoint` must be specified. + +* `included_event_types` - (Optional) A list of applicable event types that need to be part of the event subscription. + +* `subject_filter` - (Optional) A `subject_filter` block as defined below. + +* `storage_blob_dead_letter_destination` - (Optional) A `storage_blob_dead_letter_destination` block as defined below. + +* `retry_policy` - (Optional) A `retry_policy` block as defined below. + +* `labels` - (Optional) A list of labels to assign to the event subscription. + +--- + +A `storage_queue_endpoint` supports the following: + +* `storage_account_id` - (Required) Specifies the id of the storage account id where the storage queue is located. + +* `queue_name` - (Required) Specifies the name of the storage queue where the Event Subscriptio will receive events. + +--- + +A `eventhub_endpoint` supports the following: + +* `eventhub_id` - (Required) Specifies the id of the eventhub where the Event Subscription will receive events. + +--- + +A `hybrid_connection_endpoint` supports the following: + +* `hybrid_connection_id` - (Required) Specifies the id of the hybrid connection where the Event Subscription will receive events. + +A `webhook_endpoint` supports the following: + +* `url` - (Required) Specifies the url of the webhook where the Event Subscription will recieve events. + +--- + +A `subject_filter` supports the following: + +* `subject_begins_with` - (Optional) A string to filter events for an event subscription based on a resource path prefix. + +* `subject_ends_with` - (Optional) A string to filter events for an event subscription based on a resource path suffix. + +* `case_sensitive` - (Optional) Specifies if `subject_begins_with` and `subject_ends_with` case sensitive. This value defaults to `false`. + +--- + +A `storage_blob_dead_letter_destination` supports the following: + +* `storage_account_id` - (Required) Specifies the id of the storage account id where the storage blob is located. + +* `storage_blob_container_name` - (Required) Specifies the name of the Storage blob container that is the destination of the deadletter events + +--- + +A `retry_policy` supports the following: + +* `max_delivery_attempts` - (Required) Specifies the maximum number of delivery retry attempts for events. + +* `event_time_to_live` - (Required) Specifies the time to live (in minutes) for events. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the EventGrid Event Subscription. + +## Import + +EventGrid Domain's can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_eventgrid_event_subscription.eventSubscription1 +/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.EventGrid/eventSubscriptions/eventSubscription1 +``` From 17389c7bdd80b85958a4ac21073b7aed19362175 Mon Sep 17 00:00:00 2001 From: kt Date: Thu, 28 Feb 2019 14:43:29 -0800 Subject: [PATCH 02/13] Update CHANGELOG.md to include #2967 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b65d852a4f31..ec750ad5c013 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ FEATURES: * **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] From d44ade4a76050d0fcf3ed6e27dbbaab2df82883e Mon Sep 17 00:00:00 2001 From: kt Date: Thu, 28 Feb 2019 17:45:38 -0800 Subject: [PATCH 03/13] remove seemingly pointless function testAzureRMClientConfigAttr --- ...ata_source_builtin_role_definition_test.go | 8 +++--- azurerm/data_source_client_config_test.go | 14 +++------- azurerm/data_source_policy_definition_test.go | 26 +++++++++---------- azurerm/data_source_role_definition_test.go | 8 +++--- azurerm/resource_arm_kubernetes_cluster.go | 5 +++- 5 files changed, 28 insertions(+), 33 deletions(-) diff --git a/azurerm/data_source_builtin_role_definition_test.go b/azurerm/data_source_builtin_role_definition_test.go index 8995e0f3e67b..04530d3d2c59 100644 --- a/azurerm/data_source_builtin_role_definition_test.go +++ b/azurerm/data_source_builtin_role_definition_test.go @@ -16,7 +16,7 @@ func TestAccDataSourceAzureRMBuiltInRoleDefinition_contributor(t *testing.T) { { Config: testAccDataSourceBuiltInRoleDefinition("Contributor"), Check: resource.ComposeTestCheckFunc( - testAzureRMClientConfigAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"), + resource.TestCheckResourceAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"), resource.TestCheckResourceAttrSet(dataSourceName, "description"), resource.TestCheckResourceAttrSet(dataSourceName, "type"), resource.TestCheckResourceAttr(dataSourceName, "permissions.#", "1"), @@ -43,7 +43,7 @@ func TestAccDataSourceAzureRMBuiltInRoleDefinition_owner(t *testing.T) { { Config: testAccDataSourceBuiltInRoleDefinition("Owner"), Check: resource.ComposeTestCheckFunc( - testAzureRMClientConfigAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635"), + resource.TestCheckResourceAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635"), resource.TestCheckResourceAttrSet(dataSourceName, "description"), resource.TestCheckResourceAttrSet(dataSourceName, "type"), resource.TestCheckResourceAttr(dataSourceName, "permissions.#", "1"), @@ -65,7 +65,7 @@ func TestAccDataSourceAzureRMBuiltInRoleDefinition_reader(t *testing.T) { { Config: testAccDataSourceBuiltInRoleDefinition("Reader"), Check: resource.ComposeTestCheckFunc( - testAzureRMClientConfigAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7"), + resource.TestCheckResourceAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7"), resource.TestCheckResourceAttrSet(dataSourceName, "description"), resource.TestCheckResourceAttrSet(dataSourceName, "type"), resource.TestCheckResourceAttr(dataSourceName, "permissions.#", "1"), @@ -87,7 +87,7 @@ func TestAccDataSourceAzureRMBuiltInRoleDefinition_virtualMachineContributor(t * { Config: testAccDataSourceBuiltInRoleDefinition("VirtualMachineContributor"), Check: resource.ComposeTestCheckFunc( - testAzureRMClientConfigAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c"), + resource.TestCheckResourceAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c"), resource.TestCheckResourceAttrSet(dataSourceName, "description"), resource.TestCheckResourceAttrSet(dataSourceName, "type"), resource.TestCheckResourceAttr(dataSourceName, "permissions.#", "1"), diff --git a/azurerm/data_source_client_config_test.go b/azurerm/data_source_client_config_test.go index a8e367968cb7..3e39d0ac62a3 100644 --- a/azurerm/data_source_client_config_test.go +++ b/azurerm/data_source_client_config_test.go @@ -22,9 +22,9 @@ func TestAccDataSourceAzureRMClientConfig_basic(t *testing.T) { { Config: testAccCheckArmClientConfig_basic, Check: resource.ComposeTestCheckFunc( - testAzureRMClientConfigAttr(dataSourceName, "client_id", clientId), - testAzureRMClientConfigAttr(dataSourceName, "tenant_id", tenantId), - testAzureRMClientConfigAttr(dataSourceName, "subscription_id", subscriptionId), + resource.TestCheckResourceAttr(dataSourceName, "client_id", clientId), + resource.TestCheckResourceAttr(dataSourceName, "tenant_id", tenantId), + resource.TestCheckResourceAttr(dataSourceName, "subscription_id", subscriptionId), testAzureRMClientConfigGUIDAttr(dataSourceName, "service_principal_application_id"), testAzureRMClientConfigGUIDAttr(dataSourceName, "service_principal_object_id"), ), @@ -33,14 +33,6 @@ func TestAccDataSourceAzureRMClientConfig_basic(t *testing.T) { }) } -// Wraps resource.TestCheckResourceAttr to prevent leaking values to console -// in case of mismatch -func testAzureRMClientConfigAttr(name, key, value string) resource.TestCheckFunc { - return func(s *terraform.State) error { - return resource.TestCheckResourceAttr(name, key, value)(s) - } -} - func testAzureRMClientConfigGUIDAttr(name, key string) resource.TestCheckFunc { return func(s *terraform.State) error { r, err := regexp.Compile("^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$") diff --git a/azurerm/data_source_policy_definition_test.go b/azurerm/data_source_policy_definition_test.go index e65fb7f343f0..6020e79d1364 100644 --- a/azurerm/data_source_policy_definition_test.go +++ b/azurerm/data_source_policy_definition_test.go @@ -18,11 +18,11 @@ func TestAccDataSourceAzureRMPolicyDefinition_builtIn(t *testing.T) { { Config: testAccDataSourceBuiltInPolicyDefinition("Allowed resource types"), Check: resource.ComposeTestCheckFunc( - testAzureRMClientConfigAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/policyDefinitions/a08ec900-254a-4555-9bf5-e42af04b5c5c"), - testAzureRMClientConfigAttr(dataSourceName, "name", "a08ec900-254a-4555-9bf5-e42af04b5c5c"), - testAzureRMClientConfigAttr(dataSourceName, "display_name", "Allowed resource types"), - testAzureRMClientConfigAttr(dataSourceName, "type", "Microsoft.Authorization/policyDefinitions"), - testAzureRMClientConfigAttr(dataSourceName, "description", "This policy enables you to specify the resource types that your organization can deploy. Only resource types that support 'tags' and 'location' will be affected by this policy. To restrict all resources please duplicate this policy and change the 'mode' to 'All'."), + resource.TestCheckResourceAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/policyDefinitions/a08ec900-254a-4555-9bf5-e42af04b5c5c"), + resource.TestCheckResourceAttr(dataSourceName, "name", "a08ec900-254a-4555-9bf5-e42af04b5c5c"), + resource.TestCheckResourceAttr(dataSourceName, "display_name", "Allowed resource types"), + resource.TestCheckResourceAttr(dataSourceName, "type", "Microsoft.Authorization/policyDefinitions"), + resource.TestCheckResourceAttr(dataSourceName, "description", "This policy enables you to specify the resource types that your organization can deploy. Only resource types that support 'tags' and 'location' will be affected by this policy. To restrict all resources please duplicate this policy and change the 'mode' to 'All'."), ), }, }, @@ -38,7 +38,7 @@ func TestAccDataSourceAzureRMPolicyDefinition_builtIn_AtManagementGroup(t *testi { Config: testAccDataSourceBuiltInPolicyDefinitionAtManagementGroup("Allowed resource types"), Check: resource.ComposeTestCheckFunc( - testAzureRMClientConfigAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/policyDefinitions/a08ec900-254a-4555-9bf5-e42af04b5c5c"), + resource.TestCheckResourceAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/policyDefinitions/a08ec900-254a-4555-9bf5-e42af04b5c5c"), ), }, }, @@ -56,13 +56,13 @@ func TestAccDataSourceAzureRMPolicyDefinition_custom(t *testing.T) { Config: testAccDataSourceCustomPolicyDefinition(ri), Check: resource.ComposeTestCheckFunc( testAzureRMAttrExists(dataSourceName, "id"), - testAzureRMClientConfigAttr(dataSourceName, "name", fmt.Sprintf("acctestpol-%d", ri)), - testAzureRMClientConfigAttr(dataSourceName, "display_name", fmt.Sprintf("acctestpol-%d", ri)), - testAzureRMClientConfigAttr(dataSourceName, "type", "Microsoft.Authorization/policyDefinitions"), - testAzureRMClientConfigAttr(dataSourceName, "policy_type", "Custom"), - testAzureRMClientConfigAttr(dataSourceName, "policy_rule", "{\"if\":{\"not\":{\"field\":\"location\",\"in\":\"[parameters('allowedLocations')]\"}},\"then\":{\"effect\":\"audit\"}}"), - testAzureRMClientConfigAttr(dataSourceName, "parameters", "{\"allowedLocations\":{\"metadata\":{\"description\":\"The list of allowed locations for resources.\",\"displayName\":\"Allowed locations\",\"strongType\":\"location\"},\"type\":\"Array\"}}"), - testAzureRMClientConfigAttr(dataSourceName, "metadata", "{\"note\":\"azurerm acceptance test\"}"), + resource.TestCheckResourceAttr(dataSourceName, "name", fmt.Sprintf("acctestpol-%d", ri)), + resource.TestCheckResourceAttr(dataSourceName, "display_name", fmt.Sprintf("acctestpol-%d", ri)), + resource.TestCheckResourceAttr(dataSourceName, "type", "Microsoft.Authorization/policyDefinitions"), + resource.TestCheckResourceAttr(dataSourceName, "policy_type", "Custom"), + resource.TestCheckResourceAttr(dataSourceName, "policy_rule", "{\"if\":{\"not\":{\"field\":\"location\",\"in\":\"[parameters('allowedLocations')]\"}},\"then\":{\"effect\":\"audit\"}}"), + resource.TestCheckResourceAttr(dataSourceName, "parameters", "{\"allowedLocations\":{\"metadata\":{\"description\":\"The list of allowed locations for resources.\",\"displayName\":\"Allowed locations\",\"strongType\":\"location\"},\"type\":\"Array\"}}"), + resource.TestCheckResourceAttrSet(dataSourceName, "metadata", "{\"note\":\"azurerm acceptance test\"}"), ), }, }, diff --git a/azurerm/data_source_role_definition_test.go b/azurerm/data_source_role_definition_test.go index a69498b560dc..37bc6aba47b7 100644 --- a/azurerm/data_source_role_definition_test.go +++ b/azurerm/data_source_role_definition_test.go @@ -76,7 +76,7 @@ func TestAccDataSourceAzureRMRoleDefinition_builtIn_contributor(t *testing.T) { { Config: testAccDataSourceAzureRMRoleDefinition_builtIn("Contributor"), Check: resource.ComposeTestCheckFunc( - testAzureRMClientConfigAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"), + resource.TestCheckResourceAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"), resource.TestCheckResourceAttrSet(dataSourceName, "description"), resource.TestCheckResourceAttrSet(dataSourceName, "type"), resource.TestCheckResourceAttr(dataSourceName, "permissions.#", "1"), @@ -103,7 +103,7 @@ func TestAccDataSourceAzureRMRoleDefinition_builtIn_owner(t *testing.T) { { Config: testAccDataSourceAzureRMRoleDefinition_builtIn("Owner"), Check: resource.ComposeTestCheckFunc( - testAzureRMClientConfigAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635"), + resource.TestCheckResourceAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635"), resource.TestCheckResourceAttrSet(dataSourceName, "description"), resource.TestCheckResourceAttrSet(dataSourceName, "type"), resource.TestCheckResourceAttr(dataSourceName, "permissions.#", "1"), @@ -125,7 +125,7 @@ func TestAccDataSourceAzureRMRoleDefinition_builtIn_reader(t *testing.T) { { Config: testAccDataSourceAzureRMRoleDefinition_builtIn("Reader"), Check: resource.ComposeTestCheckFunc( - testAzureRMClientConfigAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7"), + resource.TestCheckResourceAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7"), resource.TestCheckResourceAttrSet(dataSourceName, "description"), resource.TestCheckResourceAttrSet(dataSourceName, "type"), resource.TestCheckResourceAttr(dataSourceName, "permissions.#", "1"), @@ -147,7 +147,7 @@ func TestAccDataSourceAzureRMRoleDefinition_builtIn_virtualMachineContributor(t { Config: testAccDataSourceAzureRMRoleDefinition_builtIn("VirtualMachineContributor"), Check: resource.ComposeTestCheckFunc( - testAzureRMClientConfigAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c"), + resource.TestCheckResourceAttr(dataSourceName, "id", "/providers/Microsoft.Authorization/roleDefinitions/9980e02c-c2be-4d73-94e8-173b1dc7cf3c"), resource.TestCheckResourceAttrSet(dataSourceName, "description"), resource.TestCheckResourceAttrSet(dataSourceName, "type"), resource.TestCheckResourceAttr(dataSourceName, "permissions.#", "1"), diff --git a/azurerm/resource_arm_kubernetes_cluster.go b/azurerm/resource_arm_kubernetes_cluster.go index 6e520657989a..19de98b89a91 100644 --- a/azurerm/resource_arm_kubernetes_cluster.go +++ b/azurerm/resource_arm_kubernetes_cluster.go @@ -749,7 +749,10 @@ func expandKubernetesClusterAddonProfiles(d *schema.ResourceData) map[string]*co return nil } - profile := profiles[0].(map[string]interface{}) + profile, ok := profiles[0].(map[string]interface{}) + if !ok { + return nil + } addonProfiles := map[string]*containerservice.ManagedClusterAddonProfile{} httpApplicationRouting := profile["http_application_routing"].([]interface{}) From 3347ed49995ddc7d290b696a5647b208e6e0ba13 Mon Sep 17 00:00:00 2001 From: kt Date: Thu, 28 Feb 2019 17:49:58 -0800 Subject: [PATCH 04/13] fix typo --- azurerm/data_source_policy_definition_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azurerm/data_source_policy_definition_test.go b/azurerm/data_source_policy_definition_test.go index 6020e79d1364..3053bdb0ab7e 100644 --- a/azurerm/data_source_policy_definition_test.go +++ b/azurerm/data_source_policy_definition_test.go @@ -62,7 +62,7 @@ func TestAccDataSourceAzureRMPolicyDefinition_custom(t *testing.T) { resource.TestCheckResourceAttr(dataSourceName, "policy_type", "Custom"), resource.TestCheckResourceAttr(dataSourceName, "policy_rule", "{\"if\":{\"not\":{\"field\":\"location\",\"in\":\"[parameters('allowedLocations')]\"}},\"then\":{\"effect\":\"audit\"}}"), resource.TestCheckResourceAttr(dataSourceName, "parameters", "{\"allowedLocations\":{\"metadata\":{\"description\":\"The list of allowed locations for resources.\",\"displayName\":\"Allowed locations\",\"strongType\":\"location\"},\"type\":\"Array\"}}"), - resource.TestCheckResourceAttrSet(dataSourceName, "metadata", "{\"note\":\"azurerm acceptance test\"}"), + resource.TestCheckResourceAttr(dataSourceName, "metadata", "{\"note\":\"azurerm acceptance test\"}"), ), }, }, From 7975a7c1501ad0e1c5666011f1727cbb0ff9feb7 Mon Sep 17 00:00:00 2001 From: kt Date: Thu, 28 Feb 2019 17:51:06 -0800 Subject: [PATCH 05/13] revert accident change --- azurerm/resource_arm_kubernetes_cluster.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/azurerm/resource_arm_kubernetes_cluster.go b/azurerm/resource_arm_kubernetes_cluster.go index 19de98b89a91..6e520657989a 100644 --- a/azurerm/resource_arm_kubernetes_cluster.go +++ b/azurerm/resource_arm_kubernetes_cluster.go @@ -749,10 +749,7 @@ func expandKubernetesClusterAddonProfiles(d *schema.ResourceData) map[string]*co return nil } - profile, ok := profiles[0].(map[string]interface{}) - if !ok { - return nil - } + profile := profiles[0].(map[string]interface{}) addonProfiles := map[string]*containerservice.ManagedClusterAddonProfile{} httpApplicationRouting := profile["http_application_routing"].([]interface{}) From a3e1fdd84c88ca429ed5bb9995abb05105af9ffb Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Fri, 1 Mar 2019 15:08:29 +0100 Subject: [PATCH 06/13] New Resource: `azurerm_api_management_product_group` --- azurerm/config.go | 15 +- azurerm/provider.go | 1 + ...source_arm_api_management_product_group.go | 121 ++++++++++++ ...e_arm_api_management_product_group_test.go | 176 ++++++++++++++++++ website/azurerm.erb | 6 +- .../r/api_management_group_user.html.markdown | 1 - .../r/api_management_product.html.markdown | 4 +- ...api_management_product_group.html.markdown | 65 +++++++ 8 files changed, 380 insertions(+), 9 deletions(-) create mode 100644 azurerm/resource_arm_api_management_product_group.go create mode 100644 azurerm/resource_arm_api_management_product_group_test.go create mode 100644 website/docs/r/api_management_product_group.html.markdown diff --git a/azurerm/config.go b/azurerm/config.go index 3f3a422c6038..d475ab0424d1 100644 --- a/azurerm/config.go +++ b/azurerm/config.go @@ -126,11 +126,12 @@ type ArmClient struct { redisPatchSchedulesClient redis.PatchSchedulesClient // API Management - apiManagementGroupClient apimanagement.GroupClient - apiManagementGroupUsersClient apimanagement.GroupUserClient - apiManagementProductsClient apimanagement.ProductClient - apiManagementServiceClient apimanagement.ServiceClient - apiManagementUsersClient apimanagement.UserClient + apiManagementGroupClient apimanagement.GroupClient + apiManagementGroupUsersClient apimanagement.GroupUserClient + apiManagementProductsClient apimanagement.ProductClient + apiManagementProductGroupsClient apimanagement.ProductGroupClient + apiManagementServiceClient apimanagement.ServiceClient + apiManagementUsersClient apimanagement.UserClient // Application Insights appInsightsClient appinsights.ComponentsClient @@ -507,6 +508,10 @@ func (c *ArmClient) registerApiManagementServiceClients(endpoint, subscriptionId c.configureClient(&productsClient.Client, auth) c.apiManagementProductsClient = productsClient + productGroupsClient := apimanagement.NewProductGroupClientWithBaseURI(endpoint, subscriptionId) + c.configureClient(&productGroupsClient.Client, auth) + c.apiManagementProductGroupsClient = productGroupsClient + usersClient := apimanagement.NewUserClientWithBaseURI(endpoint, subscriptionId) c.configureClient(&usersClient.Client, auth) c.apiManagementUsersClient = usersClient diff --git a/azurerm/provider.go b/azurerm/provider.go index ce0a57c84047..f65e00a41025 100644 --- a/azurerm/provider.go +++ b/azurerm/provider.go @@ -171,6 +171,7 @@ func Provider() terraform.ResourceProvider { "azurerm_api_management_group": resourceArmApiManagementGroup(), "azurerm_api_management_group_user": resourceArmApiManagementGroupUser(), "azurerm_api_management_product": resourceArmApiManagementProduct(), + "azurerm_api_management_product_group": resourceArmApiManagementProductGroup(), "azurerm_api_management_user": resourceArmApiManagementUser(), "azurerm_app_service_active_slot": resourceArmAppServiceActiveSlot(), "azurerm_app_service_custom_hostname_binding": resourceArmAppServiceCustomHostnameBinding(), diff --git a/azurerm/resource_arm_api_management_product_group.go b/azurerm/resource_arm_api_management_product_group.go new file mode 100644 index 000000000000..88f4aa85e641 --- /dev/null +++ b/azurerm/resource_arm_api_management_product_group.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 resourceArmApiManagementProductGroup() *schema.Resource { + return &schema.Resource{ + Create: resourceArmApiManagementProductGroupCreate, + Read: resourceArmApiManagementProductGroupRead, + Delete: resourceArmApiManagementProductGroupDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "product_id": azure.SchemaApiManagementChildName(), + + "group_name": azure.SchemaApiManagementChildName(), + + "resource_group_name": resourceGroupNameSchema(), + + "api_management_name": azure.SchemaApiManagementName(), + }, + } +} + +func resourceArmApiManagementProductGroupCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).apiManagementProductGroupsClient + ctx := meta.(*ArmClient).StopContext + + resourceGroup := d.Get("resource_group_name").(string) + serviceName := d.Get("api_management_name").(string) + groupName := d.Get("group_name").(string) + productId := d.Get("product_id").(string) + + if requireResourcesToBeImported { + resp, err := client.CheckEntityExists(ctx, resourceGroup, serviceName, productId, groupName) + if err != nil { + if !utils.ResponseWasNotFound(resp) { + return fmt.Errorf("Error checking for present of existing Product %q / Group %q (API Management Service %q / Resource Group %q): %+v", productId, groupName, serviceName, resourceGroup, err) + } + } + + if !utils.ResponseWasNotFound(resp) { + subscriptionId := meta.(*ArmClient).subscriptionId + resourceId := fmt.Sprintf("/subscriptions/%s/resourceGroups/%s/providers/Microsoft.ApiManagement/service/%s/products/%s/groups/%s", subscriptionId, resourceGroup, serviceName, groupName, productId) + return tf.ImportAsExistsError("azurerm_api_management_product_group", resourceId) + } + } + + resp, err := client.CreateOrUpdate(ctx, resourceGroup, serviceName, productId, groupName) + if err != nil { + return fmt.Errorf("Error adding Product %q to Group %q (API Management Service %q / Resource Group %q): %+v", productId, groupName, serviceName, resourceGroup, err) + } + + // there's no Read so this is best-effort + d.SetId(*resp.ID) + + return resourceArmApiManagementProductGroupRead(d, meta) +} + +func resourceArmApiManagementProductGroupRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*ArmClient).apiManagementProductGroupsClient + 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"] + productId := id.Path["products"] + + resp, err := client.CheckEntityExists(ctx, resourceGroup, serviceName, productId, groupName) + if err != nil { + if utils.ResponseWasNotFound(resp) { + log.Printf("[DEBUG] Product %q was not found in Group %q (API Management Service %q / Resource Group %q) was not found - removing from state!", productId, groupName, serviceName, resourceGroup) + d.SetId("") + return nil + } + + return fmt.Errorf("Error retrieving Product %q / Group %q (API Management Service %q / Resource Group %q): %+v", productId, groupName, serviceName, resourceGroup, err) + } + + d.Set("group_name", groupName) + d.Set("product_id", productId) + d.Set("resource_group_name", resourceGroup) + d.Set("api_management_name", serviceName) + + return nil +} + +func resourceArmApiManagementProductGroupDelete(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"] + productId := id.Path["products"] + + if resp, err := client.Delete(ctx, resourceGroup, serviceName, productId, groupName); err != nil { + if !utils.ResponseWasNotFound(resp) { + return fmt.Errorf("Error removing Product %q from Group %q (API Management Service %q / Resource Group %q): %+v", productId, groupName, serviceName, resourceGroup, err) + } + } + + return nil +} diff --git a/azurerm/resource_arm_api_management_product_group_test.go b/azurerm/resource_arm_api_management_product_group_test.go new file mode 100644 index 000000000000..d7a1731969a5 --- /dev/null +++ b/azurerm/resource_arm_api_management_product_group_test.go @@ -0,0 +1,176 @@ +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 TestAccAzureRMAPIManagementProductGroup_basic(t *testing.T) { + resourceName := "azurerm_api_management_product_group.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAPIManagementProductGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMAPIManagementProductGroup_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAPIManagementProductGroupExists(resourceName), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAzureRMAPIManagementProductGroup_requiresImport(t *testing.T) { + if !requireResourcesToBeImported { + t.Skip("Skipping since resources aren't required to be imported") + return + } + + resourceName := "azurerm_api_management_product_group.test" + ri := tf.AccRandTimeInt() + location := testLocation() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMAPIManagementProductGroupDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMAPIManagementProductGroup_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMAPIManagementProductGroupExists(resourceName), + ), + }, + { + Config: testAccAzureRMAPIManagementProductGroup_requiresImport(ri, location), + ExpectError: testRequiresImportError("azurerm_api_management_product_group"), + }, + }, + }) +} + +func testCheckAzureRMAPIManagementProductGroupDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*ArmClient).apiManagementGroupUsersClient + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_api_management_product_group" { + continue + } + + productId := rs.Primary.Attributes["product_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, productId, groupName) + if err != nil { + if !utils.ResponseWasNotFound(resp) { + return err + } + } + + return nil + } + return nil +} + +func testCheckAzureRMAPIManagementProductGroupExists(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) + } + + productId := rs.Primary.Attributes["product_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).apiManagementProductGroupsClient + ctx := testAccProvider.Meta().(*ArmClient).StopContext + resp, err := client.CheckEntityExists(ctx, resourceGroup, serviceName, productId, groupName) + if err != nil { + if utils.ResponseWasNotFound(resp) { + return fmt.Errorf("Bad: Product %q / Group %q (API Management Service %q / Resource Group %q) does not exist", productId, groupName, serviceName, resourceGroup) + } + return fmt.Errorf("Bad: Get on apiManagementProductGroupsClient: %+v", err) + } + + return nil + } +} + +func testAccAzureRMAPIManagementProductGroup_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_product" "test" { + product_id = "test-product" + api_management_name = "${azurerm_api_management.test.name}" + resource_group_name = "${azurerm_resource_group.test.name}" + display_name = "Test Product" + subscription_required = true + approval_required = true + published = true +} + +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_product_group" "test" { + product_id = "${azurerm_api_management_product.test.product_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) +} + +func testAccAzureRMAPIManagementProductGroup_requiresImport(rInt int, location string) string { + template := testAccAzureRMAPIManagementProductGroup_basic(rInt, location) + return fmt.Sprintf(` +%s + +resource "azurerm_api_management_product_group" "import" { + product_id = "${azurerm_api_management_product_group.test.product_id}" + group_name = "${azurerm_api_management_product_group.test.group_name}" + api_management_name = "${azurerm_api_management_product_group.test.api_management_name}" + resource_group_name = "${azurerm_api_management_product_group.test.resource_group_name}" +} +`, template) +} diff --git a/website/azurerm.erb b/website/azurerm.erb index 7463396c0272..fbf41b5d216e 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -338,9 +338,13 @@ azurerm_api_management_group_user - > + > azurerm_api_management_product + + > + azurerm_api_management_product_group + diff --git a/website/docs/r/api_management_group_user.html.markdown b/website/docs/r/api_management_group_user.html.markdown index b8b6f3d16c11..80b5151e8a36 100644 --- a/website/docs/r/api_management_group_user.html.markdown +++ b/website/docs/r/api_management_group_user.html.markdown @@ -52,6 +52,5 @@ In addition to all arguments above, the following attributes are exported: API Management Group Users can be imported using the `resource id`, e.g. ```shell - terraform import azurerm_api_management_group_user.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.ApiManagement/service/service1/groups/groupId/users/user123 ``` diff --git a/website/docs/r/api_management_product.html.markdown b/website/docs/r/api_management_product.html.markdown index ca0b6aca4771..c42a30241be7 100644 --- a/website/docs/r/api_management_product.html.markdown +++ b/website/docs/r/api_management_product.html.markdown @@ -1,7 +1,7 @@ --- layout: "azurerm" page_title: "Azure Resource Manager: azurerm_api_management_product" -sidebar_current: "docs-azurerm-resource-api-management-product" +sidebar_current: "docs-azurerm-resource-api-management-product-x" description: |- Manages an API Management Product. --- @@ -68,7 +68,7 @@ The following arguments are supported: * `subscriptions_limit` - (Optional) The number of subscriptions a user can have to this Product at the same time. --> **NOTE:** `subscriptions_limit` can only be set when `subscription_required` is set to `true`. +-> **NOTE:** `subscriptions_limit` can only be set when `subscription_required` is set to `true`. * `terms` - (Optional) The Terms and Conditions for this Product, which must be accepted by Developers before they can begin the Subscription process. diff --git a/website/docs/r/api_management_product_group.html.markdown b/website/docs/r/api_management_product_group.html.markdown new file mode 100644 index 000000000000..ca5ee96ba933 --- /dev/null +++ b/website/docs/r/api_management_product_group.html.markdown @@ -0,0 +1,65 @@ +--- +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_api_management_product_group" +sidebar_current: "docs-azurerm-resource-api-management-product-group" +description: |- + Manages an API Management Product Assignment to a Group. +--- + +# azurerm_api_management_product_group + +Manages an API Management Product Assignment to a Group. + +## Example Usage + +```hcl +data "azurerm_api_management" "example" { + name = "example-api" + resource_group_name = "example-resources" +} + +data "azurerm_api_management_product" "example" { + product_id = "my-product" + api_management_name = "${data.azurerm_api_management.example.name}" + resource_group_name = "${data.azurerm_api_management.example.resource_group_name}" +} + +data "azurerm_api_management_group" "example" { + name = "my-group" + api_management_name = "${data.azurerm_api_management.example.name}" + resource_group_name = "${data.azurerm_api_management.example.resource_group_name}" +} + +resource "azurerm_api_management_group_user" "example" { + user_id = "${data.azurerm_api_management_user.example.id}" + group_name = "${data.azurerm_api_management_group.example.name}" + api_management_name = "${data.azurerm_api_management.example.name}" + resource_group_name = "${data.azurerm_api_management.example.resource_group_name}" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `user_id` - (Required) The ID of the API Management User which should be assigned to this API Management Group. Changing this forces a new resource to be created. + +* `group_name` - (Required) The Name of the API Management Group within the API Management Service. Changing this forces a new resource to be created. + +* `api_management_name` - (Required) The name of the API Management Service. Changing this forces a new resource to be created. + +* `resource_group_name` - (Required) The name of the Resource Group in which the API Management Service exists. Changing this forces a new resource to be created. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The ID of the API Management Product Group. + +## Import + +API Management Product Groups can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_api_management_product_group.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.ApiManagement/service/service1/products/exampleId/groups/groupId +``` From a14c9783aafa13085dad2d9b5b2e460ff50f6819 Mon Sep 17 00:00:00 2001 From: kt Date: Fri, 1 Mar 2019 10:42:00 -0800 Subject: [PATCH 07/13] further fixes to test tf configs (#2981) --- azurerm/resource_arm_availability_set.go | 9 ++------- ..._analytics_workspace_linked_service_test.go | 1 + .../resource_arm_monitor_metric_alert_test.go | 18 +++++++++--------- azurerm/resource_arm_redis_cache_test.go | 2 +- .../resource_arm_template_deployment_test.go | 4 ++-- 5 files changed, 15 insertions(+), 19 deletions(-) diff --git a/azurerm/resource_arm_availability_set.go b/azurerm/resource_arm_availability_set.go index 10a324dc1bb5..be1ff3cff3a3 100644 --- a/azurerm/resource_arm_availability_set.go +++ b/azurerm/resource_arm_availability_set.go @@ -3,7 +3,6 @@ package azurerm import ( "fmt" "log" - "strconv" "strings" "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2018-06-01/compute" @@ -147,12 +146,8 @@ func resourceArmAvailabilitySetRead(d *schema.ResourceData, meta interface{}) er } if props := resp.AvailabilitySetProperties; props != nil { - if v := props.PlatformUpdateDomainCount; v != nil { - d.Set("platform_update_domain_count", strconv.Itoa(int(*v))) - } - if v := props.PlatformFaultDomainCount; v != nil { - d.Set("platform_fault_domain_count", strconv.Itoa(int(*v))) - } + d.Set("platform_update_domain_count", props.PlatformUpdateDomainCount) + d.Set("platform_fault_domain_count", props.PlatformFaultDomainCount) } flattenAndSetTags(d, resp.Tags) diff --git a/azurerm/resource_arm_log_analytics_workspace_linked_service_test.go b/azurerm/resource_arm_log_analytics_workspace_linked_service_test.go index 8cdade1d746f..b814ce424698 100644 --- a/azurerm/resource_arm_log_analytics_workspace_linked_service_test.go +++ b/azurerm/resource_arm_log_analytics_workspace_linked_service_test.go @@ -165,6 +165,7 @@ resource "azurerm_log_analytics_workspace_linked_service" "test" { resource_group_name = "${azurerm_resource_group.test.name}" workspace_name = "${azurerm_log_analytics_workspace.test.name}" resource_id = "${azurerm_automation_account.test.id}" +} `, template) } diff --git a/azurerm/resource_arm_monitor_metric_alert_test.go b/azurerm/resource_arm_monitor_metric_alert_test.go index 9cefe992b9c3..30d89e47aa63 100644 --- a/azurerm/resource_arm_monitor_metric_alert_test.go +++ b/azurerm/resource_arm_monitor_metric_alert_test.go @@ -281,12 +281,12 @@ resource "azurerm_monitor_metric_alert" "import" { func testAccAzureRMMonitorMetricAlert_complete(rInt int, rString, location string) string { return fmt.Sprintf(` resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" + name = "acctestRG-%[1]d" + location = "%[2]s" } resource "azurerm_storage_account" "test" { - name = "acctestsa1%s" + name = "acctestsa1%[3]s" resource_group_name = "${azurerm_resource_group.test.name}" location = "${azurerm_resource_group.test.location}" account_tier = "Standard" @@ -294,19 +294,19 @@ resource "azurerm_storage_account" "test" { } resource "azurerm_monitor_action_group" "test1" { - name = "acctestActionGroup1-%d" + name = "acctestActionGroup1-%[1]d" resource_group_name = "${azurerm_resource_group.test.name}" short_name = "acctestag1" } resource "azurerm_monitor_action_group" "test2" { - name = "acctestActionGroup2-%d" + name = "acctestActionGroup2-%[1]d" resource_group_name = "${azurerm_resource_group.test.name}" short_name = "acctestag2" } resource "azurerm_monitor_metric_alert" "test" { - name = "acctestMetricAlert-%d" + name = "acctestMetricAlert-%[1]d" resource_group_name = "${azurerm_resource_group.test.name}" scopes = ["${azurerm_storage_account.test.id}"] enabled = true @@ -324,8 +324,8 @@ resource "azurerm_monitor_metric_alert" "test" { threshold = 99 dimension { - name = GeoType - operator = Include + name = "GeoType" + operator = "Include" values = ["*"] } @@ -352,7 +352,7 @@ resource "azurerm_monitor_metric_alert" "test" { action_group_id = "${azurerm_monitor_action_group.test2.id}" } } -`, rInt, location, rString, rInt, rInt, rInt) +`, rInt, location, rString) } func testCheckAzureRMMonitorMetricAlertDestroy(s *terraform.State) error { diff --git a/azurerm/resource_arm_redis_cache_test.go b/azurerm/resource_arm_redis_cache_test.go index 2989bb9c2450..715e117234bb 100644 --- a/azurerm/resource_arm_redis_cache_test.go +++ b/azurerm/resource_arm_redis_cache_test.go @@ -935,7 +935,7 @@ resource "azurerm_redis_cache" "test" { enable_non_ssl_port = false subnet_id = "${azurerm_subnet.test.id}" private_static_ip_address = "10.0.1.20" - redis_configuration = {} + redis_configuration {} } `, ri, location, ri, ri) } diff --git a/azurerm/resource_arm_template_deployment_test.go b/azurerm/resource_arm_template_deployment_test.go index 1d18991b55d8..2b768daf3b8c 100644 --- a/azurerm/resource_arm_template_deployment_test.go +++ b/azurerm/resource_arm_template_deployment_test.go @@ -466,7 +466,7 @@ resource "azurerm_key_vault" "test-kv" { name = "vault%d" resource_group_name = "${azurerm_resource_group.test.name}" - "sku" { + sku { name = "standard" } @@ -495,7 +495,7 @@ resource "azurerm_key_vault_secret" "test-secret" { } locals { - "templated-file" = < Date: Fri, 1 Mar 2019 13:38:59 -0800 Subject: [PATCH 08/13] Update azurerm/resource_arm_api_management_product_group_test.go --- azurerm/resource_arm_api_management_product_group_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azurerm/resource_arm_api_management_product_group_test.go b/azurerm/resource_arm_api_management_product_group_test.go index d7a1731969a5..f1b29677d589 100644 --- a/azurerm/resource_arm_api_management_product_group_test.go +++ b/azurerm/resource_arm_api_management_product_group_test.go @@ -153,7 +153,7 @@ resource "azurerm_api_management_group" "test" { } resource "azurerm_api_management_product_group" "test" { - product_id = "${azurerm_api_management_product.test.product_id}" + product_id = "${azurerm_api_management_product.test.product_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}" From f10474dfbadaa8458fed4a43ec51f3b100e1d1e0 Mon Sep 17 00:00:00 2001 From: Pratik Date: Sat, 2 Mar 2019 04:42:49 +0530 Subject: [PATCH 09/13] Support more than 16 access policies (#2866) * Support more than 16 access policies Key Vault supports up to 1024 access policy entries for a key vault. * updating document for #2866 * Update resource_arm_key_vault_key_test.go Added test case for multiple access policies * Re-added the access policy test * Re-basic test cases from Master * Reset to master updated wrong file * Updating the correct key vault test file * Updated test to add access policies directly to the key vault * Added random string to reduce risk of name collision * Removed depends on string from test * Fixed function to call test * Changed format of storage account name * Update CHANGELOG.md to include #2866 --- CHANGELOG.md | 1 + azurerm/resource_arm_key_vault.go | 2 +- azurerm/resource_arm_key_vault_test.go | 90 +++++++++++++++++++ .../r/key_vault_access_policy.html.markdown | 2 +- 4 files changed, 93 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec750ad5c013..11183f957054 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ IMPROVEMENTS: * `azurerm_application_gateway` - support for setting `path` within the `backend_http_settings` block [GH-2879] * `azurerm_application_gateway` - support for setting `connection_draining` to the `backend_http_settings` [GH-2778] * `azurerm_iothub` - support for the `fallback_route` property [GH-2764] +* `azurerm_key_vault` - support for 1024 `access policies` [GH-2866] * `azurerm_redis_cache` - support for configuring the `maxfragmentationmemory_reserved` in the `redis_configuration` block [GH-2887] * `azurerm_service_fabric_cluster` - support for setting `capacities` and `placement_properties` [GH-2936] * `azurerm_storage_account` - exposing primary/secondary `_host` attributes [GH-2792] diff --git a/azurerm/resource_arm_key_vault.go b/azurerm/resource_arm_key_vault.go index 38fbf51a986b..e02881b42292 100644 --- a/azurerm/resource_arm_key_vault.go +++ b/azurerm/resource_arm_key_vault.go @@ -84,7 +84,7 @@ func resourceArmKeyVault() *schema.Resource { Type: schema.TypeList, Optional: true, Computed: true, - MaxItems: 16, + MaxItems: 1024, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "tenant_id": { diff --git a/azurerm/resource_arm_key_vault_test.go b/azurerm/resource_arm_key_vault_test.go index b711d458aff7..3b2e49ed98d3 100644 --- a/azurerm/resource_arm_key_vault_test.go +++ b/azurerm/resource_arm_key_vault_test.go @@ -4,6 +4,7 @@ import ( "fmt" "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/response" @@ -161,6 +162,29 @@ func TestAccAzureRMKeyVault_networkAcls(t *testing.T) { }) } +func TestAccAzureRMKeyVault_accessPolicyUpperLimit(t *testing.T) { + resourceName := "azurerm_key_vault.test" + ri := tf.AccRandTimeInt() + rs := acctest.RandString(10) + config := testAccAzureRMKeyVault_accessPolicyUpperLimit(ri, testLocation(), rs) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testCheckAzureRMKeyVaultDestroy, + Steps: []resource.TestStep{ + { + Config: config, + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMKeyVaultExists(resourceName), + testCheckAzureRMKeyVaultDisappears(resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func TestAccAzureRMKeyVault_disappears(t *testing.T) { resourceName := "azurerm_key_vault.test" ri := tf.AccRandTimeInt() @@ -648,3 +672,69 @@ resource "azurerm_key_vault" "test" { } `, rInt, location, rInt) } + +func testAccAzureRMKeyVault_accessPolicyUpperLimit(rInt int, location string, rs string) string { + + var storageAccountConfigs string + var accessPoliciesConfigs string + + for i := 1; i <= 20; i++ { + storageAccountConfigs += testAccAzureRMKeyVault_generateStorageAccountConfigs(i, rs) + accessPoliciesConfigs += testAccAzureRMKeyVault_generateAccessPolicyConfigs(i) + } + + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_key_vault" "test" { + name = "vault%d" + 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 = "premium" + } +%s +} + +%s +`, rInt, location, rInt, accessPoliciesConfigs, storageAccountConfigs) +} + +func testAccAzureRMKeyVault_generateStorageAccountConfigs(accountNum int, rs string) string { + return fmt.Sprintf(` +resource "azurerm_storage_account" "testsa%d" { + name = "testsa%s%d" + resource_group_name = "${azurerm_resource_group.test.name}" + location = "${azurerm_resource_group.test.location}" + account_tier = "Standard" + account_replication_type = "GRS" + + identity { + type = "SystemAssigned" + } + + tags { + environment = "testing" + } +} +`, accountNum, rs, accountNum) +} + +func testAccAzureRMKeyVault_generateAccessPolicyConfigs(accountNum int) string { + return fmt.Sprintf(` + access_policy { + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + object_id = "${azurerm_storage_account.testsa%d.identity.0.principal_id}" + + key_permissions = ["get","create","delete","list","restore","recover","unwrapkey","wrapkey","purge","encrypt","decrypt","sign","verify"] + secret_permissions = ["get"] + } +`, accountNum) +} diff --git a/website/docs/r/key_vault_access_policy.html.markdown b/website/docs/r/key_vault_access_policy.html.markdown index 31a5ac34729a..552e4316188d 100644 --- a/website/docs/r/key_vault_access_policy.html.markdown +++ b/website/docs/r/key_vault_access_policy.html.markdown @@ -12,7 +12,7 @@ Manages a Key Vault Access Policy. ~> **NOTE:** It's possible to define Key Vault Access Policies both within [the `azurerm_key_vault` resource](key_vault.html) via the `access_policy` block and by using [the `azurerm_key_vault_access_policy` resource](key_vault_access_policy.html). However it's not possible to use both methods to manage Access Policies within a KeyVault, since there'll be conflicts. --> **NOTE:** Azure permits a maximum of 16 Access Policies per Key Vault - [more information can be found in this document](https://docs.microsoft.com/en-us/azure/key-vault/key-vault-secure-your-key-vault#data-plane-access-control). +-> **NOTE:** Azure permits a maximum of 1024 Access Policies per Key Vault - [more information can be found in this document](https://docs.microsoft.com/en-us/azure/key-vault/key-vault-secure-your-key-vault#data-plane-access-control). ## Example Usage From 2dad06b06737d3b2673625603632897ff5a69e00 Mon Sep 17 00:00:00 2001 From: kt Date: Fri, 1 Mar 2019 23:17:38 -0800 Subject: [PATCH 10/13] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11183f957054..874d4c22c18d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,7 +25,7 @@ IMPROVEMENTS: * `azurerm_application_gateway` - support for setting `path` within the `backend_http_settings` block [GH-2879] * `azurerm_application_gateway` - support for setting `connection_draining` to the `backend_http_settings` [GH-2778] * `azurerm_iothub` - support for the `fallback_route` property [GH-2764] -* `azurerm_key_vault` - support for 1024 `access policies` [GH-2866] +* `azurerm_key_vault` - support for 1024 `access_policy` blocks [GH-2866] * `azurerm_redis_cache` - support for configuring the `maxfragmentationmemory_reserved` in the `redis_configuration` block [GH-2887] * `azurerm_service_fabric_cluster` - support for setting `capacities` and `placement_properties` [GH-2936] * `azurerm_storage_account` - exposing primary/secondary `_host` attributes [GH-2792] From d963b19473e312fb866c71e261d0d29a7162032f Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Mon, 4 Mar 2019 11:33:36 +0100 Subject: [PATCH 11/13] fixing the test --- azurerm/resource_arm_api_management_product_group_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azurerm/resource_arm_api_management_product_group_test.go b/azurerm/resource_arm_api_management_product_group_test.go index f1b29677d589..a5fd44fe55c2 100644 --- a/azurerm/resource_arm_api_management_product_group_test.go +++ b/azurerm/resource_arm_api_management_product_group_test.go @@ -141,7 +141,7 @@ resource "azurerm_api_management_product" "test" { resource_group_name = "${azurerm_resource_group.test.name}" display_name = "Test Product" subscription_required = true - approval_required = true + approval_required = false published = true } From 4a8615ccda70303624833430d7baabb5f58d3e13 Mon Sep 17 00:00:00 2001 From: tombuildsstuff Date: Mon, 4 Mar 2019 14:53:08 +0100 Subject: [PATCH 12/13] r/api_management_product_group: helps if you call the right client --- azurerm/resource_arm_api_management_product_group.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azurerm/resource_arm_api_management_product_group.go b/azurerm/resource_arm_api_management_product_group.go index 88f4aa85e641..13ad8e42c3a3 100644 --- a/azurerm/resource_arm_api_management_product_group.go +++ b/azurerm/resource_arm_api_management_product_group.go @@ -99,7 +99,7 @@ func resourceArmApiManagementProductGroupRead(d *schema.ResourceData, meta inter } func resourceArmApiManagementProductGroupDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).apiManagementGroupUsersClient + client := meta.(*ArmClient).apiManagementProductGroupsClient ctx := meta.(*ArmClient).StopContext id, err := parseAzureResourceID(d.Id()) From a51fddf8f0415bde794e9238a458e012ae431dbc Mon Sep 17 00:00:00 2001 From: Tom Harvey Date: Mon, 4 Mar 2019 15:54:16 +0100 Subject: [PATCH 13/13] Updating to include #2984 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 874d4c22c18d..c4c457c6d2a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ FEATURES: * **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_product_group` [GH-2984] * **New Resource:** `azurerm_api_management_user` [GH-2954] * **New Resource:** `azurerm_eventgrid_domain` [GH-2884] * **New Resource:** `azurerm_eventgrid_event_subscription` [GH-2967]