Skip to content

Commit 3ac6d63

Browse files
committed
add local file saving and reading
1 parent 9814c64 commit 3ac6d63

File tree

2 files changed

+68
-13
lines changed

2 files changed

+68
-13
lines changed

README.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@ Examples:
1616
Flags:
1717
-a, --api string API maps to EventName within CloudTrail Examples are DescribeInstances, TerminateInstances, etc
1818
-c, --call-source string CallSource maps to SourceIP in CloudTrail but AWS services will include a named source IP like eks.amazonaws.com or autoscaling.amazonaws.com
19-
-e, --end-time string End time for event filtering. Default: 30m ago (default "2023-01-26T14:28:12-06:00")
19+
-e, --end-time string End time for event filtering. Default: 30m ago (default "2023-01-27T17:32:20Z")
2020
--event-source string EventSource is the top-level service where the API call is made from (i.e. ec2.amazonaws.com)
21+
-f, --file string File of json cloudtrail events to process
2122
-h, --help help for aca
2223
-i, --identity-user-name string IdentityUserName is included in the CloudTrailEvent.userIdentity.sessionContext.sessionIssuer.userName and is useful to scope the filtering to a specific instance of an application making API calls
23-
-o, --output string Output (json|chart) Default: json (default "json")
24+
-o, --output string Output (json|chart|stats) Default: json (default "json")
2425
-r, --region string AWS Region
25-
-s, --start-time string Start time for event filtering. Default: now (default "2023-01-26T13:58:12-06:00")
26+
--save string Save filtered json events to a file. Default: a temp directory
27+
-s, --start-time string Start time for event filtering. Default: now (default "2023-01-27T17:02:20Z")
2628
-u, --user-agent string UserAgent partial will check if the passed string is contained within the user-agent field
29+
-v, --version Verison information
2730
```
2831

2932
## Installation

cmd/main.go

+62-10
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"encoding/json"
2020
"fmt"
21+
"io"
2122
"log"
2223
"os"
2324
"sort"
@@ -58,6 +59,10 @@ type Options struct {
5859
Version bool
5960
// Output json or chart
6061
Output string
62+
// File of json cloudtrail events to process rather than calling the cloudtrail API
63+
File string
64+
// Save will output the filtered json events to a file for further processing or inspection
65+
Save string
6166
}
6267

6368
var (
@@ -135,13 +140,53 @@ var rootCmd = &cobra.Command{
135140
fmt.Printf("Commit: %s\n", commit)
136141
return
137142
}
138-
total, events, err := filterEvents(cmd.Context(), processOpts(opts))
139-
if err != nil {
140-
log.Fatalln(err.Error())
143+
var events []*CloudTrailEvent
144+
var total int
145+
var err error
146+
147+
// Process events from a local file
148+
if opts.File != "" {
149+
file, err := os.Open(opts.File)
150+
if err != nil {
151+
log.Fatalf("failed reading file: %s", err)
152+
}
153+
defer file.Close()
154+
eventData, err := io.ReadAll(file)
155+
if err != nil {
156+
log.Fatalf("unable to read json events from file %s: %v", opts.File, err)
157+
}
158+
if err := json.Unmarshal(eventData, &events); err != nil {
159+
log.Fatalf("unable to parse json events from file %s: %v", opts.File, err)
160+
}
161+
total = len(events)
162+
} else { // Process events from the CloudTrail API
163+
total, events, err = filterEvents(cmd.Context(), processOpts(opts))
164+
if err != nil {
165+
log.Fatalln(err.Error())
166+
}
167+
if len(events) == 0 {
168+
log.Printf("All %d events did not match your filters\n", total)
169+
os.Exit(1)
170+
}
171+
}
172+
173+
var outputFile *os.File
174+
if opts.Save == "" {
175+
outputFile, err = os.CreateTemp("", "aca")
176+
if err != nil {
177+
log.Printf("unable to open temp file, in dir %s, to output events: %v\n", os.TempDir(), err)
178+
}
179+
} else {
180+
outputFile, err = os.Create(opts.Save)
181+
if err != nil {
182+
log.Printf("unable to open temp file (%s) to output events: %v\n", opts.Save, err)
183+
}
141184
}
142-
if len(events) == 0 {
143-
log.Printf("All %d events did not match your filters\n", total)
144-
os.Exit(1)
185+
186+
if err := writeEvents(events, outputFile); err != nil {
187+
log.Printf("unable to write events to file %s: %v\n", outputFile.Name(), err)
188+
} else {
189+
log.Printf("wrote events to file %s\n", outputFile.Name())
145190
}
146191

147192
log.Printf("Filtered to %d events out of %d. The last event's timestamp is %s and the endtime filter was %s\n",
@@ -152,14 +197,17 @@ var rootCmd = &cobra.Command{
152197
stats := computeStats(events)
153198
outputStatsChart(stats)
154199
} else {
155-
outputJSON(events)
200+
fmt.Println(asJSON(events))
156201
}
157202
},
158203
}
159204

160205
func main() {
161206
rootCmd.PersistentFlags().StringVarP(&opts.Region, "region", "r", "", "AWS Region")
162207
rootCmd.PersistentFlags().StringVarP(&opts.Output, "output", "o", "json", "Output (json|chart|stats) Default: json")
208+
rootCmd.PersistentFlags().BoolVarP(&opts.Version, "version", "v", false, "Version information")
209+
rootCmd.PersistentFlags().StringVarP(&opts.File, "file", "f", "", "File of json cloudtrail events to process")
210+
rootCmd.PersistentFlags().StringVar(&opts.Save, "save", "", "Save filtered json events to a file. Default: a temp directory")
163211

164212
rootCmd.PersistentFlags().StringVarP(&opts.CallSource, "call-source", "c", "", "CallSource maps to SourceIP in CloudTrail but AWS services will include a named source IP like eks.amazonaws.com or autoscaling.amazonaws.com")
165213
rootCmd.PersistentFlags().StringVar(&opts.EventSource, "event-source", "", "EventSource is the top-level service where the API call is made from (i.e. ec2.amazonaws.com)")
@@ -274,13 +322,12 @@ func filterEvents(ctx context.Context, opts *Options) (int, []*CloudTrailEvent,
274322
return rawEvents, filteredEvents, nil
275323
}
276324

277-
func outputJSON(events []*CloudTrailEvent) {
278-
log.Printf("Found %d events\n", len(events))
325+
func asJSON(events []*CloudTrailEvent) string {
279326
eventsJSON, err := json.MarshalIndent(events, "", " ")
280327
if err != nil {
281328
log.Fatalf(err.Error())
282329
}
283-
fmt.Println(string(eventsJSON))
330+
return string(eventsJSON)
284331
}
285332

286333
func outputChart(events []*CloudTrailEvent) {
@@ -361,3 +408,8 @@ func outputStatsChart(stats []*Stat) {
361408
table.SetFooter([]string{"", "TOTAL", strconv.Itoa(totalCalls)})
362409
table.Render()
363410
}
411+
412+
func writeEvents(events []*CloudTrailEvent, file *os.File) error {
413+
_, err := file.WriteString(asJSON(events))
414+
return err
415+
}

0 commit comments

Comments
 (0)