diff --git a/.golangci.yml b/.golangci.yml index 223cf95..4877406 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -24,4 +24,4 @@ linters: - unconvert - unparam - unused - - vet \ No newline at end of file + - govet \ No newline at end of file diff --git a/.goreleaser.yml b/.goreleaser.yml index 9bb0aa7..37c175a 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -1,5 +1,8 @@ -# Visit https://goreleaser.com for documentation on how to customize this -# behavior. +# Copyright (c) Mondoo, Inc. +# SPDX-License-Identifier: BUSL-1.1 + +--- +version: 2 before: hooks: # this is just an example and not a requirement for provider building/publishing @@ -57,4 +60,4 @@ release: # If you want to manually examine the release before its live, uncomment this line: # draft: true changelog: - skip: true + disable: true diff --git a/docs/data-sources/assets.md b/docs/data-sources/assets.md new file mode 100644 index 0000000..8b2ab75 --- /dev/null +++ b/docs/data-sources/assets.md @@ -0,0 +1,75 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "mondoo_assets Data Source - terraform-provider-mondoo" +subcategory: "" +description: |- + The asset data source allows you to fetch assets from a space. +--- + +# mondoo_assets (Data Source) + +The asset data source allows you to fetch assets from a space. + +## Example Usage + +```terraform +provider "mondoo" {} + +data "mondoo_assets" "assets_data" { + space_id = "my-space-1234567" +} + +output "asset_mrns" { + description = "MRNs of the assets" + value = [for asset in data.mondoo_assets.assets_data.assets : asset.mrn] +} + +output "asset_names" { + description = "Names of the assets" + value = [for asset in data.mondoo_assets.assets_data.assets : asset.name] +} +``` + + +## Schema + +### Optional + +- `space_id` (String) The unique identifier of the space. +- `space_mrn` (String) The unique Mondoo Resource Name (MRN) of the space. + +### Read-Only + +- `assets` (Attributes List) The list of assets in the space. (see [below for nested schema](#nestedatt--assets)) + + +### Nested Schema for `assets` + +Read-Only: + +- `annotations` (Attributes List) The annotations/tags of the asset. (see [below for nested schema](#nestedatt--assets--annotations)) +- `asset_type` (String) The type of the asset. +- `id` (String) The unique identifier of the asset. +- `mrn` (String) The unique Mondoo Resource Name (MRN) of the asset. +- `name` (String) The name of the asset. +- `reference_ids` (List of String) The reference IDs of the asset. +- `score` (Attributes) The overall score of the asset. (see [below for nested schema](#nestedatt--assets--score)) +- `state` (String) The current state of the asset. +- `updated_at` (String) The timestamp when the asset was last updated. + + +### Nested Schema for `assets.annotations` + +Read-Only: + +- `key` (String) The key of the annotation. +- `value` (String) The value of the annotation. + + + +### Nested Schema for `assets.score` + +Read-Only: + +- `grade` (String) The grade of the asset. +- `value` (Number) The score value of the asset. diff --git a/docs/data-sources/organization.md b/docs/data-sources/organization.md index 801da32..526a860 100644 --- a/docs/data-sources/organization.md +++ b/docs/data-sources/organization.md @@ -16,13 +16,23 @@ Organization data source provider "mondoo" {} data "mondoo_organization" "org" { - id = "reverent-ride-275852" + id = "your-org-1234567" } output "org_mrn" { description = "MRN of the organization" value = data.mondoo_organization.org.mrn } + +output "org_name" { + description = "Name of the organization" + value = data.mondoo_organization.org.name +} + +output "org_id" { + description = "ID of the organization" + value = data.mondoo_organization.org.id +} ``` diff --git a/docs/data-sources/policy.md b/docs/data-sources/policy.md new file mode 100644 index 0000000..ee366cd --- /dev/null +++ b/docs/data-sources/policy.md @@ -0,0 +1,54 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "mondoo_policy Data Source - terraform-provider-mondoo" +subcategory: "" +description: |- + Data source for policies and querypacks +--- + +# mondoo_policy (Data Source) + +Data source for policies and querypacks + +## Example Usage + +```terraform +data "mondoo_policy" "policy" { + space_id = "your-space-1234567" + catalog_type = "ALL" # available options: "ALL", "POLICY", "QUERYPACK" + assigned_only = true +} + +output "policies_mrn" { + value = [for policy in data.mondoo_policy.policy.policies : policy.policy_mrn] + description = "The MRN of the policies in the space according to the filter criteria." +} +``` + + +## Schema + +### Optional + +- `assigned_only` (Boolean) Only return enabled policies if set to `true` +- `catalog_type` (String) Catalog type of either `ALL`, `POLICY` or `QUERYPACK`. Defaults to `ALL` +- `space_id` (String) Space ID +- `space_mrn` (String) Space MRN + +### Read-Only + +- `policies` (Attributes List) List of policies (see [below for nested schema](#nestedatt--policies)) + + +### Nested Schema for `policies` + +Read-Only: + +- `action` (String) Policies can be set to `Null`, `IGNORE` or `ACTIVE` +- `assigned` (Boolean) Determines if a policy is enabled or disabled +- `created_at` (String) Timestamp of policy creation +- `is_public` (Boolean) Determines if a policy is public or private +- `policy_mrn` (String) Unique policy Mondoo Resource Name +- `policy_name` (String) Policy name +- `updated_at` (String) Timestamp of policy update +- `version` (String) Version diff --git a/docs/data-sources/space.md b/docs/data-sources/space.md index 64fbced..21fff70 100644 --- a/docs/data-sources/space.md +++ b/docs/data-sources/space.md @@ -21,7 +21,7 @@ variable "mondoo_org" { provider "mondoo" {} resource "mondoo_space" "test" { - org_id = var.mondoo_org.value + org_id = var.mondoo_org name = "test-space" } @@ -42,6 +42,11 @@ output "space_mrn" { description = "The MRN of the space" value = data.mondoo_space.space.mrn } + +output "space_id" { + description = "The ID of the space" + value = data.mondoo_space.space.id +} ``` diff --git a/docs/resources/custom_framework.md b/docs/resources/custom_framework.md new file mode 100644 index 0000000..0830e58 --- /dev/null +++ b/docs/resources/custom_framework.md @@ -0,0 +1,54 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "mondoo_custom_framework Resource - terraform-provider-mondoo" +subcategory: "" +description: |- + Set custom Compliance Frameworks for a Mondoo Space. +--- + +# mondoo_custom_framework (Resource) + +Set custom Compliance Frameworks for a Mondoo Space. + +## Example Usage + +```terraform +provider "mondoo" { + region = "us" +} + +variable "mondoo_org" { + description = "The Mondoo Organization ID" + type = string + default = "my-org-1234567" +} + +variable "my_custom_framework" { + description = "Path to the custom policy file. The file must be in MQL format." + type = string + default = "framework.mql.yaml" +} + +# Create a new space +resource "mondoo_space" "my_space" { + name = "Custom Framework Space" + org_id = var.mondoo_org +} + +resource "mondoo_custom_framework" "compliance_framework_example" { + space_id = mondoo_space.my_space.id + data_url = var.my_custom_framework +} +``` + + +## Schema + +### Required + +- `data_url` (String) URL to the custom Compliance Framework data. +- `space_id` (String) Mondoo Space Identifier. + +### Read-Only + +- `mrn` (String) Mondoo Resource Name. diff --git a/docs/resources/framework_assignment.md b/docs/resources/framework_assignment.md new file mode 100644 index 0000000..8174833 --- /dev/null +++ b/docs/resources/framework_assignment.md @@ -0,0 +1,47 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "mondoo_framework_assignment Resource - terraform-provider-mondoo" +subcategory: "" +description: |- + Set Compliance Frameworks for a Mondoo Space. +--- + +# mondoo_framework_assignment (Resource) + +Set Compliance Frameworks for a Mondoo Space. + +## Example Usage + +```terraform +provider "mondoo" { + region = "us" +} + +variable "mondoo_org" { + description = "The Mondoo Organization ID" + type = string + default = "my-org-1234567" +} + +# Create a new space +resource "mondoo_space" "my_space" { + name = "Framework Space" + org_id = var.mondoo_org +} + +resource "mondoo_framework_assignment" "compliance_framework_example" { + space_id = mondoo_space.my_space.id + framework_mrn = ["//policy.api.mondoo.app/frameworks/cis-controls-8", + "//policy.api.mondoo.app/frameworks/iso-27001-2022"] + enabled = true +} +``` + + +## Schema + +### Required + +- `enabled` (Boolean) Enable or disable the Compliance Framework. +- `framework_mrn` (List of String) Compliance Framework MRN. +- `space_id` (String) Mondoo Space Identifier. diff --git a/examples/data-sources/mondoo_assets/data-source.tf b/examples/data-sources/mondoo_assets/data-source.tf new file mode 100644 index 0000000..36e32c4 --- /dev/null +++ b/examples/data-sources/mondoo_assets/data-source.tf @@ -0,0 +1,15 @@ +provider "mondoo" {} + +data "mondoo_assets" "assets_data" { + space_id = "my-space-1234567" +} + +output "asset_mrns" { + description = "MRNs of the assets" + value = [for asset in data.mondoo_assets.assets_data.assets : asset.mrn] +} + +output "asset_names" { + description = "Names of the assets" + value = [for asset in data.mondoo_assets.assets_data.assets : asset.name] +} diff --git a/examples/data-sources/mondoo_assets/main.tf b/examples/data-sources/mondoo_assets/main.tf new file mode 100644 index 0000000..1483327 --- /dev/null +++ b/examples/data-sources/mondoo_assets/main.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + mondoo = { + source = "mondoohq/mondoo" + version = ">= 0.4.0" + } + } +} diff --git a/examples/data-sources/mondoo_organization/data-source.tf b/examples/data-sources/mondoo_organization/data-source.tf index 85b7329..d9f9347 100644 --- a/examples/data-sources/mondoo_organization/data-source.tf +++ b/examples/data-sources/mondoo_organization/data-source.tf @@ -1,10 +1,20 @@ provider "mondoo" {} data "mondoo_organization" "org" { - id = "reverent-ride-275852" + id = "your-org-1234567" } output "org_mrn" { description = "MRN of the organization" value = data.mondoo_organization.org.mrn -} \ No newline at end of file +} + +output "org_name" { + description = "Name of the organization" + value = data.mondoo_organization.org.name +} + +output "org_id" { + description = "ID of the organization" + value = data.mondoo_organization.org.id +} diff --git a/examples/data-sources/mondoo_policy/data-source.tf b/examples/data-sources/mondoo_policy/data-source.tf new file mode 100644 index 0000000..17d677a --- /dev/null +++ b/examples/data-sources/mondoo_policy/data-source.tf @@ -0,0 +1,10 @@ +data "mondoo_policy" "policy" { + space_id = "your-space-1234567" + catalog_type = "ALL" # available options: "ALL", "POLICY", "QUERYPACK" + assigned_only = true +} + +output "policies_mrn" { + value = [for policy in data.mondoo_policy.policy.policies : policy.policy_mrn] + description = "The MRN of the policies in the space according to the filter criteria." +} diff --git a/examples/data-sources/mondoo_policy/main.tf b/examples/data-sources/mondoo_policy/main.tf new file mode 100644 index 0000000..1483327 --- /dev/null +++ b/examples/data-sources/mondoo_policy/main.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + mondoo = { + source = "mondoohq/mondoo" + version = ">= 0.4.0" + } + } +} diff --git a/examples/data-sources/mondoo_space/data-source.tf b/examples/data-sources/mondoo_space/data-source.tf index 82c6244..cfb6976 100644 --- a/examples/data-sources/mondoo_space/data-source.tf +++ b/examples/data-sources/mondoo_space/data-source.tf @@ -6,7 +6,7 @@ variable "mondoo_org" { provider "mondoo" {} resource "mondoo_space" "test" { - org_id = var.mondoo_org.value + org_id = var.mondoo_org name = "test-space" } @@ -27,3 +27,8 @@ output "space_mrn" { description = "The MRN of the space" value = data.mondoo_space.space.mrn } + +output "space_id" { + description = "The ID of the space" + value = data.mondoo_space.space.id +} diff --git a/examples/resources/mondoo_custom_framework/framework.mql.yaml b/examples/resources/mondoo_custom_framework/framework.mql.yaml new file mode 100644 index 0000000..2638d66 --- /dev/null +++ b/examples/resources/mondoo_custom_framework/framework.mql.yaml @@ -0,0 +1,44 @@ +frameworks: + - uid: example-framework + name: Example Framework + version: "1.0" + license: open-source + tags: + example.com/icon: example + authors: + - name: Example Author + groups: + - uid: example-group-am + title: Asset Management (AM) + controls: + - uid: example-control-am-01 + title: Asset Inventory + docs: + desc: >- + A short description + evidence: + - uid: example-evidence-01 + - uid: example-control-am-02 + title: Acceptable Use and Safe Handling of Assets Policy + docs: + desc: >- + A short description + evidence: + - uid: example-evidence-02 + - uid: example-group-bcm + title: Business Continuity Management (BCM) + controls: + - uid: example-control-bcm-01 + title: Top Management Responsibility + docs: + desc: >- + A short description + evidence: + - uid: example-evidence-03 + - uid: example-control-bcm-02 + title: Business Impact Analysis Policies and Instructions + docs: + desc: >- + A short description + evidence: + - uid: example-evidence-04 diff --git a/examples/resources/mondoo_custom_framework/main.tf b/examples/resources/mondoo_custom_framework/main.tf new file mode 100644 index 0000000..1483327 --- /dev/null +++ b/examples/resources/mondoo_custom_framework/main.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + mondoo = { + source = "mondoohq/mondoo" + version = ">= 0.4.0" + } + } +} diff --git a/examples/resources/mondoo_custom_framework/resource.tf b/examples/resources/mondoo_custom_framework/resource.tf new file mode 100644 index 0000000..4e12b36 --- /dev/null +++ b/examples/resources/mondoo_custom_framework/resource.tf @@ -0,0 +1,26 @@ +provider "mondoo" { + region = "us" +} + +variable "mondoo_org" { + description = "The Mondoo Organization ID" + type = string + default = "my-org-1234567" +} + +variable "my_custom_framework" { + description = "Path to the custom policy file. The file must be in MQL format." + type = string + default = "framework.mql.yaml" +} + +# Create a new space +resource "mondoo_space" "my_space" { + name = "Custom Framework Space" + org_id = var.mondoo_org +} + +resource "mondoo_custom_framework" "compliance_framework_example" { + space_id = mondoo_space.my_space.id + data_url = var.my_custom_framework +} diff --git a/examples/resources/mondoo_framework_assignment/main.tf b/examples/resources/mondoo_framework_assignment/main.tf new file mode 100644 index 0000000..1483327 --- /dev/null +++ b/examples/resources/mondoo_framework_assignment/main.tf @@ -0,0 +1,8 @@ +terraform { + required_providers { + mondoo = { + source = "mondoohq/mondoo" + version = ">= 0.4.0" + } + } +} diff --git a/examples/resources/mondoo_framework_assignment/resource.tf b/examples/resources/mondoo_framework_assignment/resource.tf new file mode 100644 index 0000000..e6b69b2 --- /dev/null +++ b/examples/resources/mondoo_framework_assignment/resource.tf @@ -0,0 +1,22 @@ +provider "mondoo" { + region = "us" +} + +variable "mondoo_org" { + description = "The Mondoo Organization ID" + type = string + default = "my-org-1234567" +} + +# Create a new space +resource "mondoo_space" "my_space" { + name = "Framework Space" + org_id = var.mondoo_org +} + +resource "mondoo_framework_assignment" "compliance_framework_example" { + space_id = mondoo_space.my_space.id + framework_mrn = ["//policy.api.mondoo.app/frameworks/cis-controls-8", + "//policy.api.mondoo.app/frameworks/iso-27001-2022"] + enabled = true +} diff --git a/go.mod b/go.mod index eedbefc..224e154 100644 --- a/go.mod +++ b/go.mod @@ -3,14 +3,15 @@ module go.mondoo.com/terraform-provider-mondoo go 1.22 require ( - github.com/hashicorp/copywrite v0.18.0 + github.com/hashicorp/copywrite v0.19.0 github.com/hashicorp/terraform-plugin-docs v0.19.4 github.com/hashicorp/terraform-plugin-framework v1.9.0 github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 github.com/hashicorp/terraform-plugin-go v0.23.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/hashicorp/terraform-plugin-testing v1.8.0 - go.mondoo.com/mondoo-go v0.0.0-20240516194133-d6612b90fe7c + github.com/stretchr/testify v1.9.0 + go.mondoo.com/mondoo-go v0.0.0-20240611114249-2c3b9b20e67a ) require ( @@ -33,6 +34,7 @@ require ( github.com/cli/safeexec v1.0.1 // indirect github.com/cli/shurcooL-graphql v0.0.4 // indirect github.com/cloudflare/circl v1.3.8 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.16.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-jose/go-jose/v3 v3.0.3 // indirect @@ -89,16 +91,17 @@ require ( github.com/muesli/termenv v0.15.2 // indirect github.com/oklog/run v1.0.0 // indirect github.com/oklog/ulid v1.3.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/posener/complete v1.2.3 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/samber/lo v1.39.0 // indirect github.com/shopspring/decimal v1.3.1 // indirect github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect github.com/spf13/cast v1.5.0 // indirect - github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/cobra v1.8.1 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/thanhpk/randstr v1.0.6 // indirect - github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e // indirect + github.com/thlib/go-timezone-local v0.0.3 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect @@ -106,17 +109,17 @@ require ( github.com/yuin/goldmark-meta v1.1.0 // indirect github.com/zclconf/go-cty v1.14.4 // indirect go.abhg.dev/goldmark/frontmatter v0.2.0 // indirect - go.mongodb.org/mongo-driver v1.15.0 // indirect - golang.org/x/crypto v0.23.0 // indirect - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/oauth2 v0.20.0 // indirect + go.mongodb.org/mongo-driver v1.15.1 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect + golang.org/x/mod v0.18.0 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/term v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.21.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/tools v0.22.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/grpc v1.63.2 // indirect diff --git a/go.sum b/go.sum index 1508224..29ad497 100644 --- a/go.sum +++ b/go.sum @@ -79,7 +79,7 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= @@ -190,8 +190,8 @@ github.com/hashicorp/cli v1.1.6 h1:CMOV+/LJfL1tXCOKrgAX0uRKnzjj/mpmqNXloRSy2K8= github.com/hashicorp/cli v1.1.6/go.mod h1:MPon5QYlgjjo0BSoAiN0ESeT5fRzDjVRp+uioJ0piz4= github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= -github.com/hashicorp/copywrite v0.18.0 h1:6f3aBDyQLBXhD6cdGSnsEM37vCDi3JJrkbR9HPBJf5c= -github.com/hashicorp/copywrite v0.18.0/go.mod h1:6wvQH+ICDoD2bpjO1RJ6fi+h3aY5NeLEM12oTkEtFoc= +github.com/hashicorp/copywrite v0.19.0 h1:f9LVxTDBfFYeQmdBpOsZ+HWknXonI8ZwubbO/RwyuCo= +github.com/hashicorp/copywrite v0.19.0/go.mod h1:6wvQH+ICDoD2bpjO1RJ6fi+h3aY5NeLEM12oTkEtFoc= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -448,8 +448,8 @@ github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -465,8 +465,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/thanhpk/randstr v1.0.6 h1:psAOktJFD4vV9NEVb3qkhRSMvYh4ORRaj1+w/hn4B+o= github.com/thanhpk/randstr v1.0.6/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U= -github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e h1:BuzhfgfWQbX0dWzYzT1zsORLnHRv3bcRcsaUk0VmXA8= -github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI= +github.com/thlib/go-timezone-local v0.0.3 h1:ie5XtZWG5lQ4+1MtC5KZ/FeWlOKzW2nPoUnXYUbV/1s= +github.com/thlib/go-timezone-local v0.0.3/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= @@ -493,10 +493,10 @@ go.abhg.dev/goldmark/frontmatter v0.2.0/go.mod h1:XqrEkZuM57djk7zrlRUB02x8I5J0px go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= -go.mondoo.com/mondoo-go v0.0.0-20240516194133-d6612b90fe7c h1:y910hpEdf1rYW/ONzc0NhuTwVDQNJNC9+x1C+xGywAI= -go.mondoo.com/mondoo-go v0.0.0-20240516194133-d6612b90fe7c/go.mod h1:XY+tOP6vBFJKw5F3WLYEHNQxc+6YmfQ+hEbw3yRy3HI= -go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= -go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= +go.mondoo.com/mondoo-go v0.0.0-20240611114249-2c3b9b20e67a h1:+EQW5uXRyUyeiyZnTy2Jc371PTynJm5OruUWt3SqiT4= +go.mondoo.com/mondoo-go v0.0.0-20240611114249-2c3b9b20e67a/go.mod h1:4032UBD0ph9LyhXq5OQmmxkJv37HdAGi34YLWbhnMDA= +go.mongodb.org/mongo-driver v1.15.1 h1:l+RvoUOoMXFmADTLfYDm7On9dRm7p4T80/lEQM+r7HU= +go.mongodb.org/mongo-driver v1.15.1/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= @@ -508,11 +508,11 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= +golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -523,8 +523,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -546,13 +546,13 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= -golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -609,16 +609,16 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -631,8 +631,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -647,8 +647,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/provider/assets_data_source.go b/internal/provider/assets_data_source.go new file mode 100644 index 0000000..35a65b9 --- /dev/null +++ b/internal/provider/assets_data_source.go @@ -0,0 +1,243 @@ +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + mondoov1 "go.mondoo.com/mondoo-go" +) + +var _ datasource.DataSource = (*assetsDataSource)(nil) + +func NewAssetsDataSource() datasource.DataSource { + return &assetsDataSource{} +} + +type assetsDataSource struct { + client *ExtendedGqlClient +} + +type assetsDataSourceModel struct { + Id types.String `tfsdk:"id"` + Mrn types.String `tfsdk:"mrn"` + State types.String `tfsdk:"state"` + Name types.String `tfsdk:"name"` + UpdatedAt types.String `tfsdk:"updated_at"` + ReferenceIDs []types.String `tfsdk:"reference_ids"` + AssetType types.String `tfsdk:"asset_type"` + Annotations []annotationsModel `tfsdk:"annotations"` + Score scoreModel `tfsdk:"score"` +} + +type annotationsModel struct { + Key string `tfsdk:"key"` + Value string `tfsdk:"value"` +} + +type scoreModel struct { + Grade types.String `tfsdk:"grade"` + Value types.Int64 `tfsdk:"value"` +} + +type spaceAssetDataSourceModel struct { + SpaceID types.String `tfsdk:"space_id"` + SpaceMrn types.String `tfsdk:"space_mrn"` + Assets []assetsDataSourceModel `tfsdk:"assets"` +} + +func (d *assetsDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_assets" +} + +func (d *assetsDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "The asset data source allows you to fetch assets from a space.", + Attributes: map[string]schema.Attribute{ + "space_id": schema.StringAttribute{ + MarkdownDescription: "The unique identifier of the space.", + Computed: true, + Optional: true, + Validators: []validator.String{ + // Validate only this attribute or other_attr is configured. + stringvalidator.ExactlyOneOf(path.Expressions{ + path.MatchRoot("space_mrn"), + }...), + }, + }, + "space_mrn": schema.StringAttribute{ + MarkdownDescription: "The unique Mondoo Resource Name (MRN) of the space.", + Computed: true, + Optional: true, + Validators: []validator.String{ + // Validate only this attribute or other_attr is configured. + stringvalidator.ExactlyOneOf(path.Expressions{ + path.MatchRoot("space_id"), + }...), + }, + }, + "assets": schema.ListNestedAttribute{ + MarkdownDescription: "The list of assets in the space.", + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "id": schema.StringAttribute{ + MarkdownDescription: "The unique identifier of the asset.", + Computed: true, + }, + "mrn": schema.StringAttribute{ + MarkdownDescription: "The unique Mondoo Resource Name (MRN) of the asset.", + Computed: true, + }, + "state": schema.StringAttribute{ + MarkdownDescription: "The current state of the asset.", + Computed: true, + }, + "name": schema.StringAttribute{ + MarkdownDescription: "The name of the asset.", + Computed: true, + }, + "updated_at": schema.StringAttribute{ + MarkdownDescription: "The timestamp when the asset was last updated.", + Computed: true, + }, + "reference_ids": schema.ListAttribute{ + MarkdownDescription: "The reference IDs of the asset.", + Computed: true, + ElementType: types.StringType, + }, + "asset_type": schema.StringAttribute{ + MarkdownDescription: "The type of the asset.", + Computed: true, + }, + "annotations": schema.ListNestedAttribute{ + MarkdownDescription: "The annotations/tags of the asset.", + Computed: true, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "key": schema.StringAttribute{ + MarkdownDescription: "The key of the annotation.", + Computed: true, + }, + "value": schema.StringAttribute{ + MarkdownDescription: "The value of the annotation.", + Computed: true, + }, + }, + }, + }, + "score": schema.SingleNestedAttribute{ + MarkdownDescription: "The overall score of the asset.", + Computed: true, + Attributes: map[string]schema.Attribute{ + "grade": schema.StringAttribute{ + MarkdownDescription: "The grade of the asset.", + Computed: true, + }, + "value": schema.Int64Attribute{ + MarkdownDescription: "The score value of the asset.", + Computed: true, + }, + }, + }, + }, + }, + }, + }, + } +} + +func (d *assetsDataSource) 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.(*mondoov1.Client) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *mondoov1.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + d.client = &ExtendedGqlClient{client} +} + +func (d *assetsDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data spaceAssetDataSourceModel + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + spaceMrn := "" + if data.SpaceMrn.ValueString() != "" { + spaceMrn = data.SpaceMrn.ValueString() + } else if data.SpaceID.ValueString() != "" { + spaceMrn = spacePrefix + data.SpaceID.ValueString() + } + + if spaceMrn == "" { + resp.Diagnostics.AddError("Invalid Configuration", "Either `id` or `mrn` must be set") + return + } + + // Read API call logic + assets, err := d.client.GetAssets(ctx, spaceMrn) + if err != nil { + resp.Diagnostics.AddError("Failed to fetch assets", err.Error()) + return + } + + // Map API response to the model + data.Assets = make([]assetsDataSourceModel, len(assets.Edges)) + for i, asset := range assets.Edges { + + referenceIDs := make([]types.String, len(asset.Node.ReferenceIDs)) + for j, refID := range asset.Node.ReferenceIDs { + referenceIDs[j] = types.StringValue(refID) + } + + annotations := make([]annotationsModel, len(asset.Node.Annotations)) + for j, annotation := range asset.Node.Annotations { + annotations[j] = *convertToAnnotationsModel(annotation) + } + + data.Assets[i] = assetsDataSourceModel{ + Id: types.StringValue(asset.Node.Id), + Mrn: types.StringValue(asset.Node.Mrn), + State: types.StringValue(asset.Node.State), + Name: types.StringValue(asset.Node.Name), + UpdatedAt: types.StringValue(asset.Node.UpdatedAt), + AssetType: types.StringValue(asset.Node.Asset_type), + ReferenceIDs: referenceIDs, + Annotations: annotations, + Score: scoreModel{ + Grade: types.StringValue(asset.Node.Score.Grade), + Value: types.Int64Value(asset.Node.Score.Value), + }, + } + } + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func convertToAnnotationsModel(kv KeyValue) *annotationsModel { + return &annotationsModel{ + Key: kv.Key, + Value: kv.Value, + } +} diff --git a/internal/provider/compliance_framework_resource.go b/internal/provider/compliance_framework_resource.go new file mode 100644 index 0000000..9b62f56 --- /dev/null +++ b/internal/provider/compliance_framework_resource.go @@ -0,0 +1,153 @@ +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/types" + mondoov1 "go.mondoo.com/mondoo-go" +) + +var _ resource.Resource = (*complianceFrameworkResource)(nil) + +func NewComplianceFrameworkResource() resource.Resource { + return &complianceFrameworkResource{} +} + +type complianceFrameworkResource struct { + client *ExtendedGqlClient +} + +type complianceFrameworkResourceModel struct { + // scope + SpaceId types.String `tfsdk:"space_id"` + + // resource details + FrameworkMrn types.List `tfsdk:"framework_mrn"` + Enabled types.Bool `tfsdk:"enabled"` +} + +func (r *complianceFrameworkResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_framework_assignment" +} + +func (r *complianceFrameworkResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: `Set Compliance Frameworks for a Mondoo Space.`, + Attributes: map[string]schema.Attribute{ + "space_id": schema.StringAttribute{ + MarkdownDescription: "Mondoo Space Identifier.", + Required: true, + }, + "framework_mrn": schema.ListAttribute{ + MarkdownDescription: "Compliance Framework MRN.", + Required: true, + ElementType: types.StringType, + }, + "enabled": schema.BoolAttribute{ + MarkdownDescription: "Enable or disable the Compliance Framework.", + Required: true, + }, + }, + } +} + +func (r *complianceFrameworkResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*mondoov1.Client) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.client = &ExtendedGqlClient{client} +} + +func (r *complianceFrameworkResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data complianceFrameworkResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + err := r.client.BulkUpdateFramework(ctx, data.FrameworkMrn, data.SpaceId.ValueString(), data.Enabled.ValueBool()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create Compliance Framework, got error: %s", err)) + return + } + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *complianceFrameworkResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data complianceFrameworkResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Read API call logic + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *complianceFrameworkResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data complianceFrameworkResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + err := r.client.BulkUpdateFramework(ctx, data.FrameworkMrn, data.SpaceId.ValueString(), data.Enabled.ValueBool()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create Compliance Framework, got error: %s", err)) + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *complianceFrameworkResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data complianceFrameworkResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + err := r.client.BulkUpdateFramework(ctx, data.FrameworkMrn, data.SpaceId.ValueString(), false) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to create Compliance Framework, got error: %s", err)) + return + } +} + +func (r *complianceFrameworkResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("mrn"), req, resp) +} diff --git a/internal/provider/custom_compliance_framework_resource.go b/internal/provider/custom_compliance_framework_resource.go new file mode 100644 index 0000000..c803b4d --- /dev/null +++ b/internal/provider/custom_compliance_framework_resource.go @@ -0,0 +1,221 @@ +package provider + +import ( + "context" + "fmt" + "os" + + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/resource" + "github.com/hashicorp/terraform-plugin-framework/resource/schema" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" + mondoov1 "go.mondoo.com/mondoo-go" + "gopkg.in/yaml.v2" +) + +var _ resource.Resource = (*customComplianceFrameworkResource)(nil) + +func NewCustomComplianceFrameworkResource() resource.Resource { + return &customComplianceFrameworkResource{} +} + +type customComplianceFrameworkResource struct { + client *ExtendedGqlClient +} + +type customComplianceFrameworkResourceModel struct { + // scope + SpaceId types.String `tfsdk:"space_id"` + + // resource details + Mrn types.String `tfsdk:"mrn"` + DataUrl types.String `tfsdk:"data_url"` +} + +func (r *customComplianceFrameworkResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_custom_framework" +} + +type Framework struct { + UID string `yaml:"uid"` + Name string `yaml:"name"` +} + +type Config struct { + Frameworks []Framework `yaml:"frameworks"` +} + +func (r *customComplianceFrameworkResource) getFrameworkContent(data customComplianceFrameworkResourceModel) ([]byte, string, error) { + var complianceFrameworkData []byte + var config Config + if !data.DataUrl.IsNull() { + // load content from file + content, err := os.ReadFile(data.DataUrl.ValueString()) + if err != nil { + return nil, "", err + } + complianceFrameworkData = content + + // unmarshal the yaml content + err = yaml.Unmarshal(content, &config) + if err != nil { + return nil, "", fmt.Errorf("unable to unmarshal YAML: %w", err) + } + } + return complianceFrameworkData, config.Frameworks[0].UID, nil +} + +func (r *customComplianceFrameworkResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: `Set custom Compliance Frameworks for a Mondoo Space.`, + Attributes: map[string]schema.Attribute{ + "space_id": schema.StringAttribute{ + MarkdownDescription: "Mondoo Space Identifier.", + Required: true, + }, + "mrn": schema.StringAttribute{ + MarkdownDescription: "Mondoo Resource Name.", + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "data_url": schema.StringAttribute{ + MarkdownDescription: "URL to the custom Compliance Framework data.", + Required: true, + }, + }, + } +} + +func (r *customComplianceFrameworkResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*mondoov1.Client) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Resource Configure Type", + fmt.Sprintf("Expected *http.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + r.client = &ExtendedGqlClient{client} +} + +func (r *customComplianceFrameworkResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data customComplianceFrameworkResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Do GraphQL request to API to create the resource. + spaceMrn := "" + if data.SpaceId.ValueString() != "" { + spaceMrn = spacePrefix + data.SpaceId.ValueString() + } + + content, uid, err := r.getFrameworkContent(data) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get Compliance Framework Content, got error: %s", err)) + return + } + + err = r.client.UploadFramework(ctx, spaceMrn, content) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to upload Compliance Framework, got error: %s", err)) + return + } + + framework, err := r.client.GetFramework(ctx, spaceMrn, data.SpaceId.ValueString(), uid) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get Compliance Framework, got error: %s", err)) + return + } + + data.Mrn = types.StringValue(string(framework.Mrn)) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *customComplianceFrameworkResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data customComplianceFrameworkResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Read API call logic + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *customComplianceFrameworkResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data customComplianceFrameworkResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Do GraphQL request to API to create the resource. + spaceMrn := "" + if data.SpaceId.ValueString() != "" { + spaceMrn = spacePrefix + data.SpaceId.ValueString() + } + + content, _, err := r.getFrameworkContent(data) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get Compliance Framework Content, got error: %s", err)) + return + } + + err = r.client.UploadFramework(ctx, spaceMrn, content) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to upload Compliance Framework, got error: %s", err)) + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *customComplianceFrameworkResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data customComplianceFrameworkResourceModel + + // Read Terraform prior state data into the model + resp.Diagnostics.Append(req.State.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // Do GraphQL request to API to update the resource. + err := r.client.DeleteFramework(ctx, data.Mrn.ValueString()) + if err != nil { + resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to delete Compliance Framework, got error: %s", err)) + return + } +} + +func (r *customComplianceFrameworkResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + resource.ImportStatePassthroughID(ctx, path.Root("mrn"), req, resp) +} diff --git a/internal/provider/gql.go b/internal/provider/gql.go index 2ab72b2..d87b621 100644 --- a/internal/provider/gql.go +++ b/internal/provider/gql.go @@ -8,6 +8,7 @@ import ( "encoding/base64" "fmt" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" "github.com/hashicorp/terraform-plugin-log/tflog" mondoov1 "go.mondoo.com/mondoo-go" ) @@ -89,9 +90,7 @@ type spacePayload struct { Id string Mrn string Name string - Organization struct { - Id string - } + Organization orgPayload } func (c *ExtendedGqlClient) GetSpace(ctx context.Context, mrn string) (spacePayload, error) { @@ -156,6 +155,135 @@ func (c *ExtendedGqlClient) SetCustomPolicy(ctx context.Context, scopeMrn string return setCustomPolicy.SetCustomPolicyPayload, err } +type SpaceReportInput struct { + SpaceMrn mondoov1.String +} + +type Policy struct { + Mrn mondoov1.String + Name mondoov1.String + Assigned mondoov1.Boolean + Action mondoov1.String + Version mondoov1.String + IsPublic mondoov1.Boolean + CreatedAt mondoov1.String + UpdatedAt mondoov1.String +} + +type PolicyNode struct { + Policy Policy +} + +type PolicyEdge struct { + Cursor mondoov1.String + Node PolicyNode +} + +type PolicyReportSummaries struct { + TotalCount int + Edges []PolicyEdge +} + +type SpaceReport struct { + SpaceMrn mondoov1.String + PolicyReportSummaries PolicyReportSummaries +} + +type SpaceReportPayload struct { + SpaceReport SpaceReport +} + +func (c *ExtendedGqlClient) GetPolicySpaceReport(ctx context.Context, spaceMrn string) (*[]Policy, error) { + // Define the query struct according to the provided query + var spaceReportQuery struct { + Report struct { + SpaceReport SpaceReport `graphql:"... on SpaceReport"` + } `graphql:"spaceReport(input: $input)"` + } + // Define the input variable according to the provided query + input := mondoov1.SpaceReportInput{ + SpaceMrn: mondoov1.String(spaceMrn), + } + + variables := map[string]interface{}{ + "input": input, + } + + tflog.Trace(ctx, "GetSpaceReportInput", map[string]interface{}{ + "input": fmt.Sprintf("%+v", input), + }) + + // Execute the query + err := c.Query(ctx, &spaceReportQuery, variables) + if err != nil { + return nil, err + } + + var policies []Policy + for _, edges := range spaceReportQuery.Report.SpaceReport.PolicyReportSummaries.Edges { + policies = append(policies, edges.Node.Policy) + } + + return &policies, nil +} + +type ContentInput struct { + ScopeMrn string + CatalogType string + AssignedOnly bool +} + +type Node struct { + Policy Policy `graphql:"... on Policy"` +} + +type Edge struct { + Node Node +} + +type Content struct { + TotalCount int + Edges []Edge +} + +type ContentPayload struct { + Content Content +} + +func (c *ExtendedGqlClient) GetPolicies(ctx context.Context, scopeMrn string, catalogType string, assignedOnly bool) (*[]Policy, error) { + // Define the query struct according to the provided query + var contentQuery struct { + Content Content `graphql:"content(input: $input)"` + } + // Define the input variable according to the provided query + input := mondoov1.ContentSearchInput{ + ScopeMrn: mondoov1.String(scopeMrn), + CatalogType: mondoov1.CatalogType(catalogType), + AssignedOnly: mondoov1.NewBooleanPtr(mondoov1.Boolean(assignedOnly)), + } + + variables := map[string]interface{}{ + "input": input, + } + + tflog.Trace(ctx, "GetContentInput", map[string]interface{}{ + "input": fmt.Sprintf("%+v", input), + }) + + // Execute the query + err := c.Query(ctx, &contentQuery, variables) + if err != nil { + return nil, err + } + + var policies []Policy + for _, edges := range contentQuery.Content.Edges { + policies = append(policies, edges.Node.Policy) + } + + return &policies, nil +} + func (c *ExtendedGqlClient) AssignPolicy(ctx context.Context, spaceMrn string, action mondoov1.PolicyAction, policyMrns []string) error { var list *[]mondoov1.String @@ -470,6 +598,54 @@ func (c *ExtendedGqlClient) TriggerAction(ctx context.Context, integrationMrn st return q.TriggerAction, nil } +type AssetScore struct { + Grade string + Value int64 +} + +type KeyValue struct { + Key string + Value string +} + +type AssetNode struct { + Id string + Mrn string + State string + Name string + UpdatedAt string + ReferenceIDs []string `graphql:"referenceIDs"` + Asset_type string + Score AssetScore + Annotations []KeyValue +} + +type AssetEdge struct { + Cursor string + Node AssetNode +} + +type AssetsPayload struct { + TotalCount int + Edges []AssetEdge +} + +func (c *ExtendedGqlClient) GetAssets(ctx context.Context, spaceMrn string) (AssetsPayload, error) { + var q struct { + Assets AssetsPayload `graphql:"assets(spaceMrn: $spaceMrn)"` + } + variables := map[string]interface{}{ + "spaceMrn": mondoov1.String(spaceMrn), + } + + err := c.Query(ctx, &q, variables) + if err != nil { + return AssetsPayload{}, err + } + + return q.Assets, nil +} + func (c *ExtendedGqlClient) SetScimGroupMapping(ctx context.Context, orgMrn string, group string, mappings []mondoov1.ScimGroupMapping) error { var setScimGroupMappingMutation struct { SetScimGroupMapping struct { @@ -485,3 +661,104 @@ func (c *ExtendedGqlClient) SetScimGroupMapping(ctx context.Context, orgMrn stri return c.Mutate(ctx, &setScimGroupMappingMutation, setScimGroupMappingInput, nil) } + +func (c *ExtendedGqlClient) UploadFramework(ctx context.Context, spaceMrn string, content []byte) error { + // Define the mutation struct according to the provided query + var uploadMutation struct { + UploadFramework bool `graphql:"uploadFramework(input: $input)"` + } + + // Define the input variable according to the provided query + input := mondoov1.UploadFrameworkInput{ + SpaceMrn: mondoov1.String(spaceMrn), + Dataurl: mondoov1.String(newDataUrl(content)), + } + + // Execute the mutation + return c.Mutate(ctx, &uploadMutation, input, nil) +} + +type ComplianceFrameworkPayload struct { + Mrn mondoov1.String + Name mondoov1.String + State mondoov1.String + ScopeMrn mondoov1.String +} + +func (c *ExtendedGqlClient) GetFramework(ctx context.Context, spaceMrn string, spaceId string, uid string) (*ComplianceFrameworkPayload, error) { + // Define the query struct according to the provided query + var getFrameworkQuery struct { + ComplianceFramework ComplianceFrameworkPayload `graphql:"complianceFramework(input: $input)"` + } + frameworkMrn := fmt.Sprintf("//policy.api.mondoo.app/spaces/%s/frameworks/%s", spaceId, uid) + // Define the input variable according to the provided query + input := mondoov1.ComplianceFrameworkInput{ + ScopeMrn: mondoov1.String(spaceMrn), + FrameworkMrn: mondoov1.String(frameworkMrn), + } + + variables := map[string]interface{}{ + "input": input, + } + + // Execute the query + err := c.Query(ctx, &getFrameworkQuery, variables) + if err != nil { + return nil, err + } + + return &getFrameworkQuery.ComplianceFramework, nil +} + +func (c *ExtendedGqlClient) UpdateFramework(ctx context.Context, frameworkMrn string, scopeMrn string, enabled bool) error { + var updateMutation struct { + ApplyFramework bool `graphql:"applyFrameworkMutation(input: $input)"` + } + + input := mondoov1.ComplianceFrameworkMutationInput{ + FrameworkMrn: mondoov1.String(frameworkMrn), + ScopeMrn: mondoov1.String(scopeMrn), + } + + if enabled { + input.Action = mondoov1.ComplianceFrameworkMutationActionEnable + } else { + input.Action = mondoov1.ComplianceFrameworkMutationActionPreview + } + + return c.Mutate(ctx, &updateMutation, input, nil) +} + +func (c *ExtendedGqlClient) BulkUpdateFramework(ctx context.Context, frameworkMrns basetypes.ListValue, spaceId string, enabled bool) error { + scopeMrn := "" + if spaceId != "" { + scopeMrn = spacePrefix + spaceId + } + + var frameworkList []mondoov1.String + listFrameworks, _ := frameworkMrns.ToListValue(ctx) + listFrameworks.ElementsAs(ctx, &frameworkList, true) + + for _, mrn := range frameworkList { + err := c.UpdateFramework(ctx, string(mrn), scopeMrn, enabled) + if err != nil { + return err + } + } + return nil +} + +func (c *ExtendedGqlClient) DeleteFramework(ctx context.Context, mrn string) error { + // Define the mutation struct according to the provided query + var deleteMutation struct { + DeleteFramework bool `graphql:"deleteFramework(input: $input)"` + } + + // Define the input variable according to the provided query + input := mondoov1.DeleteFrameworkInput{ + Mrn: mondoov1.String(mrn), + } + + // Execute the mutation + return c.Mutate(ctx, &deleteMutation, input, nil) +} diff --git a/internal/provider/integration_aws_resource.go b/internal/provider/integration_aws_resource.go index 0fec886..4334295 100644 --- a/internal/provider/integration_aws_resource.go +++ b/internal/provider/integration_aws_resource.go @@ -7,8 +7,10 @@ import ( "context" "fmt" "strings" + "regexp" "github.com/hashicorp/terraform-plugin-framework-validators/objectvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -105,6 +107,9 @@ func (r *integrationAwsResource) Schema(ctx context.Context, req resource.Schema "name": schema.StringAttribute{ MarkdownDescription: "Name of the integration.", Required: true, + Validators: []validator.String{ + stringvalidator.LengthAtMost(250), + }, }, "credentials": schema.SingleNestedAttribute{ Required: true, @@ -134,10 +139,22 @@ func (r *integrationAwsResource) Schema(ctx context.Context, req resource.Schema "access_key": schema.StringAttribute{ Required: true, Sensitive: true, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexp.MustCompile(`^([A-Z0-9]{20})$`), + "must be a 20 character string with uppercase letters and numbers only", + ), + }, }, "secret_key": schema.StringAttribute{ Required: true, Sensitive: true, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexp.MustCompile(`^([a-zA-Z0-9+/]{40})$`), + "must be a 40 character string with alphanumeric values and + and / only", + ), + }, }, }, }, diff --git a/internal/provider/integration_azure_resource.go b/internal/provider/integration_azure_resource.go index ecf61e7..16d6234 100644 --- a/internal/provider/integration_azure_resource.go +++ b/internal/provider/integration_azure_resource.go @@ -4,13 +4,16 @@ import ( "context" "fmt" "strings" - + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" mondoov1 "go.mondoo.com/mondoo-go" @@ -69,6 +72,9 @@ func (r *integrationAzureResource) Schema(ctx context.Context, req resource.Sche "name": schema.StringAttribute{ MarkdownDescription: "Name of the integration.", Required: true, + Validators: []validator.String{ + stringvalidator.LengthAtMost(250), + }, }, "client_id": schema.StringAttribute{ MarkdownDescription: "Azure Client ID.", @@ -86,11 +92,23 @@ func (r *integrationAzureResource) Schema(ctx context.Context, req resource.Sche MarkdownDescription: "List of Azure subscriptions to scan.", Optional: true, ElementType: types.StringType, + Validators: []validator.List{ + // Validate only this attribute or other_attr is configured. + listvalidator.ConflictsWith(path.Expressions{ + path.MatchRoot("subscription_deny_list"), + }...), + }, }, "subscription_deny_list": schema.ListAttribute{ MarkdownDescription: "List of Azure subscriptions to exclude from scanning.", Optional: true, ElementType: types.StringType, + Validators: []validator.List{ + // Validate only this attribute or other_attr is configured. + listvalidator.ConflictsWith(path.Expressions{ + path.MatchRoot("subscription_allow_list"), + }...), + }, }, "credentials": schema.SingleNestedAttribute{ Required: true, diff --git a/internal/provider/integration_domain_resource.go b/internal/provider/integration_domain_resource.go index 5672d9b..bdffd91 100644 --- a/internal/provider/integration_domain_resource.go +++ b/internal/provider/integration_domain_resource.go @@ -4,12 +4,15 @@ import ( "context" "fmt" "strings" + "regexp" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" mondoov1 "go.mondoo.com/mondoo-go" ) @@ -57,6 +60,12 @@ func (r *integrationDomainResource) Schema(ctx context.Context, req resource.Sch "host": schema.StringAttribute{ MarkdownDescription: "Domain name or IP address.", Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexp.MustCompile(`^((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])$|^([a-z0-9-]+\.)+[a-z]{2,}$`), + "must contain only lowercase letters and at least one dot or be an IPv4 address", + ), + }, }, "https": schema.BoolAttribute{ MarkdownDescription: "Enable HTTPS port.", diff --git a/internal/provider/integration_gcp_resource.go b/internal/provider/integration_gcp_resource.go index 9954ea8..98b0a15 100644 --- a/internal/provider/integration_gcp_resource.go +++ b/internal/provider/integration_gcp_resource.go @@ -8,11 +8,13 @@ import ( "fmt" "strings" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-framework/types/basetypes" mondoov1 "go.mondoo.com/mondoo-go" @@ -67,6 +69,9 @@ func (r *integrationGcpResource) Schema(ctx context.Context, req resource.Schema "name": schema.StringAttribute{ MarkdownDescription: "Name of the integration.", Required: true, + Validators: []validator.String{ + stringvalidator.LengthAtMost(250), + }, }, "project_id": schema.StringAttribute{ MarkdownDescription: "GCP project id", diff --git a/internal/provider/integration_github_resource.go b/internal/provider/integration_github_resource.go index 1118c3e..bc2cf68 100644 --- a/internal/provider/integration_github_resource.go +++ b/internal/provider/integration_github_resource.go @@ -4,13 +4,17 @@ import ( "context" "fmt" "strings" - + "regexp" + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" mondoov1 "go.mondoo.com/mondoo-go" ) @@ -103,6 +107,9 @@ func (r *integrationGithubResource) Schema(ctx context.Context, req resource.Sch "name": schema.StringAttribute{ MarkdownDescription: "Name of the integration.", Required: true, + Validators: []validator.String{ + stringvalidator.LengthAtMost(250), + }, }, "owner": schema.StringAttribute{ MarkdownDescription: "GitHub Owner.", @@ -116,11 +123,23 @@ func (r *integrationGithubResource) Schema(ctx context.Context, req resource.Sch MarkdownDescription: "List of GitHub repositories to scan.", Optional: true, ElementType: types.StringType, + Validators: []validator.List{ + // Validate only this attribute or other_attr is configured. + listvalidator.ConflictsWith(path.Expressions{ + path.MatchRoot("repository_deny_list"), + }...), + }, }, "repository_deny_list": schema.ListAttribute{ MarkdownDescription: "List of GitHub repositories to exclude from scanning.", Optional: true, ElementType: types.StringType, + Validators: []validator.List{ + // Validate only this attribute or other_attr is configured. + listvalidator.ConflictsWith(path.Expressions{ + path.MatchRoot("repository_allow_list"), + }...), + }, }, "credentials": schema.SingleNestedAttribute{ Required: true, @@ -129,6 +148,12 @@ func (r *integrationGithubResource) Schema(ctx context.Context, req resource.Sch MarkdownDescription: "Token for GitHub integration.", Required: true, Sensitive: true, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexp.MustCompile(`^(ghp_[a-zA-Z0-9]{36}|github_pat_[a-zA-Z0-9]{22}_[a-zA-Z0-9]{59})$`), + "must be a valid classic GitHub Token with 40 characters in length, with a prefix of ghp_ or a fine-grained GitHub token with 93 characters in length, with a prefix of github_pat_", + ), + }, }, }, }, diff --git a/internal/provider/integration_ms365_resource.go b/internal/provider/integration_ms365_resource.go index 0a6643e..2a53eae 100644 --- a/internal/provider/integration_ms365_resource.go +++ b/internal/provider/integration_ms365_resource.go @@ -5,11 +5,13 @@ import ( "fmt" "strings" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" mondoov1 "go.mondoo.com/mondoo-go" ) @@ -64,6 +66,9 @@ func (r *integrationMs365Resource) Schema(ctx context.Context, req resource.Sche "name": schema.StringAttribute{ MarkdownDescription: "Name of the integration.", Required: true, + Validators: []validator.String{ + stringvalidator.LengthAtMost(250), + }, }, "client_id": schema.StringAttribute{ MarkdownDescription: "Azure Client ID.", diff --git a/internal/provider/integration_oci_tenant.go b/internal/provider/integration_oci_tenant.go index a45a14f..ae40ba1 100644 --- a/internal/provider/integration_oci_tenant.go +++ b/internal/provider/integration_oci_tenant.go @@ -7,11 +7,13 @@ import ( "context" "fmt" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" mondoov1 "go.mondoo.com/mondoo-go" @@ -75,6 +77,9 @@ func (r *integrationOciTenantResource) Schema(ctx context.Context, req resource. "name": schema.StringAttribute{ MarkdownDescription: "Name of the integration.", Optional: true, + Validators: []validator.String{ + stringvalidator.LengthAtMost(250), + }, }, "tenancy": schema.StringAttribute{ MarkdownDescription: "OCI tenancy", diff --git a/internal/provider/integration_slack_resource.go b/internal/provider/integration_slack_resource.go index cd637dd..5f8d53e 100644 --- a/internal/provider/integration_slack_resource.go +++ b/internal/provider/integration_slack_resource.go @@ -4,11 +4,15 @@ import ( "context" "fmt" "strings" + "regexp" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" mondoov1 "go.mondoo.com/mondoo-go" ) @@ -57,11 +61,20 @@ func (r *integrationSlackResource) Schema(ctx context.Context, req resource.Sche "name": schema.StringAttribute{ MarkdownDescription: "Name of the integration.", Required: true, + Validators: []validator.String{ + stringvalidator.LengthAtMost(250), + }, }, "slack_token": schema.StringAttribute{ Required: true, Sensitive: true, Description: "The Slack token to authenticate with the Slack API.", + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexp.MustCompile(`^xox[baprs](-[0-9a-zA-Z]{10,48})+$`), + "must start with xox and one of the following characters b, a, p, r, s, followed by one or more blocks consisting of a dash and 10-48 alphanumeric characters", + ), + }, }, }, } diff --git a/internal/provider/organization_data_source.go b/internal/provider/organization_data_source.go index deddc08..ff691c8 100644 --- a/internal/provider/organization_data_source.go +++ b/internal/provider/organization_data_source.go @@ -6,8 +6,12 @@ package provider import ( "context" "fmt" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" mondoov1 "go.mondoo.com/mondoo-go" ) @@ -44,11 +48,23 @@ func (d *OrganizationDataSource) Schema(ctx context.Context, req datasource.Sche MarkdownDescription: "Organization ID", Computed: true, Optional: true, + Validators: []validator.String{ + // Validate only this attribute or other_attr is configured. + stringvalidator.ExactlyOneOf(path.Expressions{ + path.MatchRoot("mrn"), + }...), + }, }, "mrn": schema.StringAttribute{ MarkdownDescription: "Organization MRN", Computed: true, Optional: true, + Validators: []validator.String{ + // Validate only this attribute or other_attr is configured. + stringvalidator.ExactlyOneOf(path.Expressions{ + path.MatchRoot("id"), + }...), + }, }, "name": schema.StringAttribute{ MarkdownDescription: "Organization name", @@ -95,6 +111,7 @@ func (d *OrganizationDataSource) Read(ctx context.Context, req datasource.ReadRe } else if data.OrgID.ValueString() != "" { orgMrn = orgPrefix + data.OrgID.ValueString() } + if orgMrn == "" { resp.Diagnostics.AddError("Invalid Configuration", "Either `id` or `mrn` must be set") return @@ -106,8 +123,6 @@ func (d *OrganizationDataSource) Read(ctx context.Context, req datasource.ReadRe return } - // For the purposes of this example code, hardcoding a response value to - // save into the Terraform state. data.OrgID = types.StringValue(payload.Id) data.OrgMrn = types.StringValue(payload.Mrn) data.Name = types.StringValue(payload.Name) diff --git a/internal/provider/policy_assignment_resource.go b/internal/provider/policy_assignment_resource.go index f0253ba..46ae153 100644 --- a/internal/provider/policy_assignment_resource.go +++ b/internal/provider/policy_assignment_resource.go @@ -6,9 +6,12 @@ package provider import ( "context" "fmt" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" mondoov1 "go.mondoo.com/mondoo-go" "github.com/hashicorp/terraform-plugin-framework/resource" @@ -62,6 +65,9 @@ func (r *policyAssignmentResource) Schema(ctx context.Context, req resource.Sche Default: stringdefault.StaticString("enabled"), Computed: true, Optional: true, + Validators: []validator.String{ + stringvalidator.OneOf("enabled", "disabled", "preview"), + }, }, }, } diff --git a/internal/provider/policy_data_source.go b/internal/provider/policy_data_source.go new file mode 100644 index 0000000..0cf697c --- /dev/null +++ b/internal/provider/policy_data_source.go @@ -0,0 +1,199 @@ +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" + "github.com/hashicorp/terraform-plugin-framework/types" + mondoov1 "go.mondoo.com/mondoo-go" +) + +var _ datasource.DataSource = (*policyDataSource)(nil) + +func NewPolicyDataSource() datasource.DataSource { + return &policyDataSource{} +} + +type policyDataSource struct { + client *ExtendedGqlClient +} + +type policyModel struct { + PolicyMrn types.String `tfsdk:"policy_mrn"` + PolicyName types.String `tfsdk:"policy_name"` + Assigned types.Bool `tfsdk:"assigned"` + Action types.String `tfsdk:"action"` + Version types.String `tfsdk:"version"` + IsPublic types.Bool `tfsdk:"is_public"` + CreatedAt types.String `tfsdk:"created_at"` + UpdatedAt types.String `tfsdk:"updated_at"` +} + +type policyDataSourceModel struct { + SpaceID types.String `tfsdk:"space_id"` + SpaceMrn types.String `tfsdk:"space_mrn"` + CatalogType types.String `tfsdk:"catalog_type"` + AssignedOnly types.Bool `tfsdk:"assigned_only"` + Policies []policyModel `tfsdk:"policies"` +} + +func (d *policyDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_policy" +} + +func (d *policyDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "Data source for policies and querypacks", + Attributes: map[string]schema.Attribute{ + "space_id": schema.StringAttribute{ + Computed: true, + Optional: true, + MarkdownDescription: "Space ID", + Validators: []validator.String{ + // Validate only this attribute or other_attr is configured. + stringvalidator.ExactlyOneOf(path.Expressions{ + path.MatchRoot("space_mrn"), + }...), + }, + }, + "space_mrn": schema.StringAttribute{ + Computed: true, + Optional: true, + MarkdownDescription: "Space MRN", + Validators: []validator.String{ + // Validate only this attribute or other_attr is configured. + stringvalidator.ExactlyOneOf(path.Expressions{ + path.MatchRoot("space_id"), + }...), + }, + }, + "catalog_type": schema.StringAttribute{ + Computed: true, + Optional: true, + MarkdownDescription: "Catalog type of either `ALL`, `POLICY` or `QUERYPACK`. Defaults to `ALL`", + Validators: []validator.String{ + stringvalidator.OneOf("ALL", "POLICY", "QUERYPACK"), + }, + }, + "assigned_only": schema.BoolAttribute{ + Computed: true, + Optional: true, + MarkdownDescription: "Only return enabled policies if set to `true`", + }, + "policies": schema.ListNestedAttribute{ + Computed: true, + MarkdownDescription: "List of policies", + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "policy_mrn": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Unique policy Mondoo Resource Name", + }, + "policy_name": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Policy name", + }, + "assigned": schema.BoolAttribute{ + Computed: true, + MarkdownDescription: "Determines if a policy is enabled or disabled", + }, + "action": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Policies can be set to `Null`, `IGNORE` or `ACTIVE`", + }, + "version": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Version", + }, + "is_public": schema.BoolAttribute{ + Computed: true, + MarkdownDescription: "Determines if a policy is public or private", + }, + "created_at": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Timestamp of policy creation", + }, + "updated_at": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Timestamp of policy update", + }, + }, + }, + }, + }, + } +} + +func (d *policyDataSource) 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.(*mondoov1.Client) + + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *mondoov1.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + + return + } + + d.client = &ExtendedGqlClient{client} +} + +func (d *policyDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { + var data policyDataSourceModel + + // Read Terraform configuration data into the model + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + // generate space mrn + scopeMrn := "" + if data.SpaceMrn.ValueString() != "" { + scopeMrn = data.SpaceMrn.ValueString() + } else if data.SpaceID.ValueString() != "" { + scopeMrn = spacePrefix + data.SpaceID.ValueString() + } + + if scopeMrn == "" { + resp.Diagnostics.AddError("Invalid Configuration", "Either `id` or `mrn` must be set") + return + } + + // Fetch policies + policies, err := d.client.GetPolicies(ctx, scopeMrn, data.CatalogType.ValueString(), data.AssignedOnly.ValueBool()) + if err != nil { + resp.Diagnostics.AddError("Failed to fetch policies", err.Error()) + return + } + + // Convert policies to the model + data.Policies = make([]policyModel, len(*policies)) + for i, policy := range *policies { + data.Policies[i] = policyModel{ + PolicyMrn: types.StringValue(string(policy.Mrn)), + PolicyName: types.StringValue(string(policy.Name)), + Assigned: types.BoolValue(bool(policy.Assigned)), + Action: types.StringValue(string(policy.Action)), + Version: types.StringValue(string(policy.Version)), + IsPublic: types.BoolValue(bool(policy.IsPublic)), + CreatedAt: types.StringValue(string(policy.CreatedAt)), + UpdatedAt: types.StringValue(string(policy.UpdatedAt)), + } + } + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 98884d8..115a26b 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -170,6 +170,8 @@ func (p *MondooProvider) Resources(ctx context.Context) []func() resource.Resour NewIntegrationSlackResource, NewIntegrationMs365Resource, NewIntegrationGithubResource, + NewComplianceFrameworkResource, + NewCustomComplianceFrameworkResource, } } @@ -177,6 +179,8 @@ func (p *MondooProvider) DataSources(ctx context.Context) []func() datasource.Da return []func() datasource.DataSource{ NewOrganizationDataSource, NewSpaceDataSource, + NewPolicyDataSource, + NewAssetsDataSource, } } diff --git a/internal/provider/querypack_assignment_resource.go b/internal/provider/querypack_assignment_resource.go index dcbe9fc..de7f251 100644 --- a/internal/provider/querypack_assignment_resource.go +++ b/internal/provider/querypack_assignment_resource.go @@ -6,11 +6,14 @@ package provider import ( "context" "fmt" + "strings" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" mondoov1 "go.mondoo.com/mondoo-go" - "strings" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -63,6 +66,9 @@ func (r *queryPackAssignmentResource) Schema(ctx context.Context, req resource.S Default: stringdefault.StaticString("enabled"), Computed: true, Optional: true, + Validators: []validator.String{ + stringvalidator.OneOf("enabled", "disabled"), + }, }, }, } diff --git a/internal/provider/space_data_source.go b/internal/provider/space_data_source.go index 822a21a..b837baf 100644 --- a/internal/provider/space_data_source.go +++ b/internal/provider/space_data_source.go @@ -6,8 +6,12 @@ package provider import ( "context" "fmt" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/path" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" mondoov1 "go.mondoo.com/mondoo-go" ) @@ -44,11 +48,23 @@ func (d *SpaceDataSource) Schema(ctx context.Context, req datasource.SchemaReque MarkdownDescription: "Space ID", Computed: true, Optional: true, + Validators: []validator.String{ + // Validate only this attribute or other_attr is configured. + stringvalidator.ExactlyOneOf(path.Expressions{ + path.MatchRoot("mrn"), + }...), + }, }, "mrn": schema.StringAttribute{ MarkdownDescription: "Space MRN", Computed: true, Optional: true, + Validators: []validator.String{ + // Validate only this attribute or other_attr is configured. + stringvalidator.ExactlyOneOf(path.Expressions{ + path.MatchRoot("id"), + }...), + }, }, "name": schema.StringAttribute{ MarkdownDescription: "Space name", @@ -92,9 +108,10 @@ func (d *SpaceDataSource) Read(ctx context.Context, req datasource.ReadRequest, spaceMrn := "" if data.SpaceMrn.ValueString() != "" { spaceMrn = data.SpaceMrn.ValueString() - } else if data.SpaceMrn.ValueString() != "" { - spaceMrn = orgPrefix + data.SpaceID.ValueString() + } else if data.SpaceID.ValueString() != "" { + spaceMrn = spacePrefix + data.SpaceID.ValueString() } + if spaceMrn == "" { resp.Diagnostics.AddError("Invalid Configuration", "Either `id` or `mrn` must be set") return @@ -106,8 +123,6 @@ func (d *SpaceDataSource) Read(ctx context.Context, req datasource.ReadRequest, return } - // For the purposes of this example code, hardcoding a response value to - // save into the Terraform state. data.SpaceID = types.StringValue(payload.Id) data.SpaceMrn = types.StringValue(payload.Mrn) data.Name = types.StringValue(payload.Name) diff --git a/internal/provider/space_resource.go b/internal/provider/space_resource.go index f88eb01..c3ee9f9 100644 --- a/internal/provider/space_resource.go +++ b/internal/provider/space_resource.go @@ -6,12 +6,15 @@ package provider import ( "context" "fmt" + "regexp" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" mondoov1 "go.mondoo.com/mondoo-go" @@ -56,6 +59,12 @@ func (r *SpaceResource) Schema(ctx context.Context, req resource.SchemaRequest, "name": schema.StringAttribute{ MarkdownDescription: "Name of the space.", Optional: true, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexp.MustCompile(`^([a-zA-Z \-'_]|\d){2,30}$`), + "must contain 2 to 30 characters, where each character can be a letter (uppercase or lowercase), a space, a dash, an underscore, or a digit", + ), + }, }, "id": schema.StringAttribute{ MarkdownDescription: "Id of the space. Must be globally unique.", @@ -64,6 +73,12 @@ func (r *SpaceResource) Schema(ctx context.Context, req resource.SchemaRequest, PlanModifiers: []planmodifier.String{ stringplanmodifier.UseStateForUnknown(), }, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexp.MustCompile(`^[a-z]([\d-_]|[a-z]){6,35}[a-z\d]$`), + "must contain 6 to 35 digits, dashes, underscores, or lowercase letters, and ending with either a lowercase letter or a digit", + ), + }, }, "mrn": schema.StringAttribute{ MarkdownDescription: "Mrn of the space.", @@ -75,6 +90,12 @@ func (r *SpaceResource) Schema(ctx context.Context, req resource.SchemaRequest, "org_id": schema.StringAttribute{ MarkdownDescription: "Id of the organization.", Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches( + regexp.MustCompile(`^[a-z]([\d-_]|[a-z]){6,35}[a-z\d]$`), + "must contain 6 to 35 digits, dashes, underscores, or lowercase letters, and ending with either a lowercase letter or a digit", + ), + }, }, }, }