Skip to content
This repository has been archived by the owner on Jul 28, 2023. It is now read-only.

Add table turbot_policy_value closes #14 #31

Merged
merged 11 commits into from
Jan 27, 2022
75 changes: 75 additions & 0 deletions docs/tables/turbot_policy_value.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Table: turbot_policy_value

A policy value is the effective policy setting on an instance of a resource type. Every resource that is targeted by a given policy setting will have its own value for that policy, which will be the resultant calculated policy for the "winning" policy in the hierarchy.

Policy settings are inherited through the resource hierarchy, and values for a resource are calculated according to policy settings at or above it in the resource hierarchy. For example, a policy setting at the Turbot level will be inherited by all resources below.

It is recommended that queries to this table should include (usually in the `where` clause) at least one
of these columns: `state`, `policy_type_id`, `resource_type_id`, `resource_type_uri` or `filter`.

## Examples
cbruno10 marked this conversation as resolved.
Show resolved Hide resolved

### List policy values by policy type ID

```sql
select
id,
state,
is_default,
is_calculated,
policy_type_id,
type_mod_uri
from
turbot_policy_value
where
policy_type_id = 221505068398189;
```

### List policy values by resource ID

```sql
select
id,
state,
is_default,
is_calculated,
resource_id,
type_mod_uri
from
turbot_policy_value
where
resource_id = 161587219904115;
```

### List non-default calculated policy values

```sql
select
id,
state,
is_default,
is_calculated,
resource_type_id,
type_mod_uri
from
turbot_policy_value
where
is_calculated and not is_default;
```

### Filter policy values using Turbot filter syntax

