driftctl/pkg/resource/aws/aws_network_acl_rule.go

253 lines
7.4 KiB
Go

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()))
}