Skip to content

Commit

Permalink
Add Osquery query
Browse files Browse the repository at this point in the history
  • Loading branch information
np5 committed Mar 1, 2023
1 parent 5286d7f commit eae07ae
Show file tree
Hide file tree
Showing 8 changed files with 722 additions and 0 deletions.
33 changes: 33 additions & 0 deletions docs/data-sources/osquery_query.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "zentral_osquery_query Data Source - terraform-provider-zentral"
subcategory: ""
description: |-
The data source zentral_osquery_query allows details of a Osquery query to be retrieved by its ID or name.
---

# zentral_osquery_query (Data Source)

The data source `zentral_osquery_query` allows details of a Osquery query to be retrieved by its `ID` or name.



<!-- schema generated by tfplugindocs -->
## Schema

### Optional

- `id` (Number) `ID` of the query.
- `name` (String) Name of the query.

### Read-Only

- `compliance_check_enabled` (Boolean) If `true`, the query will be used as compliance check. Defaults to `false`.
- `description` (String) Description of the query.
- `minimum_osquery_version` (String) Only run on Osquery versions greater than or equal-to this version string
- `platforms` (Set of String) Restrict the query to some platforms, default is 'all' platforms
- `sql` (String) The SQL query to run.
- `value` (String) Description of the results returned by the query.
- `version` (Number) Version of the query.


36 changes: 36 additions & 0 deletions docs/resources/osquery_query.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "zentral_osquery_query Resource - terraform-provider-zentral"
subcategory: ""
description: |-
The resource zentral_osquery_query manages Osquery queries.
---

# zentral_osquery_query (Resource)

The resource `zentral_osquery_query` manages Osquery queries.



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `name` (String) Name of the query.
- `sql` (String) The SQL query to run.

### Optional

- `compliance_check_enabled` (Boolean) If `true`, the query will be used as compliance check. Defaults to `false`.
- `description` (String) Description of the query.
- `minimum_osquery_version` (String) Only run on Osquery versions greater than or equal-to this version string
- `platforms` (Set of String) Restrict the query to some platforms, default is 'all' platforms
- `value` (String) Description of the results returned by the query.

### Read-Only

- `id` (Number) `ID` of the query.
- `version` (Number) Version of the query.


67 changes: 67 additions & 0 deletions internal/provider/osquery_query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package provider

import (
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/zentralopensource/goztl"
)

type osqueryQuery struct {
ID types.Int64 `tfsdk:"id"`
Name types.String `tfsdk:"name"`
SQL types.String `tfsdk:"sql"`
Platforms types.Set `tfsdk:"platforms"`
MinOsqueryVersion types.String `tfsdk:"minimum_osquery_version"`
Description types.String `tfsdk:"description"`
Value types.String `tfsdk:"value"`
Version types.Int64 `tfsdk:"version"`
ComplianceCheckEnabled types.Bool `tfsdk:"compliance_check_enabled"`
}

func osqueryQueryForState(oq *goztl.OsqueryQuery) osqueryQuery {
platforms := make([]attr.Value, 0)
for _, platform := range oq.Platforms {
platforms = append(platforms, types.StringValue(platform))
}

var minOsqueryVersion types.String
if oq.MinOsqueryVersion != nil {
minOsqueryVersion = types.StringValue(*oq.MinOsqueryVersion)
} else {
minOsqueryVersion = types.StringNull()
}

return osqueryQuery{
ID: types.Int64Value(int64(oq.ID)),
Name: types.StringValue(oq.Name),
SQL: types.StringValue(oq.SQL),
Platforms: types.SetValueMust(types.StringType, platforms),
MinOsqueryVersion: minOsqueryVersion,
Description: types.StringValue(oq.Description),
Value: types.StringValue(oq.Value),
Version: types.Int64Value(int64(oq.Version)),
ComplianceCheckEnabled: types.BoolValue(oq.ComplianceCheckEnabled),
}
}

