diff --git a/tencentcloud/provider.go b/tencentcloud/provider.go index bb0e2a2463..1deb6045b2 100644 --- a/tencentcloud/provider.go +++ b/tencentcloud/provider.go @@ -477,6 +477,7 @@ SSL Certificates Resource tencentcloud_ssl_certificate tencentcloud_ssl_pay_certificate + tencentcloud_ssl_free_certificate SSM Data Source @@ -1022,6 +1023,7 @@ func Provider() terraform.ResourceProvider { "tencentcloud_gaap_domain_error_page": resourceTencentCloudGaapDomainErrorPageInfo(), "tencentcloud_ssl_certificate": resourceTencentCloudSslCertificate(), "tencentcloud_ssl_pay_certificate": resourceTencentCloudSSLInstance(), + "tencentcloud_ssl_free_certificate": resourceTencentCloudSSLFreeCertificate(), "tencentcloud_cam_role": resourceTencentCloudCamRole(), "tencentcloud_cam_user": resourceTencentCloudCamUser(), "tencentcloud_cam_policy": resourceTencentCloudCamPolicy(), diff --git a/tencentcloud/resource_tc_ssl_free_certificate.go b/tencentcloud/resource_tc_ssl_free_certificate.go new file mode 100644 index 0000000000..ac1388ca72 --- /dev/null +++ b/tencentcloud/resource_tc_ssl_free_certificate.go @@ -0,0 +1,399 @@ +/* +Provide a resource to create a Free Certificate. +~> **NOTE:** Once certificat created, it cannot be removed within 1 hours. + +Example Usage + +```hcl +resource "tencentcloud_ssl_free_certificate" "foo" { + dv_auth_method = "DNS_AUTO" + domain = "example.com" + package_type = "2" + contact_email = "foo@example.com" + contact_phone = "12345678901" + validity_period = 12 + csr_encrypt_algo = "RSA" + csr_key_parameter = "2048" + csr_key_password = "xxxxxxxx" + alias = "my_free_cert" +} +``` + + +Import + +FreeCertificate instance can be imported, e.g. +``` +$ terraform import tencentcloud_ssl_free_certificate.test free_certificate-id +``` + +*/ +package tencentcloud + +import ( + "context" + "fmt" + "strconv" + + ssl2 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/internal/helper" +) + +func resourceTencentCloudSSLFreeCertificate() *schema.Resource { + return &schema.Resource{ + Read: resourceTencentCloudFreeCertificateRead, + Create: resourceTencentCloudFreeCertificateCreate, + Update: resourceTencentCloudFreeCertificateUpdate, + Delete: resourceTencentCloudFreeCertificateDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "dv_auth_method": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Specify DV authorize method, available values: `DNS_AUTO` - automatic DNS auth, `DNS` - manual DNS auth, `FILE` - auth by file.", + }, + "domain": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Specify domain name.", + }, + "package_type": { + Type: schema.TypeString, + Optional: true, + Description: "Type of package. Only support `\"2\"` (TrustAsia TLS RSA CA).", + }, + "contact_email": { + Type: schema.TypeString, + Optional: true, + Description: "Email address.", + }, + "contact_phone": { + Type: schema.TypeString, + Optional: true, + Description: "Phone number.", + }, + "validity_period": { + Type: schema.TypeString, + Optional: true, + Description: "Specify validity period in month, only support `\"12\"` months for now.", + }, + "csr_encrypt_algo": { + Type: schema.TypeString, + Optional: true, + Description: "Specify CSR encrypt algorithm, only support `RSA` for now.", + }, + "csr_key_parameter": { + Type: schema.TypeString, + Optional: true, + Description: "Specify CSR key parameter, only support `\"2048\"` for now.", + }, + "csr_key_password": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + Description: "Specify CSR key password.", + }, + "alias": { + Type: schema.TypeString, + Optional: true, + Description: "Specify alias for remark.", + }, + "project_id": { + Type: schema.TypeInt, + Optional: true, + Description: "ID of projects which this certification belong to.", + }, + "old_certificate_id": { + Type: schema.TypeString, + ForceNew: true, + Optional: true, + Description: "Specify old certificate ID, used for re-apply.", + }, + "status": { + Type: schema.TypeInt, + Computed: true, + Description: "Certificate status. 0 = Approving, 1 = Approved, 2 = Approve failed, 3 = expired, 4 = DNS record added, 5 = OV/EV Certificate and confirm letter needed, 6 = Order canceling, 7 = Order canceled, 8 = Submitted and confirm letter needed, 9 = Revoking, 10 = Revoked, 11 = re-applying, 12 = Revoke and confirm letter needed, 13 = Free SSL and confirm letter needed.", + }, + "status_name": { + Type: schema.TypeString, + Computed: true, + Description: "Certificate status name.", + }, + "status_msg": { + Type: schema.TypeString, + Computed: true, + Description: "Certificate status message.", + }, + "product_zh_name": { + Type: schema.TypeString, + Computed: true, + Description: "Product zh name.", + }, + "vulnerability_status": { + Type: schema.TypeString, + Computed: true, + Description: "Vulnerability status.", + }, + "cert_begin_time": { + Type: schema.TypeString, + Computed: true, + Description: "Certificate begin time.", + }, + "cert_end_time": { + Type: schema.TypeString, + Computed: true, + Description: "Certificate begin time.", + }, + "insert_time": { + Type: schema.TypeString, + Computed: true, + Description: "Certificate insert time.", + }, + "certificate_private_key": { + Type: schema.TypeString, + Computed: true, + Description: "Certificate private key.", + }, + "certificate_public_key": { + Type: schema.TypeString, + Computed: true, + Description: "Certificate public key.", + }, + "renewable": { + Type: schema.TypeBool, + Computed: true, + Description: "Indicates whether the certificate renewable.", + }, + "deployable": { + Type: schema.TypeBool, + Computed: true, + Description: "Indicates whether the certificate deployable.", + }, + }, + } +} + +func resourceTencentCloudFreeCertificateRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_ssl_free_certificate.read")() + defer inconsistentCheck(d, meta)() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + client := meta.(*TencentCloudClient).apiV3Conn + service := SSLService{client} + id := d.Id() + + request := ssl2.NewDescribeCertificateDetailRequest() + request.CertificateId = &id + + response, err := service.DescribeCertificateDetail(ctx, request) + + if err != nil { + d.SetId("") + return err + } + + detail := response.Response + + d.SetId(id) + + if detail.Domain != nil { + _ = d.Set("domain", detail.Domain) + } + if detail.PackageType != nil { + _ = d.Set("package_type", detail.PackageType) + } + if detail.ValidityPeriod != nil { + _ = d.Set("validity_period", detail.ValidityPeriod) + } + if detail.Alias != nil { + _ = d.Set("alias", detail.Alias) + } + + if detail.ProductZhName != nil { + _ = d.Set("product_zh_name", detail.ProductZhName) + } + if detail.Status != nil { + _ = d.Set("status", detail.Status) + } + if detail.StatusMsg != nil { + _ = d.Set("status_msg", detail.StatusMsg) + } + if detail.VulnerabilityStatus != nil { + _ = d.Set("vulnerability_status", detail.VulnerabilityStatus) + } + if detail.CertBeginTime != nil { + _ = d.Set("cert_begin_time", detail.CertBeginTime) + } + if detail.CertEndTime != nil { + _ = d.Set("cert_end_time", detail.CertEndTime) + } + if detail.InsertTime != nil { + _ = d.Set("insert_time", detail.InsertTime) + } + if detail.CertificatePrivateKey != nil { + _ = d.Set("certificate_private_key", detail.CertificatePrivateKey) + } + if detail.CertificatePublicKey != nil { + _ = d.Set("certificate_public_key", detail.CertificatePublicKey) + } + if detail.StatusName != nil { + _ = d.Set("status_name", detail.StatusName) + } + if detail.RenewAble != nil { + _ = d.Set("renewable", detail.RenewAble) + } + if detail.Deployable != nil { + _ = d.Set("deployable", detail.Deployable) + } + + if detail.ProjectId != nil { + pid, err := strconv.Atoi(*detail.ProjectId) + if err != nil { + return err + } + _ = d.Set("project_id", pid) + } + + return nil +} + +func resourceTencentCloudFreeCertificateCreate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_ssl_free_certificate.create")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + client := meta.(*TencentCloudClient).apiV3Conn + service := SSLService{client} + + var ( + authMethod = d.Get("dv_auth_method").(string) + domain = d.Get("domain").(string) + ) + + request := ssl2.NewApplyCertificateRequest() + + request.DvAuthMethod = &authMethod + request.DomainName = &domain + + if v, ok := d.GetOk("package_type"); ok { + request.PackageType = helper.String(v.(string)) + } + if v, ok := d.GetOk("contact_email"); ok { + request.ContactEmail = helper.String(v.(string)) + } + if v, ok := d.GetOk("contact_phone"); ok { + request.ContactPhone = helper.String(v.(string)) + } + if v, ok := d.GetOk("validity_period"); ok { + request.ValidityPeriod = helper.String(v.(string)) + } + if v, ok := d.GetOk("csr_encrypt_algo"); ok { + request.CsrEncryptAlgo = helper.String(v.(string)) + } + if v, ok := d.GetOk("csr_key_parameter"); ok { + request.CsrKeyParameter = helper.String(v.(string)) + } + if v, ok := d.GetOk("csr_key_password"); ok { + request.CsrKeyPassword = helper.String(v.(string)) + } + if v, ok := d.GetOk("alias"); ok { + request.Alias = helper.String(v.(string)) + } + if v, ok := d.GetOk("project_id"); ok && v.(int) > 0 { + request.ProjectId = helper.IntUint64(v.(int)) + } + if v, ok := d.GetOk("old_certificate_id"); ok { + request.OldCertificateId = helper.String(v.(string)) + } + + id, err := service.ApplyCertificate(ctx, request) + if err != nil { + return err + } + + d.SetId(id) + + return resourceTencentCloudFreeCertificateRead(d, meta) +} + +func resourceTencentCloudFreeCertificateUpdate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_ssl_free_certificate.update")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + client := meta.(*TencentCloudClient).apiV3Conn + service := SSLService{client} + + id := d.Id() + + if d.HasChange("alias") { + request := ssl2.NewModifyCertificateAliasRequest() + alias, ok := d.GetOk("alias") + if ok { + request.Alias = helper.String(alias.(string)) + } else { + request.Alias = helper.String("") + } + request.CertificateId = &id + if err := service.ModifyCertificateAlias(ctx, request); err != nil { + return err + } + } + + if d.HasChange("project_id") { + request := ssl2.NewModifyCertificateProjectRequest() + pid, ok := d.GetOk("project_id") + if ok { + request.ProjectId = helper.IntUint64(pid.(int)) + } else { + request.ProjectId = helper.IntUint64(-1) + } + request.CertificateIdList = []*string{&id} + if err := service.ModifyCertificateProject(ctx, request); err != nil { + return err + } + } + + immutableFields := []string{ + "package_type", + "contact_email", + "contact_phone", + "validity_period", + "csr_encrypt_algo", + "csr_key_parameter", + "csr_key_password", + } + for _, f := range immutableFields { + if d.HasChange(f) { + return fmt.Errorf("cannot update argument `%s`, please reset to previous value or replace by creating a new resource", f) + } + } + + return resourceTencentCloudFreeCertificateRead(d, meta) +} + +func resourceTencentCloudFreeCertificateDelete(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_ssl_free_certificate.delete")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + client := meta.(*TencentCloudClient).apiV3Conn + service := SSLService{client} + id := d.Id() + + request := ssl2.NewDeleteCertificateRequest() + request.CertificateId = &id + + _, err := service.DeleteCertificate(ctx, request) + if err != nil { + return err + } + return nil +} diff --git a/tencentcloud/resource_tc_ssl_free_certificate_test.go b/tencentcloud/resource_tc_ssl_free_certificate_test.go new file mode 100644 index 0000000000..c59d7d8f2e --- /dev/null +++ b/tencentcloud/resource_tc_ssl_free_certificate_test.go @@ -0,0 +1,94 @@ +package tencentcloud + +import ( + "context" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + ssl2 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205" + "github.com/tencentcloudstack/terraform-provider-tencentcloud/tencentcloud/internal/helper" +) + +func init() { + // go test -v ./tencentcloud -sweep=ap-guangzhou -sweep-run=tencentcloud_ssl_free_certificate + resource.AddTestSweepers("tencentcloud_ssl_free_certificate", &resource.Sweeper{ + Name: "tencentcloud_ssl_free_certificate", + F: func(r string) error { + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), logIdKey, logId) + cli, _ := sharedClientForRegion(r) + client := cli.(*TencentCloudClient).apiV3Conn + service := SSLService{client} + + request := ssl2.NewDescribeCertificatesRequest() + request.SearchKey = helper.String("my_free_cert") + certs, err := service.DescribeCertificates(ctx, request) + if err != nil { + return err + } + + for i := range certs { + cert := certs[i] + name := cert.Alias + created, err := time.Parse("2006-01-02 15:04:05", *cert.InsertTime) + if err != nil { + created = time.Time{} + } + + if isResourcePersist(*name, &created) { + continue + } + request := ssl2.NewDeleteCertificateRequest() + request.CertificateId = cert.CertificateId + + _, err = service.DeleteCertificate(ctx, request) + if err != nil { + continue + } + } + + return nil + }, + }) +} + +func TestAccTencentCloudNeedFixSSLFreeCertificate(t *testing.T) { + t.Parallel() + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccSSLFreeCertificateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccSSLFreeCertificateBasic, + Destroy: false, + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckSslCertificateExists("tencentcloud_ssl_free_certificate.foo"), + resource.TestCheckResourceAttr("tencentcloud_ssl_free_certificate.foo", "alias", "my_free_cert"), + resource.TestCheckResourceAttrSet("tencentcloud_ssl_free_certificate.foo", "domain"), + ), + }, + }, + }) +} + +func testAccSSLFreeCertificateDestroy(s *terraform.State) error { + return nil +} + +const testAccSSLFreeCertificateBasic = ` +resource "tencentcloud_ssl_free_certificate" "foo" { + dv_auth_method = "DNS_AUTO" + domain = "example.com" + package_type = "2" + contact_email = "foo@example.com" + contact_phone = "12345678901" + validity_period = 12 + csr_encrypt_algo = "RSA" + csr_key_parameter = "2048" + csr_key_password = "xxxxxxxx" + alias = "my_free_cert" +} +` diff --git a/tencentcloud/service_tencent_ssl_certificate.go b/tencentcloud/service_tencent_ssl_certificate.go index 4386d4d6a0..977f4d2f1f 100644 --- a/tencentcloud/service_tencent_ssl_certificate.go +++ b/tencentcloud/service_tencent_ssl_certificate.go @@ -16,6 +16,35 @@ type SSLService struct { client *connectivity.TencentCloudClient } +func (me *SSLService) ApplyCertificate(ctx context.Context, request *ssl.ApplyCertificateRequest) (id string, errRet error) { + logId := getLogId(ctx) + 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()) + } + }() + + ratelimit.Check(request.GetAction()) + response, err := me.client.UseSSLCertificateClient().ApplyCertificate(request) + + if err != nil { + errRet = err + return + } + + if response.Response.CertificateId != nil { + id = *response.Response.CertificateId + } else { + errRet = fmt.Errorf("[%s] error, no certificate id response: %s", request.GetAction(), response.ToJsonString()) + } + + log.Printf("[DEBUG]%s api[%s] success, request body [%s], response body [%s]\n", + logId, request.GetAction(), request.ToJsonString(), response.ToJsonString()) + + return +} + func (me *SSLService) CreateCertificate(ctx context.Context, request *ssl.CreateCertificateRequest) (certificateId, dealId string, errRet error) { logId := getLogId(ctx) client := me.client.UseSSLCertificateClient() diff --git a/website/docs/r/ssl_free_certificate.html.markdown b/website/docs/r/ssl_free_certificate.html.markdown new file mode 100644 index 0000000000..1ba6ace3ee --- /dev/null +++ b/website/docs/r/ssl_free_certificate.html.markdown @@ -0,0 +1,75 @@ +--- +subcategory: "SSL Certificates" +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_ssl_free_certificate" +sidebar_current: "docs-tencentcloud-resource-ssl_free_certificate" +description: |- + Provide a resource to create a Free Certificate. +~> **NOTE:** Once certificat created, it cannot be removed within 1 hours. +--- + +# tencentcloud_ssl_free_certificate + +Provide a resource to create a Free Certificate. +~> **NOTE:** Once certificat created, it cannot be removed within 1 hours. + +## Example Usage + +```hcl +resource "tencentcloud_ssl_free_certificate" "foo" { + dv_auth_method = "DNS_AUTO" + domain = "example.com" + package_type = "2" + contact_email = "foo@example.com" + contact_phone = "12345678901" + validity_period = 12 + csr_encrypt_algo = "RSA" + csr_key_parameter = "2048" + csr_key_password = "xxxxxxxx" + alias = "my_free_cert" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `domain` - (Required, ForceNew) Specify domain name. +* `dv_auth_method` - (Required, ForceNew) Specify DV authorize method, available values: `DNS_AUTO` - automatic DNS auth, `DNS` - manual DNS auth, `FILE` - auth by file. +* `alias` - (Optional) Specify alias for remark. +* `contact_email` - (Optional) Email address. +* `contact_phone` - (Optional) Phone number. +* `csr_encrypt_algo` - (Optional) Specify CSR encrypt algorithm, only support `RSA` for now. +* `csr_key_parameter` - (Optional) Specify CSR key parameter, only support `"2048"` for now. +* `csr_key_password` - (Optional) Specify CSR key password. +* `old_certificate_id` - (Optional, ForceNew) Specify old certificate ID, used for re-apply. +* `package_type` - (Optional) Type of package. Only support `"2"` (TrustAsia TLS RSA CA). +* `project_id` - (Optional) ID of projects which this certification belong to. +* `validity_period` - (Optional) Specify validity period in month, only support `"12"` months for now. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - ID of the resource. +* `cert_begin_time` - Certificate begin time. +* `cert_end_time` - Certificate begin time. +* `certificate_private_key` - Certificate private key. +* `certificate_public_key` - Certificate public key. +* `deployable` - Indicates whether the certificate deployable. +* `insert_time` - Certificate insert time. +* `product_zh_name` - Product zh name. +* `renewable` - Indicates whether the certificate renewable. +* `status_msg` - Certificate status message. +* `status_name` - Certificate status name. +* `status` - Certificate status. 0 = Approving, 1 = Approved, 2 = Approve failed, 3 = expired, 4 = DNS record added, 5 = OV/EV Certificate and confirm letter needed, 6 = Order canceling, 7 = Order canceled, 8 = Submitted and confirm letter needed, 9 = Revoking, 10 = Revoked, 11 = re-applying, 12 = Revoke and confirm letter needed, 13 = Free SSL and confirm letter needed. +* `vulnerability_status` - Vulnerability status. + + +## Import + +FreeCertificate instance can be imported, e.g. +``` +$ terraform import tencentcloud_ssl_free_certificate.test free_certificate-id +``` + diff --git a/website/tencentcloud.erb b/website/tencentcloud.erb index 2f5abc691b..1d6e51bc16 100644 --- a/website/tencentcloud.erb +++ b/website/tencentcloud.erb @@ -1315,6 +1315,9 @@