```sql
select
id,
state,
is_default,
is_calculated,
policy_type_id,
resource_id,
resource_type_id
from
turbot_policy_value
where
filter = 'state:ok';
```
1 change: 1 addition & 0 deletions turbot/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ func Plugin(ctx context.Context) *plugin.Plugin {
"turbot_notification": tableTurbotNotification(ctx),
"turbot_policy_setting": tableTurbotPolicySetting(ctx),
"turbot_policy_type": tableTurbotPolicyType(ctx),
"turbot_policy_value": tableTurbotPolicyValue(ctx),
"turbot_resource": tableTurbotResource(ctx),
"turbot_resource_type": tableTurbotResourceType(ctx),
"turbot_smart_folder": tableTurbotSmartFolder(ctx),
Expand Down
211 changes: 211 additions & 0 deletions turbot/table_turbot_policy_value.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package turbot

import (
"context"
"fmt"
"strconv"

"github.com/turbot/steampipe-plugin-sdk/grpc/proto"
"github.com/turbot/steampipe-plugin-sdk/plugin"
"github.com/turbot/steampipe-plugin-sdk/plugin/transform"
)

func tableTurbotPolicyValue(ctx context.Context) *plugin.Table {
return &plugin.Table{
Name: "turbot_policy_value",
Description: "Policy value define the value of policy known to Turbot.",
List: &plugin.ListConfig{
Hydrate: listPolicyValue,
KeyColumns: []*plugin.KeyColumn{
{Name: "state", Require: plugin.Optional},
{Name: "policy_type_id", Require: plugin.Optional},
{Name: "resource_id", Require: plugin.Optional},
{Name: "resource_type_id", Require: plugin.Optional},
{Name: "filter", Require: plugin.Optional},
},
},
Columns: []*plugin.Column{
// Top columns
{Name: "id", Type: proto.ColumnType_INT, Transform: transform.FromField("Turbot.ID"), Description: "Unique identifier of the policy value."},
{Name: "policy_type_title", Type: proto.ColumnType_STRING, Transform: transform.FromField("Type.Title"), Description: "Title of the policy type."},
{Name: "poliy_type_trunk_title", Type: proto.ColumnType_STRING, Transform: transform.FromField("Type.Trunk.Title"), Description: "Title with full path of the policy type."},
{Name: "is_default", Type: proto.ColumnType_BOOL, Transform: transform.FromField("Default"), Description: "If true this value is derived from the default value of the type."},
{Name: "is_calculated", Type: proto.ColumnType_BOOL, Description: "If true this value is derived from calculated setting inputs e.g. templateInput and template."},
{Name: "precedence", Type: proto.ColumnType_STRING, Description: "Precedence of the setting: REQUIRED or RECOMMENDED."},
{Name: "resource_id", Type: proto.ColumnType_INT, Transform: transform.FromField("Turbot.ResourceId"), Description: "ID of the resource for the policy value."},
{Name: "resource_trunk_title", Type: proto.ColumnType_STRING, Transform: transform.FromField("Resource.Trunk.Title"), Description: "Full title (including ancestor trunk) of the resource."},
cbruno10 marked this conversation as resolved.
Show resolved Hide resolved
{Name: "resource_type_id", Type: proto.ColumnType_INT, Transform: transform.FromField("Turbot.ResourceTypeID"), Description: "ID of the resource type for this policy setting."},
{Name: "state", Type: proto.ColumnType_STRING, Description: "State of the policy value."},
{Name: "secret_value", Type: proto.ColumnType_STRING, Transform: transform.FromField("SecretValue").Transform(convToString), Description: "Secrect value of the policy value."},
{Name: "value", Type: proto.ColumnType_STRING, Transform: transform.FromField("Value").Transform(convToString), Description: "Value of the policy value."},
{Name: "type_mod_uri", Type: proto.ColumnType_STRING, Transform: transform.FromField("Type.ModURI"), Description: "URI of the mod that contains the policy value."},

// Other columns
{Name: "filter", Type: proto.ColumnType_STRING, Transform: transform.FromQual("filter"), Description: "Filter used for this policy value list."},
{Name: "policy_type_id", Type: proto.ColumnType_INT, Transform: transform.FromField("Turbot.PolicyTypeId"), Description: "ID of the policy type for this policy value."},
{Name: "policy_type_default_template", Type: proto.ColumnType_STRING, Transform: transform.FromField("Type.DefaultTemplate"), Description: "Default template used to calculate template-based policy values. Should be a Jinja based YAML string."},
{Name: "setting_id", Type: proto.ColumnType_INT, Transform: transform.FromField("Turbot.SettingId").Transform(transform.NullIfZeroValue), Description: "Policy setting Id for the policy value."},
{Name: "dependent_controls", Type: proto.ColumnType_JSON, Description: "The controls that depends on this policy value."},
{Name: "dependent_policy_values", Type: proto.ColumnType_JSON, Description: "The policy values that depends on this policy value."},
{Name: "create_timestamp", Type: proto.ColumnType_TIMESTAMP, Transform: transform.FromField("Turbot.CreateTimestamp"), Description: "When the policy value was first set by Turbot. (It may have been created earlier.)"},
{Name: "timestamp", Type: proto.ColumnType_TIMESTAMP, Transform: transform.FromField("Turbot.Timestamp"), Description: "Timestamp when the policy value was last modified (created, updated or deleted)."},
{Name: "update_timestamp", Type: proto.ColumnType_TIMESTAMP, Transform: transform.FromField("Turbot.UpdateTimestamp"), Description: "When the policy value was last updated in Turbot."},
{Name: "version_id", Type: proto.ColumnType_INT, Transform: transform.FromField("Turbot.VersionID"), Description: "Unique identifier for this version of the policy value."},
{Name: "workspace", Type: proto.ColumnType_STRING, Hydrate: plugin.HydrateFunc(getTurbotWorkspace).WithCache(), Transform: transform.FromValue(), Description: "Specifies the workspace URL."},
},
}
}

const (
queryPolicyValueList = `
query MyQuery($filter: [String!], $next_token: String) {
policyValues(filter: $filter, paging: $next_token) {
items {
default
value
state
reason
details
secretValue
isCalculated
precedence
type {
modUri
defaultTemplate
title
trunk {
title
}
}
resource {
trunk {
title
}
}
turbot {
id
policyTypeId
resourceId
resourceTypeId
settingId
createTimestamp
deleteTimestamp
timestamp
updateTimestamp
versionId
}
dependentControls {
items {
turbot {
controlTypeId
controlTypePath
controlCategoryId
controlCategoryPath
id
resourceId
resourceTypeId
}
type {
modUri
title
trunk {
title
}
}
}
}
dependentPolicyValues {
items {
type {
modUri
uri
title
trunk {
title
}
turbot {
id
title
}
}
}
}
}
paging {
next
}
}
}
`
)

func listPolicyValue(ctx context.Context, d *plugin.QueryData, _ *plugin.HydrateData) (interface{}, error) {
conn, err := connect(ctx, d)
if err != nil {
plugin.Logger(ctx).Error("turbot_policy_type.listPolicyType", "connection_error", err)
return nil, err
}

filters := []string{}
quals := d.KeyColumnQuals

filter := ""
if quals["filter"] != nil {
filter = quals["filter"].GetStringValue()
filters = append(filters, filter)
}

// Additional filters
if quals["state"] != nil {
filters = append(filters, fmt.Sprintf("state:%s ", getQualListValues(ctx, quals, "state", "string")))
}

if quals["policy_type_id"] != nil {
filters = append(filters, fmt.Sprintf("policyTypeId:%s policyTypeLevel:self", getQualListValues(ctx, quals, "policy_type_id", "int64")))
}

if quals["resource_id"] != nil {
filters = append(filters, fmt.Sprintf("resourceId:%s resourceTypeLevel:self", getQualListValues(ctx, quals, "resource_id", "int64")))
}

if quals["resource_type_id"] != nil {
filters = append(filters, fmt.Sprintf("resourceTypeId:%s resourceTypeLevel:self", getQualListValues(ctx, quals, "resource_type_id", "int64")))
}

// Setting a high limit and page all results
var pageLimit int64 = 5000

// Adjust page limit, if less than default value
limit := d.QueryContext.Limit
if d.QueryContext.Limit != nil {
if *limit < pageLimit {
pageLimit = *limit
}
}

// Setting page limit
filters = append(filters, fmt.Sprintf("limit:%s", strconv.Itoa(int(pageLimit))))

nextToken := ""
for {
result := &PolicyValuesResponse{}
err = conn.DoRequest(queryPolicyValueList, map[string]interface{}{"filter": filters, "next_token": nextToken}, result)
if err != nil {
plugin.Logger(ctx).Error("turbot_policy_value.listPolicyValue", "query_error", err)
return nil, err
}
for _, r := range result.PolicyValues.Items {
d.StreamListItem(ctx, r)

// Context can be cancelled due to manual cancellation or the limit has been hit
if d.QueryStatus.RowsRemaining(ctx) == 0 {
return nil, nil
}
}
if result.PolicyValues.Paging.Next == "" {
break
}
nextToken = result.PolicyValues.Paging.Next
}
return nil, nil
}
47 changes: 47 additions & 0 deletions turbot/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,53 @@ type PolicyType struct {
URI string
}

type PolicyValuesResponse struct {
PolicyValues struct {
Items []PolicyValue
Paging struct {
Next string
}
}
}

type PolicyValue struct {
Default bool
Value interface{}
State string
Reason string
Details interface{}
SecretValue interface{}
IsCalculated bool
Precedence string
Type PolicyValueType
Resource PolicyValueResourceDetails
DependentControls interface{}
DependentPolicyValues interface{}
Turbot PolicyValueTurbotProperty
}

type PolicyValueResourceDetails struct {
Trunk struct {
Title string
}
}

type PolicyValueType struct {
ModURI string
DefaultTemplate string
Title string
Trunk struct {
Title string
}
}

type PolicyValueTurbotProperty struct {
TurbotResourceMetadata
PolicyTypeId string
ResourceId string
SettingId string
}

type TurbotResourceMetadata struct {
ActorIdentityID string
ActorPersonaID string
Expand Down
5 changes: 5 additions & 0 deletions turbot/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ func intToBool(ctx context.Context, d *transform.TransformData) (interface{}, er
return v > 0, nil
}

func convToString(ctx context.Context, d *transform.TransformData) (interface{}, error) {
var v interface{} = fmt.Sprint(d.Value)
return v, nil
}

func attachedResourceIDs(_ context.Context, d *transform.TransformData) (interface{}, error) {
objs := d.Value.([]TurbotIDObject)
ids := []int64{}
Expand Down