# This policy requires resources of type `aws_elasticsearch_domain` have the `subnet_ids` should not be empty inside 'vpc_options'.

# Copyright IBM Corp. 2025, 2026
# SPDX-License-Identifier: BUSL-1.1

# Import

import "tfconfig/v2" as tfconfig
import "tfresources" as tf
import "report" as report
import "collection" as collection
import "collection/maps" as maps

# Constants
const = {
	"policy_name":                       "elasticsearch-in-vpc-only",
	"message":                           "Attribute 'subnet_ids' should not be empty for the attribute 'vpc_options' for 'aws_elasticsearch_domain' resources. Refer to https://docs.aws.amazon.com/securityhub/latest/userguide/es-controls.html#es-2 for more details.",
	"resource_aws_elasticsearch_domain": "aws_elasticsearch_domain",
	"subnet_ids":                        "subnet_ids",
	"constant_value":                    "constant_value",
	"references":                        "references",
}

# Functions

get_violations = func(resources) {
	return collection.reject(resources, func(res) {
		vpc_options_values = maps.get(res, "config.vpc_options", [])
		if vpc_options_values is empty {
			return false
		}
		subnet_ids_values = maps.get(vpc_options_values[0], const.subnet_ids, [])
		if subnet_ids_values is empty {
			return false
		}
		return maps.get(subnet_ids_values, const.constant_value, []) is not empty or maps.get(subnet_ids_values, const.references, []) is not empty
	})
}

# Variables

elasticsearch_resources = tf.config(tfconfig.resources).type(const.resource_aws_elasticsearch_domain).resources
violations = get_violations(elasticsearch_resources)

summary = {
	"policy_name": const.policy_name,
	"violations": map violations as _, v {
		{
			"address":        v.address,
			"module_address": v.module_address,
			"message":        const.message,
		}
	},
}

# Outputs

print(report.generate_policy_report(summary))

# Rules

main = rule {
	violations is empty
}
