Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## X.Y.Z (Unreleased)
* Add new change notes here
* Add support for local windows event log source for installed collectors

## 3.1.1 (July 8, 2025)

Expand Down
1 change: 1 addition & 0 deletions sumologic/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ func Provider() *schema.Provider {
"sumologic_source_template": resourceSumologicSourceTemplate(),
"sumologic_azure_metrics_source": resourceSumologicGenericPollingSource(),
"sumologic_scan_budget": resourceSumologicScanBudget(),
"sumologic_local_windows_event_log_source": resourceSumologicLocalWindowsEventLogSource(),
},
DataSourcesMap: map[string]*schema.Resource{
"sumologic_cse_log_mapping_vendor_product": dataSourceCSELogMappingVendorAndProduct(),
Expand Down
151 changes: 151 additions & 0 deletions sumologic/resource_sumologic_local_windows_event_log_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package sumologic

import (
"fmt"
"log"
"strconv"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
)

func resourceSumologicLocalWindowsEventLogSource() *schema.Resource {
LocalWindowsEventLogSource := resourceSumologicSource()
LocalWindowsEventLogSource.Create = resourceSumologicLocalWindowsEventLogSourceCreate
LocalWindowsEventLogSource.Read = resourceSumologicLocalWindowsEventLogSourceRead
LocalWindowsEventLogSource.Update = resourceSumologicLocalWindowsEventLogSourceUpdate
// LocalWindowsEventLogSource.Delete: resourceSumologicSourceDelete
LocalWindowsEventLogSource.Importer = &schema.ResourceImporter{
State: resourceSumologicSourceImport,
}

// Windows Event Log specific fields
LocalWindowsEventLogSource.Schema["log_names"] = &schema.Schema{
Type: schema.TypeList,
Required: true,
Elem: &schema.Schema{Type: schema.TypeString},
Description: "List of Windows log types to collect (e.g., Security, Application, System)",
}

LocalWindowsEventLogSource.Schema["render_messages"] = &schema.Schema{
Type: schema.TypeBool,
Optional: true,
Default: true,
Description: "When using legacy format, indicates if full event messages are collected",
}

LocalWindowsEventLogSource.Schema["event_format"] = &schema.Schema{
Type: schema.TypeInt,
Optional: true,
Default: 0,
ValidateFunc: validation.IntInSlice([]int{0, 1}),
Description: "0 for legacy format (XML), 1 for JSON format",
}

LocalWindowsEventLogSource.Schema["event_message"] = &schema.Schema{
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntInSlice([]int{0, 1, 2}),
Description: "0 for complete message, 1 for message title, 2 for metadata only. Required if event_format is 0",
}

LocalWindowsEventLogSource.Schema["deny_list"] = &schema.Schema{
Type: schema.TypeString,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
Description: "Comma-separated list of event IDs to deny",
}

// "allowlist": is not implmemented yet
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we get the allow_list also added?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've done that, tidied up a few loose ends and fixed the two linting bugs


return LocalWindowsEventLogSource
}

func resourceSumologicLocalWindowsEventLogSourceCreate(d *schema.ResourceData, meta interface{}) error {
c := meta.(*Client)

if d.Id() == "" {
source := resourceToLocalWindowsEventLogSource(d)
collectorID := d.Get("collector_id").(int)

id, err := c.CreateLocalWindowsEventLogSource(source, collectorID)
if err != nil {
return err
}

d.SetId(strconv.Itoa(id))
}

return resourceSumologicLocalWindowsEventLogSourceRead(d, meta)
}

func resourceSumologicLocalWindowsEventLogSourceUpdate(d *schema.ResourceData, meta interface{}) error {
c := meta.(*Client)

source := resourceToLocalWindowsEventLogSource(d)

err := c.UpdateLocalWindowsEventLogSource(source, d.Get("collector_id").(int))

if err != nil {
return err
}

return resourceSumologicLocalWindowsEventLogSourceRead(d, meta)
}

func resourceToLocalWindowsEventLogSource(d *schema.ResourceData) LocalWindowsEventLogSource {

source := resourceToSource(d)
source.Type = "LocalWindowsEventLog"

LocalWindowsEventLogSource := LocalWindowsEventLogSource{
Source: source,
LogNames: d.Get("log_names").([]interface{}),
RenderMessages: d.Get("render_messages").(bool),
EventFormat: d.Get("event_format").(int),
DenyList: d.Get("deny_list").(string),
}

// Handle optional deny_list
if DenyList, ok := d.GetOk("deny_list"); ok {
LocalWindowsEventLogSource.DenyList = DenyList.(string)
}

// Handle optional event_message field
if eventMessage, ok := d.GetOk("event_message"); ok {
eventMessageInt := eventMessage.(int)
LocalWindowsEventLogSource.EventMessage = &eventMessageInt
}

return LocalWindowsEventLogSource

}

func resourceSumologicLocalWindowsEventLogSourceRead(d *schema.ResourceData, meta interface{}) error {
c := meta.(*Client)

id, _ := strconv.Atoi(d.Id())
source, err := c.GetLocalWindowsEventLogSource(d.Get("collector_id").(int), id)

if err != nil {
return err
}

if source == nil {
log.Printf("[WARN] Local Windows Event Log source not found, removing from state: %v - %v", id, err)
d.SetId("")
return nil
}

if err := resourceSumologicSourceRead(d, source.Source); err != nil {
return fmt.Errorf("%s", err)
}
d.Set("log_names", source.LogNames)
d.Set("render_messages", source.RenderMessages)
d.Set("event_format", source.EventFormat)
d.Set("deny_list", source.DenyList)
d.Set("event_format", source.EventFormat)
d.Set("event_message", *source.EventMessage)

return nil
}
83 changes: 83 additions & 0 deletions sumologic/sumologic_local_windows_event_log_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package sumologic

import (
"encoding/json"
"fmt"
)

type LocalWindowsEventLogSource struct {
Source
LogNames []interface{} `json:"logNames"`
RenderMessages bool `json:"renderMessages"`
EventFormat int `json:"eventFormat"`
EventMessage *int `json:"eventMessage,omitempty"`
// Allowlist []string `json:"allowlist,omitempty"`
DenyList string `json:"denylist,omitempty"`
}

func (s *Client) CreateLocalWindowsEventLogSource(source LocalWindowsEventLogSource, collectorID int) (int, error) {

type LocalWindowsEventLogSourceMessage struct {
Source LocalWindowsEventLogSource `json:"source"`
}

request := LocalWindowsEventLogSourceMessage{
Source: source,
}

urlPath := fmt.Sprintf("v1/collectors/%d/sources", collectorID)
body, err := s.Post(urlPath, request)

if err != nil {
return -1, err
}

var response LocalWindowsEventLogSourceMessage

err = json.Unmarshal(body, &response)
if err != nil {
return -1, err
}

return response.Source.ID, nil
}

func (s *Client) GetLocalWindowsEventLogSource(collectorID, sourceID int) (*LocalWindowsEventLogSource, error) {
body, err := s.Get(fmt.Sprintf("v1/collectors/%d/sources/%d", collectorID, sourceID))
if err != nil {
return nil, err
}

if body == nil {
return nil, nil
}

type LocalWindowsEventLogSourceResponse struct {
Source LocalWindowsEventLogSource `json:"source"`
}

var response LocalWindowsEventLogSourceResponse
err = json.Unmarshal(body, &response)
if err != nil {
return nil, err
}

return &response.Source, nil

}

func (s *Client) UpdateLocalWindowsEventLogSource(source LocalWindowsEventLogSource, collectorID int) error {

type LocalWindowsEventLogMessage struct {
Source LocalWindowsEventLogSource `json:"source"`
}

request := LocalWindowsEventLogMessage{
Source: source,
}

urlPath := fmt.Sprintf("v1/collectors/%d/sources/%d", collectorID, source.ID)
_, err := s.Put(urlPath, request)

return err
}
87 changes: 87 additions & 0 deletions website/docs/r/local_windows_event_source.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
layout: "sumologic"
page_title: "SumoLogic: sumologic_local_windows_event_log_source"
description: |-
Provides a Sumologic Local Windows Event Log Source.
---

# sumologic_local_windows_event_source
Provides a [Sumologic Local Windows Event Log Source][1].

Note that installed collector sources must be treated as a special case as the user must have a pipeline to install them outside of terraform as it is not possible to install a local collector via the API, that must be done locally on the instance. Make sure the collector is in cloud managed not local json file mode to allow for API based configuration.

Use the installed collector data source to map to installed collector instances by name or id.

## Example Usage

Example: 1 This will configure JSON format with "concise" setting and pickup System and Application logs with /os/windows/events as the sourcecateogry.

```hcl
data "sumologic_collector" "installed_collector" {
name = "terraform_source_testing"
}

resource "sumologic_local_windows_event_log_source" "local" {
name = "windows_logs"
description = "windows system and application logs in json format"
category = "/os/windows/events"
collector_id = "${data.sumologic_collector.installed_collector.id}"
log_names = ["System","Application"]
event_format = 1 // 0 = XML, 1 = JSON
}
```

Example 2: Using custom logs and a deny list
```hcl
resource "sumologic_local_windows_event_log_source" "local" {
name = "windows_logs"
description = "windows logs in json format"
category = "/os/windows/events"
collector_id = "${data.sumologic_collector.installed_collector.id}"
log_names = ["System","Application","Microsoft-Windows-PowerShell/Operational", "Microsoft-Windows-TaskScheduler/Operational"]
deny_list = "9999,7890"
event_format = 1 // 0 = XML, 1 = JSON
}
```



## Argument Reference

The following arguments are supported:

* `name` - (Required) The name of the local file source. This is required, and has to be unique. Changing this will force recreation the source.
* `description` - (Optional) The description of the source.
* `log_names` - List of Windows log types to collect (e.g., Security, Application, System)
* `render_messages` - When using legacy format, indicates if full event messages are collected
* `event_format` - 0 for legacy format (XML), 1 for JSON format. Default 0.
* `event_message` - 0 for complete message, 1 for message title, 2 for metadata only. Required if event_format is 0
* `deny_list` - Comma-separated list of event IDs to deny
* `category` - (Optional) The default source category for the source.
* `fields` - (Optional) Map containing [key/value pairs][2].
* `denylist` - (Optional) Comma-separated list of valid path expressions from which logs will not be collected.

### See also
* [Common Source Properties](https://github.com/terraform-providers/terraform-provider-sumologic/tree/master/website#common-source-properties)

## Attributes Reference
The following attributes are exported:

* `id` - The internal ID of the local file source.

## Import
Local file sources can be imported using the collector and source IDs, e.g.:

```hcl
terraform import sumologic_local_windows_event_source.test 123/456
```

Local file sources can also be imported using the collector name and source name, e.g.:

```hcl
terraform import sumologic_local_windows_event_source.test my-test-collector/my-test-source
```

[1]: https://help.sumologic.com/docs/send-data/installed-collectors/sources/local-windows-event-log-source/
[2]: https://help.sumologic.com/Manage/Fields
[3]: https://help.sumologic.com/docs/send-data/use-json-configure-sources/json-parameters-installed-sources/#local-windows-event-logsource
Loading