Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resource/aws_security_group_rule: Add import functionality for security group rules #6027

Merged
merged 4 commits into from
Oct 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions aws/resource_aws_security_group_rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"log"
"sort"
"strconv"
"strings"
"time"

Expand All @@ -23,6 +24,25 @@ func resourceAwsSecurityGroupRule() *schema.Resource {
Read: resourceAwsSecurityGroupRuleRead,
Update: resourceAwsSecurityGroupRuleUpdate,
Delete: resourceAwsSecurityGroupRuleDelete,
Importer: &schema.ResourceImporter{
State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
importParts, err := validateSecurityGroupRuleImportString(d.Id())
if err != nil {
return nil, err
}
if err := populateSecurityGroupRuleFromImport(d, importParts); err != nil {
return nil, err
}
err = resourceAwsSecurityGroupRuleRead(d, meta)
YakDriver marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return nil, err
}

results := make([]*schema.ResourceData, 1)
results[0] = d
return results, nil
},
},

SchemaVersion: 2,
MigrateState: resourceAwsSecurityGroupRuleMigrateState,
Expand Down Expand Up @@ -256,6 +276,7 @@ func resourceAwsSecurityGroupRuleRead(d *schema.ResourceData, meta interface{})
default:
rules = sg.IpPermissionsEgress
}
log.Printf("[DEBUG] Rules %v", rules)

p, err := expandIPPerm(d, sg)
if err != nil {
Expand Down Expand Up @@ -287,6 +308,12 @@ func resourceAwsSecurityGroupRuleRead(d *schema.ResourceData, meta interface{})

d.Set("description", descriptionFromIPPerm(d, rule))

if strings.Contains(d.Id(), "_") {
// import so fix the id
id := ipPermissionIDHash(sg_id, ruleType, p)
d.SetId(id)
}

return nil
}

Expand Down Expand Up @@ -845,3 +872,104 @@ func resourceSecurityGroupRuleDescriptionUpdate(conn *ec2.EC2, d *schema.Resourc

return nil
}

// validateSecurityGroupRuleImportString does minimal validation of import string without going to AWS
func validateSecurityGroupRuleImportString(importStr string) ([]string, error) {
// example: sg-09a093729ef9382a6_ingress_tcp_8000_8000_10.0.3.0/24
// example: sg-09a093729ef9382a6_ingress_92_0_65536_10.0.3.0/24_10.0.4.0/24
// example: sg-09a093729ef9382a6_egress_tcp_8000_8000_10.0.3.0/24
// example: sg-09a093729ef9382a6_egress_tcp_8000_8000_pl-34800000
// example: sg-09a093729ef9382a6_ingress_all_0_65536_sg-08123412342323
// example: sg-09a093729ef9382a6_ingress_tcp_100_121_10.1.0.0/16_2001:db8::/48_10.2.0.0/16_2002:db8::/48

log.Printf("[DEBUG] Validating import string %s", importStr)

importParts := strings.Split(strings.ToLower(importStr), "_")
errStr := "unexpected format of import string (%q), expected SECURITYGROUPID_TYPE_PROTOCOL_FROMPORT_TOPORT_SOURCE[_SOURCE]*: %s"
if len(importParts) < 6 {
return nil, fmt.Errorf(errStr, importStr, "too few parts")
}

sgID := importParts[0]
ruleType := importParts[1]
YakDriver marked this conversation as resolved.
Show resolved Hide resolved
protocol := importParts[2]
fromPort := importParts[3]
toPort := importParts[4]
sources := importParts[5:]

if !strings.HasPrefix(sgID, "sg-") {
return nil, fmt.Errorf(errStr, importStr, "invalid security group ID")
}

if ruleType != "ingress" && ruleType != "egress" {
return nil, fmt.Errorf(errStr, importStr, "expecting 'ingress' or 'egress'")
}

if _, ok := sgProtocolIntegers()[protocol]; !ok {
if _, err := strconv.Atoi(protocol); err != nil {
return nil, fmt.Errorf(errStr, importStr, "protocol must be tcp/udp/icmp/all or a number")
}
}

if p1, err := strconv.Atoi(fromPort); err != nil {
return nil, fmt.Errorf(errStr, importStr, "invalid port")
} else if p2, err := strconv.Atoi(toPort); err != nil || p2 < p1 {
return nil, fmt.Errorf(errStr, importStr, "invalid port")
}

for _, source := range sources {
// will be properly validated later
if source != "self" && !strings.Contains(source, "sg-") && !strings.Contains(source, "pl-") && !strings.Contains(source, ":") && !strings.Contains(source, ".") {
return nil, fmt.Errorf(errStr, importStr, "source must be cidr, ipv6cidr, prefix list, 'self', or a sg ID")
}
}

log.Printf("[DEBUG] Validated import string %s", importStr)
return importParts, nil
}

func populateSecurityGroupRuleFromImport(d *schema.ResourceData, importParts []string) error {
log.Printf("[DEBUG] Populating resource data on import: %v", importParts)

sgID := importParts[0]
ruleType := importParts[1]
protocol := importParts[2]
fromPort, _ := strconv.Atoi(importParts[3])
toPort, _ := strconv.Atoi(importParts[4])
sources := importParts[5:]

d.Set("security_group_id", sgID)

if ruleType == "ingress" {
d.Set("type", ruleType)
} else {
d.Set("type", "egress")
}

d.Set("protocol", protocolForValue(protocol))
d.Set("from_port", fromPort)
d.Set("to_port", toPort)

d.Set("self", false)
var cidrs []string
var prefixList []string
var ipv6cidrs []string
for _, source := range sources {
if source == "self" {
d.Set("self", true)
} else if strings.Contains(source, "sg-") {
d.Set("source_security_group_id", source)
} else if strings.Contains(source, "pl-") {
prefixList = append(prefixList, source)
} else if strings.Contains(source, ":") {
ipv6cidrs = append(ipv6cidrs, source)
} else {
cidrs = append(cidrs, source)
}
}
d.Set("ipv6_cidr_blocks", ipv6cidrs)
d.Set("cidr_blocks", cidrs)
d.Set("prefix_list_ids", prefixList)

return nil
}
Loading