func osqueryQueryRequestWithState(data osqueryQuery) *goztl.OsqueryQueryRequest {
platforms := make([]string, 0)
for _, platform := range data.Platforms.Elements() { // nil if null or unknown → no iterations
platforms = append(platforms, platform.(types.String).ValueString())
}

var minOsqueryVersion *string
if !data.MinOsqueryVersion.IsNull() {
minOsqueryVersion = goztl.String(data.MinOsqueryVersion.ValueString())
}

return &goztl.OsqueryQueryRequest{
Name: data.Name.ValueString(),
SQL: data.SQL.ValueString(),
Platforms: platforms,
MinOsqueryVersion: minOsqueryVersion,
Description: data.Description.ValueString(),
Value: data.Value.ValueString(),
ComplianceCheckEnabled: data.ComplianceCheckEnabled.ValueBool(),
}
}
159 changes: 159 additions & 0 deletions internal/provider/osquery_query_data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package provider

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/zentralopensource/goztl"
)

// Ensure provider defined types fully satisfy framework interfaces
var _ datasource.DataSource = &OsqueryQueryDataSource{}

func NewOsqueryQueryDataSource() datasource.DataSource {
return &OsqueryQueryDataSource{}
}

// OsqueryQueryDataSource defines the data source implementation.
type OsqueryQueryDataSource struct {
client *goztl.Client
}

func (d *OsqueryQueryDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_osquery_query"
}

func (d *OsqueryQueryDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Allows details of a Osquery query to be retrieved by its ID or name.",
MarkdownDescription: "The data source `zentral_osquery_query` allows details of a Osquery query to be retrieved by its `ID` or name.",

Attributes: map[string]schema.Attribute{
"id": schema.Int64Attribute{
Description: "ID of the query.",
MarkdownDescription: "`ID` of the query.",
Optional: true,
},
"name": schema.StringAttribute{
Description: "Name of the query.",
MarkdownDescription: "Name of the query.",
Optional: true,
},
"sql": schema.StringAttribute{
Description: "The SQL query to run.",
MarkdownDescription: "The SQL query to run.",
Computed: true,
},
"platforms": schema.SetAttribute{
Description: "Restrict the query to some platforms, default is 'all' platforms",
MarkdownDescription: "Restrict the query to some platforms, default is 'all' platforms",
ElementType: types.StringType,
Computed: true,
},
"minimum_osquery_version": schema.StringAttribute{
Description: "Only run on Osquery versions greater than or equal-to this version string",
MarkdownDescription: "Only run on Osquery versions greater than or equal-to this version string",
Computed: true,
},
"description": schema.StringAttribute{
Description: "Description of the query.",
MarkdownDescription: "Description of the query.",
Computed: true,
},
"value": schema.StringAttribute{
Description: "Description of the results returned by the query.",
MarkdownDescription: "Description of the results returned by the query.",
Computed: true,
},
"version": schema.Int64Attribute{
Description: "Version of the query.",
MarkdownDescription: "Version of the query.",
Computed: true,
},
"compliance_check_enabled": schema.BoolAttribute{
Description: "If true, the query will be used as compliance check. Defaults to false.",
MarkdownDescription: "If `true`, the query will be used as compliance check. Defaults to `false`.",
Computed: true,
},
},
}
}

func (d *OsqueryQueryDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}

client, ok := req.ProviderData.(*goztl.Client)

if !ok {
resp.Diagnostics.AddError(
"Unexpected Data Source Configure Type",
fmt.Sprintf("Expected *goztl.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)

return
}

d.client = client
}

func (d *OsqueryQueryDataSource) ValidateConfig(ctx context.Context, req datasource.ValidateConfigRequest, resp *datasource.ValidateConfigResponse) {
var data osqueryQuery
diags := req.Config.Get(ctx, &data)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}

if data.ID.IsNull() && data.Name.IsNull() {
resp.Diagnostics.AddError(
"Invalid `zentral_osquery_query` data source",
"`id` or `name` missing",
)
} else if !data.ID.IsNull() && !data.Name.IsNull() {
resp.Diagnostics.AddError(
"Invalid `zentral_osquery_query` data source",
"`id` and `name` cannot be both set",
)
}
}

