From c4d0b2fd93e4657107a0513b4f7dc11bb1e62770 Mon Sep 17 00:00:00 2001 From: Salim Afiune Maya Date: Thu, 9 Jan 2025 11:44:42 +0100 Subject: [PATCH] =?UTF-8?q?=E2=AD=90=EF=B8=8F=20new=20resource=20mondoo=5F?= =?UTF-8?q?integration=5Fcrowdstrike?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Salim Afiune Maya --- docs/resources/integration_crowdstrike.md | 57 ++++ .../mondoo_integration_crowdstrike/main.tf | 9 + .../resource.tf | 22 ++ go.mod | 10 +- go.sum | 20 +- internal/provider/gql.go | 6 + .../integration_crowdstrike_resource.go | 261 ++++++++++++++++++ .../integration_crowdstrike_resource_test.go | 89 ++++++ internal/provider/provider.go | 1 + 9 files changed, 460 insertions(+), 15 deletions(-) create mode 100644 docs/resources/integration_crowdstrike.md create mode 100644 examples/resources/mondoo_integration_crowdstrike/main.tf create mode 100644 examples/resources/mondoo_integration_crowdstrike/resource.tf create mode 100644 internal/provider/integration_crowdstrike_resource.go create mode 100644 internal/provider/integration_crowdstrike_resource_test.go diff --git a/docs/resources/integration_crowdstrike.md b/docs/resources/integration_crowdstrike.md new file mode 100644 index 0000000..93e78ce --- /dev/null +++ b/docs/resources/integration_crowdstrike.md @@ -0,0 +1,57 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "mondoo_integration_crowdstrike Resource - terraform-provider-mondoo" +subcategory: "" +description: |- + CrowdStrike Falcon for Cloud integration. +--- + +# mondoo_integration_crowdstrike (Resource) + +CrowdStrike Falcon for Cloud integration. + +## Example Usage + +```terraform +variable "client_id" { + description = "The Client ID" + type = string + sensitive = true +} + +variable "client_secret" { + description = "The Client Secret" + type = string + sensitive = true +} + +provider "mondoo" { + space = "hungry-poet-123456" +} + +# Setup the CrowdStrike integration +resource "mondoo_integration_crowdstrike" "crowdstrike_integration" { + name = "CrowdStrike Integration" + client_id = var.client_id + client_secret = var.client_secret +} +``` + + +## Schema + +### Required + +- `client_id` (String) Client ID used for authentication with CrowdStrike Falcon platform. +- `client_secret` (String, Sensitive) Client Secret used for authentication with CrowdStrike Falcon platform. +- `name` (String) Name of the integration. + +### Optional + +- `cloud` (String) The Falcon Cloud to connect. +- `member_cid` (String) CID selector for cases when the client ID and secret has access to multiple CIDs. +- `space_id` (String) Mondoo space identifier. If there is no space ID, the provider space is used. + +### Read-Only + +- `mrn` (String) Integration identifier diff --git a/examples/resources/mondoo_integration_crowdstrike/main.tf b/examples/resources/mondoo_integration_crowdstrike/main.tf new file mode 100644 index 0000000..baa2b26 --- /dev/null +++ b/examples/resources/mondoo_integration_crowdstrike/main.tf @@ -0,0 +1,9 @@ +terraform { + required_providers { + mondoo = { + source = "mondoohq/mondoo" + version = ">= 0.19" + } + } +} + diff --git a/examples/resources/mondoo_integration_crowdstrike/resource.tf b/examples/resources/mondoo_integration_crowdstrike/resource.tf new file mode 100644 index 0000000..473f7e9 --- /dev/null +++ b/examples/resources/mondoo_integration_crowdstrike/resource.tf @@ -0,0 +1,22 @@ +variable "client_id" { + description = "The Client ID" + type = string + sensitive = true +} + +variable "client_secret" { + description = "The Client Secret" + type = string + sensitive = true +} + +provider "mondoo" { + space = "hungry-poet-123456" +} + +# Setup the CrowdStrike integration +resource "mondoo_integration_crowdstrike" "crowdstrike_integration" { + name = "CrowdStrike Integration" + client_id = var.client_id + client_secret = var.client_secret +} diff --git a/go.mod b/go.mod index 45191b2..9f75879 100644 --- a/go.mod +++ b/go.mod @@ -14,7 +14,7 @@ require ( github.com/hashicorp/terraform-plugin-testing v1.11.0 github.com/stretchr/testify v1.10.0 go.mondoo.com/cnquery/v11 v11.35.0 - go.mondoo.com/mondoo-go v0.0.0-20241204230241-b8e4fdb12bda + go.mondoo.com/mondoo-go v0.0.0-20250108144440-673a4fac8289 gopkg.in/yaml.v2 v2.4.0 ) @@ -158,14 +158,14 @@ require ( go.opentelemetry.io/otel/trace v1.32.0 // indirect go.uber.org/mock v0.5.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.30.0 // indirect + golang.org/x/crypto v0.32.0 // indirect golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d // indirect golang.org/x/mod v0.22.0 // indirect golang.org/x/net v0.32.0 // indirect - golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/oauth2 v0.25.0 // indirect golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/term v0.27.0 // indirect + golang.org/x/sys v0.29.0 // indirect + golang.org/x/term v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/tools v0.28.0 // indirect google.golang.org/appengine v1.6.8 // indirect diff --git a/go.sum b/go.sum index 7fe32e4..7985b2f 100644 --- a/go.sum +++ b/go.sum @@ -607,8 +607,8 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3 go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= go.mondoo.com/cnquery/v11 v11.35.0 h1:gEAkX2pjHQzohVKsuuPl7DEkfnA3gkDCm0fD76FSeEw= go.mondoo.com/cnquery/v11 v11.35.0/go.mod h1:8ax++xvDAt7LQue8mI7kcwoaNo/T1D95Yx4JvmD6lGM= -go.mondoo.com/mondoo-go v0.0.0-20241204230241-b8e4fdb12bda h1:Hx3Wmd4Xo69hBde7nEdJC5FwkaKgXdSauWW5PpNg9hE= -go.mondoo.com/mondoo-go v0.0.0-20241204230241-b8e4fdb12bda/go.mod h1:dGj5d8BoLzVppdYI2k0Oay9pcg7bqsCYbyiBH9uhKGc= +go.mondoo.com/mondoo-go v0.0.0-20250108144440-673a4fac8289 h1:D47xahKosrO4gjRtjnBte3tlHbtDAGYkEWyPXheRaac= +go.mondoo.com/mondoo-go v0.0.0-20250108144440-673a4fac8289/go.mod h1:dGj5d8BoLzVppdYI2k0Oay9pcg7bqsCYbyiBH9uhKGc= go.mondoo.com/ranger-rpc v0.6.4 h1:q01kjESvF2HSnbFO+TjpUQSiI2IM8JWGJLH3u0vNxZA= go.mondoo.com/ranger-rpc v0.6.4/go.mod h1:XltLDrmTYlEzStXa0+8vGS5/Cik4OKS5DY/o/qOG8SU= go.mongodb.org/mongo-driver v1.10.0 h1:UtV6N5k14upNp4LTduX0QCufG124fSu25Wz9tu94GLg= @@ -637,8 +637,8 @@ golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4 golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= -golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0= golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= @@ -685,8 +685,8 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG 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.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= -golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= -golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.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= @@ -750,8 +750,8 @@ 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.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.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-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -760,8 +760,8 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 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.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= +golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= 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= diff --git a/internal/provider/gql.go b/internal/provider/gql.go index c40c7f3..9bd5fd2 100644 --- a/internal/provider/gql.go +++ b/internal/provider/gql.go @@ -657,6 +657,11 @@ type MicrosoftDefenderConfigurationOptionsInput struct { SubscriptionsAllowlist []string SubscriptionsDenylist []string } +type CrowdstrikeFalconConfigurationOptionsInput struct { + ClientId string + Cloud string + MemberCID string +} type ClientIntegrationConfigurationOptions struct { AzureConfigurationOptions AzureConfigurationOptions `graphql:"... on AzureConfigurationOptions"` @@ -672,6 +677,7 @@ type ClientIntegrationConfigurationOptions struct { EmailConfigurationOptions EmailConfigurationOptions `graphql:"... on EmailConfigurationOptions"` GitlabConfigurationOptions GitlabConfigurationOptions `graphql:"... on GitlabConfigurationOptions"` MicrosoftDefenderConfigurationOptionsInput MicrosoftDefenderConfigurationOptionsInput `graphql:"... on MicrosoftDefenderConfigurationOptions"` + CrowdstrikeFalconConfigurationOptionsInput CrowdstrikeFalconConfigurationOptionsInput `graphql:"... on CrowdstrikeFalconConfigurationOptions"` // Add other configuration options here } diff --git a/internal/provider/integration_crowdstrike_resource.go b/internal/provider/integration_crowdstrike_resource.go new file mode 100644 index 0000000..78624c8 --- /dev/null +++ b/internal/provider/integration_crowdstrike_resource.go @@ -0,0 +1,261 @@ +package provider + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "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" +) + +var _ resource.Resource = (*integrationCrowdstrikeResource)(nil) + +func NewIntegrationCrowdstrikeResource() resource.Resource { + return &integrationCrowdstrikeResource{} +} + +type integrationCrowdstrikeResource struct { + client *ExtendedGqlClient +} + +type integrationCrowdstrikeResourceModel struct { + // scope + SpaceID types.String `tfsdk:"space_id"` + + // integration details + Mrn types.String `tfsdk:"mrn"` + Name types.String `tfsdk:"name"` + ClientId types.String `tfsdk:"client_id"` + ClientSecret types.String `tfsdk:"client_secret"` + Cloud types.String `tfsdk:"cloud"` + MemberCID types.String `tfsdk:"member_cid"` +} + +func (m integrationCrowdstrikeResourceModel) GetConfigurationOptions() *mondoov1.CrowdstrikeFalconConfigurationOptionsInput { + return &mondoov1.CrowdstrikeFalconConfigurationOptionsInput{ + ClientID: mondoov1.String(m.ClientId.ValueString()), + ClientSecret: mondoov1.String(m.ClientSecret.ValueString()), + Cloud: mondoov1.NewStringPtr(mondoov1.String(m.Cloud.ValueString())), + MemberCID: mondoov1.NewStringPtr(mondoov1.String(m.MemberCID.ValueString())), + } +} + +func (r *integrationCrowdstrikeResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_integration_crowdstrike" +} + +func (r *integrationCrowdstrikeResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) { + resp.Schema = schema.Schema{ + MarkdownDescription: "CrowdStrike Falcon for Cloud integration.", + Attributes: map[string]schema.Attribute{ + "space_id": schema.StringAttribute{ + MarkdownDescription: "Mondoo space identifier. If there is no space ID, the provider space is used.", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "mrn": schema.StringAttribute{ + Computed: true, + MarkdownDescription: "Integration identifier", + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "name": schema.StringAttribute{ + MarkdownDescription: "Name of the integration.", + Required: true, + Validators: []validator.String{ + stringvalidator.LengthAtMost(250), + }, + }, + "client_id": schema.StringAttribute{ + MarkdownDescription: "Client ID used for authentication with CrowdStrike Falcon platform.", + Required: true, + }, + "client_secret": schema.StringAttribute{ + MarkdownDescription: "Client Secret used for authentication with CrowdStrike Falcon platform.", + Required: true, + Sensitive: true, + }, + "cloud": schema.StringAttribute{ + MarkdownDescription: "The Falcon Cloud to connect.", + Optional: true, + }, + "member_cid": schema.StringAttribute{ + MarkdownDescription: "CID selector for cases when the client ID and secret has access to multiple CIDs.", + Optional: true, + }, + }, + } +} + +func (r *integrationCrowdstrikeResource) 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.(*ExtendedGqlClient) + + 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 = client +} + +func (r *integrationCrowdstrikeResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { + var data integrationCrowdstrikeResourceModel + + // Read Terraform plan data into the model + resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...) + + if resp.Diagnostics.HasError() { + return + } + + space, err := r.client.ComputeSpace(data.SpaceID) + if err != nil { + resp.Diagnostics.AddError("Invalid Configuration", err.Error()) + return + } + ctx = tflog.SetField(ctx, "space_mrn", space.MRN()) + + // Do GraphQL request to API to create the resource. + tflog.Debug(ctx, "Creating integration") + integration, err := r.client.CreateIntegration(ctx, + space.MRN(), + data.Name.ValueString(), + mondoov1.ClientIntegrationTypeCrowdstrikeFalcon, + mondoov1.ClientIntegrationConfigurationInput{ + CrowdstrikeFalconConfigurationOptions: data.GetConfigurationOptions(), + }) + if err != nil { + resp.Diagnostics. + AddError("Client Error", + fmt.Sprintf("Unable to create %s integration. Got error: %s", mondoov1.IntegrationTypeCrowdstrikeFalcon, err), + ) + return + } + + // trigger integration to gather results quickly after the first setup + // NOTE: we ignore the error since the integration state does not depend on it + _, err = r.client.TriggerAction(ctx, string(integration.Mrn), mondoov1.ActionTypeRunImport) + if err != nil { + resp.Diagnostics. + AddWarning("Client Error", + fmt.Sprintf("Unable to trigger integration. Got error: %s", err), + ) + } + + // Save space mrn into the Terraform state. + data.Mrn = types.StringValue(string(integration.Mrn)) + data.Name = types.StringValue(string(integration.Name)) + data.SpaceID = types.StringValue(space.ID()) + + // Save data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *integrationCrowdstrikeResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { + var data integrationCrowdstrikeResourceModel + + // 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 *integrationCrowdstrikeResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { + var data integrationCrowdstrikeResourceModel + + // 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 update the resource. + opts := mondoov1.ClientIntegrationConfigurationInput{ + CrowdstrikeFalconConfigurationOptions: data.GetConfigurationOptions(), + } + + _, err := r.client.UpdateIntegration(ctx, + data.Mrn.ValueString(), + data.Name.ValueString(), + mondoov1.ClientIntegrationTypeCrowdstrikeFalcon, + opts, + ) + if err != nil { + resp.Diagnostics. + AddError("Client Error", + fmt.Sprintf("Unable to update %s integration. Got error: %s", mondoov1.IntegrationTypeCrowdstrikeFalcon, err), + ) + return + } + + // Save updated data into Terraform state + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) +} + +func (r *integrationCrowdstrikeResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { + var data integrationCrowdstrikeResourceModel + + // 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.DeleteIntegration(ctx, data.Mrn.ValueString()) + if err != nil { + resp.Diagnostics. + AddError("Client Error", + fmt.Sprintf("Unable to delete %s integration. Got error: %s", mondoov1.IntegrationTypeCrowdstrikeFalcon, err), + ) + return + } +} + +func (r *integrationCrowdstrikeResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) { + integration, ok := r.client.ImportIntegration(ctx, req, resp) + if !ok { + return + } + + model := integrationCrowdstrikeResourceModel{ + Mrn: types.StringValue(integration.Mrn), + Name: types.StringValue(integration.Name), + SpaceID: types.StringValue(integration.SpaceID()), + ClientId: types.StringValue(integration.ConfigurationOptions.CrowdstrikeFalconConfigurationOptionsInput.ClientId), + ClientSecret: types.StringPointerValue(nil), + Cloud: types.StringValue(integration.ConfigurationOptions.CrowdstrikeFalconConfigurationOptionsInput.Cloud), + MemberCID: types.StringValue(integration.ConfigurationOptions.CrowdstrikeFalconConfigurationOptionsInput.MemberCID), + } + + resp.State.Set(ctx, &model) +} diff --git a/internal/provider/integration_crowdstrike_resource_test.go b/internal/provider/integration_crowdstrike_resource_test.go new file mode 100644 index 0000000..f3b5836 --- /dev/null +++ b/internal/provider/integration_crowdstrike_resource_test.go @@ -0,0 +1,89 @@ +// Copyright (c) Mondoo, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package provider + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccCrowdstrikeIntegrationResource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + // Create and Read testing + { + Config: testAccCrowdstrikeIntegrationResourceConfig(accSpace.ID(), "one", "a-client-id", "a-client-secret", "us-2", "a-member-cid"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "name", "one"), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "space_id", accSpace.ID()), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "client_id", "a-client-id"), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "client_secret", "a-client-secret"), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "cloud", "us-2"), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "member_cid", "a-member-cid"), + ), + }, + { + Config: testAccCrowdstrikeIntegrationResourceWithSpaceInProviderConfig(accSpace.ID(), "two", "id", "secret"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "name", "two"), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "space_id", accSpace.ID()), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "client_id", "id"), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "client_secret", "secret"), + ), + }, + // Update and Read testing + { + Config: testAccCrowdstrikeIntegrationResourceConfig(accSpace.ID(), "three", "new-id", "new-secret", "us-1", "new-cid"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "name", "three"), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "space_id", accSpace.ID()), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "client_id", "new-id"), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "client_secret", "new-secret"), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "cloud", "us-1"), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "member_cid", "new-cid"), + ), + }, + { + Config: testAccCrowdstrikeIntegrationResourceWithSpaceInProviderConfig(accSpace.ID(), "four", "abc", "xyz"), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "name", "four"), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "space_id", accSpace.ID()), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "client_id", "abc"), + resource.TestCheckResourceAttr("mondoo_integration_crowdstrike.test", "client_secret", "xyz"), + ), + }, + // Delete testing automatically occurs in TestCase + }, + }) +} + +func testAccCrowdstrikeIntegrationResourceConfig(spaceID, intName, clientID, clientSecret, cloud, cid string) string { + return fmt.Sprintf(` +resource "mondoo_integration_crowdstrike" "test" { + space_id = %[1]q + name = %[2]q + client_id = %[3]q + client_secret = %[4]q + cloud = %[5]q + member_cid = %[6]q +} +`, spaceID, intName, clientID, clientSecret, cloud, cid) +} + +func testAccCrowdstrikeIntegrationResourceWithSpaceInProviderConfig(spaceID, intName, clientID, clientSecret string) string { + return fmt.Sprintf(` +provider "mondoo" { + space = %[1]q +} +resource "mondoo_integration_crowdstrike" "test" { + name = %[2]q + client_id = %[3]q + client_secret = %[4]q +} +`, spaceID, intName, clientID, clientSecret) +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index cedfe93..622f609 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -210,6 +210,7 @@ func (p *MondooProvider) Resources(ctx context.Context) []func() resource.Resour NewIntegrationGitlabResource, NewExceptionResource, NewIntegrationMsDefenderResource, + NewIntegrationCrowdstrikeResource, } }