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] }