func (d *OsqueryQueryDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data osqueryQuery

// Read Terraform configuration data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

var ztlOQ *goztl.OsqueryQuery
var err error
if data.ID.ValueInt64() > 0 {
ztlOQ, _, err = d.client.OsqueryQueries.GetByID(ctx, int(data.ID.ValueInt64()))
if err != nil {
resp.Diagnostics.AddError(
"Client Error",
fmt.Sprintf("Unable to get Osquery query '%d' by ID, got error: %s", data.ID.ValueInt64(), err),
)
}
} else {
ztlOQ, _, err = d.client.OsqueryQueries.GetByName(ctx, data.Name.ValueString())
if err != nil {
resp.Diagnostics.AddError(
"Client Error",
fmt.Sprintf("Unable to get Osquery query '%s' by name, got error: %s", data.Name.ValueString(), err),
)
}
}

if ztlOQ != nil {
resp.Diagnostics.Append(resp.State.Set(ctx, osqueryQueryForState(ztlOQ))...)
}
}
97 changes: 97 additions & 0 deletions internal/provider/osquery_query_data_source_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package provider

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
)

func TestAccOsqueryQueryDataSource(t *testing.T) {
q1Name := acctest.RandString(12)
q2Name := acctest.RandString(12)
q1ResourceName := "zentral_osquery_query.check1"
q2ResourceName := "zentral_osquery_query.check2"
ds1ResourceName := "data.zentral_osquery_query.check1_by_name"
ds2ResourceName := "data.zentral_osquery_query.check2_by_id"

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
{
Config: testAccOsqueryQueryDataSourceConfig(q1Name, q2Name),
Check: resource.ComposeAggregateTestCheckFunc(
// Read by name
resource.TestCheckResourceAttrPair(
ds1ResourceName, "id", q1ResourceName, "id"),
resource.TestCheckResourceAttr(
ds1ResourceName, "name", q1Name),
resource.TestCheckResourceAttr(
ds1ResourceName, "sql", "SELECT * FROM users;"),
resource.TestCheckResourceAttr(
ds1ResourceName, "platforms.#", "0"),
resource.TestCheckNoResourceAttr(
ds1ResourceName, "minimum_osquery_version"),
resource.TestCheckResourceAttr(
ds1ResourceName, "description", ""),
resource.TestCheckResourceAttr(
ds1ResourceName, "value", ""),
resource.TestCheckResourceAttr(
ds1ResourceName, "version", "1"),
resource.TestCheckResourceAttr(
ds1ResourceName, "compliance_check_enabled", "false"),
// Read by ID
resource.TestCheckResourceAttrPair(
ds2ResourceName, "id", q2ResourceName, "id"),
resource.TestCheckResourceAttr(
ds2ResourceName, "name", q2Name),
resource.TestCheckResourceAttr(
ds2ResourceName, "sql", "SELECT 'FAILED' AS ztl_status, 'No reason!' AS why;"),
resource.TestCheckResourceAttr(
ds2ResourceName, "platforms.#", "1"),
resource.TestCheckTypeSetElemAttr(
ds2ResourceName, "platforms.*", "darwin"),
resource.TestCheckResourceAttr(
ds2ResourceName, "minimum_osquery_version", "0.1.0"),
resource.TestCheckResourceAttr(
ds2ResourceName, "description", "A compliance check that always fails"),
resource.TestCheckResourceAttr(
ds2ResourceName, "value", "Not much"),
resource.TestCheckResourceAttr(
ds2ResourceName, "version", "1"),
resource.TestCheckResourceAttr(
ds2ResourceName, "compliance_check_enabled", "true"),
),
},
},
})
}

func testAccOsqueryQueryDataSourceConfig(q1Name string, q2Name string) string {
return fmt.Sprintf(`
resource "zentral_osquery_query" "check1" {
name = %[1]q
sql = "SELECT * FROM users;"
}
resource "zentral_osquery_query" "check2" {
name = %[2]q
sql = "SELECT 'FAILED' AS ztl_status, 'No reason!' AS why;"
platforms = ["darwin"]
minimum_osquery_version = "0.1.0"
description = "A compliance check that always fails"
value = "Not much"
compliance_check_enabled = true
}
data "zentral_osquery_query" "check1_by_name" {
name = zentral_osquery_query.check1.name
}
data "zentral_osquery_query" "check2_by_id" {
id = zentral_osquery_query.check2.id
}
`, q1Name, q2Name)
}
Loading

0 comments on commit eae07ae

Please sign in to comment.