Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
42 changes: 42 additions & 0 deletions docs/resources/tag.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "prismacloudcompute_tag Resource - terraform-provider-prismacloudcompute"
subcategory: ""
description: |-

---

# prismacloudcompute_tag (Resource)





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

### Required

- `name` (String) A unique tag name.

### Optional

- `assignment` (Block List) Specify how vulnerabilities are tagged, based on CVE ID, package, and resources. (see [below for nested schema](#nestedblock--assignment))
- `color` (String) A hex color code for the tag to display in the Console.
- `description` (String) A free-form text description of the tag.

### Read-Only

- `id` (String) The ID of the tag.

<a id="nestedblock--assignment"></a>
### Nested Schema for `assignment`

Optional:

- `check_base_layer` (Boolean) Whether or not to check the base layer.
- `comment` (String) Free-form text field.
- `id` (String) Common Vulnerability and Exposures (CVE) ID.
- `package_name` (String) Source or binary package name where the vulnerability is found.
- `resource_type` (String) Specifies the resource type for tagging where the vulnerability is found.
- `resources` (List of String) Resource names separated by a comma or use the wildcard * to apply the tag to all the resources where the vulnerability is found.
87 changes: 87 additions & 0 deletions internal/api/tag/tag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package tag

import (
"fmt"
"net/http"

"github.com/PaloAltoNetworks/terraform-provider-prismacloudcompute/internal/api"
)

const TagsEndpoint = "api/v1/tags"

type Vuln struct {
CheckBaseLayer bool `json:"checkBaseLayer,omitempty"`
Comment string `json:"comment,omitempty"`
Id string `json:"id,omitempty"`
PackageName string `json:"packageName,omitempty"`
ResourceType string `json:"resourceType,omitempty"`
Resources []string `json:"resources,omitempty"`
}

type Tag struct {
Color string `json:"color,omitempty"`
Description string `json:"description,omitempty"`
Name string `json:"name,omitempty"`
Vulns []Vuln `json:"vulns,omitempty"`
}

// Get all tags.
func ListTags(c api.Client) ([]Tag, error) {
var ans []Tag
if err := c.Request(http.MethodGet, TagsEndpoint, nil, nil, &ans); err != nil {
return nil, fmt.Errorf("error listing tags: %s", err)
}
return ans, nil
}

// Get a specific tag.
func GetTag(c api.Client, name string) (*Tag, error) {
tags, err := ListTags(c)
if err != nil {
return nil, err
}
for _, val := range tags {
if val.Name == name {
if val.Vulns == nil {
val.Vulns = []Vuln{}
}
return &val, nil
}
}
return nil, fmt.Errorf("tag '%s' not found", name)
}

// Create a new tag.
func CreateTag(c api.Client, tag Tag) error {
// there's a bug in Prisma that causes tags to not work correctly if created
// with vulns, so we create it without vulns and then add them individually
tagWithoutVulns := tag
tagWithoutVulns.Vulns = []Vuln{}

err := c.Request(http.MethodPost, TagsEndpoint, nil, tagWithoutVulns, nil)
if err != nil {
return err
}

return createVuln(c, tag, tag.Vulns)
}

func createVuln(c api.Client, tag Tag, vuln []Vuln) error {
for _, val := range vuln {
err := c.Request(http.MethodPost, fmt.Sprintf("%s/%s/vuln", TagsEndpoint, tag.Name), nil, val, nil)
if err != nil {
return err
}
}
return nil
}

// Update an existing tag.
func UpdateTag(c api.Client, tag Tag) error {
return c.Request(http.MethodPut, fmt.Sprintf("%s/%s", TagsEndpoint, tag.Name), nil, tag, nil)
}

// Delete an existing tag.
func DeleteTag(c api.Client, name string) error {
return c.Request(http.MethodDelete, fmt.Sprintf("%s/%s", TagsEndpoint, name), nil, nil, nil)
}
49 changes: 49 additions & 0 deletions internal/convert/tag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package convert

import (
"github.com/PaloAltoNetworks/terraform-provider-prismacloudcompute/internal/api/tag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

// Converts a tag schema to a tag object for SDK compatibility.
func SchemaToTag(d *schema.ResourceData) tag.Tag {
ans := tag.Tag{
Name: d.Get("name").(string),
Description: d.Get("description").(string),
Color: d.Get("color").(string),
Vulns: []tag.Vuln{},
}
if assignments, ok := d.GetOk("assignment"); ok {
presentAssignments := assignments.([]interface{})
for _, val := range presentAssignments {
presentAssignment := val.(map[string]interface{})
parsedAssignment := tag.Vuln{
CheckBaseLayer: presentAssignment["check_base_layer"].(bool),
Comment: presentAssignment["comment"].(string),
Id: presentAssignment["id"].(string),
PackageName: presentAssignment["package_name"].(string),
ResourceType: presentAssignment["resource_type"].(string),
Resources: SchemaToStringSlice(presentAssignment["resources"].([]interface{})),
}
if len(parsedAssignment.Resources) == 0 {
parsedAssignment.Resources = []string{"*"}
}
ans.Vulns = append(ans.Vulns, parsedAssignment)
}
}
return ans
}

func TagVulnsToSchema(in []tag.Vuln) []interface{} {
ans := make([]interface{}, 0, len(in))
for _, val := range in {
m := make(map[string]interface{})
m["resources"] = val.Resources
m["comment"] = val.Comment
m["id"] = val.Id
m["resource_type"] = val.ResourceType
m["package_name"] = val.PackageName
ans = append(ans, m)
}
return ans
}
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func Provider() *schema.Provider {
"prismacloudcompute_credential": resourceCredentials(),
"prismacloudcompute_custom_compliance": resourceCustomCompliance(),
"prismacloudcompute_cloud_account": resourceCloudAccount(),
"prismacloudcompute_tag": resourceTag(),
},

DataSourcesMap: map[string]*schema.Resource{
Expand Down
169 changes: 169 additions & 0 deletions internal/provider/resource_tag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
package provider

import (
"context"
"fmt"

"github.com/PaloAltoNetworks/terraform-provider-prismacloudcompute/internal/api"
"github.com/PaloAltoNetworks/terraform-provider-prismacloudcompute/internal/api/tag"
"github.com/PaloAltoNetworks/terraform-provider-prismacloudcompute/internal/convert"
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceTag() *schema.Resource {
return &schema.Resource{
CreateContext: createTag,
ReadContext: readTag,
UpdateContext: updateTag,
DeleteContext: deleteTag,

Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
"id": {
Description: "The ID of the tag.",
Type: schema.TypeString,
Computed: true,
},
"color": {
Type: schema.TypeString,
Optional: true,
Description: "A hex color code for the tag to display in the Console.",
Default: "#A020F0",
},
"description": {
Type: schema.TypeString,
Optional: true,
Description: "A free-form text description of the tag.",
},
"name": {
Type: schema.TypeString,
Required: true,
Description: "A unique tag name.",
},
"assignment": {
Type: schema.TypeList,
Optional: true,
Description: "Specify how vulnerabilities are tagged, based on CVE ID, package, and resources.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"check_base_layer": {
Type: schema.TypeBool,
Optional: true,
Description: "Whether or not to check the base layer.",
},
"comment": {
Type: schema.TypeString,
Optional: true,
Description: "Free-form text field.",
},
"id": {
Type: schema.TypeString,
Required: true,
Description: "Common Vulnerability and Exposures (CVE) ID.",
},
"package_name": {
Type: schema.TypeString,
Optional: true,
Description: "Source or binary package name where the vulnerability is found.",
Default: "*",
},
"resource_type": {
Type: schema.TypeString,
Optional: true,
Default: "*",
Description: "Specifies the resource type for tagging where the vulnerability is found.",
ValidateDiagFunc: func(v interface{}, p cty.Path) diag.Diagnostics {
validValues := []string{"image", "host", "function", "codeRepo"}
value := v.(string)

for _, item := range validValues {
if item == value {
return diag.Diagnostics{}
}
}
return diag.Diagnostics{
diag.Diagnostic{
Severity: diag.Error,
Summary: "Invalid resource_type",
Detail: fmt.Sprintf("Valid values are %v", validValues),
},
}
},
},
"resources": {
Type: schema.TypeList,
Optional: true,
Description: "Resource names separated by a comma or use the wildcard * to apply the tag to all the resources where the vulnerability is found.",
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
},
},
},
},
}
}

func createTag(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*api.Client)
parsedTag := convert.SchemaToTag(d)
if err := tag.CreateTag(*client, parsedTag); err != nil {
return diag.Errorf("error creating tag'%+v': %s", parsedTag, err)
}

d.SetId(parsedTag.Name)

return readTag(ctx, d, meta)
}

func readTag(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*api.Client)

var diags diag.Diagnostics

retrievedTag, err := tag.GetTag(*client, d.Id())
if err != nil {
return diag.Errorf("error reading tag: %s", err)
}

if err := d.Set("assignment", convert.TagVulnsToSchema(retrievedTag.Vulns)); err != nil {
return diag.Errorf("error reading tag: %s, %v", err, retrievedTag.Vulns)
}
d.Set("color", retrievedTag.Color)
d.Set("description", retrievedTag.Description)
d.Set("name", retrievedTag.Name)

return diags
}

func updateTag(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*api.Client)

parsedTag := convert.SchemaToTag(d)

if err := tag.UpdateTag(*client, parsedTag); err != nil {
return diag.Errorf("error updating tag: %s", err)
}

return readTag(ctx, d, meta)
}

func deleteTag(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*api.Client)

var diags diag.Diagnostics

if err := tag.DeleteTag(*client, d.Id()); err != nil {
return diag.Errorf("error updating tag '%s': %s", d.Id(), err)
}

d.SetId("")

return diags
}
Loading