From d15461c940d48645a69fd2100c085fc099aca21a Mon Sep 17 00:00:00 2001 From: Anmol Maheshwari Date: Tue, 10 Sep 2024 20:20:08 +0530 Subject: [PATCH] feat:[CCM-16472]: Terraform support for Governance-Enforcements (#1052) --- .changelog/1052.txt | 3 + .../governance_rule_enforcement.md | 41 +++ docs/resources/governance_rule_enforcement.md | 47 ++++ .../data-source.tf | 3 + .../import.sh | 2 + .../resources.tf | 14 + go.mod | 2 +- go.sum | 4 +- helpers/schema.go | 10 + internal/provider/provider.go | 3 + .../governance/enforcement/enforcement.go | 266 ++++++++++++++++++ .../enforcement/enforcement_data_source.go | 109 +++++++ .../enforcement_data_source_test.go | 61 ++++ .../enforcement/enforcement_test.go | 115 ++++++++ 14 files changed, 677 insertions(+), 3 deletions(-) create mode 100644 .changelog/1052.txt create mode 100644 docs/data-sources/governance_rule_enforcement.md create mode 100644 docs/resources/governance_rule_enforcement.md create mode 100644 examples/data-sources/harness_governance_rule_enforcement/data-source.tf create mode 100644 examples/resources/harness_governance_rule_enforcement/import.sh create mode 100644 examples/resources/harness_governance_rule_enforcement/resources.tf create mode 100644 internal/service/platform/governance/enforcement/enforcement.go create mode 100644 internal/service/platform/governance/enforcement/enforcement_data_source.go create mode 100644 internal/service/platform/governance/enforcement/enforcement_data_source_test.go create mode 100644 internal/service/platform/governance/enforcement/enforcement_test.go diff --git a/.changelog/1052.txt b/.changelog/1052.txt new file mode 100644 index 000000000..5f8d0b7d0 --- /dev/null +++ b/.changelog/1052.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +harness_governance_rule_enforcement - Added Governance Enforcement resource in Harness terraform provider +``` \ No newline at end of file diff --git a/docs/data-sources/governance_rule_enforcement.md b/docs/data-sources/governance_rule_enforcement.md new file mode 100644 index 000000000..752cebe18 --- /dev/null +++ b/docs/data-sources/governance_rule_enforcement.md @@ -0,0 +1,41 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "harness_governance_rule_enforcement Data Source - terraform-provider-harness" +subcategory: "Next Gen" +description: |- + Datasource for looking up a rule enforcement. +--- + +# harness_governance_rule_enforcement (Data Source) + +Datasource for looking up a rule enforcement. + +## Example Usage + +```terraform +data "harness_governance_rule_enforcement" "example" { + enforcement_id = "enforcement_id" +} +``` + + +## Schema + +### Required + +- `enforcement_id` (String) Id of rule enforcement. + +### Read-Only + +- `cloud_provider` (String) The cloud provider for the rule enforcement. +- `description` (String) Description for rule enforcement. +- `execution_schedule` (String) Execution schedule in cron format. +- `execution_timezone` (String) Timezone for the execution schedule. +- `id` (String) The ID of this resource. +- `is_dry_run` (Boolean) Indicates if the rule enforcement is a dry run. +- `is_enabled` (Boolean) Indicates if the rule enforcement is enabled. +- `name` (String) Name of the rule enforcement. +- `rule_ids` (Map of String) List of rule IDs. +- `rule_set_ids` (Map of String) List of rule set IDs. +- `target_accounts` (List of String) List of target accounts. +- `target_regions` (List of String) List of target regions. diff --git a/docs/resources/governance_rule_enforcement.md b/docs/resources/governance_rule_enforcement.md new file mode 100644 index 000000000..0704f44ed --- /dev/null +++ b/docs/resources/governance_rule_enforcement.md @@ -0,0 +1,47 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "harness_governance_rule_enforcement Resource - terraform-provider-harness" +subcategory: "Next Gen" +description: |- + Resource for creating, updating, and managing rule enforcement. +--- + +# harness_governance_rule_enforcement (Resource) + +Resource for creating, updating, and managing rule enforcement. + + + + +## Schema + +### Required + +- `cloud_provider` (String) The cloud provider for the rule enforcement. It should be either AWS, AZURE or GCP. +- `execution_schedule` (String) Execution schedule in cron format. +- `execution_timezone` (String) Timezone for the execution schedule. +- `name` (String) Name of the rule enforcement. +- `target_accounts` (List of String) List of target account which can be either AWS Account Ids or Azure Subscription Ids or Gcp Project Ids. + +### Optional + +- `description` (String) Description for rule enforcement. +- `is_dry_run` (Boolean) Indicates if the rule enforcement is a dry run. This by default is set to false. +- `is_enabled` (Boolean) Indicates if the rule enforcement is enabled. This by default is set to true. +- `rule_ids` (List of String) List of rule IDs. Either rule_ids or rule_set_ids should be provided. +- `rule_set_ids` (List of String) List of rule set IDs. Either rule_ids or rule_set_ids should be provided. +- `target_regions` (List of String) List of target regions. For GCP it should be left empty but is required in case of AWS or Azure. + +### Read-Only + +- `enforcement_id` (String) Id of the rule enforcement. +- `id` (String) The ID of this resource. + +## Import + +Import is supported using the following syntax: + +```shell +# Import governance enforcement +terraform import harness_governance_rule_enforcement.example +``` diff --git a/examples/data-sources/harness_governance_rule_enforcement/data-source.tf b/examples/data-sources/harness_governance_rule_enforcement/data-source.tf new file mode 100644 index 000000000..1e45b1a9f --- /dev/null +++ b/examples/data-sources/harness_governance_rule_enforcement/data-source.tf @@ -0,0 +1,3 @@ +data "harness_governance_rule_enforcement" "example" { + enforcement_id = "enforcement_id" +} diff --git a/examples/resources/harness_governance_rule_enforcement/import.sh b/examples/resources/harness_governance_rule_enforcement/import.sh new file mode 100644 index 000000000..01e9f28a4 --- /dev/null +++ b/examples/resources/harness_governance_rule_enforcement/import.sh @@ -0,0 +1,2 @@ +# Import governance enforcement +terraform import harness_governance_rule_enforcement.example diff --git a/examples/resources/harness_governance_rule_enforcement/resources.tf b/examples/resources/harness_governance_rule_enforcement/resources.tf new file mode 100644 index 000000000..53096b1ba --- /dev/null +++ b/examples/resources/harness_governance_rule_enforcement/resources.tf @@ -0,0 +1,14 @@ +resource "harness_governance_rule_enforcement" "example" { + identifier = "identifier" + name = "name" + cloud_provider = "AWS/AZURE/GCP" + rule_ids = ["rule_id1"] + rule_set_ids = ["rule_set_id1"] + execution_schedule = "0 0 * * * *" + execution_timezone = "UTC" + is_enabled = true + target_accounts = ["awsAccountId/azureSubscriptionId/gcpProjectId"] + target_regions = ["us-east-1/eastus"] + is_dry_run = false + description = "description" +} \ No newline at end of file diff --git a/go.mod b/go.mod index 13968fd17..743d7db06 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/antihax/optional v1.0.0 github.com/aws/aws-sdk-go v1.46.4 github.com/docker/docker v24.0.5+incompatible - github.com/harness/harness-go-sdk v0.4.3 + github.com/harness/harness-go-sdk v0.4.4 github.com/harness/harness-openapi-go-client v0.0.21 github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 diff --git a/go.sum b/go.sum index 062d5c688..d1c4ccdb7 100644 --- a/go.sum +++ b/go.sum @@ -54,8 +54,8 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/harness/harness-go-sdk v0.4.3 h1:lAyQT7paCayYIOPrZPmCHOtG2uWCgUHugcaN9MJtYnE= -github.com/harness/harness-go-sdk v0.4.3/go.mod h1:a/1HYTgVEuNEoh3Z3IsOHZdlUNxl94KcX57ZSNVGll0= +github.com/harness/harness-go-sdk v0.4.4 h1:ob+zPJTcMjuq+s8u5O1/SGOR+ZjqRfwGloAdKvKgd/E= +github.com/harness/harness-go-sdk v0.4.4/go.mod h1:a/1HYTgVEuNEoh3Z3IsOHZdlUNxl94KcX57ZSNVGll0= github.com/harness/harness-openapi-go-client v0.0.21 h1:VtJnpQKZvCAlaCmUPbNR69OT3c5WRdhNN5TOgUwtwZ4= github.com/harness/harness-openapi-go-client v0.0.21/go.mod h1:u0vqYb994BJGotmEwJevF4L3BNAdU9i8ui2d22gmLPA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= diff --git a/helpers/schema.go b/helpers/schema.go index c79d69c73..293ea06ad 100644 --- a/helpers/schema.go +++ b/helpers/schema.go @@ -610,3 +610,13 @@ var GitWebhookResourceImporter = &schema.ResourceImporter{ return []*schema.ResourceData{d}, nil }, } + +var AccountLevelResourceImporter = &schema.ResourceImporter{ + State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + if d.Id() != "" { + return []*schema.ResourceData{d}, nil + } + + return nil, fmt.Errorf("invalid identifier: %s", d.Id()) + }, +} diff --git a/internal/provider/provider.go b/internal/provider/provider.go index ddcef6054..19971e779 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -8,6 +8,7 @@ import ( dbinstance "github.com/harness/terraform-provider-harness/internal/service/platform/db_instance" dbschema "github.com/harness/terraform-provider-harness/internal/service/platform/db_schema" "github.com/harness/terraform-provider-harness/internal/service/platform/gitx/webhook" + governance_enforcement "github.com/harness/terraform-provider-harness/internal/service/platform/governance/enforcement" "github.com/harness/terraform-provider-harness/internal/service/platform/notification_rule" "github.com/harness/terraform-provider-harness/internal/service/platform/feature_flag" @@ -280,6 +281,7 @@ func Provider(version string) func() *schema.Provider { "harness_platform_repo_webhook": repo_webhook.DataSourceRepoWebhook(), "harness_platform_gitops_app_project": gitops_project.DataSourceGitOpsProject(), "harness_platform_gitx_webhook": webhook.DataSourceWebhook(), + "harness_governance_rule_enforcement": governance_enforcement.DatasourceRuleEnforcement(), }, ResourcesMap: map[string]*schema.Resource{ "harness_platform_template": pl_template.ResourceTemplate(), @@ -422,6 +424,7 @@ func Provider(version string) func() *schema.Provider { "harness_platform_repo_webhook": repo_webhook.ResourceRepoWebhook(), "harness_platform_connector_custom_secret_manager": connector.ResourceConnectorCSM(), "harness_platform_gitx_webhook": webhook.ResourceWebhook(), + "harness_governance_rule_enforcement": governance_enforcement.ResourceRuleEnforcement(), }, } diff --git a/internal/service/platform/governance/enforcement/enforcement.go b/internal/service/platform/governance/enforcement/enforcement.go new file mode 100644 index 000000000..71c7ce97c --- /dev/null +++ b/internal/service/platform/governance/enforcement/enforcement.go @@ -0,0 +1,266 @@ +package governance_enforcement + +import ( + "context" + "fmt" + "net/http" + + "github.com/antihax/optional" + "github.com/harness/harness-go-sdk/harness/nextgen" + "github.com/harness/terraform-provider-harness/helpers" + "github.com/harness/terraform-provider-harness/internal" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +func ResourceRuleEnforcement() *schema.Resource { + resource := &schema.Resource{ + Description: "Resource for creating, updating, and managing rule enforcement.", + ReadContext: resourceRuleEnforcementRead, + CreateContext: resourceRuleEnforcementCreateOrUpdate, + UpdateContext: resourceRuleEnforcementCreateOrUpdate, + DeleteContext: resourceRuleEnforcementDelete, + Importer: helpers.AccountLevelResourceImporter, + + Schema: map[string]*schema.Schema{ + "name": { + Description: "Name of the rule enforcement.", + Type: schema.TypeString, + Required: true, + }, + "description": { + Description: "Description for rule enforcement.", + Type: schema.TypeString, + Optional: true, + }, + "cloud_provider": { + Description: "The cloud provider for the rule enforcement. It should be either AWS, AZURE or GCP.", + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{"AWS", "GCP", "AZURE"}, false), + }, + "rule_ids": { + Description: "List of rule IDs. Either rule_ids or rule_set_ids should be provided.", + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "rule_set_ids": { + Description: "List of rule set IDs. Either rule_ids or rule_set_ids should be provided.", + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "execution_schedule": { + Description: "Execution schedule in cron format.", + Type: schema.TypeString, + Required: true, + }, + "execution_timezone": { + Description: "Timezone for the execution schedule.", + Type: schema.TypeString, + Required: true, + }, + "is_enabled": { + Description: "Indicates if the rule enforcement is enabled. This by default is set to true.", + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "target_accounts": { + Description: "List of target account which can be either AWS Account Ids or Azure Subscription Ids or Gcp Project Ids.", + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "target_regions": { + Description: "List of target regions. For GCP it should be left empty but is required in case of AWS or Azure.", + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "is_dry_run": { + Description: "Indicates if the rule enforcement is a dry run. This by default is set to false.", + Type: schema.TypeBool, + Optional: true, + Default: false, + }, + "enforcement_id": { + Description: "Id of the rule enforcement.", + Type: schema.TypeString, + Computed: true, + }, + }, + + CustomizeDiff: func(ctx context.Context, d *schema.ResourceDiff, v interface{}) error { + // Ensure at least one of rule_ids or rule_set_ids is provided + ruleIDs, ruleSetIDs := d.Get("rule_ids").([]interface{}), d.Get("rule_set_ids").([]interface{}) + if len(ruleIDs) == 0 && len(ruleSetIDs) == 0 { + return fmt.Errorf("either 'rule_ids' or 'rule_set_ids' must be provided") + } + + // Conditionally require target_regions when cloud_provider is AWS or AZURE + cloudProvider := d.Get("cloud_provider").(string) + targetRegions := d.Get("target_regions").([]interface{}) + if (cloudProvider == "AWS" || cloudProvider == "AZURE") && len(targetRegions) == 0 { + return fmt.Errorf("'target_regions' is required when cloud_provider is 'AWS' or 'AZURE'") + } + + return nil + }, + } + + return resource +} + +func resourceRuleEnforcementRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + c, ctx := meta.(*internal.Session).GetPlatformClientWithContext(ctx) + + id := d.Id() + resp, httpResp, err := c.RuleEnforcementApi.EnforcementDetails(ctx, c.AccountId, readRuleEnforcementRequest(id)) + + if err != nil { + return helpers.HandleReadApiError(err, d, httpResp) + } + + if resp.Data != nil { + err := readRuleEnforcementResponse(d, resp.Data) + if err != nil { + return helpers.HandleReadApiError(err, d, httpResp) + } + } + + return nil +} + +func resourceRuleEnforcementCreateOrUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + c, ctx := meta.(*internal.Session).GetPlatformClientWithContext(ctx) + + var err error + var resp nextgen.ResponseDtoRuleEnforcement + var httpResp *http.Response + + id := d.Id() + + if id == "" { + resp, httpResp, err = c.RuleEnforcementApi.AddRuleEnforcement(ctx, buildRuleEnforcement(d, false), c.AccountId) + } else { + resp, httpResp, err = c.RuleEnforcementApi.UpdateEnforcement(ctx, buildRuleEnforcement(d, true), c.AccountId) + } + + if err != nil { + return helpers.HandleApiError(err, d, httpResp) + } + + if resp.Data != nil { + createOrUpdateRuleEnforcementResponse(d, resp.Data) + } + + return nil +} + +func resourceRuleEnforcementDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + c, ctx := meta.(*internal.Session).GetPlatformClientWithContext(ctx) + + id := d.Id() + + _, httpResp, err := c.RuleEnforcementApi.DeleteRuleEnforcement(ctx, c.AccountId, id) + + if err != nil { + return helpers.HandleApiError(err, d, httpResp) + } + + return nil +} + +func buildRuleEnforcement(d *schema.ResourceData, update bool) nextgen.CreateRuleEnforcementDto { + ruleEnforcement := &nextgen.RuleEnforcement{ + Name: d.Get("name").(string), + CloudProvider: d.Get("cloud_provider").(string), + RuleIds: expandStringList(d.Get("rule_ids").([]interface{})), + RuleSetIDs: expandStringList(d.Get("rule_set_ids").([]interface{})), + ExecutionSchedule: d.Get("execution_schedule").(string), + ExecutionTimezone: d.Get("execution_timezone").(string), + IsEnabled: d.Get("is_enabled").(bool), + TargetAccounts: expandStringList(d.Get("target_accounts").([]interface{})), + TargetRegions: expandStringList(d.Get("target_regions").([]interface{})), + IsDryRun: d.Get("is_dry_run").(bool), + Description: d.Get("description").(string), + } + + if update { + ruleEnforcement.Uuid = d.Id() + } + + return nextgen.CreateRuleEnforcementDto{ + RuleEnforcement: ruleEnforcement, + } +} + +func createOrUpdateRuleEnforcementResponse(d *schema.ResourceData, ruleEnforcement *nextgen.RuleEnforcement) error { + d.SetId(ruleEnforcement.Uuid) + d.Set("name", ruleEnforcement.Name) + d.Set("cloud_provider", ruleEnforcement.CloudProvider) + d.Set("rule_ids", ruleEnforcement.RuleIds) + d.Set("rule_set_ids", ruleEnforcement.RuleSetIDs) + d.Set("execution_schedule", ruleEnforcement.ExecutionSchedule) + d.Set("execution_timezone", ruleEnforcement.ExecutionTimezone) + d.Set("is_enabled", ruleEnforcement.IsEnabled) + d.Set("target_accounts", ruleEnforcement.TargetAccounts) + d.Set("target_regions", ruleEnforcement.TargetRegions) + d.Set("is_dry_run", ruleEnforcement.IsDryRun) + d.Set("enforcement_id", ruleEnforcement.Uuid) + d.Set("description", ruleEnforcement.Description) + + return nil +} + +func readRuleEnforcementResponse(d *schema.ResourceData, ruleEnforcement *nextgen.EnforcementDetails) error { + d.Set("name", ruleEnforcement.EnforcementName) + d.Set("cloud_provider", ruleEnforcement.CloudProvider) + d.Set("rule_ids", getMapKeys(ruleEnforcement.RuleIds)) + d.Set("rule_set_ids", getMapKeys(ruleEnforcement.RuleSetIds)) + d.Set("execution_schedule", ruleEnforcement.Schedule) + d.Set("execution_timezone", ruleEnforcement.ExecutionTimezone) + d.Set("is_enabled", ruleEnforcement.IsEnabled) + d.Set("target_accounts", ruleEnforcement.Accounts) + d.Set("target_regions", ruleEnforcement.Regions) + d.Set("is_dry_run", ruleEnforcement.IsDryRun) + d.Set("description", ruleEnforcement.Description) + + return nil +} + +func readRuleEnforcementRequest(id string) *nextgen.RuleEnforcementApiEnforcementDetailsOpts { + return &nextgen.RuleEnforcementApiEnforcementDetailsOpts{ + EnforcementId: optional.NewString(id), + } +} + +func expandStringList(givenStringListInterface []interface{}) []string { + var expandedStringList []string + + if len(givenStringListInterface) > 0 { + for _, id := range givenStringListInterface { + expandedStringList = append(expandedStringList, id.(string)) + } + } + return expandedStringList +} + +func getMapKeys(m map[string]string) []string { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + return keys +} diff --git a/internal/service/platform/governance/enforcement/enforcement_data_source.go b/internal/service/platform/governance/enforcement/enforcement_data_source.go new file mode 100644 index 000000000..3a64274fa --- /dev/null +++ b/internal/service/platform/governance/enforcement/enforcement_data_source.go @@ -0,0 +1,109 @@ +package governance_enforcement + +import ( + "context" + + "github.com/harness/terraform-provider-harness/helpers" + "github.com/harness/terraform-provider-harness/internal" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func DatasourceRuleEnforcement() *schema.Resource { + return &schema.Resource{ + Description: "Datasource for looking up a rule enforcement.", + + ReadContext: resourceRuleEnforcementReadDataSource, + + Schema: map[string]*schema.Schema{ + "enforcement_id": { + Description: "Id of rule enforcement.", + Type: schema.TypeString, + Required: true, + }, + "name": { + Description: "Name of the rule enforcement.", + Type: schema.TypeString, + Computed: true, + }, + "cloud_provider": { + Description: "The cloud provider for the rule enforcement.", + Type: schema.TypeString, + Computed: true, + }, + "rule_ids": { + Description: "List of rule IDs.", + Type: schema.TypeMap, + Computed: true, + }, + "rule_set_ids": { + Description: "List of rule set IDs.", + Type: schema.TypeMap, + Computed: true, + }, + "execution_schedule": { + Description: "Execution schedule in cron format.", + Type: schema.TypeString, + Computed: true, + }, + "execution_timezone": { + Description: "Timezone for the execution schedule.", + Type: schema.TypeString, + Computed: true, + }, + "is_enabled": { + Description: "Indicates if the rule enforcement is enabled.", + Type: schema.TypeBool, + Computed: true, + }, + "target_accounts": { + Description: "List of target accounts.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "target_regions": { + Description: "List of target regions.", + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "is_dry_run": { + Description: "Indicates if the rule enforcement is a dry run.", + Type: schema.TypeBool, + Computed: true, + }, + "description": { + Description: "Description for rule enforcement.", + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceRuleEnforcementReadDataSource(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + c, ctx := meta.(*internal.Session).GetPlatformClientWithContext(ctx) + + id := d.Get("enforcement_id").(string) + resp, httpResp, err := c.RuleEnforcementApi.EnforcementDetails(ctx, c.AccountId, readRuleEnforcementRequest(id)) + + if err != nil { + return helpers.HandleReadApiError(err, d, httpResp) + } + + if resp.Data != nil { + err := readRuleEnforcementResponse(d, resp.Data) + if err != nil { + return helpers.HandleReadApiError(err, d, httpResp) + } + } + + d.SetId(id) + + return nil +} diff --git a/internal/service/platform/governance/enforcement/enforcement_data_source_test.go b/internal/service/platform/governance/enforcement/enforcement_data_source_test.go new file mode 100644 index 000000000..56b35c067 --- /dev/null +++ b/internal/service/platform/governance/enforcement/enforcement_data_source_test.go @@ -0,0 +1,61 @@ +package governance_enforcement_test + +import ( + "fmt" + "os" + "testing" + + "github.com/harness/harness-go-sdk/harness/utils" + "github.com/harness/terraform-provider-harness/internal/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccDataSourceRuleEnforcement(t *testing.T) { + var ( + name = fmt.Sprintf("%s_%s", t.Name(), utils.RandStringBytes(4)) + resourceName = "data.harness_governance_rule_enforcement.test" + awsAccount = os.Getenv("AWS_ACCOUNT_ID") + ) + + resource.UnitTest(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProviderFactories: acctest.ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceRuleEnforcement(name, awsAccount), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "cloud_provider", "AWS"), + resource.TestCheckResourceAttr(resourceName, "execution_schedule", "0 0 * * * *"), + resource.TestCheckResourceAttr(resourceName, "execution_timezone", "Asia/Calcutta"), + resource.TestCheckResourceAttr(resourceName, "is_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "target_accounts.#", "1"), + resource.TestCheckResourceAttr(resourceName, "target_regions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "description", "Dummy"), + ), + }, + }, + }) +} + +func testAccDataSourceRuleEnforcement(name, awsAccount string) string { + return fmt.Sprintf(` + resource "harness_governance_rule_enforcement" "test" { + name = "%[1]s" + cloud_provider = "AWS" + rule_ids = ["YW-qYiJRSaO3Fqei2EqqRQ"] + rule_set_ids = [] + execution_schedule = "0 0 * * * *" + execution_timezone = "Asia/Calcutta" + is_enabled = true + target_accounts = ["%[2]s"] + target_regions = ["us-east-1"] + is_dry_run = true + description = "Dummy" + } + + data "harness_governance_rule_enforcement" "test" { + enforcement_id = harness_governance_rule_enforcement.test.enforcement_id + } + `, name, awsAccount) +} diff --git a/internal/service/platform/governance/enforcement/enforcement_test.go b/internal/service/platform/governance/enforcement/enforcement_test.go new file mode 100644 index 000000000..c55ce919c --- /dev/null +++ b/internal/service/platform/governance/enforcement/enforcement_test.go @@ -0,0 +1,115 @@ +package governance_enforcement_test + +import ( + "fmt" + "os" + "testing" + + "github.com/antihax/optional" + "github.com/harness/harness-go-sdk/harness/nextgen" + "github.com/harness/harness-go-sdk/harness/utils" + "github.com/harness/terraform-provider-harness/internal/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +) + +func TestAccResourceRuleEnforcement(t *testing.T) { + name := fmt.Sprintf("%s_%s", t.Name(), utils.RandStringBytes(5)) + updatedName := fmt.Sprintf("%s_updated", name) + resourceName := "harness_governance_rule_enforcement.test" + awsAccountId := os.Getenv("AWS_ACCOUNT_ID") + + resource.UnitTest(t, resource.TestCase{ + PreCheck: func() { acctest.TestAccPreCheck(t) }, + ProviderFactories: acctest.ProviderFactories, + CheckDestroy: testAccRuleEnforcementDestroy(resourceName), + Steps: []resource.TestStep{ + { + Config: testAccResourceRuleEnforcement(name, awsAccountId), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + resource.TestCheckResourceAttr(resourceName, "cloud_provider", "AWS"), + resource.TestCheckResourceAttr(resourceName, "execution_schedule", "0 0 * * * *"), + resource.TestCheckResourceAttr(resourceName, "execution_timezone", "UTC"), + resource.TestCheckResourceAttr(resourceName, "is_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "target_accounts.#", "1"), + resource.TestCheckResourceAttr(resourceName, "target_regions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "is_dry_run", "true"), + resource.TestCheckResourceAttr(resourceName, "description", "Dummy"), + ), + }, + { + Config: testAccResourceRuleEnforcement(updatedName, awsAccountId), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", updatedName), + resource.TestCheckResourceAttr(resourceName, "cloud_provider", "AWS"), + resource.TestCheckResourceAttr(resourceName, "execution_schedule", "0 0 * * * *"), + resource.TestCheckResourceAttr(resourceName, "execution_timezone", "UTC"), + resource.TestCheckResourceAttr(resourceName, "is_enabled", "true"), + resource.TestCheckResourceAttr(resourceName, "target_accounts.#", "1"), + resource.TestCheckResourceAttr(resourceName, "target_regions.#", "1"), + resource.TestCheckResourceAttr(resourceName, "is_dry_run", "true"), + resource.TestCheckResourceAttr(resourceName, "description", "Dummy"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: false, + ImportStateVerifyIgnore: []string{"identifier"}, + ImportStateIdFunc: acctest.AccountLevelResourceImportStateIdFunc(resourceName), + }, + }, + }) +} + +func testAccResourceRuleEnforcement(name, awsAccountId string) string { + return fmt.Sprintf(` + resource "harness_governance_rule_enforcement" "test" { + name = "%[1]s" + cloud_provider = "AWS" + rule_ids = ["YW-qYiJRSaO3Fqei2EqqRQ"] + rule_set_ids = [] + execution_schedule = "0 0 * * * *" + execution_timezone = "UTC" + is_enabled = true + target_accounts = ["%[2]s"] + target_regions = ["us-east-1"] + is_dry_run = true + description = "Dummy" + } + `, name, awsAccountId) +} + +func testAccRuleEnforcementDestroy(resourceName string) resource.TestCheckFunc { + return func(state *terraform.State) error { + ruleEnforcement, _ := testGetRuleEnforcement(resourceName, state) + if ruleEnforcement != nil { + return fmt.Errorf("Found rule enforcement: %s", ruleEnforcement.EnforcementName) + } + return nil + } +} + +func testGetRuleEnforcement(resourceName string, state *terraform.State) (*nextgen.EnforcementDetails, error) { + r := acctest.TestAccGetResource(resourceName, state) + c, ctx := acctest.TestAccGetPlatformClientWithContext() + enforcementId := r.Primary.ID + + resp, _, err := c.RuleEnforcementApi.EnforcementDetails(ctx, c.AccountId, readRuleEnforcementRequest(enforcementId)) + + if err != nil { + return nil, err + } + + if resp.Data != nil { + return resp.Data, nil + } + return nil, nil +} + +func readRuleEnforcementRequest(id string) *nextgen.RuleEnforcementApiEnforcementDetailsOpts { + return &nextgen.RuleEnforcementApiEnforcementDetailsOpts{ + EnforcementId: optional.NewString(id), + } +}