package aws import ( "bytes" "fmt" "strconv" "github.com/cloudskiff/driftctl/pkg/resource" "github.com/hashicorp/terraform/helper/hashcode" ) const AwsNetworkACLRuleResourceType = "aws_network_acl_rule" var protocolsNumbers = map[string]int{ // defined at https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml "all": -1, "hopopt": 0, "icmp": 1, "igmp": 2, "ggp": 3, "ipv4": 4, "st": 5, "tcp": 6, "cbt": 7, "egp": 8, "igp": 9, "bbn-rcc-mon": 10, "nvp-ii": 11, "pup": 12, "argus": 13, "emcon": 14, "xnet": 15, "chaos": 16, "udp": 17, "mux": 18, "dcn-meas": 19, "hmp": 20, "prm": 21, "xns-idp": 22, "trunk-1": 23, "trunk-2": 24, "leaf-1": 25, "leaf-2": 26, "rdp": 27, "irtp": 28, "iso-tp4": 29, "netblt": 30, "mfe-nsp": 31, "merit-inp": 32, "dccp": 33, "3pc": 34, "idpr": 35, "xtp": 36, "ddp": 37, "idpr-cmtp": 38, "tp++": 39, "il": 40, "ipv6": 41, "sdrp": 42, "ipv6-route": 43, "ipv6-frag": 44, "idrp": 45, "rsvp": 46, "gre": 47, "dsr": 48, "bna": 49, "esp": 50, "ah": 51, "i-nlsp": 52, "swipe": 53, "narp": 54, "mobile": 55, "tlsp": 56, "ipv6-icmp": 58, "ipv6-nonxt": 59, "ipv6-opts": 60, "61": 61, "cftp": 62, "63": 63, "sat-expak": 64, "kryptolan": 65, "rvd": 66, "ippc": 67, "68": 68, "sat-mon": 69, "visa": 70, "ipcv": 71, "cpnx": 72, "cphb": 73, "wsn": 74, "pvp": 75, "br-sat-mon": 76, "sun-nd": 77, "wb-mon": 78, "wb-expak": 79, "iso-ip": 80, "vmtp": 81, "secure-vmtp": 82, "vines": 83, "ttp": 84, "nsfnet-igp": 85, "dgp": 86, "tcf": 87, "eigrp": 88, "ospfigp": 89, "sprite-rpc": 90, "larp": 91, "mtp": 92, "ax.25": 93, "ipip": 94, "micp": 95, "scc-sp": 96, "etherip": 97, "encap": 98, "99": 99, "gmtp": 100, "ifmp": 101, "pnni": 102, "pim": 103, "aris": 104, "scps": 105, "qnx": 106, "a/n": 107, "ipcomp": 108, "snp": 109, "compaq-peer": 110, "ipx-in-ip": 111, "vrrp": 112, "pgm": 113, "114": 114, "l2tp": 115, "dd": 116, "iatp": 117, "stp": 118, "srp": 119, "uti": 120, "smp": 121, "sm": 122, "ptp": 123, "isis-over-ipv4": 124, "fire": 125, "crtp": 126, "crudp": 127, "sscopmce": 128, "iplt": 129, "sps": 130, "pipe": 131, "sctp": 132, "fc": 133, "rsvp-e2e-ignore": 134, "mobility-header": 135, "udplite": 136, "mpls-in-ip": 137, "manet": 138, "hip": 139, "shim6": 140, "wesp": 141, "rohc": 142, "253": 253, "254": 254, } func initAwsNetworkACLRuleMetaData(resourceSchemaRepository resource.SchemaRepositoryInterface) { resourceSchemaRepository.SetFlags(AwsNetworkACLRuleResourceType, resource.FlagDeepMode) resourceSchemaRepository.SetHumanReadableAttributesFunc(AwsNetworkACLRuleResourceType, func(res *resource.Resource) map[string]string { ruleNumber := strconv.FormatInt(int64(*res.Attrs.GetFloat64("rule_number")), 10) if ruleNumber == "32767" { ruleNumber = "*" } attrs := map[string]string{ "Network": *res.Attrs.GetString("network_acl_id"), "Egress": strconv.FormatBool(*res.Attrs.GetBool("egress")), "Rule number": ruleNumber, } if proto := res.Attrs.GetString("protocol"); proto != nil { if *proto == "-1" { *proto = "All" } attrs["Protocol"] = *proto } if res.Attrs.GetFloat64("from_port") != nil && res.Attrs.GetFloat64("to_port") != nil { attrs["Port range"] = fmt.Sprintf("%d - %d", int64(*res.Attrs.GetFloat64("from_port")), int64(*res.Attrs.GetFloat64("to_port")), ) } if cidr := res.Attrs.GetString("cidr_block"); cidr != nil && *cidr != "" { attrs["CIDR"] = *cidr } if cidr := res.Attrs.GetString("ipv6_cidr_block"); cidr != nil && *cidr != "" { attrs["CIDR"] = *cidr } return attrs }) resourceSchemaRepository.SetResolveReadAttributesFunc(AwsNetworkACLRuleResourceType, func(res *resource.Resource) map[string]string { return map[string]string{ "network_acl_id": *res.Attrs.GetString("network_acl_id"), "rule_number": strconv.FormatInt(int64(*res.Attrs.GetFloat64("rule_number")), 10), "egress": strconv.FormatBool(*res.Attrs.GetBool("egress")), } }) resourceSchemaRepository.SetNormalizeFunc(AwsNetworkACLRuleResourceType, func(res *resource.Resource) { res.Attrs.DeleteIfDefault("icmp_code") res.Attrs.DeleteIfDefault("icmp_type") // Since it seems that AWS only works with protocol number, we should normalize when we got a protocol string // and transform it to its proper protocol number // We iterate on ingress and egresses to modify protocols that are full string like "tcp" to "6" // // References: // - https://github.com/hashicorp/terraform-provider-aws/blob/1194e7a11e6b74f1f4834c90940ffef0f6557982/aws/network_acl_entry.go#L69 proto := res.Attrs.GetString("protocol") if number, isNotProtoAsNumber := protocolsNumbers[*proto]; isNotProtoAsNumber { _ = res.Attrs.SafeSet([]string{"protocol"}, strconv.Itoa(number)) } // ID can be different even if the resource is the same. // protocol is taken into account while creating the ID, if you set protocol="tcp" you'll end with // a resource with a different ID than if you set protocol="6" which is the same // To be able to match resources, we rewrite ID to always use protocol as a number (we just normalized this above) // // While reading remote we always got protocol as a number. // We cannot predict how the user decided to write the protocol on IaC side. // This workaround is mandatory to harmonize resources ID res.Id = CreateNetworkACLRuleID( *res.Attrs.GetString("network_acl_id"), int(*res.Attrs.GetFloat64("rule_number")), *res.Attrs.GetBool("egress"), *res.Attrs.GetString("protocol"), ) _ = res.Attrs.SafeSet([]string{"id"}, res.Id) res.Attrs.DeleteIfDefault("cidr_block") res.Attrs.DeleteIfDefault("ipv6_cidr_block") }) } func CreateNetworkACLRuleID(networkAclId string, ruleNumber int, egress bool, protocol string) string { var buf bytes.Buffer buf.WriteString(fmt.Sprintf("%s-", networkAclId)) buf.WriteString(fmt.Sprintf("%d-", ruleNumber)) buf.WriteString(fmt.Sprintf("%t-", egress)) buf.WriteString(fmt.Sprintf("%s-", protocol)) return fmt.Sprintf("nacl-%d", hashcode.String(buf.String())) }