alicloud/resource_alicloud_security_group_rule.go (503 lines of code) (raw):
package alicloud
import (
"fmt"
"log"
"strconv"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"github.com/aliyun/alibaba-cloud-sdk-go/sdk/requests"
"github.com/aliyun/alibaba-cloud-sdk-go/services/ecs"
"github.com/aliyun/terraform-provider-alicloud/alicloud/connectivity"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)
func resourceAliyunSecurityGroupRule() *schema.Resource {
return &schema.Resource{
Create: resourceAliyunSecurityGroupRuleCreate,
Read: resourceAliyunSecurityGroupRuleRead,
Update: resourceAliyunSecurityGroupRuleUpdate,
Delete: resourceAliyunSecurityGroupRuleDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"security_group_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"type": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{"ingress", "egress"}, false),
Description: "Type of rule, ingress (inbound) or egress (outbound).",
},
"ip_protocol": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringInSlice([]string{"tcp", "udp", "icmp", "gre", "all"}, false),
},
"policy": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: GroupRulePolicyAccept,
ValidateFunc: validation.StringInSlice([]string{"accept", "drop"}, false),
},
"priority": {
Type: schema.TypeInt,
Optional: true,
ForceNew: true,
Default: 1,
ValidateFunc: validation.IntBetween(1, 100),
},
"cidr_ip": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
AtLeastOneOf: []string{"cidr_ip", "ipv6_cidr_ip", "source_security_group_id", "prefix_list_id"},
},
"ipv6_cidr_ip": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"cidr_ip"},
DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool {
v, _ := compressIPv6OrCIDR(new)
return v == old
},
},
"source_security_group_id": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ConflictsWith: []string{"cidr_ip"},
},
"source_group_owner_account": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"prefix_list_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
DiffSuppressFunc: ecsSecurityGroupRulePreFixListIdDiffSuppressFunc,
},
"port_range": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Default: AllPortRange,
DiffSuppressFunc: ecsSecurityGroupRulePortRangeDiffSuppressFunc,
},
"nic_type": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
ValidateFunc: validation.StringInSlice([]string{"internet", "intranet"}, false),
},
"description": {
Type: schema.TypeString,
Optional: true,
},
"security_group_rule_id": {
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceAliyunSecurityGroupRuleCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AliyunClient)
ecsService := EcsService{client}
var response map[string]interface{}
var cidrIp string
var sourceSecurityGroupId string
var prefixListId string
request := make(map[string]interface{})
var err error
request["RegionId"] = client.RegionId
securityGroupId := d.Get("security_group_id").(string)
request["SecurityGroupId"] = securityGroupId
direction := d.Get("type").(string)
permissionsMaps := make([]map[string]interface{}, 0)
permissionsMap := map[string]interface{}{}
permissionsMap["IpProtocol"] = d.Get("ip_protocol")
if v, ok := d.GetOk("policy"); ok {
permissionsMap["Policy"] = v
}
if v, ok := d.GetOk("priority"); ok {
permissionsMap["Priority"] = strconv.Itoa(v.(int))
}
if v, ok := d.GetOk("cidr_ip"); ok {
cidrIp = v.(string)
if direction == string(DirectionIngress) {
permissionsMap["SourceCidrIp"] = v
} else {
permissionsMap["DestCidrIp"] = v
}
}
if v, ok := d.GetOk("ipv6_cidr_ip"); ok {
cidrIp = strings.Replace(v.(string), ":", "_", -1)
if direction == string(DirectionIngress) {
permissionsMap["Ipv6SourceCidrIp"] = v
} else {
permissionsMap["Ipv6DestCidrIp"] = v
}
}
if v, ok := d.GetOk("source_security_group_id"); ok {
cidrIp = v.(string)
sourceSecurityGroupId = v.(string)
if direction == string(DirectionIngress) {
permissionsMap["SourceGroupId"] = v
} else {
permissionsMap["DestGroupId"] = v
}
}
if v, ok := d.GetOk("source_group_owner_account"); ok {
if direction == string(DirectionIngress) {
permissionsMap["SourceGroupOwnerAccount"] = v
} else {
permissionsMap["DestGroupOwnerAccount"] = v
}
}
if v, ok := d.GetOk("prefix_list_id"); ok {
prefixListId = v.(string)
if direction == string(DirectionIngress) {
permissionsMap["SourcePrefixListId"] = v
} else {
permissionsMap["DestPrefixListId"] = v
}
}
if v, ok := d.GetOk("port_range"); ok {
permissionsMap["PortRange"] = v
if permissionsMap["IpProtocol"].(string) == string(Tcp) || permissionsMap["IpProtocol"].(string) == string(Udp) {
if v.(string) == AllPortRange {
return fmt.Errorf(" 'tcp' and 'udp' can support port range: [1, 65535]. Please correct it and try again.")
}
} else if v.(string) != AllPortRange {
return fmt.Errorf(" 'icmp', 'gre' and 'all' only support port range '-1/-1'. Please correct it and try again.")
}
}
securityGroup, err := ecsService.DescribeSecurityGroup(securityGroupId)
if err != nil {
return WrapError(err)
}
if v, ok := d.GetOk("nic_type"); ok {
if securityGroup.VpcId != "" || sourceSecurityGroupId != "" {
if GroupRuleNicType(v.(string)) != GroupRuleIntranet {
return fmt.Errorf(" When security group in the vpc or authorizing permission for source/destination security group, the nic_type must be 'intranet'.")
}
}
permissionsMap["NicType"] = v
}
if v, ok := d.GetOk("description"); ok {
permissionsMap["Description"] = v
}
permissionsMaps = append(permissionsMaps, permissionsMap)
request["Permissions"] = permissionsMaps
if direction == string(DirectionIngress) {
action := "AuthorizeSecurityGroup"
wait := incrementalWait(3*time.Second, 5*time.Second)
err = resource.Retry(client.GetRetryTimeout(d.Timeout(schema.TimeoutCreate)), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, request, true)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, "alicloud_security_group_rule", action, AlibabaCloudSdkGoERROR)
}
} else {
action := "AuthorizeSecurityGroupEgress"
wait := incrementalWait(3*time.Second, 5*time.Second)
err = resource.Retry(client.GetRetryTimeout(d.Timeout(schema.TimeoutCreate)), func() *resource.RetryError {
response, err = client.RpcPost("Ecs", "2014-05-26", action, nil, request, true)
if err != nil {
if NeedRetry(err) {
wait()
return resource.RetryableError(err)
}
return resource.NonRetryableError(err)
}
return nil
})
addDebug(action, response, request)
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, "alicloud_security_group_rule", action, AlibabaCloudSdkGoERROR)
}
}
if len(cidrIp) != 0 {
d.SetId(fmt.Sprintf("%v:%v:%v:%v:%v:%v:%v:%v", request["SecurityGroupId"], direction, permissionsMap["IpProtocol"], permissionsMap["PortRange"], permissionsMap["NicType"], cidrIp, permissionsMap["Policy"], permissionsMap["Priority"]))
} else {
d.SetId(fmt.Sprintf("%v:%v:%v:%v:%v:%v:%v:%v", request["SecurityGroupId"], direction, permissionsMap["IpProtocol"], permissionsMap["PortRange"], permissionsMap["NicType"], prefixListId, permissionsMap["Policy"], permissionsMap["Priority"]))
}
return resourceAliyunSecurityGroupRuleRead(d, meta)
}
func resourceAliyunSecurityGroupRuleRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AliyunClient)
ecsService := EcsService{client}
parts := strings.Split(d.Id(), ":")
policy := parseSecurityRuleId(d, meta, 6)
strPriority := parseSecurityRuleId(d, meta, 7)
var priority int
if policy == "" || strPriority == "" {
policy = d.Get("policy").(string)
priority = d.Get("priority").(int)
d.SetId(d.Id() + ":" + policy + ":" + strconv.Itoa(priority))
} else {
prior, err := strconv.Atoi(strPriority)
if err != nil {
return WrapError(err)
}
priority = prior
}
sgId := parts[0]
direction := parts[1]
// wait the rule exist
var object ecs.Permission
wait := incrementalWait(3*time.Second, 5*time.Second)
err := resource.Retry(10*time.Minute, func() *resource.RetryError {
obj, err := ecsService.DescribeSecurityGroupRule(d.Id())
if err != nil && d.IsNewResource() {
wait()
return resource.RetryableError(err)
} else {
object = obj
return resource.NonRetryableError(err)
}
})
if err != nil {
if NotFoundError(err) && !d.IsNewResource() {
log.Printf("[DEBUG] Resource alicloud_security_group_rule ecsService.DescribeSecurityGroupRule Failed!!! %s", err)
d.SetId("")
return nil
}
return WrapError(err)
}
d.Set("type", object.Direction)
d.Set("ip_protocol", strings.ToLower(string(object.IpProtocol)))
d.Set("nic_type", object.NicType)
d.Set("policy", strings.ToLower(string(object.Policy)))
d.Set("port_range", object.PortRange)
d.Set("description", object.Description)
d.Set("security_group_rule_id", object.SecurityGroupRuleId)
if pri, err := strconv.Atoi(object.Priority); err != nil {
return WrapError(err)
} else {
d.Set("priority", pri)
}
d.Set("security_group_id", sgId)
//support source and desc by type
if direction == string(DirectionIngress) {
d.Set("cidr_ip", object.SourceCidrIp)
d.Set("ipv6_cidr_ip", object.Ipv6SourceCidrIp)
d.Set("source_security_group_id", object.SourceGroupId)
d.Set("source_group_owner_account", object.SourceGroupOwnerAccount)
d.Set("prefix_list_id", object.SourcePrefixListId)
} else {
d.Set("cidr_ip", object.DestCidrIp)
d.Set("ipv6_cidr_ip", object.Ipv6DestCidrIp)
d.Set("source_security_group_id", object.DestGroupId)
d.Set("source_group_owner_account", object.DestGroupOwnerAccount)
d.Set("prefix_list_id", object.DestPrefixListId)
}
return nil
}
func resourceAliyunSecurityGroupRuleUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AliyunClient)
policy := parseSecurityRuleId(d, meta, 6)
strPriority := parseSecurityRuleId(d, meta, 7)
var priority int
if policy == "" || strPriority == "" {
policy = d.Get("policy").(string)
priority = d.Get("priority").(int)
d.SetId(d.Id() + ":" + policy + ":" + strconv.Itoa(priority))
} else {
prior, err := strconv.Atoi(strPriority)
if err != nil {
return WrapError(err)
}
priority = prior
}
request, err := buildAliyunSGRuleRequest(d, meta)
if err != nil {
return WrapError(err)
}
direction := d.Get("type").(string)
if direction == string(DirectionIngress) {
request.ApiName = "ModifySecurityGroupRule"
_, err = client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ProcessCommonRequest(request)
})
} else {
request.ApiName = "ModifySecurityGroupEgressRule"
_, err = client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ProcessCommonRequest(request)
})
}
raw, err := client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ProcessCommonRequest(request)
})
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), request.GetActionName(), AlibabaCloudSdkGoERROR)
}
addDebug(request.GetActionName(), raw, request.Headers, request)
return resourceAliyunSecurityGroupRuleRead(d, meta)
}
func deleteSecurityGroupRule(d *schema.ResourceData, meta interface{}) error {
client := meta.(*connectivity.AliyunClient)
ruleType := d.Get("type").(string)
request, err := buildAliyunSGRuleRequest(d, meta)
if err != nil {
return WrapError(err)
}
if ruleType == string(DirectionIngress) {
request.ApiName = "RevokeSecurityGroup"
_, err = client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ProcessCommonRequest(request)
})
} else {
request.ApiName = "RevokeSecurityGroupEgress"
_, err = client.WithEcsClient(func(ecsClient *ecs.Client) (interface{}, error) {
return ecsClient.ProcessCommonRequest(request)
})
}
if err != nil {
return WrapErrorf(err, DefaultErrorMsg, d.Id(), request.GetActionName(), AlibabaCloudSdkGoERROR)
}
return nil
}
func resourceAliyunSecurityGroupRuleDelete(d *schema.ResourceData, meta interface{}) error {
policy := parseSecurityRuleId(d, meta, 6)
strPriority := parseSecurityRuleId(d, meta, 7)
var priority int
if policy == "" || strPriority == "" {
policy = d.Get("policy").(string)
priority = d.Get("priority").(int)
d.SetId(d.Id() + ":" + policy + ":" + strconv.Itoa(priority))
} else {
prior, err := strconv.Atoi(strPriority)
if err != nil {
return WrapError(err)
}
priority = prior
}
err := resource.Retry(5*time.Minute, func() *resource.RetryError {
err := deleteSecurityGroupRule(d, meta)
if err != nil {
if NotFoundError(err) || IsExpectedErrors(err, []string{"InvalidSecurityGroupId.NotFound"}) {
return nil
}
return resource.RetryableError(err)
}
return nil
})
if err != nil {
return WrapError(err)
}
return nil
}
func buildAliyunSGRuleRequest(d *schema.ResourceData, meta interface{}) (*requests.CommonRequest, error) {
client := meta.(*connectivity.AliyunClient)
ecsService := EcsService{client}
// Get product code from the built request
ruleReq := ecs.CreateModifySecurityGroupRuleRequest()
request, err := client.NewCommonRequest(ruleReq.GetProduct(), ruleReq.GetLocationServiceCode(), strings.ToUpper(string(Https)), connectivity.ApiVersion20140526)
if err != nil {
return request, WrapError(err)
}
direction := d.Get("type").(string)
port_range := d.Get("port_range").(string)
request.QueryParams["PortRange"] = port_range
if v, ok := d.GetOk("ip_protocol"); ok {
request.QueryParams["IpProtocol"] = v.(string)
if v.(string) == string(Tcp) || v.(string) == string(Udp) {
if port_range == AllPortRange {
return nil, fmt.Errorf("'tcp' and 'udp' can support port range: [1, 65535]. Please correct it and try again.")
}
} else if port_range != AllPortRange {
return nil, fmt.Errorf("'icmp', 'gre' and 'all' only support port range '-1/-1'. Please correct it and try again.")
}
}
if v, ok := d.GetOk("policy"); ok {
request.QueryParams["Policy"] = v.(string)
}
if v, ok := d.GetOk("priority"); ok {
request.QueryParams["Priority"] = strconv.Itoa(v.(int))
}
if v, ok := d.GetOk("cidr_ip"); ok {
if direction == string(DirectionIngress) {
request.QueryParams["SourceCidrIp"] = v.(string)
} else {
request.QueryParams["DestCidrIp"] = v.(string)
}
}
if v, ok := d.GetOk("ipv6_cidr_ip"); ok {
if direction == string(DirectionIngress) {
request.QueryParams["Ipv6SourceCidrIp"] = v.(string)
} else {
request.QueryParams["Ipv6DestCidrIp"] = v.(string)
}
}
if v, ok := d.GetOk("prefix_list_id"); ok {
if direction == string(DirectionIngress) {
request.QueryParams["SourcePrefixListId"] = v.(string)
} else {
request.QueryParams["DestPrefixListId"] = v.(string)
}
}
var targetGroupId string
if v, ok := d.GetOk("source_security_group_id"); ok {
targetGroupId = v.(string)
if direction == string(DirectionIngress) {
request.QueryParams["SourceGroupId"] = targetGroupId
} else {
request.QueryParams["DestGroupId"] = targetGroupId
}
}
if v, ok := d.GetOk("source_group_owner_account"); ok {
if direction == string(DirectionIngress) {
request.QueryParams["SourceGroupOwnerAccount"] = v.(string)
} else {
request.QueryParams["DestGroupOwnerAccount"] = v.(string)
}
}
sgId := d.Get("security_group_id").(string)
group, err := ecsService.DescribeSecurityGroup(sgId)
if err != nil {
return nil, WrapError(err)
}
if v, ok := d.GetOk("nic_type"); ok {
if group.VpcId != "" || targetGroupId != "" {
if GroupRuleNicType(v.(string)) != GroupRuleIntranet {
return nil, fmt.Errorf("When security group in the vpc or authorizing permission for source/destination security group, " +
"the nic_type must be 'intranet'.")
}
}
request.QueryParams["NicType"] = v.(string)
}
request.QueryParams["SecurityGroupId"] = sgId
description := d.Get("description").(string)
request.QueryParams["Description"] = description
return request, nil
}
func parseSecurityRuleId(d *schema.ResourceData, meta interface{}, index int) (result string) {
parts := strings.Split(d.Id(), ":")
defer func() {
if e := recover(); e != nil {
log.Printf("Panicing %s\r\n", e)
result = ""
}
}()
return parts[index]
}