From 79e5518bb0499fe6df0e895f68428acf6d801f45 Mon Sep 17 00:00:00 2001 From: bbhupesh Date: Fri, 3 Jan 2025 16:04:46 +0530 Subject: [PATCH] feat:Service | added local policies (#279) * SLK-88212:Updated service changes with local policies schema and example --- aquasec/data_service.go | 124 ++++++- aquasec/data_service_test.go | 66 ++-- aquasec/resource_service.go | 134 +++++++- aquasec/resource_service_test.go | 317 +++++++++--------- client/service.go | 14 + docs/data-sources/service.md | 44 ++- docs/resources/service.md | 97 +++++- .../resources/aquasec_service/resource.tf | 58 +++- 8 files changed, 654 insertions(+), 200 deletions(-) diff --git a/aquasec/data_service.go b/aquasec/data_service.go index 50881cb7..b9ca2fe8 100644 --- a/aquasec/data_service.go +++ b/aquasec/data_service.go @@ -95,7 +95,84 @@ func dataSourceService() *schema.Resource { Type: schema.TypeString, }, Description: "The service's policies; an array of container firewall policy names.", - Computed: true, + Required: true, + }, + "local_policies": { + Type: schema.TypeList, + Description: "A list of local policies for the service, including inbound and outbound network rules.", + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Description: "The name of the local policy.", + Required: true, + }, + "type": { + Type: schema.TypeString, + Description: "The type of the local policy, e.g., access.control.", + Required: true, + }, + "description": { + Type: schema.TypeString, + Description: "A description of the local policy.", + Optional: true, + }, + "inbound_networks": { + Type: schema.TypeList, + Description: "Inbound network rules for the local policy.", + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "port_range": { + Type: schema.TypeString, + Description: "The port range for the inbound network rule.", + Required: true, + }, + "resource_type": { + Type: schema.TypeString, + Description: "The resource type for the inbound network rule (e.g., anywhere).", + Required: true, + }, + "allow": { + Type: schema.TypeBool, + Description: "Whether the inbound network rule is allowed.", + Required: true, + }, + }, + }, + }, + "outbound_networks": { + Type: schema.TypeList, + Description: "Outbound network rules for the local policy.", + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "port_range": { + Type: schema.TypeString, + Description: "The port range for the outbound network rule.", + Required: true, + }, + "resource_type": { + Type: schema.TypeString, + Description: "The resource type for the outbound network rule (e.g., anywhere).", + Required: true, + }, + "allow": { + Type: schema.TypeBool, + Description: "Whether the outbound network rule is allowed.", + Required: true, + }, + }, + }, + }, + "block_metadata_service": { + Type: schema.TypeBool, + Description: "Whether to block access to the metadata service.", + Optional: true, + }, + }, + }, }, "evaluated": { Type: schema.TypeBool, @@ -196,7 +273,9 @@ func dataServiceRead(ctx context.Context, d *schema.ResourceData, m interface{}) d.Set("unregistered_count", service.UnregisteredCount) d.Set("is_registered", service.IsRegistered) d.Set("application_scopes", service.ApplicationScopes) - + if err := d.Set("local_policies", flattenLocalPolicies(service.LocalPolicies)); err != nil { + return diag.FromErr(err) + } d.SetId(name) } else { return diag.FromErr(err) @@ -204,3 +283,44 @@ func dataServiceRead(ctx context.Context, d *schema.ResourceData, m interface{}) return nil } +func flattenLocalPolicies(policies []client.LocalPolicy) []map[string]interface{} { + if policies == nil { + return []map[string]interface{}{} + } + + var result []map[string]interface{} + for _, policy := range policies { + p := map[string]interface{}{ + "name": policy.Name, + "type": policy.Type, + "description": policy.Description, + "block_metadata_service": policy.BlockMetadataService, + } + + // Flatten inbound_networks + var inboundNetworks []map[string]interface{} + for _, inbound := range policy.InboundNetworks { + inboundNetworks = append(inboundNetworks, map[string]interface{}{ + "port_range": inbound.PortRange, + "resource_type": inbound.ResourceType, + "allow": inbound.Allow, + }) + } + p["inbound_networks"] = inboundNetworks + + // Flatten outbound_networks + var outboundNetworks []map[string]interface{} + for _, outbound := range policy.OutboundNetworks { + outboundNetworks = append(outboundNetworks, map[string]interface{}{ + "port_range": outbound.PortRange, + "resource_type": outbound.ResourceType, + "allow": outbound.Allow, + }) + } + p["outbound_networks"] = outboundNetworks + + result = append(result, p) + } + + return result +} diff --git a/aquasec/data_service_test.go b/aquasec/data_service_test.go index d85ca48c..c6d40919 100644 --- a/aquasec/data_service_test.go +++ b/aquasec/data_service_test.go @@ -26,7 +26,7 @@ func TestDataSourceServiceBasic(t *testing.T) { resource.TestCheckResourceAttr(rootRef, "policies.#", fmt.Sprintf("%d", len(basicService.Policies))), resource.TestCheckResourceAttr(rootRef, "policies.0", basicService.Policies[0]), resource.TestCheckResourceAttr(rootRef, "enforce", "false"), - resource.TestCheckResourceAttr(rootRef, "application_scopes.#", fmt.Sprintf("%v", len(basicService.ApplicationScopes))), + resource.TestCheckResourceAttr(rootRef, "application_scopes.#", fmt.Sprintf("%d", len(basicService.ApplicationScopes))), resource.TestCheckResourceAttr(rootRef, "application_scopes.0", basicService.ApplicationScopes[0]), resource.TestCheckResourceAttr(rootRef, "priority", "100"), resource.TestCheckResourceAttr(rootRef, "target", basicService.MembershipRules.Target), @@ -38,12 +38,23 @@ func TestDataSourceServiceBasic(t *testing.T) { resource.TestCheckResourceAttrSet(rootRef, "lastupdate"), resource.TestCheckResourceAttrSet(rootRef, "evaluated"), resource.TestCheckResourceAttrSet(rootRef, "is_registered"), + + // Assert no local policies + resource.TestCheckResourceAttr(rootRef, "local_policies.#", "0"), ), }, }, }) } +func getBasicServiceData() string { + return getBasicServiceResource() + ` + data "aquasec_service" "test-svc" { + name = aquasec_service.test-basic-svc.id + policies = aquasec_service.test-basic-svc.policies + } + ` +} func TestDataSourceServiceComplex(t *testing.T) { t.Parallel() rootRef := "data.aquasec_service.test-svc" @@ -56,24 +67,32 @@ func TestDataSourceServiceComplex(t *testing.T) { { Config: getComplexServiceData(), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(rootRef, "name", complexService.Name), - resource.TestCheckResourceAttr(rootRef, "description", complexService.Description), + resource.TestCheckResourceAttr(rootRef, "name", "test-complex-svc"), + resource.TestCheckResourceAttr(rootRef, "description", "Test complex service"), resource.TestCheckResourceAttr(rootRef, "monitoring", "false"), - resource.TestCheckResourceAttr(rootRef, "policies.#", fmt.Sprintf("%d", len(complexService.Policies))), - resource.TestCheckResourceAttr(rootRef, "policies.0", complexService.Policies[0]), - resource.TestCheckResourceAttr(rootRef, "enforce", fmt.Sprintf("%v", complexService.Enforce)), - resource.TestCheckResourceAttr(rootRef, "application_scopes.#", fmt.Sprintf("%d", len(complexService.ApplicationScopes))), - resource.TestCheckResourceAttr(rootRef, "application_scopes.0", complexService.ApplicationScopes[0]), - resource.TestCheckResourceAttr(rootRef, "priority", fmt.Sprintf("%d", complexService.MembershipRules.Priority)), - resource.TestCheckResourceAttr(rootRef, "target", complexService.MembershipRules.Target), - resource.TestCheckResourceAttr(rootRef, "scope_expression", complexService.MembershipRules.Scope.Expression), - resource.TestCheckResourceAttr(rootRef, "scope_variables.#", fmt.Sprintf("%v", len(complexService.MembershipRules.Scope.Variables))), - resource.TestCheckResourceAttr(rootRef, "scope_variables.0.attribute", complexService.MembershipRules.Scope.Variables[0].Attribute), - resource.TestCheckResourceAttr(rootRef, "scope_variables.0.value", complexService.MembershipRules.Scope.Variables[0].Value), - resource.TestCheckResourceAttr(rootRef, "scope_variables.1.attribute", complexService.MembershipRules.Scope.Variables[1].Attribute), - resource.TestCheckResourceAttr(rootRef, "scope_variables.1.value", complexService.MembershipRules.Scope.Variables[1].Value), - resource.TestCheckResourceAttr(rootRef, "scope_variables.2.attribute", complexService.MembershipRules.Scope.Variables[2].Attribute), - resource.TestCheckResourceAttr(rootRef, "scope_variables.2.value", complexService.MembershipRules.Scope.Variables[2].Value), + resource.TestCheckResourceAttr(rootRef, "policies.#", "2"), + resource.TestCheckResourceAttr(rootRef, "policies.0", "local-policy-1"), + resource.TestCheckResourceAttr(rootRef, "policies.1", "default"), + resource.TestCheckResourceAttr(rootRef, "local_policies.#", "1"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.name", "local-policy-1"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.type", "access.control"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.description", "Local policy for testing"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.block_metadata_service", "true"), + + // Inbound Networks + resource.TestCheckResourceAttr(rootRef, "local_policies.0.inbound_networks.#", "1"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.inbound_networks.0.port_range", "22-80"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.inbound_networks.0.resource_type", "anywhere"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.inbound_networks.0.allow", "true"), + + // Outbound Networks + resource.TestCheckResourceAttr(rootRef, "local_policies.0.outbound_networks.#", "1"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.outbound_networks.0.port_range", "443"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.outbound_networks.0.resource_type", "anywhere"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.outbound_networks.0.allow", "false"), + + resource.TestCheckResourceAttr(rootRef, "priority", "1"), + resource.TestCheckResourceAttr(rootRef, "target", "container"), resource.TestCheckResourceAttr(rootRef, "author", os.Getenv("AQUA_USER")), resource.TestCheckResourceAttrSet(rootRef, "containers_count"), resource.TestCheckResourceAttrSet(rootRef, "lastupdate"), @@ -85,20 +104,11 @@ func TestDataSourceServiceComplex(t *testing.T) { }) } -func getBasicServiceData() string { - return getBasicServiceResource() + fmt.Sprintf(` - - data "aquasec_service" "test-svc" { - name = aquasec_service.test-basic-svc.id - } -`) -} - func getComplexServiceData() string { return getComplexServiceResource() + fmt.Sprintf(` - data "aquasec_service" "test-svc" { name = aquasec_service.test-complex-svc.id + policies = aquasec_service.test-complex-svc.policies } `) } diff --git a/aquasec/resource_service.go b/aquasec/resource_service.go index 6ec98191..293a5690 100644 --- a/aquasec/resource_service.go +++ b/aquasec/resource_service.go @@ -109,6 +109,83 @@ func resourceService() *schema.Resource { Description: "The service's policies; an array of container firewall policy names.", Required: true, }, + "local_policies": { + Type: schema.TypeList, + Description: "A list of local policies for the service, including inbound and outbound network rules.", + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Description: "The name of the local policy.", + Required: true, + }, + "type": { + Type: schema.TypeString, + Description: "The type of the local policy, e.g., access.control.", + Required: true, + }, + "description": { + Type: schema.TypeString, + Description: "A description of the local policy.", + Optional: true, + }, + "inbound_networks": { + Type: schema.TypeList, + Description: "Inbound network rules for the local policy.", + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "port_range": { + Type: schema.TypeString, + Description: "The port range for the inbound network rule.", + Required: true, + }, + "resource_type": { + Type: schema.TypeString, + Description: "The resource type for the inbound network rule (e.g., anywhere).", + Required: true, + }, + "allow": { + Type: schema.TypeBool, + Description: "Whether the inbound network rule is allowed.", + Required: true, + }, + }, + }, + }, + "outbound_networks": { + Type: schema.TypeList, + Description: "Outbound network rules for the local policy.", + Optional: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "port_range": { + Type: schema.TypeString, + Description: "The port range for the outbound network rule.", + Required: true, + }, + "resource_type": { + Type: schema.TypeString, + Description: "The resource type for the outbound network rule (e.g., anywhere).", + Required: true, + }, + "allow": { + Type: schema.TypeBool, + Description: "Whether the outbound network rule is allowed.", + Required: true, + }, + }, + }, + }, + "block_metadata_service": { + Type: schema.TypeBool, + Description: "Whether to block access to the metadata service.", + Optional: true, + }, + }, + }, + }, "evaluated": { Type: schema.TypeBool, Description: "Whether the service has been evaluated for security vulnerabilities.", @@ -231,16 +308,41 @@ func resourceServiceRead(ctx context.Context, d *schema.ResourceData, m interfac d.Set("unregistered_count", service.UnregisteredCount) d.Set("is_registered", service.IsRegistered) d.Set("application_scopes", service.ApplicationScopes) + localPolicies := make([]map[string]interface{}, 0) + for _, policy := range service.LocalPolicies { + localPolicies = append(localPolicies, map[string]interface{}{ + "name": policy.Name, + "type": policy.Type, + "description": policy.Description, + "inbound_networks": convertNetworkRulesToNetworks(policy.InboundNetworks), + "outbound_networks": convertNetworkRulesToNetworks(policy.OutboundNetworks), + "block_metadata_service": policy.BlockMetadataService, + }) + } + d.Set("local_policies", localPolicies) + d.SetId(service.Name) return nil } +func convertNetworkRulesToNetworks(networkRules []client.NetworkRule) []map[string]interface{} { + networkMaps := make([]map[string]interface{}, 0) + for _, networkRule := range networkRules { + networkMaps = append(networkMaps, map[string]interface{}{ + "allow": networkRule.Allow, + "port_range": networkRule.PortRange, + "resource_type": networkRule.ResourceType, + }) + } + return networkMaps +} + func resourceServiceUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { c := m.(*client.Client) name := d.Get("name").(string) - if d.HasChanges("description", "monitoring", "policies", "enforce", "application_scopes", "target", "priority", "scope_expression", "scope_variables") { + if d.HasChanges("description", "monitoring", "policies", "enforce", "application_scopes", "target", "priority", "scope_expression", "scope_variables", "local_policies") { service := expandService(d) err := c.UpdateService(service) if err == nil { @@ -409,10 +511,40 @@ func expandService(d *schema.ResourceData) *client.Service { if ok { service.ApplicationScopes = convertStringArr(applicationScope.([]interface{})) } + localPolicies, ok := d.GetOk("local_policies") + if ok { + localPoliciesList := make([]client.LocalPolicy, 0) + for _, v := range localPolicies.([]interface{}) { + policy := v.(map[string]interface{}) + + localPoliciesList = append(localPoliciesList, client.LocalPolicy{ + Name: policy["name"].(string), + Type: policy["type"].(string), + Description: policy["description"].(string), + InboundNetworks: expandNetworks(policy["inbound_networks"].([]interface{})), + OutboundNetworks: expandNetworks(policy["outbound_networks"].([]interface{})), + BlockMetadataService: policy["block_metadata_service"].(bool), + }) + } + service.LocalPolicies = localPoliciesList + } return &service } +func expandNetworks(networks []interface{}) []client.NetworkRule { + networkRules := make([]client.NetworkRule, 0) + for _, n := range networks { + rule := n.(map[string]interface{}) + networkRules = append(networkRules, client.NetworkRule{ + PortRange: rule["port_range"].(string), + ResourceType: rule["resource_type"].(string), + Allow: rule["allow"].(bool), + }) + } + return networkRules +} + func flattenScopeVariables(variables []client.Variable) []map[string]interface{} { specs := make([]map[string]interface{}, len(variables)) for i := range variables { diff --git a/aquasec/resource_service_test.go b/aquasec/resource_service_test.go index 6fc834e8..65fd4c77 100644 --- a/aquasec/resource_service_test.go +++ b/aquasec/resource_service_test.go @@ -2,7 +2,6 @@ package aquasec import ( "fmt" - "os" "testing" "github.com/aquasecurity/terraform-provider-aquasec/client" @@ -108,6 +107,7 @@ func TestResourceAquasecServiceBasicCreate(t *testing.T) { func TestResourceAquasecServiceComplexCreate(t *testing.T) { rootRef := serviceResourceRef("test-complex-svc") + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) @@ -118,101 +118,112 @@ func TestResourceAquasecServiceComplexCreate(t *testing.T) { { Config: getComplexServiceResource(), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(rootRef, "name", complexService.Name), - resource.TestCheckResourceAttr(rootRef, "description", complexService.Description), - resource.TestCheckResourceAttr(rootRef, "monitoring", "false"), - resource.TestCheckResourceAttr(rootRef, "policies.#", fmt.Sprintf("%d", len(complexService.Policies))), - resource.TestCheckResourceAttr(rootRef, "policies.0", complexService.Policies[0]), - resource.TestCheckResourceAttr(rootRef, "enforce", fmt.Sprintf("%v", complexService.Enforce)), - resource.TestCheckResourceAttr(rootRef, "application_scopes.#", fmt.Sprintf("%d", len(complexService.ApplicationScopes))), - resource.TestCheckResourceAttr(rootRef, "application_scopes.0", complexService.ApplicationScopes[0]), - resource.TestCheckResourceAttr(rootRef, "priority", fmt.Sprintf("%d", complexService.MembershipRules.Priority)), - resource.TestCheckResourceAttr(rootRef, "target", complexService.MembershipRules.Target), - resource.TestCheckResourceAttr(rootRef, "scope_expression", complexService.MembershipRules.Scope.Expression), - resource.TestCheckResourceAttr(rootRef, "scope_variables.#", fmt.Sprintf("%v", len(complexService.MembershipRules.Scope.Variables))), - resource.TestCheckResourceAttr(rootRef, "scope_variables.0.attribute", complexService.MembershipRules.Scope.Variables[0].Attribute), - resource.TestCheckResourceAttr(rootRef, "scope_variables.0.value", complexService.MembershipRules.Scope.Variables[0].Value), - resource.TestCheckResourceAttr(rootRef, "scope_variables.1.attribute", complexService.MembershipRules.Scope.Variables[1].Attribute), - resource.TestCheckResourceAttr(rootRef, "scope_variables.1.value", complexService.MembershipRules.Scope.Variables[1].Value), - resource.TestCheckResourceAttr(rootRef, "scope_variables.2.attribute", complexService.MembershipRules.Scope.Variables[2].Attribute), - resource.TestCheckResourceAttr(rootRef, "scope_variables.2.value", complexService.MembershipRules.Scope.Variables[2].Value), - resource.TestCheckResourceAttr(rootRef, "author", os.Getenv("AQUA_USER")), - resource.TestCheckResourceAttrSet(rootRef, "containers_count"), - resource.TestCheckResourceAttrSet(rootRef, "lastupdate"), - resource.TestCheckResourceAttrSet(rootRef, "evaluated"), - resource.TestCheckResourceAttrSet(rootRef, "is_registered"), + // Basic Service Attributes + resource.TestCheckResourceAttr(rootRef, "name", "test-complex-svc"), + resource.TestCheckResourceAttr(rootRef, "description", "Test complex service"), + resource.TestCheckResourceAttr(rootRef, "enforce", "true"), + resource.TestCheckResourceAttr(rootRef, "application_scopes.#", "1"), + resource.TestCheckResourceAttr(rootRef, "application_scopes.0", "Global"), + resource.TestCheckResourceAttr(rootRef, "policies.#", "2"), + resource.TestCheckResourceAttr(rootRef, "policies.0", "local-policy-1"), + resource.TestCheckResourceAttr(rootRef, "priority", "1"), + resource.TestCheckResourceAttr(rootRef, "target", "container"), + + // Local Policies + resource.TestCheckResourceAttr(rootRef, "local_policies.#", "1"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.name", "local-policy-1"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.type", "access.control"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.description", "Local policy for testing"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.block_metadata_service", "true"), + + // Inbound Networks + resource.TestCheckResourceAttr(rootRef, "local_policies.0.inbound_networks.#", "1"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.inbound_networks.0.port_range", "22-80"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.inbound_networks.0.resource_type", "anywhere"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.inbound_networks.0.allow", "true"), + + // Outbound Networks + resource.TestCheckResourceAttr(rootRef, "local_policies.0.outbound_networks.#", "1"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.outbound_networks.0.port_range", "443"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.outbound_networks.0.resource_type", "anywhere"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.outbound_networks.0.allow", "false"), ), }, }, }) } - func TestResourceAquasecServiceUpdate(t *testing.T) { - rootRef := serviceResourceRef("test-basic-svc") - basicService.Name = acctest.RandomWithPrefix("updated-basic-service") - updatedService := basicService - updatedService.Description = "this description is updated version of the basic service description" - updatedService.Policies = append(updatedService.Policies, acctest.RandomWithPrefix("another-firewall-policy")) - updatedService.MembershipRules.Scope.Expression = "v1 || v2" - updatedService.MembershipRules.Scope.Variables = append(updatedService.MembershipRules.Scope.Variables, client.Variable{ - Attribute: "kubernetes.namespace", - Value: "default", - }) - updatedService.MembershipRules.Priority = 80 + // Generate unique resource names and configurations dynamically + basicService := struct { + Name string + Description string + }{ + Name: acctest.RandomWithPrefix("update-service"), + Description: "Basic service description", + } + + updatedService := struct { + Name string + Description string + }{ + Name: basicService.Name, // Name remains unchanged + Description: "This description is the updated version of the basic service description", + } + + defaultPolicyName := "default" + localPolicyName := "local-policy-1" + + rootRef := serviceResourceRef(basicService.Name) // Reference for test assertions + resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, Providers: testAccProviders, - CheckDestroy: CheckDestroy("aquasec_service.test-basic-svc"), + CheckDestroy: CheckDestroy(fmt.Sprintf("aquasec_service.%s", basicService.Name)), Steps: []resource.TestStep{ + // Step 1: Create a simple service with no local policies { - Config: getBasicServiceResource(), + Config: getBasicServiceResourcenolocalpolicy(basicService, defaultPolicyName), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(rootRef, "name", basicService.Name), - resource.TestCheckResourceAttr(rootRef, "description", basicService.Description), - resource.TestCheckResourceAttr(rootRef, "monitoring", "false"), - resource.TestCheckResourceAttr(rootRef, "policies.#", fmt.Sprintf("%d", len(basicService.Policies))), - resource.TestCheckResourceAttr(rootRef, "policies.0", basicService.Policies[0]), - resource.TestCheckResourceAttr(rootRef, "enforce", "false"), - resource.TestCheckResourceAttr(rootRef, "application_scopes.#", fmt.Sprintf("%v", len(basicService.ApplicationScopes))), - resource.TestCheckResourceAttr(rootRef, "application_scopes.0", basicService.ApplicationScopes[0]), - resource.TestCheckResourceAttr(rootRef, "priority", "100"), - resource.TestCheckResourceAttr(rootRef, "target", basicService.MembershipRules.Target), - resource.TestCheckResourceAttr(rootRef, "scope_expression", basicService.MembershipRules.Scope.Expression), - resource.TestCheckResourceAttr(rootRef, "scope_variables.#", fmt.Sprintf("%d", len(basicService.MembershipRules.Scope.Variables))), - resource.TestCheckResourceAttr(rootRef, "scope_variables.0.attribute", basicService.MembershipRules.Scope.Variables[0].Attribute), - resource.TestCheckResourceAttr(rootRef, "scope_variables.0.value", basicService.MembershipRules.Scope.Variables[0].Value), - resource.TestCheckResourceAttrSet(rootRef, "containers_count"), - resource.TestCheckResourceAttrSet(rootRef, "lastupdate"), - resource.TestCheckResourceAttrSet(rootRef, "evaluated"), - resource.TestCheckResourceAttrSet(rootRef, "is_registered"), + resource.TestCheckResourceAttr(rootRef, "name", basicService.Name), // Validate service name + resource.TestCheckResourceAttr(rootRef, "description", basicService.Description), // Validate description + resource.TestCheckResourceAttr(rootRef, "policies.#", "1"), // Only one global policy + resource.TestCheckResourceAttr(rootRef, "policies.0", defaultPolicyName), // Validate default global policy + resource.TestCheckResourceAttr(rootRef, "local_policies.#", "0"), // No local policies initially + resource.TestCheckResourceAttr(rootRef, "application_scopes.#", "1"), // Validate application scope + resource.TestCheckResourceAttr(rootRef, "application_scopes.0", "Global"), // Global scope + resource.TestCheckResourceAttr(rootRef, "priority", "1"), // Validate priority + resource.TestCheckResourceAttr(rootRef, "target", "container"), // Validate target + resource.TestCheckResourceAttr(rootRef, "enforce", "false"), // Validate enforce flag ), }, + // Step 2: Update the service to include a local policy { - Config: getServiceResourceUpdate(&updatedService), + Config: getServiceResourceUpdate(updatedService, defaultPolicyName, localPolicyName), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckResourceAttr(rootRef, "name", updatedService.Name), - resource.TestCheckResourceAttr(rootRef, "description", updatedService.Description), - resource.TestCheckResourceAttr(rootRef, "monitoring", "false"), - resource.TestCheckResourceAttr(rootRef, "policies.#", fmt.Sprintf("%d", len(updatedService.Policies))), - resource.TestCheckResourceAttr(rootRef, "policies.0", updatedService.Policies[0]), - resource.TestCheckResourceAttr(rootRef, "enforce", fmt.Sprintf("%v", updatedService.Enforce)), - resource.TestCheckResourceAttr(rootRef, "application_scopes.#", fmt.Sprintf("%d", len(updatedService.ApplicationScopes))), - resource.TestCheckResourceAttr(rootRef, "application_scopes.0", updatedService.ApplicationScopes[0]), - resource.TestCheckResourceAttr(rootRef, "priority", fmt.Sprintf("%d", updatedService.MembershipRules.Priority)), - resource.TestCheckResourceAttr(rootRef, "target", updatedService.MembershipRules.Target), - resource.TestCheckResourceAttr(rootRef, "scope_expression", updatedService.MembershipRules.Scope.Expression), - resource.TestCheckResourceAttr(rootRef, "scope_variables.#", fmt.Sprintf("%v", len(updatedService.MembershipRules.Scope.Variables))), - resource.TestCheckResourceAttr(rootRef, "scope_variables.0.attribute", updatedService.MembershipRules.Scope.Variables[0].Attribute), - resource.TestCheckResourceAttr(rootRef, "scope_variables.0.value", updatedService.MembershipRules.Scope.Variables[0].Value), - resource.TestCheckResourceAttr(rootRef, "scope_variables.1.attribute", updatedService.MembershipRules.Scope.Variables[1].Attribute), - resource.TestCheckResourceAttr(rootRef, "scope_variables.1.value", updatedService.MembershipRules.Scope.Variables[1].Value), - resource.TestCheckResourceAttr(rootRef, "author", os.Getenv("AQUA_USER")), - resource.TestCheckResourceAttrSet(rootRef, "containers_count"), - resource.TestCheckResourceAttrSet(rootRef, "lastupdate"), - resource.TestCheckResourceAttrSet(rootRef, "evaluated"), - resource.TestCheckResourceAttrSet(rootRef, "is_registered"), + resource.TestCheckResourceAttr(rootRef, "name", updatedService.Name), // Name remains unchanged + resource.TestCheckResourceAttr(rootRef, "description", updatedService.Description), // Updated description + resource.TestCheckResourceAttr(rootRef, "policies.#", "2"), // Now two policies (default + local) + resource.TestCheckResourceAttr(rootRef, "policies.0", defaultPolicyName), // Validate default global policy + resource.TestCheckResourceAttr(rootRef, "policies.1", localPolicyName), // Validate added local policy + resource.TestCheckResourceAttr(rootRef, "local_policies.#", "1"), // Local policies now present + resource.TestCheckResourceAttr(rootRef, "local_policies.0.name", localPolicyName), // Validate local policy name + resource.TestCheckResourceAttr(rootRef, "local_policies.0.type", "access.control"), // Validate local policy type + resource.TestCheckResourceAttr(rootRef, "local_policies.0.description", "Local policy for testing"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.block_metadata_service", "true"), + + // Inbound Networks + resource.TestCheckResourceAttr(rootRef, "local_policies.0.inbound_networks.#", "1"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.inbound_networks.0.port_range", "22-80"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.inbound_networks.0.resource_type", "anywhere"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.inbound_networks.0.allow", "true"), + + // Outbound Networks + resource.TestCheckResourceAttr(rootRef, "local_policies.0.outbound_networks.#", "1"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.outbound_networks.0.port_range", "443"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.outbound_networks.0.resource_type", "anywhere"), + resource.TestCheckResourceAttr(rootRef, "local_policies.0.outbound_networks.0.allow", "false"), ), }, }, @@ -251,97 +262,93 @@ func getBasicServiceResource() string { } func getComplexServiceResource() string { - return fmt.Sprintf(` - resource "aquasec_firewall_policy" "test" { - name = "%s" - description = "this is created for the unit test of service resource" - } - + return fmt.Sprintf(` resource "aquasec_service" "test-complex-svc" { - name = "%s" - description = "%s" - application_scopes = [ - "%s" - ] + name = "test-complex-svc" + description = "Test complex service" + application_scopes = [ + "Global" + ] policies = [ - "%s", - aquasec_firewall_policy.test.id + "local-policy-1", + "default" ] - priority = "%d" - target = "%s" - enforce = "%v" - scope_expression = "%s" - scope_variables { - attribute = "%s" - value = "%s" + priority = 1 + target = "container" + enforce = true + + // Local Policies + local_policies { + name = "local-policy-1" + type = "access.control" + description = "Local policy for testing" + block_metadata_service = true + + inbound_networks { + port_range = "22-80" + resource_type = "anywhere" + allow = true } - scope_variables { - attribute = "%s" - value = "%s" + + outbound_networks { + port_range = "443" + resource_type = "anywhere" + allow = false } - scope_variables { - attribute = "%s" - value = "%s" } - }`, - complexService.Policies[1], - complexService.Name, - complexService.Description, - complexService.ApplicationScopes[0], - complexService.Policies[0], - complexService.MembershipRules.Priority, - complexService.MembershipRules.Target, - complexService.Enforce, - complexService.MembershipRules.Scope.Expression, - complexService.MembershipRules.Scope.Variables[0].Attribute, - complexService.MembershipRules.Scope.Variables[0].Value, - complexService.MembershipRules.Scope.Variables[1].Attribute, - complexService.MembershipRules.Scope.Variables[1].Value, - complexService.MembershipRules.Scope.Variables[2].Attribute, - complexService.MembershipRules.Scope.Variables[2].Value, - ) + }`) } -func getServiceResourceUpdate(updatedService *client.Service) string { +func getBasicServiceResourcenolocalpolicy(basicService struct{ Name, Description string }, defaultPolicyName string) string { return fmt.Sprintf(` - resource "aquasec_firewall_policy" "test" { - name = "%s" - description = "this is created for the unit test of service resource" - } - - resource "aquasec_service" "test-basic-svc" { - name = "%s" - description = "%s" + resource "aquasec_service" "%s" { + name = "%s" + description = "%s" application_scopes = [ + "Global" + ] + policies = [ "%s" ] + priority = 1 + target = "container" + enforce = false + }`, basicService.Name, basicService.Name, basicService.Description, defaultPolicyName) +} + +func getServiceResourceUpdate(updatedService struct{ Name, Description string }, defaultPolicyName, localPolicyName string) string { + return fmt.Sprintf(` + resource "aquasec_service" "%s" { + name = "%s" + description = "%s" + application_scopes = [ + "Global" + ] policies = [ "%s", - aquasec_firewall_policy.test.id + "%s" ] - priority = %d - target = "%s" - scope_expression = "%s" - scope_variables { - attribute = "%s" - value = "%s" - } - scope_variables { - attribute = "%s" - value = "%s" + priority = 1 + target = "container" + enforce = false + + local_policies { + name = "%s" + type = "access.control" + description = "Local policy for testing" + block_metadata_service = true + + inbound_networks { + port_range = "22-80" + resource_type = "anywhere" + allow = true + } + + outbound_networks { + port_range = "443" + resource_type = "anywhere" + allow = false + } } - }`, - updatedService.Policies[1], - updatedService.Name, - updatedService.Description, - updatedService.ApplicationScopes[0], - updatedService.Policies[0], - updatedService.MembershipRules.Priority, - updatedService.MembershipRules.Target, - updatedService.MembershipRules.Scope.Expression, - updatedService.MembershipRules.Scope.Variables[0].Attribute, - updatedService.MembershipRules.Scope.Variables[0].Value, - updatedService.MembershipRules.Scope.Variables[1].Attribute, - updatedService.MembershipRules.Scope.Variables[1].Value, - ) + }`, updatedService.Name, updatedService.Name, updatedService.Description, defaultPolicyName, localPolicyName, localPolicyName) } diff --git a/client/service.go b/client/service.go index c1d23c44..aeda55af 100644 --- a/client/service.go +++ b/client/service.go @@ -20,6 +20,7 @@ type Service struct { Evaluated bool `json:"evaluated"` Monitoring bool `json:"monitoring"` Policies []string `json:"policies"` + LocalPolicies []LocalPolicy `json:"local_policies,omitempty"` Lastupdate int `json:"lastupdate"` Vulnerabilities VulnerabilitiesTypes `json:"vulnerabilities"` Enforce bool `json:"enforce"` @@ -30,6 +31,19 @@ type Service struct { ApplicationScopes []string `json:"application_scopes"` } +type LocalPolicy struct { + Name string `json:"name"` + Type string `json:"type"` + Description string `json:"description,omitempty"` + InboundNetworks []NetworkRule `json:"inbound_networks,omitempty"` + OutboundNetworks []NetworkRule `json:"outbound_networks,omitempty"` + BlockMetadataService bool `json:"block_metadata_service"` +} +type NetworkRule struct { + PortRange string `json:"port_range"` + ResourceType string `json:"resource_type"` + Allow bool `json:"allow"` +} type VulnerabilitiesTypes struct { Total int `json:"total"` High int `json:"high"` diff --git a/docs/data-sources/service.md b/docs/data-sources/service.md index 4cf441ec..2fdc7cc1 100644 --- a/docs/data-sources/service.md +++ b/docs/data-sources/service.md @@ -18,6 +18,11 @@ description: |- ### Required - `name` (String) The name of the service. It is recommended not to use whitespace characters in the name. +- `policies` (List of String) The service's policies; an array of container firewall policy names. + +### Optional + +- `local_policies` (Block List) A list of local policies for the service, including inbound and outbound network rules. (see [below for nested schema](#nestedblock--local_policies)) ### Read-Only @@ -32,7 +37,6 @@ description: |- - `lastupdate` (Number) Timestamp of the last update in Unix time format. - `monitoring` (Boolean) Indicates if monitoring is enabled or not - `not_evaluated_count` (Number) The number of container that are not evaluated. -- `policies` (List of String) The service's policies; an array of container firewall policy names. - `priority` (Number) Rules priority, must be between 1-100. - `scope_expression` (String) Logical expression of how to compute the dependency of the scope variables. - `scope_variables` (List of Object) List of scope attributes. (see [below for nested schema](#nestedatt--scope_variables)) @@ -47,6 +51,42 @@ description: |- - `vulnerabilities_sensitive` (Number) Number of sensitive vulnerabilities. - `vulnerabilities_total` (Number) Total number of vulnerabilities. + +### Nested Schema for `local_policies` + +Required: + +- `name` (String) The name of the local policy. +- `type` (String) The type of the local policy, e.g., access.control. + +Optional: + +- `block_metadata_service` (Boolean) Whether to block access to the metadata service. +- `description` (String) A description of the local policy. +- `inbound_networks` (Block List) Inbound network rules for the local policy. (see [below for nested schema](#nestedblock--local_policies--inbound_networks)) +- `outbound_networks` (Block List) Outbound network rules for the local policy. (see [below for nested schema](#nestedblock--local_policies--outbound_networks)) + + +### Nested Schema for `local_policies.inbound_networks` + +Required: + +- `allow` (Boolean) Whether the inbound network rule is allowed. +- `port_range` (String) The port range for the inbound network rule. +- `resource_type` (String) The resource type for the inbound network rule (e.g., anywhere). + + + +### Nested Schema for `local_policies.outbound_networks` + +Required: + +- `allow` (Boolean) Whether the outbound network rule is allowed. +- `port_range` (String) The port range for the outbound network rule. +- `resource_type` (String) The resource type for the outbound network rule (e.g., anywhere). + + + ### Nested Schema for `scope_variables` @@ -55,5 +95,3 @@ Read-Only: - `attribute` (String) - `name` (String) - `value` (String) - - diff --git a/docs/resources/service.md b/docs/resources/service.md index bf8e3b12..12333342 100644 --- a/docs/resources/service.md +++ b/docs/resources/service.md @@ -10,7 +10,63 @@ description: |- - +## Example Usage + +```terraform +resource "aquasec_service" "example_service" { + name = "svc_example" + description = "Example service with global and local policies" + target = "container" + priority = 90 + application_scopes = ["Global"] + enforce = true + + // Global policies applied to this service + policies = ["default", "policy1", "policy2"] + + // Local policy 1 + local_policies { + name = "policy1" + type = "access.control" + description = "Local policy 1 for inbound and outbound control" + + inbound_networks { + port_range = "22/22" # Allow SSH traffic + resource_type = "anywhere" # Allow from any source + allow = true # Permit traffic + } + + outbound_networks { + port_range = "80/80" # Allow HTTP traffic + resource_type = "anywhere" # Allow to any destination + allow = true # Permit traffic + } + + block_metadata_service = false # Do not block metadata service + } + + // Local policy 2 + local_policies { + name = "policy2" + type = "access.control" + description = "Local policy 2 with stricter outbound control" + + inbound_networks { + port_range = "443/443" # Allow HTTPS traffic + resource_type = "anywhere" # Allow from any source + allow = true # Permit traffic + } + + outbound_networks { + port_range = "8080/8080" # Allow specific application traffic + resource_type = "specific" # Allow only to specific destinations + allow = false # Block traffic to unspecified destinations + } + + block_metadata_service = true # Block metadata service access for security + } +} +``` ## Schema @@ -26,6 +82,7 @@ description: |- - `description` (String) A textual description of the service record; maximum 500 characters. - `enforce` (Boolean) Enforcement status of the service. +- `local_policies` (Block List) A list of local policies for the service, including inbound and outbound network rules. (see [below for nested schema](#nestedblock--local_policies)) - `monitoring` (Boolean) Indicates if monitoring is enabled or not - `priority` (Number) Rules priority, must be between 1-100. - `scope_expression` (String) Logical expression of how to compute the dependency of the scope variables. @@ -50,6 +107,42 @@ description: |- - `vulnerabilities_sensitive` (Number) Number of sensitive vulnerabilities. - `vulnerabilities_total` (Number) Total number of vulnerabilities. + +### Nested Schema for `local_policies` + +Required: + +- `name` (String) The name of the local policy. +- `type` (String) The type of the local policy, e.g., access.control. + +Optional: + +- `block_metadata_service` (Boolean) Whether to block access to the metadata service. +- `description` (String) A description of the local policy. +- `inbound_networks` (Block List) Inbound network rules for the local policy. (see [below for nested schema](#nestedblock--local_policies--inbound_networks)) +- `outbound_networks` (Block List) Outbound network rules for the local policy. (see [below for nested schema](#nestedblock--local_policies--outbound_networks)) + + +### Nested Schema for `local_policies.inbound_networks` + +Required: + +- `allow` (Boolean) Whether the inbound network rule is allowed. +- `port_range` (String) The port range for the inbound network rule. +- `resource_type` (String) The resource type for the inbound network rule (e.g., anywhere). + + + +### Nested Schema for `local_policies.outbound_networks` + +Required: + +- `allow` (Boolean) Whether the outbound network rule is allowed. +- `port_range` (String) The port range for the outbound network rule. +- `resource_type` (String) The resource type for the outbound network rule (e.g., anywhere). + + + ### Nested Schema for `scope_variables` @@ -58,5 +151,3 @@ Optional: - `attribute` (String) Class of supported scope. - `name` (String) Name assigned to the attribute. - `value` (String) Value assigned to the attribute. - - diff --git a/examples/resources/aquasec_service/resource.tf b/examples/resources/aquasec_service/resource.tf index 9a9e493f..7f72dfc6 100644 --- a/examples/resources/aquasec_service/resource.tf +++ b/examples/resources/aquasec_service/resource.tf @@ -1,11 +1,53 @@ resource "aquasec_service" "example_service" { - // Required values - application_scopes = ["Global"] - name = "example_service" - policies = ["example_firewall_policy"] + name = "svc_example" + description = "Example service with global and local policies" target = "container" + priority = 90 + application_scopes = ["Global"] + enforce = true + + // Global policies applied to this service + policies = ["default", "policy1", "policy2"] + + // Local policy 1 + local_policies { + name = "policy1" + type = "access.control" + description = "Local policy 1 for inbound and outbound control" + + inbound_networks { + port_range = "22/22" # Allow SSH traffic + resource_type = "anywhere" # Allow from any source + allow = true # Permit traffic + } + + outbound_networks { + port_range = "80/80" # Allow HTTP traffic + resource_type = "anywhere" # Allow to any destination + allow = true # Permit traffic + } + + block_metadata_service = false # Do not block metadata service + } + + // Local policy 2 + local_policies { + name = "policy2" + type = "access.control" + description = "Local policy 2 with stricter outbound control" + + inbound_networks { + port_range = "443/443" # Allow HTTPS traffic + resource_type = "anywhere" # Allow from any source + allow = true # Permit traffic + } + + outbound_networks { + port_range = "8080/8080" # Allow specific application traffic + resource_type = "specific" # Allow only to specific destinations + allow = false # Block traffic to unspecified destinations + } - enforce = true - monitoring = true - priority = 10 -} \ No newline at end of file + block_metadata_service = true # Block metadata service access for security + } +}