diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b75ac352..4679b9a2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -101,6 +101,9 @@ jobs: SUMOLOGIC_DATA_FORWARDING_BUCKET: ${{ secrets.SUMOLOGIC_DATA_FORWARDING_BUCKET }} SUMOLOGIC_DATA_FORWARDING_ROLE_ARN: ${{ secrets.SUMOLOGIC_DATA_FORWARDING_ROLE_ARN }} SUMOLOGIC_DATA_FORWARDING_AWS_REGION: ${{ secrets.SUMOLOGIC_DATA_FORWARDING_AWS_REGION }} + SUMOLOGIC_TEST_AZURE_TENANT_ID: ${{ secrets.SUMOLOGIC_TEST_AZURE_TENANT_ID }} + SUMOLOGIC_TEST_AZURE_CLIENT_ID: ${{ secrets.SUMOLOGIC_TEST_AZURE_CLIENT_ID }} + SUMOLOGIC_TEST_AZURE_CLIENT_SECRET: ${{ secrets.SUMOLOGIC_TEST_AZURE_CLIENT_SECRET }} # disable go test timeout. We rely on GitHub action timeout. run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a77fafa..0f78153c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## X.Y.Z (Unreleased) -* Add new change notes here -* Add support for update method to change state of the fields resource +FEATURES: +* **New Resource:** sumologic_azure_metrics_source (GH-710) ## 3.0.0 (December 09, 2024) **REMOVALS:** diff --git a/sumologic/provider.go b/sumologic/provider.go index 9320777b..90b4e5ee 100644 --- a/sumologic/provider.go +++ b/sumologic/provider.go @@ -119,6 +119,7 @@ func Provider() terraform.ResourceProvider { "sumologic_rum_source": resourceSumologicRumSource(), "sumologic_role_v2": resourceSumologicRoleV2(), "sumologic_azure_event_hub_log_source": resourceSumologicGenericPollingSource(), + "sumologic_azure_metrics_source": resourceSumologicGenericPollingSource(), }, DataSourcesMap: map[string]*schema.Resource{ "sumologic_cse_log_mapping_vendor_product": dataSourceCSELogMappingVendorAndProduct(), diff --git a/sumologic/resource_sumologic_azure_metrics_source_test.go b/sumologic/resource_sumologic_azure_metrics_source_test.go new file mode 100644 index 00000000..b86ca49f --- /dev/null +++ b/sumologic/resource_sumologic_azure_metrics_source_test.go @@ -0,0 +1,209 @@ +package sumologic + +import ( + "fmt" + "os" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccSumologicAzureMetricsSource_create(t *testing.T) { + var azureMetricsSource PollingSource + var collector Collector + cName, cDescription, cCategory := getRandomizedParams() + sName, sDescription, sCategory := getRandomizedParams() + azureMetricsResourceName := "sumologic_azure_metrics_source.azure" + testTenantId := os.Getenv("SUMOLOGIC_TEST_AZURE_TENANT_ID") + testClientId := os.Getenv("SUMOLOGIC_TEST_AZURE_CLIENT_ID") + testClientSecret := os.Getenv("SUMOLOGIC_TEST_AZURE_CLIENT_SECRET") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAzureMetricsSourceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccSumologicAzureMetricsSourceConfig(cName, cDescription, cCategory, sName, sDescription, sCategory, testTenantId, testClientId, testClientSecret), + Check: resource.ComposeTestCheckFunc( + testAccCheckAzureMetricsSourceExists(azureMetricsResourceName, &azureMetricsSource), + testAccCheckAzureMetricsSourceValues(&azureMetricsSource, sName, sDescription, sCategory), + testAccCheckCollectorExists("sumologic_collector.test", &collector), + testAccCheckCollectorValues(&collector, cName, cDescription, cCategory, "Etc/UTC", ""), + resource.TestCheckResourceAttrSet(azureMetricsResourceName, "id"), + resource.TestCheckResourceAttr(azureMetricsResourceName, "name", sName), + resource.TestCheckResourceAttr(azureMetricsResourceName, "description", sDescription), + resource.TestCheckResourceAttr(azureMetricsResourceName, "category", sCategory), + resource.TestCheckResourceAttr(azureMetricsResourceName, "content_type", "AzureMetrics"), + resource.TestCheckResourceAttr(azureMetricsResourceName, "path.0.type", "AzureMetricsPath"), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + +func TestAccSumologicAzureMetricsSource_update(t *testing.T) { + var azureMetricsSource PollingSource + cName, cDescription, cCategory := getRandomizedParams() + sName, sDescription, sCategory := getRandomizedParams() + sNameUpdated, sDescriptionUpdated, sCategoryUpdated := getRandomizedParams() + azureMetricsResourceName := "sumologic_azure_metrics_source.azure" + testTenantId := os.Getenv("SUMOLOGIC_TEST_AZURE_TENANT_ID") + testClientId := os.Getenv("SUMOLOGIC_TEST_AZURE_CLIENT_ID") + testClientSecret := os.Getenv("SUMOLOGIC_TEST_AZURE_CLIENT_SECRET") + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAzureMetricsSourceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccSumologicAzureMetricsSourceConfig(cName, cDescription, cCategory, sName, sDescription, sCategory, testTenantId, testClientId, testClientSecret), + Check: resource.ComposeTestCheckFunc( + testAccCheckAzureMetricsSourceExists(azureMetricsResourceName, &azureMetricsSource), + testAccCheckAzureMetricsSourceValues(&azureMetricsSource, sName, sDescription, sCategory), + resource.TestCheckResourceAttrSet(azureMetricsResourceName, "id"), + resource.TestCheckResourceAttr(azureMetricsResourceName, "name", sName), + resource.TestCheckResourceAttr(azureMetricsResourceName, "description", sDescription), + resource.TestCheckResourceAttr(azureMetricsResourceName, "category", sCategory), + resource.TestCheckResourceAttr(azureMetricsResourceName, "content_type", "AzureMetrics"), + resource.TestCheckResourceAttr(azureMetricsResourceName, "path.0.type", "AzureMetricsPath"), + ), + ExpectNonEmptyPlan: true, + }, + { + Config: testAccSumologicAzureMetricsSourceConfig(cName, cDescription, cCategory, sNameUpdated, sDescriptionUpdated, sCategoryUpdated, testTenantId, testClientId, testClientSecret), + Check: resource.ComposeTestCheckFunc( + testAccCheckAzureMetricsSourceExists(azureMetricsResourceName, &azureMetricsSource), + testAccCheckAzureMetricsSourceValues(&azureMetricsSource, sNameUpdated, sDescriptionUpdated, sCategoryUpdated), + resource.TestCheckResourceAttrSet(azureMetricsResourceName, "id"), + resource.TestCheckResourceAttr(azureMetricsResourceName, "name", sNameUpdated), + resource.TestCheckResourceAttr(azureMetricsResourceName, "description", sDescriptionUpdated), + resource.TestCheckResourceAttr(azureMetricsResourceName, "category", sCategoryUpdated), + resource.TestCheckResourceAttr(azureMetricsResourceName, "content_type", "AzureMetrics"), + resource.TestCheckResourceAttr(azureMetricsResourceName, "path.0.type", "AzureMetricsPath"), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) + +} + +func testAccCheckAzureMetricsSourceDestroy(s *terraform.State) error { + client := testAccProvider.Meta().(*Client) + for _, rs := range s.RootModule().Resources { + if rs.Type != "sumologic_azure_metrics_source" { + continue + } + if rs.Primary.ID == "" { + return fmt.Errorf("Azure Event Hub Log Source destruction check: Azure Event Hub Log Source ID is not set") + } + id, err := strconv.Atoi(rs.Primary.ID) + if err != nil { + return fmt.Errorf("Encountered an error: " + err.Error()) + } + collectorID, err := strconv.Atoi(rs.Primary.Attributes["collector_id"]) + if err != nil { + return fmt.Errorf("Encountered an error: " + err.Error()) + } + s, err := client.GetPollingSource(collectorID, id) + if err != nil { + return fmt.Errorf("Encountered an error: " + err.Error()) + } + if s != nil { + return fmt.Errorf("Polling Source still exists") + } + } + return nil +} + +func testAccCheckAzureMetricsSourceExists(n string, pollingSource *PollingSource) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("not found: %s", n) + } + if rs.Primary.ID == "" { + return fmt.Errorf("Polling Source ID is not set") + } + id, err := strconv.Atoi(rs.Primary.ID) + if err != nil { + return fmt.Errorf("Polling Source id should be int; got %s", rs.Primary.ID) + } + collectorID, err := strconv.Atoi(rs.Primary.Attributes["collector_id"]) + if err != nil { + return fmt.Errorf("Encountered an error: " + err.Error()) + } + c := testAccProvider.Meta().(*Client) + pollingSourceResp, err := c.GetPollingSource(collectorID, id) + if err != nil { + return err + } + *pollingSource = *pollingSourceResp + return nil + } +} + +func testAccCheckAzureMetricsSourceValues(pollingSource *PollingSource, name, description, category string) resource.TestCheckFunc { + return func(s *terraform.State) error { + if pollingSource.Name != name { + return fmt.Errorf("bad name, expected \"%s\", got: %#v", name, pollingSource.Name) + } + if pollingSource.Description != description { + return fmt.Errorf("bad description, expected \"%s\", got: %#v", description, pollingSource.Description) + } + if pollingSource.Category != category { + return fmt.Errorf("bad category, expected \"%s\", got: %#v", category, pollingSource.Category) + } + return nil + } +} + +func testAccSumologicAzureMetricsSourceConfig(cName, cDescription, cCategory, sName, sDescription, sCategory, testTenantId, testClientId, testClientSecret string) string { + return fmt.Sprintf(` +resource "sumologic_collector" "test" { + name = "%s" + description = "%s" + category = "%s" +} +resource "sumologic_azure_metrics_source" "azure" { + name = "%s" + description = "%s" + category = "%s" + content_type = "AzureMetrics" + collector_id = "${sumologic_collector.test.id}" + + authentication { + type = "AzureClientSecretAuthentication" + tenant_id = "%s" + client_id = "%s" + client_secret = "%s" + } + + path { + type = "AzureMetricsPath" + environment = "Azure" + limit_to_namespaces = ["Microsoft.ClassicStorage/storageAccounts"] + azure_tag_filters { + type = "AzureTagFilters" + namespace = "Microsoft.ClassicStorage/storageAccounts" + tags { + name = "test-name-1" + values = ["value1"] + } + tags { + name = "test-name-2" + values = ["value2"] + } + } + } + + lifecycle { + ignore_changes = [authentication.0.client_secret] + } +}`, cName, cDescription, cCategory, sName, sDescription, sCategory, testTenantId, testClientId, testClientSecret) +} diff --git a/sumologic/resource_sumologic_generic_polling_source.go b/sumologic/resource_sumologic_generic_polling_source.go index ed6aa9c5..c9515800 100644 --- a/sumologic/resource_sumologic_generic_polling_source.go +++ b/sumologic/resource_sumologic_generic_polling_source.go @@ -25,7 +25,7 @@ func resourceSumologicGenericPollingSource() *schema.Resource { Required: true, ForceNew: true, ValidateFunc: validation.StringInSlice([]string{"AwsS3Bucket", "AwsElbBucket", "AwsCloudFrontBucket", - "AwsCloudTrailBucket", "AwsS3AuditBucket", "AwsCloudWatch", "AwsInventory", "AwsXRay", "GcpMetrics", "AwsS3ArchiveBucket", "AzureEventHubLog"}, false), + "AwsCloudTrailBucket", "AwsS3AuditBucket", "AwsCloudWatch", "AwsInventory", "AwsXRay", "GcpMetrics", "AwsS3ArchiveBucket", "AzureEventHubLog", "AzureMetrics"}, false), } pollingSource.Schema["scan_interval"] = &schema.Schema{ Type: schema.TypeInt, @@ -56,7 +56,7 @@ func resourceSumologicGenericPollingSource() *schema.Resource { "type": { Type: schema.TypeString, Required: true, - ValidateFunc: validation.StringInSlice([]string{"S3BucketAuthentication", "AWSRoleBasedAuthentication", "service_account", "AzureEventHubAuthentication"}, false), + ValidateFunc: validation.StringInSlice([]string{"S3BucketAuthentication", "AWSRoleBasedAuthentication", "service_account", "AzureEventHubAuthentication", "AzureClientSecretAuthentication"}, false), }, "access_key": { Type: schema.TypeString, @@ -118,6 +118,14 @@ func resourceSumologicGenericPollingSource() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "tenant_id": { + Type: schema.TypeString, + Optional: true, + }, + "client_secret": { + Type: schema.TypeString, + Optional: true, + }, }, }, } @@ -133,7 +141,7 @@ func resourceSumologicGenericPollingSource() *schema.Resource { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{"S3BucketPathExpression", "CloudWatchPath", - "AwsInventoryPath", "AwsXRayPath", "GcpMetricsPath", "AzureEventHubPath"}, false), + "AwsInventoryPath", "AwsXRayPath", "GcpMetricsPath", "AzureEventHubPath", "AzureMetricsPath"}, false), }, "bucket_name": { Type: schema.TypeString, @@ -211,6 +219,41 @@ func resourceSumologicGenericPollingSource() *schema.Resource { }, }, }, + "azure_tag_filters": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "type": { + Type: schema.TypeString, + Required: true, + }, + "namespace": { + Type: schema.TypeString, + Optional: true, + }, + "tags": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "values": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + }, + }, + }, "sns_topic_or_subscription_arn": { Type: schema.TypeList, Computed: true, @@ -243,10 +286,13 @@ func resourceSumologicGenericPollingSource() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "environment": { + Type: schema.TypeString, + Optional: true, + }, }, }, } - return pollingSource } @@ -378,6 +424,7 @@ func getPollingThirdPartyPathAttributes(pollingResource []PollingResource) []map "use_versioned_api": t.Path.UseVersionedApi, "custom_services": flattenCustomServices(t.Path.CustomServices), "tag_filters": flattenPollingTagFilters(t.Path.TagFilters), + "azure_tag_filters": flattenPollingAzureTagFilters(t.Path.TagFilters), "sns_topic_or_subscription_arn": flattenPollingSnsTopicOrSubscriptionArn(t.Path.SnsTopicOrSubscriptionArn), "namespace": t.Path.Namespace, "event_hub_name": t.Path.EventHubName, @@ -412,6 +459,8 @@ func getPollingThirdPartyAuthenticationAttributes(pollingResource []PollingResou "client_x509_cert_url": t.Authentication.ClientX509CertUrl, "shared_access_policy_name": t.Authentication.SharedAccessPolicyName, "shared_access_policy_key": t.Authentication.SharedAccessPolicyKey, + "tenant_id": t.Authentication.TenantId, + "client_secret": t.Authentication.ClientSecret, } s = append(s, mapping) } @@ -450,25 +499,66 @@ func getCustomServices(path map[string]interface{}) []string { return customServices } -func flattenPollingTagFilters(v []TagFilter) []map[string]interface{} { +func flattenPollingTagFilters(v []interface{}) []map[string]interface{} { var filters []map[string]interface{} - for _, d := range v { - filter := map[string]interface{}{ - "type": d.Type, - "namespace": d.Namespace, - "tags": d.Tags, + if len(v) > 0 { + filter := v[0].(map[string]interface{}) + switch filterType, ok := filter["type"].(string); { + case !ok || filterType == "AzureTagFilters": // do nothing + default: + for _, d := range v { + rawFilter := d.(map[string]interface{}) + filter := map[string]interface{}{ + "type": rawFilter["type"].(string), + "namespace": rawFilter["namespace"].(string), + "tags": rawFilter["tags"].([]interface{}), + } + filters = append(filters, filter) + } } - filters = append(filters, filter) } + return filters +} +func flattenPollingAzureTagFilters(v []interface{}) []map[string]interface{} { + var filters []map[string]interface{} + if len(v) > 0 { + filter := v[0].(map[string]interface{}) + switch filterType, ok := filter["type"].(string); { + case !ok || filterType == "AzureTagFilters": + for _, d := range v { + rawFilter := d.(map[string]interface{}) + filter := map[string]interface{}{ + "type": "AzureTagFilters", + "namespace": rawFilter["namespace"].(string), + "tags": flattenAzureTagKeyValuePair(rawFilter["tags"].([]interface{})), + } + filters = append(filters, filter) + } + default: // do nothing + } + } return filters } -func getPollingTagFilters(d *schema.ResourceData) []TagFilter { +func flattenAzureTagKeyValuePair(v []interface{}) []map[string]interface{} { + var tags []map[string]interface{} + for _, d := range v { + rawTag := d.(map[string]interface{}) + tag := map[string]interface{}{ + "name": rawTag["name"].(string), + "values": rawTag["values"].([]interface{}), + } + tags = append(tags, tag) + } + return tags +} + +func getPollingTagFilters(d *schema.ResourceData) []interface{} { paths := d.Get("path").([]interface{}) path := paths[0].(map[string]interface{}) rawTagFilterConfig := path["tag_filters"].([]interface{}) - var filters []TagFilter + var filters []interface{} for _, rawConfig := range rawTagFilterConfig { config := rawConfig.(map[string]interface{}) @@ -484,7 +574,39 @@ func getPollingTagFilters(d *schema.ResourceData) []TagFilter { filter.Tags = Tags filters = append(filters, filter) } + return filters +} +func getPollingAzureTagFilters(d *schema.ResourceData) []interface{} { + paths := d.Get("path").([]interface{}) + path := paths[0].(map[string]interface{}) + rawTagFilterConfig := path["azure_tag_filters"].([]interface{}) + var filters []interface{} + + for _, rawConfig := range rawTagFilterConfig { + config := rawConfig.(map[string]interface{}) + filter := AzureTagFilter{} + filter.Type = config["type"].(string) + filter.Namespace = config["namespace"].(string) + + rawTags := config["tags"].([]interface{}) + Tags := make([]AzureTagKeyValuePair, len(rawTags)) + for i, rawTagPair := range rawTags { + tagPair := rawTagPair.(map[string]interface{}) + pair := AzureTagKeyValuePair{} + pair.Name = tagPair["name"].(string) + // get the values from the azure key value pair + rawValues := tagPair["values"].([]interface{}) + valueList := make([]string, len(rawValues)) + for j, v := range rawValues { + valueList[j] = v.(string) + } + pair.Values = valueList + Tags[i] = pair + } + filter.Tags = Tags + filters = append(filters, filter) + } return filters } @@ -577,7 +699,11 @@ func getPollingAuthentication(d *schema.ResourceData) (PollingAuthentication, er authSettings.Type = "AzureEventHubAuthentication" authSettings.SharedAccessPolicyName = auth["shared_access_policy_name"].(string) authSettings.SharedAccessPolicyKey = auth["shared_access_policy_key"].(string) - + case "AzureClientSecretAuthentication": + authSettings.Type = "AzureClientSecretAuthentication" + authSettings.TenantId = auth["tenant_id"].(string) + authSettings.AzureClientId = auth["client_id"].(string) + authSettings.ClientSecret = auth["client_secret"].(string) default: errorMessage := fmt.Sprintf("[ERROR] Unknown authType: %v", authType) log.Print(errorMessage) @@ -671,6 +797,18 @@ func getPollingPathSettings(d *schema.ResourceData) (PollingPath, error) { if path["region"] != nil { pathSettings.Region = path["region"].(string) } + case "AzureMetricsPath": + pathSettings.Type = "AzureMetricsPath" + pathSettings.Environment = path["environment"].(string) + rawLimitToNamespaces := path["limit_to_namespaces"].([]interface{}) + LimitToNamespaces := make([]string, 0, len(rawLimitToNamespaces)) + for _, v := range rawLimitToNamespaces { + if v != nil { + LimitToNamespaces = append(LimitToNamespaces, v.(string)) + } + } + pathSettings.LimitToNamespaces = LimitToNamespaces + pathSettings.TagFilters = getPollingAzureTagFilters(d) default: errorMessage := fmt.Sprintf("[ERROR] Unknown resourceType in path: %v", pathType) log.Print(errorMessage) diff --git a/sumologic/resource_sumologic_polling_source.go b/sumologic/resource_sumologic_polling_source.go index 06dc7eba..ad537a9d 100644 --- a/sumologic/resource_sumologic_polling_source.go +++ b/sumologic/resource_sumologic_polling_source.go @@ -242,25 +242,27 @@ func getThirdPartyPathAttributes(pollingResource []PollingResource) []map[string return s } -func flattenTagFilters(v []TagFilter) []map[string]interface{} { +func flattenTagFilters(v []interface{}) []map[string]interface{} { var filters []map[string]interface{} for _, d := range v { - filter := map[string]interface{}{ - "type": d.Type, - "namespace": d.Namespace, - "tags": d.Tags, + switch t := d.(type) { + case TagFilter: + filter := map[string]interface{}{ + "type": t.Type, + "namespace": t.Namespace, + "tags": t.Tags, + } + filters = append(filters, filter) } - filters = append(filters, filter) } - return filters } -func getTagFilters(d *schema.ResourceData) []TagFilter { +func getTagFilters(d *schema.ResourceData) []interface{} { paths := d.Get("path").([]interface{}) path := paths[0].(map[string]interface{}) rawTagFilterConfig := path["tag_filters"].([]interface{}) - var filters []TagFilter + var filters []interface{} for _, rawConfig := range rawTagFilterConfig { config := rawConfig.(map[string]interface{}) @@ -276,7 +278,6 @@ func getTagFilters(d *schema.ResourceData) []TagFilter { filter.Tags = Tags filters = append(filters, filter) } - return filters } diff --git a/sumologic/sumologic_polling_source.go b/sumologic/sumologic_polling_source.go index cf367d4e..756e26e4 100644 --- a/sumologic/sumologic_polling_source.go +++ b/sumologic/sumologic_polling_source.go @@ -41,6 +41,9 @@ type PollingAuthentication struct { ClientX509CertUrl string `json:"client_x509_cert_url"` SharedAccessPolicyName string `json:"sharedAccessPolicyName"` SharedAccessPolicyKey string `json:"sharedAccessPolicyKey"` + AzureClientId string `json:"clientId"` + TenantId string `json:"tenantId"` + ClientSecret string `json:"clientSecret"` } type PollingPath struct { @@ -51,13 +54,14 @@ type PollingPath struct { LimitToNamespaces []string `json:"limitToNamespaces,omitempty"` LimitToServices []string `json:"limitToServices,omitempty"` CustomServices []string `json:"customServices,omitempty"` - TagFilters []TagFilter `json:"tagFilters,omitempty"` + TagFilters []interface{} `json:"tagFilters,omitempty"` SnsTopicOrSubscriptionArn PollingSnsTopicOrSubscriptionArn `json:"snsTopicOrSubscriptionArn,omitempty"` UseVersionedApi *bool `json:"useVersionedApi,omitempty"` Namespace string `json:"namespace,omitempty"` EventHubName string `json:"eventHubName,omitempty"` ConsumerGroup string `json:"consumerGroup,omitempty"` Region string `json:"region,omitempty"` + Environment string `json:"environment,omitempty"` } type TagFilter struct { @@ -66,6 +70,17 @@ type TagFilter struct { Tags []string `json:"tags"` } +type AzureTagFilter struct { + Type string `json:"type"` + Namespace string `json:"namespace"` + Tags []AzureTagKeyValuePair `json:"tags"` +} + +type AzureTagKeyValuePair struct { + Name string `json:"name"` + Values []string `json:"values"` +} + type PollingSnsTopicOrSubscriptionArn struct { IsSuccess bool `json:"isSuccess"` Arn string `json:"arn"` diff --git a/website/docs/r/azure_metrics_source.html.markdown b/website/docs/r/azure_metrics_source.html.markdown new file mode 100644 index 00000000..e6d10d32 --- /dev/null +++ b/website/docs/r/azure_metrics_source.html.markdown @@ -0,0 +1,67 @@ +--- +layout: "sumologic" +page_title: "SumoLogic: sumologic_azure_metrics_source" +description: |- + Provides a Sumologic Azure Metrics Source. +--- + +# sumologic_azure_metrics_source +Provides a [Sumologic Azure Metrics Source](https://help.sumologic.com/docs/send-data/hosted-collectors/microsoft-source/azure-metrics-source/) + +__IMPORTANT:__ The Azure Event Hub credentials are stored in plain-text in the state. This is a potential security issue. + +## Example Usage +```hcl +resource "sumologic_azure_metrics_source" "terraform_azure_metrics_source" { + name = "Azure Metrics Source" + description = "My description" + category = "azure/metrics" + content_type = "AzureMetrics" + collector_id = "${sumologic_collector.collector.id}" + + authentication { + type = "AzureClientSecretAuthentication" + tenant_id = "azure_tenant_id" + client_id = "azure_client_id" + client_secret = "azure_client_secret" + } + + path { + type = "AzureMetricsPath" + environment = "Azure" + limit_to_namespaces = ["Microsoft.ClassicStorage/storageAccounts"] + azure_tag_filters { + type = "AzureTagFilters" + namespace = "Microsoft.ClassicStorage/storageAccounts" + tags { + name = "test-name-1" + values = ["value1", "value2"] + } + } + } +} + +resource "sumologic_collector" "collector" { + name = "my-collector" + description = "Just testing this" +} +``` + +## Argument reference +In addition to the [Common Source Properties](https://registry.terraform.io/providers/SumoLogic/sumologic/latest/docs#common-source-properties), the following arguments are supported: + - `content_type` - (Required) Must be `AzureMetrics`. +- `authentication` - (Required) Authentication details for connecting to ingest metrics from Azure. + + `type` - (Required) Must be `AzureClientSecretAuthentication`. + + `tenant_id` - (Required) Your tenant id collected from [Azure platform](https://help.sumologic.com/docs/send-data/hosted-collectors/microsoft-source/azure-metrics-source/#vendor-configuration). + + `client_id` - (Required) Your client id collected from [Azure platform](https://help.sumologic.com/docs/send-data/hosted-collectors/microsoft-source/azure-metrics-source/#vendor-configuration). + + `client_secret` - (Required) Your client secret collected from [Azure platform](https://help.sumologic.com/docs/send-data/hosted-collectors/microsoft-source/azure-metrics-source/#vendor-configuration). + - `path` - (Required) The location to scan for new data. + + `type` - (Required) Must be `AzureMetricsPath`. + + `environment` - (Required) The environment to collect Azure metrics. + + `limit_to_namespaces` - (Opitonal) The list of namespaces to collect metrics. By default all namespaces are selected. + + `azure_tag_filters` - (Optional) Tag filters allow you to filter the Azure metrics by the tags you have assigned to your Azure resources. You can define tag filters for each supported namespace. If you do not define any tag filters, all metrics will be collected for namespaces you configured for the source above. + + `type` - (Required) This value has to be set to `AzureTagFilters` + + `namespace` - Namespace for which you want to define the tag filters. + + `tags` - List of key and value list of tag filters. + + `name`: The name of tag. + + `values`: The list of accepted values for the tag name.