Skip to content

Commit 51a35a1

Browse files
feat(audit_trail): add new data source audit_trail_event
1 parent 51063ed commit 51a35a1

File tree

7 files changed

+460
-1
lines changed

7 files changed

+460
-1
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
subcategory: "Audit Trail"
3+
page_title: "Scaleway: scaleway_audit_trail_event"
4+
---
5+
6+
# scaleway_audit_trail_event
7+
8+
Use this data source to get a list of existing Audit Trail events.
9+
For more information refer to the [Audit Trail API documentation](https://www.scaleway.com/en/developers/api/audit-trail/).
10+
11+
## Example Usage
12+
13+
```hcl
14+
# Retrieve all audit trail events on the default organization
15+
data "scaleway_audit_trail_event" "find_all" {
16+
region = "fr-par"
17+
}
18+
19+
# Retrieve audit trail events on a specific organization
20+
data "scaleway_audit_trail_event" "find_by_org" {
21+
organization_id = "11111111-1111-1111-1111-111111111111"
22+
}
23+
24+
# Retrieve audit trail events on a specific project
25+
data "scaleway_audit_trail_event" "find_by_project" {
26+
region = "fr-par"
27+
project_id = "11111111-1111-1111-1111-111111111111"
28+
}
29+
30+
# Retrieve audit trail events for a specific type of resource
31+
data "scaleway_audit_trail_event" "find_by_resource_type" {
32+
resource_type = "instance_server"
33+
}
34+
35+
# Retrieve audit trail for a specific resource
36+
data "scaleway_audit_trail_event" "find_by_resource_id" {
37+
resource_id = "11111111-1111-1111-1111-111111111111"
38+
}
39+
40+
# Retrieve audit trail for a specific Scaleway product
41+
data "scaleway_audit_trail_event" "find_by_product_name" {
42+
region = "nl-ams"
43+
product_name = "secret-manager"
44+
}
45+
```
46+
47+
## Argument Reference
48+
49+
- `region` - (Optional) The [region](../guides/regions_and_zones.md#regions) you want to target. Defaults to the region specified in the [provider configuration](../index.md#region).
50+
- `organization_id` - (Optional. Defaults to [provider](../index.md#organization_id) `organization_id`) ID of the Organization containing the Audit Trail events.
51+
- `project_id` - (Optional) ID of the Project containing the Audit Trail events.
52+
- `resource_type` - (Optional) Type of the scaleway resources associated with the listed events. Possible values are: `secm_secret`, `secm_secret_version`, `kube_cluster`, `kube_pool`, `kube_node`, `kube_acl`, `keym_key`, `iam_user`, `iam_application`, `iam_group`, `iam_policy`, `iam_api_key`, `iam_ssh_key`, `iam_rule`, `iam_saml`, `iam_saml_certificate`, `secret_manager_secret`, `secret_manager_version`, `key_manager_key`, `account_user`, `account_organization`, `account_project`, `instance_server`, `instance_placement_group`, `instance_security_group`, `instance_volume`, `instance_snapshot`, `instance_image`, `apple_silicon_server`, `baremetal_server`, `baremetal_setting`, `ipam_ip`, `sbs_volume`, `sbs_snapshot`, `load_balancer_lb`, `load_balancer_ip`, `load_balancer_frontend`, `load_balancer_backend`, `load_balancer_route`, `load_balancer_acl`, `load_balancer_certificate`, `sfs_filesystem`, or `vpc_private_network`.
53+
- `resource_id` - (Optional) ID of the Scaleway resource associated with the listed events.
54+
- `product_name` - (Optional) Name of the Scaleway product in a hyphenated format.
55+
56+
57+
## Attributes Reference
58+
59+
In addition to all arguments above, the following attributes are exported:
60+
61+
- `events` - List of Audit Trail events matching the requested criteria.
62+
- `id` - ID of the event. (UUID format)
63+
- `recorded_at` - Timestamp of the event. (RFC 3339 format)
64+
- `locality` - Locality of the resource attached to the event.
65+
- `principal_id` - ID of the user or IAM application at the origin of the event.
66+
- `organization_id` - ID of the Organization containing the Audit Trail events. (UUID format)
67+
- `project_id` - Project of the resource attached to the event. (UUID format)
68+
- `source_ip` - IP address at the origin of the event. (IP address)
69+
- `user_agent` - User Agent at the origin of the event.
70+
- `product_name` - Product name of the resource attached to the event.
71+
- `service_name` - API name called to trigger the event.
72+
- `method_name` - API method called to trigger the event.
73+
- `resources` - List of resources attached to the event.
74+
- `id` - ID of the resource attached to the event. (UUID format)
75+
- `type` - Type of the Scaleway resource.
76+
- `request_id` - Unique identifier of the request at the origin of the event. (UUID format)
77+
- `request_body` - Request at the origin of the event.
78+
- `status_code` - HTTP status code resulting of the API call.
79+

internal/meta/errors.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@ package meta
22

33
import "errors"
44

5-
// ErrProjectIDNotFound is returned when no region can be detected
5+
// ErrProjectIDNotFound is returned when no project ID can be detected
66
var ErrProjectIDNotFound = errors.New("could not detect project id")
7+
8+
// ErrOrganizationIDNotFound is returned when no organization ID can be detected
9+
var ErrOrganizationIDNotFound = errors.New("could not detect organization id")

internal/meta/extractors.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,24 @@ func ExtractProjectID(d terraformResourceData, m any) (projectID string, isDefau
116116
return "", false, ErrProjectIDNotFound
117117
}
118118

119+
// ExtractOrganizationID will try to guess the organization id from the following:
120+
// - organization_id field of the resource data
121+
// - default organization_id from config
122+
func ExtractOrganizationID(d terraformResourceData, m any) (organizationID string, err error) {
123+
rawOrgID, exist := d.GetOk("organization_id")
124+
125+
if exist {
126+
return rawOrgID.(string), nil
127+
}
128+
129+
defaultOrgID, defaultOrgIDExists := m.(*Meta).ScwClient().GetDefaultOrganizationID()
130+
if defaultOrgIDExists {
131+
return defaultOrgID, nil
132+
}
133+
134+
return "", ErrOrganizationIDNotFound
135+
}
136+
119137
func ExtractScwClient(m any) *scw.Client {
120138
return m.(*Meta).ScwClient()
121139
}
Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
package audittrail
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/google/uuid"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10+
audittrailSDK "github.com/scaleway/scaleway-sdk-go/api/audit_trail/v1alpha1"
11+
"github.com/scaleway/scaleway-sdk-go/scw"
12+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
13+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/types"
14+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/verify"
15+
)
16+
17+
func DataSourceEvent() *schema.Resource {
18+
return &schema.Resource{
19+
ReadContext: DataSourceEventsRead,
20+
Schema: map[string]*schema.Schema{
21+
"organization_id": {
22+
Type: schema.TypeString,
23+
Description: "Organization containing the listed events",
24+
Optional: true,
25+
ValidateDiagFunc: verify.IsUUID(),
26+
},
27+
"region": regional.Schema(),
28+
"project_id": {
29+
Type: schema.TypeString,
30+
Description: "Project containing the listed events",
31+
Optional: true,
32+
ValidateDiagFunc: verify.IsUUID(),
33+
},
34+
"resource_type": {
35+
Type: schema.TypeString,
36+
Description: "Type of the scaleway resources associated with the listed events",
37+
Optional: true,
38+
ValidateDiagFunc: verify.ValidateEnum[audittrailSDK.ResourceType](),
39+
},
40+
"resource_id": {
41+
Type: schema.TypeString,
42+
Description: "ID of the Scaleway resource associated with the listed events",
43+
Optional: true,
44+
ValidateDiagFunc: verify.IsUUID(),
45+
},
46+
"product_name": {
47+
Type: schema.TypeString,
48+
Description: "Scaleway product associated with the listed events",
49+
Optional: true,
50+
},
51+
"events": {
52+
Type: schema.TypeList,
53+
Computed: true,
54+
Description: "List of Audit Trail events",
55+
Elem: &schema.Resource{
56+
Schema: map[string]*schema.Schema{
57+
"id": {
58+
Type: schema.TypeString,
59+
Description: "ID of the event",
60+
Computed: true,
61+
},
62+
"recorded_at": {
63+
Type: schema.TypeString,
64+
Description: "Timestamp of the event",
65+
Computed: true,
66+
},
67+
"locality": {
68+
Type: schema.TypeString,
69+
Description: "Locality of the resource attached to the event",
70+
Computed: true,
71+
},
72+
"principal_id": {
73+
Type: schema.TypeString,
74+
Description: "ID of the user or IAM application at the origin of the event",
75+
Computed: true,
76+
},
77+
"organization_id": {
78+
Type: schema.TypeString,
79+
Description: "Organization of the resource attached to the event",
80+
Computed: true,
81+
},
82+
"project_id": {
83+
Type: schema.TypeString,
84+
Description: "Project of the resource attached to the event",
85+
Computed: true,
86+
},
87+
"source_ip": {
88+
Type: schema.TypeString,
89+
Description: "IP address at the origin of the event",
90+
Computed: true,
91+
},
92+
"user_agent": {
93+
Type: schema.TypeString,
94+
Description: "User Agent at the origin of the event",
95+
Computed: true,
96+
},
97+
"product_name": {
98+
Type: schema.TypeString,
99+
Description: "Product name of the resource attached to the event",
100+
Computed: true,
101+
},
102+
"service_name": {
103+
Type: schema.TypeString,
104+
Description: "API name called to trigger the event",
105+
Computed: true,
106+
},
107+
"method_name": {
108+
Type: schema.TypeString,
109+
Description: "API method called to trigger the event",
110+
Computed: true,
111+
},
112+
"resources": {
113+
Type: schema.TypeList,
114+
Description: "List of resources attached to the event",
115+
Computed: true,
116+
Elem: &schema.Resource{
117+
Schema: map[string]*schema.Schema{
118+
"id": {
119+
Type: schema.TypeString,
120+
Computed: true,
121+
Description: "ID of the resource attached to the event",
122+
},
123+
"type": {
124+
Type: schema.TypeString,
125+
Computed: true,
126+
Description: "Type of the Scaleway resource",
127+
},
128+
},
129+
},
130+
},
131+
"request_id": {
132+
Type: schema.TypeString,
133+
Description: "Unique identifier of the request at the origin of the event",
134+
Computed: true,
135+
},
136+
"request_body": {
137+
Type: schema.TypeString,
138+
Description: "Request at the origin of the event",
139+
Computed: true,
140+
},
141+
"status_code": {
142+
Type: schema.TypeString,
143+
Description: "HTTP status code resulting of the API call",
144+
Computed: true,
145+
},
146+
},
147+
},
148+
},
149+
},
150+
}
151+
}
152+
153+
func DataSourceEventsRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
154+
auditTrailAPI, region, orgID, err := newAPIWithRegionAndOrgID(d, m)
155+
if err != nil {
156+
return diag.FromErr(err)
157+
}
158+
159+
req := audittrailSDK.ListEventsRequest{
160+
OrganizationID: orgID,
161+
Region: region,
162+
}
163+
164+
if projectID, ok := d.GetOk("project_id"); ok {
165+
req.ProjectID = types.ExpandStringPtr(projectID)
166+
}
167+
168+
if resourceType, ok := d.GetOk("resource_type"); ok {
169+
req.ResourceType = audittrailSDK.ResourceType(resourceType.(string))
170+
}
171+
172+
if productName, ok := d.GetOk("product_name"); ok {
173+
req.ProductName = types.ExpandStringPtr(productName)
174+
}
175+
176+
if resourceID, ok := d.GetOk("resource_id"); ok {
177+
req.ResourceID = types.ExpandStringPtr(resourceID)
178+
}
179+
180+
res, err := auditTrailAPI.ListEvents(&req, scw.WithContext(ctx))
181+
if err != nil {
182+
return diag.FromErr(err)
183+
}
184+
185+
d.SetId(uuid.New().String())
186+
_ = d.Set("organization_id", orgID)
187+
_ = d.Set("region", region)
188+
189+
flattenedEvents, err := flattenEvents(res.Events)
190+
if err != nil {
191+
return diag.FromErr(err)
192+
}
193+
194+
_ = d.Set("events", flattenedEvents)
195+
196+
return nil
197+
}
198+
199+
func flattenEvents(events []*audittrailSDK.Event) ([]map[string]any, error) {
200+
flattenedEvents := make([]map[string]any, len(events))
201+
for i, event := range events {
202+
var principalID string
203+
204+
if event.Principal != nil {
205+
principalID = event.Principal.ID
206+
}
207+
208+
requestBody, err := scw.EncodeJSONObject(*event.RequestBody, scw.NoEscape)
209+
210+
if err != nil {
211+
return nil, err
212+
}
213+
214+
flattenedEvents[i] = map[string]any{
215+
"id": event.ID,
216+
"recorded_at": event.RecordedAt.String(),
217+
"locality": event.Locality,
218+
"principal_id": principalID,
219+
"organization_id": event.OrganizationID,
220+
"project_id": event.ProjectID,
221+
"source_ip": event.SourceIP.String(),
222+
"user_agent": event.UserAgent,
223+
"product_name": event.ProductName,
224+
"service_name": event.ServiceName,
225+
"method_name": event.MethodName,
226+
"resources": flattenResources(event.Resources),
227+
"request_id": event.RequestID,
228+
"request_body": requestBody,
229+
"status_code": fmt.Sprint(event.StatusCode),
230+
}
231+
}
232+
233+
return flattenedEvents, nil
234+
}
235+
236+
func flattenResources(resources []*audittrailSDK.Resource) []map[string]any {
237+
flattenedResources := make([]map[string]any, len(resources))
238+
for i, r := range resources {
239+
flattenedResources[i] = map[string]any{
240+
"id": r.ID,
241+
"type": string(r.Type),
242+
}
243+
}
244+
245+
return flattenedResources
246+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package audittrail
2+
3+
import (
4+
"fmt"
5+
"slices"
6+
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
8+
audittrailSDK "github.com/scaleway/scaleway-sdk-go/api/audit_trail/v1alpha1"
9+
"github.com/scaleway/scaleway-sdk-go/scw"
10+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/meta"
11+
)
12+
13+
// newAPIWithRegionAndProjectID returns a new Audit Trail API, with region and projectID
14+
func newAPIWithRegionAndOrgID(d *schema.ResourceData, m any) (*audittrailSDK.API, scw.Region, string, error) {
15+
api := audittrailSDK.NewAPI(meta.ExtractScwClient(m))
16+
17+
region, err := meta.ExtractRegion(d, m)
18+
if err != nil {
19+
return nil, "", "", err
20+
}
21+
// In audit trail sdk-go so far only fr-par and nl-ams are supported
22+
if !slices.Contains(api.Regions(), region) {
23+
return nil, "", "", fmt.Errorf("invalid api region, expected one of %s, got: %s", api.Regions(), region)
24+
}
25+
26+
orgID, err := meta.ExtractOrganizationID(d, m)
27+
if err != nil {
28+
return nil, "", "", err
29+
}
30+
31+
return api, region, orgID, nil
32+
}

0 commit comments

Comments
 (0)