From 13c0699355c0d8f717fb8c4f75cab7de4d91ec8c Mon Sep 17 00:00:00 2001 From: Matthias Theuermann Date: Wed, 18 Dec 2024 11:41:29 +0100 Subject: [PATCH] feat: added read functionality Signed-off-by: Matthias Theuermann --- internal/provider/gql.go | 28 +++--- .../integration_msdefender_resource.go | 88 +++++++++++++++++-- 2 files changed, 97 insertions(+), 19 deletions(-) diff --git a/internal/provider/gql.go b/internal/provider/gql.go index 9058d61..dcac8f4 100644 --- a/internal/provider/gql.go +++ b/internal/provider/gql.go @@ -651,7 +651,7 @@ type EmailRecipient struct { ReferenceURL string } -type MicrosoftDefenderConfigurationOptionsInput struct { +type MicrosoftDefenderConfigurationOptions struct { TenantId string ClientId string SubscriptionsAllowlist []string @@ -659,19 +659,19 @@ type MicrosoftDefenderConfigurationOptionsInput struct { } type ClientIntegrationConfigurationOptions struct { - AzureConfigurationOptions AzureConfigurationOptions `graphql:"... on AzureConfigurationOptions"` - HostConfigurationOptions HostConfigurationOptions `graphql:"... on HostConfigurationOptions"` - Ms365ConfigurationOptions Ms365ConfigurationOptions `graphql:"... on Ms365ConfigurationOptions"` - GcpConfigurationOptions GcpConfigurationOptions `graphql:"... on GcpConfigurationOptions"` - SlackConfigurationOptions SlackConfigurationOptions `graphql:"... on SlackConfigurationOptions"` - GithubConfigurationOptions GithubConfigurationOptions `graphql:"... on GithubConfigurationOptions"` - HostedAwsConfigurationOptions HostedAwsConfigurationOptions `graphql:"... on HostedAwsConfigurationOptions"` - ShodanConfigurationOptions ShodanConfigurationOptions `graphql:"... on ShodanConfigurationOptions"` - ZendeskConfigurationOptions ZendeskConfigurationOptions `graphql:"... on ZendeskConfigurationOptions"` - JiraConfigurationOptions JiraConfigurationOptions `graphql:"... on JiraConfigurationOptions"` - EmailConfigurationOptions EmailConfigurationOptions `graphql:"... on EmailConfigurationOptions"` - GitlabConfigurationOptions GitlabConfigurationOptions `graphql:"... on GitlabConfigurationOptions"` - MicrosoftDefenderConfigurationOptionsInput MicrosoftDefenderConfigurationOptionsInput `graphql:"... on MicrosoftDefenderConfigurationOptions"` + AzureConfigurationOptions AzureConfigurationOptions `graphql:"... on AzureConfigurationOptions"` + HostConfigurationOptions HostConfigurationOptions `graphql:"... on HostConfigurationOptions"` + Ms365ConfigurationOptions Ms365ConfigurationOptions `graphql:"... on Ms365ConfigurationOptions"` + GcpConfigurationOptions GcpConfigurationOptions `graphql:"... on GcpConfigurationOptions"` + SlackConfigurationOptions SlackConfigurationOptions `graphql:"... on SlackConfigurationOptions"` + GithubConfigurationOptions GithubConfigurationOptions `graphql:"... on GithubConfigurationOptions"` + HostedAwsConfigurationOptions HostedAwsConfigurationOptions `graphql:"... on HostedAwsConfigurationOptions"` + ShodanConfigurationOptions ShodanConfigurationOptions `graphql:"... on ShodanConfigurationOptions"` + ZendeskConfigurationOptions ZendeskConfigurationOptions `graphql:"... on ZendeskConfigurationOptions"` + JiraConfigurationOptions JiraConfigurationOptions `graphql:"... on JiraConfigurationOptions"` + EmailConfigurationOptions EmailConfigurationOptions `graphql:"... on EmailConfigurationOptions"` + GitlabConfigurationOptions GitlabConfigurationOptions `graphql:"... on GitlabConfigurationOptions"` + MicrosoftDefenderConfigurationOptions MicrosoftDefenderConfigurationOptions `graphql:"... on MicrosoftDefenderConfigurationOptions"` // Add other configuration options here } diff --git a/internal/provider/integration_msdefender_resource.go b/internal/provider/integration_msdefender_resource.go index 398f098..b6f5535 100644 --- a/internal/provider/integration_msdefender_resource.go +++ b/internal/provider/integration_msdefender_resource.go @@ -3,12 +3,15 @@ package provider import ( "context" "fmt" + "regexp" "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" + "github.com/hashicorp/terraform-plugin-framework/attr" "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/listdefault" "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" @@ -73,6 +76,48 @@ func (r *integrationMsDefenderResource) Metadata(ctx context.Context, req resour resp.TypeName = req.ProviderTypeName + "_integration_msdefender" } +// NotEqualValidator ensures two string attributes are not equal. +type NotEqualValidator struct { + OtherAttribute string +} + +// ValidateString performs the validation. +func (v NotEqualValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) { + // Retrieve the value of the other attribute + var otherAttr types.String + diags := req.Config.GetAttribute(ctx, path.Root(v.OtherAttribute), &otherAttr) + if diags.HasError() || otherAttr.IsNull() || otherAttr.IsUnknown() { + // Skip validation if the other attribute is not set + return + } + + // Check if the values of the two attributes are equal + if req.ConfigValue.ValueString() == otherAttr.ValueString() { + resp.Diagnostics.AddAttributeError( + req.Path, + "Attributes Cannot Be Equal", + fmt.Sprintf("The value of '%s' cannot be the same as '%s'.", req.Path.String(), v.OtherAttribute), + ) + } +} + +// Description returns a plain-text description of the validator's purpose. +func (v NotEqualValidator) Description(ctx context.Context) string { + return "Ensures that two attributes are not equal." +} + +// MarkdownDescription returns a markdown-formatted description of the validator's purpose. +func (v NotEqualValidator) MarkdownDescription(ctx context.Context) string { + return "Ensures that two attributes are not equal." +} + +// NewNotEqualValidator is a convenience function to create an instance of the validator. +func NewNotEqualValidator(otherAttribute string) validator.String { + return &NotEqualValidator{ + OtherAttribute: otherAttribute, + } +} + func (r *integrationMsDefenderResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { resp.Schema = schema.Schema{ MarkdownDescription: "Microsoft Defender for Cloud integration.", @@ -102,14 +147,24 @@ func (r *integrationMsDefenderResource) Schema(ctx context.Context, req resource "client_id": schema.StringAttribute{ MarkdownDescription: "Azure Client ID.", Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`), "Client ID must be a valid GUID."), + NewNotEqualValidator("tenant_id"), + }, }, "tenant_id": schema.StringAttribute{ MarkdownDescription: "Azure Tenant ID.", Required: true, + Validators: []validator.String{ + stringvalidator.RegexMatches(regexp.MustCompile(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`), "Tenant ID must be a valid GUID."), + NewNotEqualValidator("client_id"), + }, }, "subscription_allow_list": schema.ListAttribute{ MarkdownDescription: "List of Azure subscriptions to scan.", Optional: true, + Computed: true, + Default: listdefault.StaticValue(types.ListValueMust(types.StringType, []attr.Value{})), ElementType: types.StringType, Validators: []validator.List{ // Validate only this attribute or other_attr is configured. @@ -121,6 +176,8 @@ func (r *integrationMsDefenderResource) Schema(ctx context.Context, req resource "subscription_deny_list": schema.ListAttribute{ MarkdownDescription: "List of Azure subscriptions to exclude from scanning.", Optional: true, + Computed: true, + Default: listdefault.StaticValue(types.ListValueMust(types.StringType, []attr.Value{})), ElementType: types.StringType, Validators: []validator.List{ // Validate only this attribute or other_attr is configured. @@ -227,9 +284,30 @@ func (r *integrationMsDefenderResource) Read(ctx context.Context, req resource.R } // Read API call logic + integration, err := r.client.GetClientIntegration(ctx, data.Mrn.ValueString()) + if err != nil { + resp.State.RemoveResource(ctx) + return + } + + allowList := ConvertListValue(integration.ConfigurationOptions.MicrosoftDefenderConfigurationOptions.SubscriptionsAllowlist) + denyList := ConvertListValue(integration.ConfigurationOptions.MicrosoftDefenderConfigurationOptions.SubscriptionsDenylist) + + model := integrationMsDefenderResourceModel{ + Mrn: types.StringValue(integration.Mrn), + Name: types.StringValue(integration.Name), + SpaceID: types.StringValue(integration.SpaceID()), + ClientId: types.StringValue(integration.ConfigurationOptions.MicrosoftDefenderConfigurationOptions.ClientId), + TenantId: types.StringValue(integration.ConfigurationOptions.MicrosoftDefenderConfigurationOptions.TenantId), + SubscriptionAllowList: allowList, + SubscriptionDenyList: denyList, + Credential: integrationMsDefenderCredentialModel{ + PEMFile: types.StringValue(data.Credential.PEMFile.ValueString()), + }, + } // Save updated data into Terraform state - resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + resp.Diagnostics.Append(resp.State.Set(ctx, &model)...) } func (r *integrationMsDefenderResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { @@ -292,15 +370,15 @@ func (r *integrationMsDefenderResource) ImportState(ctx context.Context, req res return } - allowList := ConvertListValue(integration.ConfigurationOptions.MicrosoftDefenderConfigurationOptionsInput.SubscriptionsAllowlist) - denyList := ConvertListValue(integration.ConfigurationOptions.MicrosoftDefenderConfigurationOptionsInput.SubscriptionsDenylist) + allowList := ConvertListValue(integration.ConfigurationOptions.MicrosoftDefenderConfigurationOptions.SubscriptionsAllowlist) + denyList := ConvertListValue(integration.ConfigurationOptions.MicrosoftDefenderConfigurationOptions.SubscriptionsDenylist) model := integrationMsDefenderResourceModel{ Mrn: types.StringValue(integration.Mrn), Name: types.StringValue(integration.Name), SpaceID: types.StringValue(integration.SpaceID()), - ClientId: types.StringValue(integration.ConfigurationOptions.MicrosoftDefenderConfigurationOptionsInput.ClientId), - TenantId: types.StringValue(integration.ConfigurationOptions.MicrosoftDefenderConfigurationOptionsInput.TenantId), + ClientId: types.StringValue(integration.ConfigurationOptions.MicrosoftDefenderConfigurationOptions.ClientId), + TenantId: types.StringValue(integration.ConfigurationOptions.MicrosoftDefenderConfigurationOptions.TenantId), SubscriptionAllowList: allowList, SubscriptionDenyList: denyList, Credential: integrationMsDefenderCredentialModel{