diff --git a/src/tirith/providers/terraform_plan/handler.py b/src/tirith/providers/terraform_plan/handler.py index 4bedafb2..2235a35c 100644 --- a/src/tirith/providers/terraform_plan/handler.py +++ b/src/tirith/providers/terraform_plan/handler.py @@ -69,7 +69,7 @@ def provide(provider_inputs, input_data): is_attribute_found = False for resource_change in resource_changes: - if resource_change["type"] == resource_type: + if resource_type in (resource_change["type"], "*"): is_resource_found = True input_resource_change_attrs = resource_change["change"]["after"] if input_resource_change_attrs: @@ -146,11 +146,11 @@ def provide(provider_inputs, input_data): resource_meta = {} resource_type = provider_inputs["terraform_resource_type"] for resource_change in resource_changes: - if resource_change["type"] == resource_type: + if resource_type in (resource_change["type"], "*"): # No need to check if the resource is not found # because the count of a resource can be zero resource_meta = resource_change - count = +1 + count += 1 outputs.append( { @@ -282,6 +282,7 @@ def direct_dependencies_operator(input_data: dict, provider_inputs: dict, output is_resource_found = False for resource in config_resources: + if resource.get("type") != resource_type: continue is_resource_found = True @@ -406,6 +407,7 @@ def direct_references_operator_references_to(input_data: dict, provider_inputs: is_resource_found = False for resource_change in resource_changes: + if resource_change.get("type") != resource_type or resource_change.get("change", {}).get("actions") == [ "destroy" ]: @@ -479,6 +481,7 @@ def direct_references_operator(input_data: dict, provider_inputs: dict, outputs: is_resource_found = False for resource in config_resources: + if resource.get("type") != resource_type: continue is_resource_found = True diff --git a/tests/providers/terraform_plan/fixtures/input_costcenter_tags.json b/tests/providers/terraform_plan/fixtures/input_costcenter_tags.json new file mode 100644 index 00000000..1572f22e --- /dev/null +++ b/tests/providers/terraform_plan/fixtures/input_costcenter_tags.json @@ -0,0 +1,447 @@ +{ + "format_version": "1.2", + "terraform_version": "1.10.1", + "planned_values": { + "root_module": { + "resources": [ + { + "address": "aws_instance.web", + "mode": "managed", + "type": "aws_instance", + "name": "web", + "provider_name": "registry.terraform.io/hashicorp/aws", + "schema_version": 1, + "values": { + "ami": "ami-0c55b159cbfafe1f0", + "credit_specification": [], + "get_password_data": false, + "hibernation": null, + "instance_type": "t2.micro", + "launch_template": [], + "source_dest_check": true, + "tags": { + "Name": "web-server", + "costcenter": "product-456", + "environment": "production" + }, + "tags_all": { + "Name": "web-server", + "costcenter": "product-456", + "environment": "production" + }, + "timeouts": null, + "user_data_replace_on_change": false, + "volume_tags": null + }, + "sensitive_values": { + "capacity_reservation_specification": [], + "cpu_options": [], + "credit_specification": [], + "ebs_block_device": [], + "enclave_options": [], + "ephemeral_block_device": [], + "ipv6_addresses": [], + "launch_template": [], + "maintenance_options": [], + "metadata_options": [], + "network_interface": [], + "private_dns_name_options": [], + "root_block_device": [], + "secondary_private_ips": [], + "security_groups": [], + "tags": {}, + "tags_all": {}, + "vpc_security_group_ids": [] + } + }, + { + "address": "aws_s3_bucket.logs", + "mode": "managed", + "type": "aws_s3_bucket", + "name": "logs", + "provider_name": "registry.terraform.io/hashicorp/aws", + "schema_version": 0, + "values": { + "bucket": "my-logs-bucket", + "force_destroy": false, + "tags": { + "Name": "logs-bucket", + "costcenter": "product-789", + "environment": "production" + }, + "tags_all": { + "Name": "logs-bucket", + "costcenter": "product-789", + "environment": "production" + }, + "timeouts": null + }, + "sensitive_values": { + "cors_rule": [], + "grant": [], + "lifecycle_rule": [], + "logging": [], + "object_lock_configuration": [], + "replication_configuration": [], + "server_side_encryption_configuration": [], + "tags": {}, + "tags_all": {}, + "versioning": [], + "website": [] + } + }, + { + "address": "aws_vpc.main", + "mode": "managed", + "type": "aws_vpc", + "name": "main", + "provider_name": "registry.terraform.io/hashicorp/aws", + "schema_version": 1, + "values": { + "assign_generated_ipv6_cidr_block": null, + "cidr_block": "10.0.0.0/16", + "enable_dns_support": true, + "instance_tenancy": "default", + "ipv4_ipam_pool_id": null, + "ipv4_netmask_length": null, + "ipv6_ipam_pool_id": null, + "ipv6_netmask_length": null, + "tags": { + "Name": "main-vpc", + "costcenter": "product-123", + "environment": "production" + }, + "tags_all": { + "Name": "main-vpc", + "costcenter": "product-123", + "environment": "production" + } + }, + "sensitive_values": { + "tags": {}, + "tags_all": {} + } + } + ] + } + }, + "resource_changes": [ + { + "address": "aws_instance.web", + "mode": "managed", + "type": "aws_instance", + "name": "web", + "provider_name": "registry.terraform.io/hashicorp/aws", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "ami": "ami-0c55b159cbfafe1f0", + "credit_specification": [], + "get_password_data": false, + "hibernation": null, + "instance_type": "t2.micro", + "launch_template": [], + "source_dest_check": true, + "tags": { + "Name": "web-server", + "costcenter": "product-456", + "environment": "production" + }, + "tags_all": { + "Name": "web-server", + "costcenter": "product-456", + "environment": "production" + }, + "timeouts": null, + "user_data_replace_on_change": false, + "volume_tags": null + }, + "after_unknown": { + "arn": true, + "associate_public_ip_address": true, + "availability_zone": true, + "capacity_reservation_specification": true, + "cpu_core_count": true, + "cpu_options": true, + "cpu_threads_per_core": true, + "credit_specification": [], + "disable_api_stop": true, + "disable_api_termination": true, + "ebs_block_device": true, + "ebs_optimized": true, + "enclave_options": true, + "ephemeral_block_device": true, + "host_id": true, + "host_resource_group_arn": true, + "iam_instance_profile": true, + "id": true, + "instance_initiated_shutdown_behavior": true, + "instance_state": true, + "ipv6_address_count": true, + "ipv6_addresses": true, + "key_name": true, + "launch_template": [], + "maintenance_options": true, + "metadata_options": true, + "monitoring": true, + "network_interface": true, + "outpost_arn": true, + "password_data": true, + "placement_group": true, + "placement_partition_number": true, + "primary_network_interface_id": true, + "private_dns": true, + "private_dns_name_options": true, + "private_ip": true, + "public_dns": true, + "public_ip": true, + "root_block_device": true, + "secondary_private_ips": true, + "security_groups": true, + "subnet_id": true, + "tags": {}, + "tags_all": {}, + "tenancy": true, + "user_data": true, + "user_data_base64": true, + "vpc_security_group_ids": true + }, + "before_sensitive": false, + "after_sensitive": { + "capacity_reservation_specification": [], + "cpu_options": [], + "credit_specification": [], + "ebs_block_device": [], + "enclave_options": [], + "ephemeral_block_device": [], + "ipv6_addresses": [], + "launch_template": [], + "maintenance_options": [], + "metadata_options": [], + "network_interface": [], + "private_dns_name_options": [], + "root_block_device": [], + "secondary_private_ips": [], + "security_groups": [], + "tags": {}, + "tags_all": {}, + "vpc_security_group_ids": [] + } + } + }, + { + "address": "aws_s3_bucket.logs", + "mode": "managed", + "type": "aws_s3_bucket", + "name": "logs", + "provider_name": "registry.terraform.io/hashicorp/aws", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "bucket": "my-logs-bucket", + "force_destroy": false, + "tags": { + "Name": "logs-bucket", + "costcenter": "product-789", + "environment": "production" + }, + "tags_all": { + "Name": "logs-bucket", + "costcenter": "product-789", + "environment": "production" + }, + "timeouts": null + }, + "after_unknown": { + "acceleration_status": true, + "acl": true, + "arn": true, + "bucket_domain_name": true, + "bucket_prefix": true, + "bucket_regional_domain_name": true, + "cors_rule": true, + "grant": true, + "hosted_zone_id": true, + "id": true, + "lifecycle_rule": true, + "logging": true, + "object_lock_configuration": true, + "object_lock_enabled": true, + "policy": true, + "region": true, + "replication_configuration": true, + "request_payer": true, + "server_side_encryption_configuration": true, + "tags": {}, + "tags_all": {}, + "versioning": true, + "website": true, + "website_domain": true, + "website_endpoint": true + }, + "before_sensitive": false, + "after_sensitive": { + "cors_rule": [], + "grant": [], + "lifecycle_rule": [], + "logging": [], + "object_lock_configuration": [], + "replication_configuration": [], + "server_side_encryption_configuration": [], + "tags": {}, + "tags_all": {}, + "versioning": [], + "website": [] + } + } + }, + { + "address": "aws_vpc.main", + "mode": "managed", + "type": "aws_vpc", + "name": "main", + "provider_name": "registry.terraform.io/hashicorp/aws", + "change": { + "actions": [ + "create" + ], + "before": null, + "after": { + "assign_generated_ipv6_cidr_block": null, + "cidr_block": "10.0.0.0/16", + "enable_dns_support": true, + "instance_tenancy": "default", + "ipv4_ipam_pool_id": null, + "ipv4_netmask_length": null, + "ipv6_ipam_pool_id": null, + "ipv6_netmask_length": null, + "tags": { + "Name": "main-vpc", + "costcenter": "product-123", + "environment": "production" + }, + "tags_all": { + "Name": "main-vpc", + "costcenter": "product-123", + "environment": "production" + } + }, + "after_unknown": { + "arn": true, + "default_network_acl_id": true, + "default_route_table_id": true, + "default_security_group_id": true, + "dhcp_options_id": true, + "enable_classiclink": true, + "enable_classiclink_dns_support": true, + "enable_dns_hostnames": true, + "enable_network_address_usage_metrics": true, + "id": true, + "ipv6_association_id": true, + "ipv6_cidr_block": true, + "ipv6_cidr_block_network_border_group": true, + "main_route_table_id": true, + "owner_id": true, + "tags": {}, + "tags_all": {} + }, + "before_sensitive": false, + "after_sensitive": { + "tags": {}, + "tags_all": {} + } + } + } + ], + "configuration": { + "provider_config": { + "aws": { + "name": "aws", + "full_name": "registry.terraform.io/hashicorp/aws", + "version_constraint": "~> 4.0", + "expressions": { + "region": { + "constant_value": "us-west-2" + } + } + } + }, + "root_module": { + "resources": [ + { + "address": "aws_instance.web", + "mode": "managed", + "type": "aws_instance", + "name": "web", + "provider_config_key": "aws", + "expressions": { + "ami": { + "constant_value": "ami-0c55b159cbfafe1f0" + }, + "instance_type": { + "constant_value": "t2.micro" + }, + "tags": { + "constant_value": { + "Name": "web-server", + "costcenter": "product-456", + "environment": "production" + } + } + }, + "schema_version": 1 + }, + { + "address": "aws_s3_bucket.logs", + "mode": "managed", + "type": "aws_s3_bucket", + "name": "logs", + "provider_config_key": "aws", + "expressions": { + "bucket": { + "constant_value": "my-logs-bucket" + }, + "tags": { + "constant_value": { + "Name": "logs-bucket", + "costcenter": "product-789", + "environment": "production" + } + } + }, + "schema_version": 0 + }, + { + "address": "aws_vpc.main", + "mode": "managed", + "type": "aws_vpc", + "name": "main", + "provider_config_key": "aws", + "expressions": { + "cidr_block": { + "constant_value": "10.0.0.0/16" + }, + "tags": { + "constant_value": { + "Name": "main-vpc", + "costcenter": "product-123", + "environment": "production" + } + } + }, + "schema_version": 1 + } + ] + } + }, + "timestamp": "2025-02-10T08:56:14Z", + "applyable": true, + "complete": true, + "errored": false +} diff --git a/tests/providers/terraform_plan/fixtures/policy_costcenter_tags.json b/tests/providers/terraform_plan/fixtures/policy_costcenter_tags.json new file mode 100644 index 00000000..69758340 --- /dev/null +++ b/tests/providers/terraform_plan/fixtures/policy_costcenter_tags.json @@ -0,0 +1,23 @@ +{ + "evaluators": [ + { + "description": "All resources must have a 'costcenter' tag with a non-empty value", + "condition": { + "type": "IsNotEmpty", + "value": "", + "error_tolerance": 1 + }, + "id": "eval-id-1", + "provider_args": { + "operation_type": "attribute", + "terraform_resource_attribute": "tags.costcenter", + "terraform_resource_type": "*" + } + } + ], + "meta": { + "required_provider": "stackguardian/terraform_plan", + "version": "v1" + }, + "eval_expression": "eval-id-1 " +} \ No newline at end of file diff --git a/tests/providers/terraform_plan/test_provider_config.py b/tests/providers/terraform_plan/test_provider_config.py index 8cbdc610..238e92ef 100644 --- a/tests/providers/terraform_plan/test_provider_config.py +++ b/tests/providers/terraform_plan/test_provider_config.py @@ -53,3 +53,17 @@ def test_get_terraform_provider_get_version_constraint(): assert len(result) == 1 assert result[0]["value"] == ">= 3.11.0, < 4.0.0" + + +def test_get_terraform_resource_attribute_with_wildcard(): + provider_args_dict = { + "operation_type": "attribute", + "terraform_resource_type": "*", + "terraform_resource_attribute": "tags.costcenter", + } + result = handler.provide(provider_args_dict, load_terraform_plan_json("input_costcenter_tags.json")) + + assert len(result) == 3 + assert result[0]["value"] == "product-456" # EC2 instance + assert result[1]["value"] == "product-789" # S3 bucket + assert result[2]["value"] == "product-123" # VPC