diff --git a/.changelog/3605.txt b/.changelog/3605.txt new file mode 100644 index 0000000000..daf1667a01 --- /dev/null +++ b/.changelog/3605.txt @@ -0,0 +1,3 @@ +```release-note:new-resource +tencentcloud_vpc_private_nat_gateway_translation_acl_rule +``` \ No newline at end of file diff --git a/tencentcloud/provider.go b/tencentcloud/provider.go index 3cac2cbf41..6ff378893c 100644 --- a/tencentcloud/provider.go +++ b/tencentcloud/provider.go @@ -1374,6 +1374,7 @@ func Provider() *schema.Provider { "tencentcloud_nat_refresh_nat_dc_route": vpc.ResourceTencentCloudNatRefreshNatDcRoute(), "tencentcloud_vpc_private_nat_gateway": vpc.ResourceTencentCloudVpcPrivateNatGateway(), "tencentcloud_vpc_private_nat_gateway_translation_nat_rule": vpc.ResourceTencentCloudVpcPrivateNatGatewayTranslationNatRule(), + "tencentcloud_vpc_private_nat_gateway_translation_acl_rule": vpc.ResourceTencentCloudVpcPrivateNatGatewayTranslationAclRule(), "tencentcloud_oceanus_job": oceanus.ResourceTencentCloudOceanusJob(), "tencentcloud_oceanus_job_config": oceanus.ResourceTencentCloudOceanusJobConfig(), "tencentcloud_oceanus_job_copy": oceanus.ResourceTencentCloudOceanusJobCopy(), diff --git a/tencentcloud/provider.md b/tencentcloud/provider.md index 859c8c47dc..6553b536b0 100644 --- a/tencentcloud/provider.md +++ b/tencentcloud/provider.md @@ -1297,6 +1297,7 @@ tencentcloud_protocol_template_group tencentcloud_route_table tencentcloud_vpc_private_nat_gateway tencentcloud_vpc_private_nat_gateway_translation_nat_rule +tencentcloud_vpc_private_nat_gateway_translation_acl_rule tencentcloud_route_table_association tencentcloud_route_entry tencentcloud_route_table_entry diff --git a/tencentcloud/services/vpc/resource_tc_vpc_private_nat_gateway_translation_acl_rule.go b/tencentcloud/services/vpc/resource_tc_vpc_private_nat_gateway_translation_acl_rule.go new file mode 100644 index 0000000000..74ce3a7479 --- /dev/null +++ b/tencentcloud/services/vpc/resource_tc_vpc_private_nat_gateway_translation_acl_rule.go @@ -0,0 +1,436 @@ +package vpc + +import ( + "context" + "fmt" + "log" + "strings" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + vpcv20170312 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" + + tccommon "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/common" + "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/internal/helper" +) + +func ResourceTencentCloudVpcPrivateNatGatewayTranslationAclRule() *schema.Resource { + return &schema.Resource{ + Create: resourceTencentCloudVpcPrivateNatGatewayTranslationAclRuleCreate, + Read: resourceTencentCloudVpcPrivateNatGatewayTranslationAclRuleRead, + Update: resourceTencentCloudVpcPrivateNatGatewayTranslationAclRuleUpdate, + Delete: resourceTencentCloudVpcPrivateNatGatewayTranslationAclRuleDelete, + Schema: map[string]*schema.Schema{ + "nat_gateway_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The unique ID of the private NAT gateway, in the format: `intranat-xxxxxxxx`.", + }, + + "translation_direction": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The target of the translation rule, optional value: LOCAL.", + }, + + "translation_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The type of translation rule, optional values: NETWORK_LAYER, TRANSPORT_LAYER. Corresponding to layer 3 and layer 4 respectively.", + }, + + "translation_ip": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "The mapped IP address. When the translation rule type is layer 4, it represents an IP pool.", + }, + + "translation_acl_rule": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + MinItems: 1, + Description: "Access control.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "protocol": { + Type: schema.TypeString, + Required: true, + Description: "ACL protocol type, optional values: \"ALL\", \"TCP\", \"UDP\".", + }, + "source_port": { + Type: schema.TypeString, + Required: true, + Description: "Source port.", + }, + "source_cidr": { + Type: schema.TypeString, + Required: true, + Description: "Source address. Supports `ip` or `cidr` format \"xxx.xxx.xxx.000/xx\".", + }, + "destination_port": { + Type: schema.TypeString, + Required: true, + Description: "Destination port.", + }, + "destination_cidr": { + Type: schema.TypeString, + Required: true, + Description: "Destination address.", + }, + "acl_rule_id": { + Type: schema.TypeInt, + Computed: true, + Description: "ACL rule ID.", + }, + "action": { + Type: schema.TypeInt, + Optional: true, + Description: "Whether to match.", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: "ACL rule description.", + }, + }, + }, + }, + + "original_ip": { + Type: schema.TypeString, + Optional: true, + Description: "The original IP address before mapping. Valid when the translation rule type is layer 3.", + }, + }, + } +} + +func resourceTencentCloudVpcPrivateNatGatewayTranslationAclRuleCreate(d *schema.ResourceData, meta interface{}) error { + defer tccommon.LogElapsed("resource.tencentcloud_vpc_private_nat_gateway_translation_acl_rule.create")() + defer tccommon.InconsistentCheck(d, meta)() + + var ( + logId = tccommon.GetLogId(tccommon.ContextNil) + ctx = tccommon.NewResourceLifeCycleHandleFuncContext(context.Background(), logId, d, meta) + request = vpcv20170312.NewCreatePrivateNatGatewayTranslationAclRuleRequest() + response = vpcv20170312.NewCreatePrivateNatGatewayTranslationAclRuleResponse() + natGatewayId string + translationDirection string + translationType string + translationIp string + aclRuleId string + ) + + if v, ok := d.GetOk("nat_gateway_id"); ok { + request.NatGatewayId = helper.String(v.(string)) + natGatewayId = v.(string) + } + + if v, ok := d.GetOk("translation_direction"); ok { + request.TranslationDirection = helper.String(v.(string)) + translationDirection = v.(string) + } + + if v, ok := d.GetOk("translation_type"); ok { + request.TranslationType = helper.String(v.(string)) + translationType = v.(string) + } + + if v, ok := d.GetOk("translation_ip"); ok { + request.TranslationIp = helper.String(v.(string)) + translationIp = v.(string) + } + + if v, ok := d.GetOk("translation_acl_rules"); ok { + for _, item := range v.([]interface{}) { + translationAclRulesMap := item.(map[string]interface{}) + translationAclRule := vpcv20170312.TranslationAclRule{} + if v, ok := translationAclRulesMap["protocol"].(string); ok && v != "" { + translationAclRule.Protocol = helper.String(v) + } + + if v, ok := translationAclRulesMap["source_port"].(string); ok && v != "" { + translationAclRule.SourcePort = helper.String(v) + } + + if v, ok := translationAclRulesMap["source_cidr"].(string); ok && v != "" { + translationAclRule.SourceCidr = helper.String(v) + } + + if v, ok := translationAclRulesMap["destination_port"].(string); ok && v != "" { + translationAclRule.DestinationPort = helper.String(v) + } + + if v, ok := translationAclRulesMap["destination_cidr"].(string); ok && v != "" { + translationAclRule.DestinationCidr = helper.String(v) + } + + if v, ok := translationAclRulesMap["action"].(int); ok { + translationAclRule.Action = helper.IntUint64(v) + } + + if v, ok := translationAclRulesMap["description"].(string); ok && v != "" { + translationAclRule.Description = helper.String(v) + } + + request.TranslationAclRules = append(request.TranslationAclRules, &translationAclRule) + } + } + + if v, ok := d.GetOk("original_ip"); ok { + request.OriginalIp = helper.String(v.(string)) + } + + reqErr := resource.Retry(tccommon.WriteRetryTimeout, func() *resource.RetryError { + result, e := meta.(tccommon.ProviderMeta).GetAPIV3Conn().UseVpcClient().CreatePrivateNatGatewayTranslationAclRuleWithContext(ctx, request) + if e != nil { + return tccommon.RetryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + + if result == nil || result.Response == nil { + return resource.NonRetryableError(fmt.Errorf("Create vpc private nat gateway translation acl rule failed, Response is nil.")) + } + + response = result + return nil + }) + + if reqErr != nil { + log.Printf("[CRITAL]%s create vpc private nat gateway translation acl rule failed, reason:%+v", logId, reqErr) + return reqErr + } + + if response.Response.AclRuleId == nil { + return fmt.Errorf("AclRuleId is nil.") + } + + aclRuleId = response.Response.AclRuleId + d.SetId(strings.Join([]string{natGatewayId, translationDirection, translationType, translationIp, aclRuleId}, tccommon.FILED_SP)) + return resourceTencentCloudVpcPrivateNatGatewayTranslationAclRuleRead(d, meta) +} + +func resourceTencentCloudVpcPrivateNatGatewayTranslationAclRuleRead(d *schema.ResourceData, meta interface{}) error { + defer tccommon.LogElapsed("resource.tencentcloud_vpc_private_nat_gateway_translation_acl_rule.read")() + defer tccommon.InconsistentCheck(d, meta)() + + var ( + logId = tccommon.GetLogId(tccommon.ContextNil) + ctx = tccommon.NewResourceLifeCycleHandleFuncContext(context.Background(), logId, d, meta) + service = VpcService{client: meta.(tccommon.ProviderMeta).GetAPIV3Conn()} + ) + + idSplit := strings.Split(d.Id(), tccommon.FILED_SP) + if len(idSplit) != 5 { + return fmt.Errorf("id is broken,%s", d.Id()) + } + + natGatewayId := idSplit[0] + translationDirection := idSplit[1] + translationType := idSplit[2] + translationIp := idSplit[3] + aclRuleId := idSplit[4] + + respData, err := service.DescribeVpcPrivateNatGatewayTranslationAclRuleById(ctx, natGatewayId, translationDirection, translationType, translationIp, aclRuleId) + if err != nil { + return err + } + + if respData == nil { + log.Printf("[WARN]%s resource `tencentcloud_vpc_private_nat_gateway_translation_acl_rule` [%s] not found, please check if it has been deleted.\n", logId, d.Id()) + d.SetId("") + return nil + } + + _ = d.Set("nat_gateway_id", natGatewayId) + _ = d.Set("translation_direction", translationDirection) + _ = d.Set("translation_type", translationType) + _ = d.Set("translation_ip", translationIp) + + translationAclRuleSetMap := map[string]interface{}{} + if respData.Protocol != nil { + translationAclRuleSetMap["protocol"] = respData.Protocol + } + + if respData.SourcePort != nil { + translationAclRuleSetMap["source_port"] = respData.SourcePort + } + + if respData.SourceCidr != nil { + translationAclRuleSetMap["source_cidr"] = respData.SourceCidr + } + + if respData.DestinationPort != nil { + translationAclRuleSetMap["destination_port"] = respData.DestinationPort + } + + if respData.DestinationCidr != nil { + translationAclRuleSetMap["destination_cidr"] = respData.DestinationCidr + } + + if respData.AclRuleId != nil { + translationAclRuleSetMap["acl_rule_id"] = respData.AclRuleId + } + + if respData.Action != nil { + translationAclRuleSetMap["action"] = respData.Action + } + + if respData.Description != nil { + translationAclRuleSetMap["description"] = respData.Description + } + + _ = d.Set("translation_acl_rule", []interface{}{translationAclRuleSetMap}) + return nil +} + +func resourceTencentCloudVpcPrivateNatGatewayTranslationAclRuleUpdate(d *schema.ResourceData, meta interface{}) error { + defer tccommon.LogElapsed("resource.tencentcloud_vpc_private_nat_gateway_translation_acl_rule.update")() + defer tccommon.InconsistentCheck(d, meta)() + + var ( + logId = tccommon.GetLogId(tccommon.ContextNil) + ctx = tccommon.NewResourceLifeCycleHandleFuncContext(context.Background(), logId, d, meta) + ) + + idSplit := strings.Split(d.Id(), tccommon.FILED_SP) + if len(idSplit) != 5 { + return fmt.Errorf("id is broken,%s", d.Id()) + } + + natGatewayId := idSplit[0] + translationDirection := idSplit[1] + translationType := idSplit[2] + translationIp := idSplit[3] + aclRuleId := idSplit[4] + + needChange := false + mutableArgs := []string{"translation_acl_rules", "original_ip"} + for _, v := range mutableArgs { + if d.HasChange(v) { + needChange = true + break + } + } + + if needChange { + request := vpcv20170312.NewModifyPrivateNatGatewayTranslationAclRuleRequest() + if v, ok := d.GetOk("translation_acl_rule"); ok { + for _, item := range v.([]interface{}) { + translationAclRulesMap := item.(map[string]interface{}) + translationAclRule := vpcv20170312.TranslationAclRule{} + if v, ok := translationAclRulesMap["protocol"].(string); ok && v != "" { + translationAclRule.Protocol = helper.String(v) + } + + if v, ok := translationAclRulesMap["source_port"].(string); ok && v != "" { + translationAclRule.SourcePort = helper.String(v) + } + + if v, ok := translationAclRulesMap["source_cidr"].(string); ok && v != "" { + translationAclRule.SourceCidr = helper.String(v) + } + + if v, ok := translationAclRulesMap["destination_port"].(string); ok && v != "" { + translationAclRule.DestinationPort = helper.String(v) + } + + if v, ok := translationAclRulesMap["destination_cidr"].(string); ok && v != "" { + translationAclRule.DestinationCidr = helper.String(v) + } + + if v, ok := translationAclRulesMap["acl_rule_id"].(int); ok { + translationAclRule.AclRuleId = helper.IntUint64(v) + } + + if v, ok := translationAclRulesMap["action"].(int); ok { + translationAclRule.Action = helper.IntUint64(v) + } + + if v, ok := translationAclRulesMap["description"].(string); ok && v != "" { + translationAclRule.Description = helper.String(v) + } + + request.TranslationAclRules = append(request.TranslationAclRules, &translationAclRule) + } + } + + if v, ok := d.GetOk("original_ip"); ok { + request.OriginalIp = helper.String(v.(string)) + } + + request.NatGatewayId = &natGatewayId + request.TranslationDirection = &translationDirection + request.TranslationType = &translationType + request.TranslationIp = &translationIp + reqErr := resource.Retry(tccommon.WriteRetryTimeout, func() *resource.RetryError { + result, e := meta.(tccommon.ProviderMeta).GetAPIV3Conn().UseVpcClient().ModifyPrivateNatGatewayTranslationAclRuleWithContext(ctx, request) + if e != nil { + return tccommon.RetryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + + return nil + }) + + if reqErr != nil { + log.Printf("[CRITAL]%s update vpc private nat gateway translation acl rule failed, reason:%+v", logId, reqErr) + return reqErr + } + } + + return resourceTencentCloudVpcPrivateNatGatewayTranslationAclRuleRead(d, meta) +} + +func resourceTencentCloudVpcPrivateNatGatewayTranslationAclRuleDelete(d *schema.ResourceData, meta interface{}) error { + defer tccommon.LogElapsed("resource.tencentcloud_vpc_private_nat_gateway_translation_acl_rule.delete")() + defer tccommon.InconsistentCheck(d, meta)() + + var ( + logId = tccommon.GetLogId(tccommon.ContextNil) + ctx = tccommon.NewResourceLifeCycleHandleFuncContext(context.Background(), logId, d, meta) + request = vpcv20170312.NewDeletePrivateNatGatewayTranslationAclRuleRequest() + response = vpcv20170312.NewDeletePrivateNatGatewayTranslationAclRuleResponse() + ) + + idSplit := strings.Split(d.Id(), tccommon.FILED_SP) + if len(idSplit) != 5 { + return fmt.Errorf("id is broken,%s", d.Id()) + } + + natGatewayId := idSplit[0] + translationDirection := idSplit[1] + translationType := idSplit[2] + translationIp := idSplit[3] + aclRuleId := idSplit[4] + + request.NatGatewayId = &natGatewayId + request.TranslationDirection = &translationDirection + request.TranslationType = &translationType + request.TranslationIp = &translationIp + request.AclRuleIds = append(request.AclRuleIds, helper.IntUint64(aclRuleId)) + reqErr := resource.Retry(tccommon.WriteRetryTimeout, func() *resource.RetryError { + result, e := meta.(tccommon.ProviderMeta).GetAPIV3Conn().UseVpcClient().DeletePrivateNatGatewayTranslationAclRuleWithContext(ctx, request) + if e != nil { + return tccommon.RetryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + + return nil + }) + + if reqErr != nil { + log.Printf("[CRITAL]%s delete vpc private nat gateway translation acl rule failed, reason:%+v", logId, reqErr) + return reqErr + } + + return nil +} diff --git a/tencentcloud/services/vpc/resource_tc_vpc_private_nat_gateway_translation_acl_rule.md b/tencentcloud/services/vpc/resource_tc_vpc_private_nat_gateway_translation_acl_rule.md new file mode 100644 index 0000000000..52ed759cc3 --- /dev/null +++ b/tencentcloud/services/vpc/resource_tc_vpc_private_nat_gateway_translation_acl_rule.md @@ -0,0 +1,30 @@ +Provides a resource to create a VPC private nat gateway translation acl rule + +Example Usage + +```hcl +resource "tencentcloud_vpc_private_nat_gateway_translation_acl_rule" "example" { + nat_gateway_id = "" + translation_direction = "" + translation_type = "" + translation_ip = "" + original_ip = "" + translation_acl_rule { + protocol = "" + source_port = "" + source_cidr = "" + destination_port = "" + destination_cidr = "" + action = "" + description = "" + } +} +``` + +Import + +VPC private nat gateway translation acl rule can be imported using the natGatewayId#translationDirection#translationType#translationIp#aclRuleId, e.g. + +``` +terraform import tencentcloud_vpc_private_nat_gateway_translation_acl_rule.example vpc_private_nat_gateway_translation_acl_rule_id +``` diff --git a/tencentcloud/services/vpc/resource_tc_vpc_private_nat_gateway_translation_acl_rule_test.go b/tencentcloud/services/vpc/resource_tc_vpc_private_nat_gateway_translation_acl_rule_test.go new file mode 100644 index 0000000000..4a356f9825 --- /dev/null +++ b/tencentcloud/services/vpc/resource_tc_vpc_private_nat_gateway_translation_acl_rule_test.go @@ -0,0 +1,76 @@ +package vpc_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + tcacctest "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/acctest" +) + +func TestAccTencentCloudVpcPrivateNatGatewayTranslationAclRuleResource_basic(t *testing.T) { + t.Parallel() + resource.Test(t, resource.TestCase{ + PreCheck: func() { + tcacctest.AccPreCheck(t) + }, + Providers: tcacctest.AccProviders, + Steps: []resource.TestStep{ + { + Config: testAccVpcPrivateNatGatewayTranslationAclRule, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("tencentcloud_vpc_private_nat_gateway_translation_acl_rule.example", "id"), + ), + }, + { + Config: testAccVpcPrivateNatGatewayTranslationAclRuleUpdate, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("tencentcloud_vpc_private_nat_gateway_translation_acl_rule.example", "id"), + ), + }, + { + ResourceName: "tencentcloud_vpc_private_nat_gateway_translation_acl_rule.example", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +const testAccVpcPrivateNatGatewayTranslationAclRule = ` +resource "tencentcloud_vpc_private_nat_gateway_translation_acl_rule" "example" { + nat_gateway_id = "" + translation_direction = "" + translation_type = "" + translation_ip = "" + original_ip = "" + translation_acl_rule { + protocol = "" + source_port = "" + source_cidr = "" + destination_port = "" + destination_cidr = "" + action = "" + description = "" + } +} +` + +const testAccVpcPrivateNatGatewayTranslationAclRuleUpdate = ` +resource "tencentcloud_vpc_private_nat_gateway_translation_acl_rule" "example" { + nat_gateway_id = "" + translation_direction = "" + translation_type = "" + translation_ip = "" + original_ip = "" + translation_acl_rule { + protocol = "" + source_port = "" + source_cidr = "" + destination_port = "" + destination_cidr = "" + action = "" + description = "" + } +} +` diff --git a/tencentcloud/services/vpc/service_tencentcloud_vpc.go b/tencentcloud/services/vpc/service_tencentcloud_vpc.go index 8860856e57..60b2c85ed2 100644 --- a/tencentcloud/services/vpc/service_tencentcloud_vpc.go +++ b/tencentcloud/services/vpc/service_tencentcloud_vpc.go @@ -8618,3 +8618,72 @@ func (me *VpcService) DescribeVpcPrivateNatGatewayTranslationNatRuleById(ctx con return } + +func (me *VpcService) DescribeVpcPrivateNatGatewayTranslationAclRuleById(ctx context.Context, natGatewayId, translationDirection, translationType, translationIp, aclRuleId string) (ret *vpc.TranslationAclRule, errRet error) { + logId := tccommon.GetLogId(ctx) + + request := vpc.NewDescribePrivateNatGatewayTranslationAclRulesRequest() + response := vpc.NewDescribePrivateNatGatewayTranslationAclRulesResponse() + request.NatGatewayId = &natGatewayId + request.TranslationDirection = &translationDirection + request.TranslationType = &translationType + request.TranslationIp = &translationIp + + defer func() { + if errRet != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", logId, request.GetAction(), request.ToJsonString(), errRet.Error()) + } + }() + + var ( + offset uint64 = 0 + limit uint64 = 100 + ruleSet []*vpc.TranslationAclRule + ) + + for { + request.Offset = &offset + request.Limit = &limit + err := resource.Retry(tccommon.ReadRetryTimeout, func() *resource.RetryError { + ratelimit.Check(request.GetAction()) + result, e := me.client.UseVpcClient().DescribePrivateNatGatewayTranslationAclRules(request) + if e != nil { + return tccommon.RetryError(e) + } else { + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", logId, request.GetAction(), request.ToJsonString(), result.ToJsonString()) + } + + if result == nil || result.Response == nil || result.Response.TranslationAclRuleSet == nil { + return resource.NonRetryableError(fmt.Errorf("Describe private nat gateway translation acl rules failed, Response is nil.")) + } + + response = result + return nil + }) + + if err != nil { + errRet = err + return + } + + if len(response.Response.TranslationAclRuleSet) < 1 { + break + } + + ruleSet = append(ruleSet, response.Response.TranslationAclRuleSet...) + if len(response.Response.TranslationAclRuleSet) < int(limit) { + break + } + + offset += limit + } + + for _, item := range ruleSet { + if item != nil && item.AclRuleId != nil && helper.UInt64ToStr(*item.AclRuleId) == aclRuleId { + ret = item + return + } + } + + return +}