Files

737 lines
24 KiB
Go

package resource
import (
"context"
"fmt"
kmsclient "github.com/hanzokms/terraform-provider/internal/client"
"strings"
"time"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64default"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
)
var (
SUPPORTED_CERT_ISSUE_KEY_ALGORITHMS = []string{
"RSA_2048", "RSA_3072", "RSA_4096",
"ECDSA_P256", "ECDSA_P384", "ECDSA_P521",
}
SUPPORTED_CERT_ISSUE_SIGNATURE_ALGORITHMS = []string{
"RSA-SHA256", "RSA-SHA384", "RSA-SHA512",
"ECDSA-SHA256", "ECDSA-SHA384", "ECDSA-SHA512",
}
SUPPORTED_CERT_ISSUE_KEY_USAGES = []string{
"digital_signature", "key_encipherment", "non_repudiation",
"data_encipherment", "key_agreement", "key_cert_sign",
"crl_sign", "encipher_only", "decipher_only",
}
SUPPORTED_CERT_ISSUE_EXTENDED_KEY_USAGES = []string{
"client_auth", "server_auth", "code_signing",
"email_protection", "ocsp_signing", "time_stamping",
}
)
var (
_ resource.Resource = &certManagerCertificateResource{}
)
func NewCertManagerCertificateResource() resource.Resource {
return &certManagerCertificateResource{}
}
type certManagerCertificateResource struct {
client *kmsclient.Client
}
type certManagerCertificateResourceModel struct {
ProfileId types.String `tfsdk:"profile_id"`
CSR types.String `tfsdk:"csr"`
CommonName types.String `tfsdk:"common_name"`
AltNames types.List `tfsdk:"alt_names"`
Organization types.String `tfsdk:"organization"`
OU types.String `tfsdk:"ou"`
Country types.String `tfsdk:"country"`
Province types.String `tfsdk:"province"`
Locality types.String `tfsdk:"locality"`
KeyAlgorithm types.String `tfsdk:"key_algorithm"`
SignatureAlgorithm types.String `tfsdk:"signature_algorithm"`
KeyUsages types.List `tfsdk:"key_usages"`
ExtendedKeyUsages types.List `tfsdk:"extended_key_usages"`
TTL types.String `tfsdk:"ttl"`
TimeoutSeconds types.Int64 `tfsdk:"timeout_seconds"`
Id types.String `tfsdk:"id"`
CertificateRequestId types.String `tfsdk:"certificate_request_id"`
Status types.String `tfsdk:"status"`
SerialNumber types.String `tfsdk:"serial_number"`
NotBefore types.String `tfsdk:"not_before"`
NotAfter types.String `tfsdk:"not_after"`
Certificate types.String `tfsdk:"certificate"`
PrivateKey types.String `tfsdk:"private_key"`
CertificateChain types.String `tfsdk:"certificate_chain"`
}
func (r *certManagerCertificateResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_cert_manager_certificate"
}
func (r *certManagerCertificateResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
Description: "Request and manage certificates from Kms certificate profiles. Supports both CSR-based and direct field requests. The resource will poll until the certificate is issued or a timeout is reached. Only Machine Identity authentication is supported for this resource.",
Attributes: map[string]schema.Attribute{
"profile_id": schema.StringAttribute{
Description: "The ID of the certificate profile to use for issuance",
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"csr": schema.StringAttribute{
Description: "Certificate Signing Request (CSR) in PEM format. If provided, the certificate will be issued based on the CSR. Use Terraform's file() function to read from a file (e.g., file(\"./my-certificate.csr\"))",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"common_name": schema.StringAttribute{
Description: "The common name (CN) for the certificate. Required when not using CSR",
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"alt_names": schema.ListAttribute{
Description: "Subject alternative names (SANs) for the certificate",
ElementType: types.StringType,
Optional: true,
Computed: true,
PlanModifiers: []planmodifier.List{
listplanmodifier.RequiresReplace(),
},
},
"organization": schema.StringAttribute{
Description: "The organization (O) for the certificate",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"ou": schema.StringAttribute{
Description: "The organizational unit (OU) for the certificate",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"country": schema.StringAttribute{
Description: "The country (C) for the certificate (2-letter code)",
Optional: true,
Validators: []validator.String{
stringvalidator.LengthBetween(2, 2),
},
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"province": schema.StringAttribute{
Description: "The state/province (ST) for the certificate",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"locality": schema.StringAttribute{
Description: "The locality (L) for the certificate",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"key_algorithm": schema.StringAttribute{
Description: "The key algorithm for the certificate. Supported: " + strings.Join(SUPPORTED_CERT_ISSUE_KEY_ALGORITHMS, ", "),
Optional: true,
Computed: true,
Validators: []validator.String{
stringvalidator.OneOf(SUPPORTED_CERT_ISSUE_KEY_ALGORITHMS...),
},
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"signature_algorithm": schema.StringAttribute{
Description: "The signature algorithm for the certificate. Supported: " + strings.Join(SUPPORTED_CERT_ISSUE_SIGNATURE_ALGORITHMS, ", "),
Optional: true,
Computed: true,
Validators: []validator.String{
stringvalidator.OneOf(SUPPORTED_CERT_ISSUE_SIGNATURE_ALGORITHMS...),
},
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"key_usages": schema.ListAttribute{
Description: "Key usages for the certificate. Supported: " + strings.Join(SUPPORTED_CERT_ISSUE_KEY_USAGES, ", "),
Optional: true,
ElementType: types.StringType,
Validators: []validator.List{
listvalidator.ValueStringsAre(stringvalidator.OneOf(SUPPORTED_CERT_ISSUE_KEY_USAGES...)),
},
PlanModifiers: []planmodifier.List{
listplanmodifier.RequiresReplace(),
},
},
"extended_key_usages": schema.ListAttribute{
Description: "Extended key usages for the certificate. Supported: " + strings.Join(SUPPORTED_CERT_ISSUE_EXTENDED_KEY_USAGES, ", "),
Optional: true,
ElementType: types.StringType,
Validators: []validator.List{
listvalidator.ValueStringsAre(stringvalidator.OneOf(SUPPORTED_CERT_ISSUE_EXTENDED_KEY_USAGES...)),
},
PlanModifiers: []planmodifier.List{
listplanmodifier.RequiresReplace(),
},
},
"ttl": schema.StringAttribute{
Description: "Time to live for the certificate (e.g., '30d', '90d', '1y')",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"timeout_seconds": schema.Int64Attribute{
Description: "Maximum time to wait for certificate issuance in seconds. Defaults to 3600 (1 hour)",
Optional: true,
Computed: true,
Default: int64default.StaticInt64(3600),
},
"id": schema.StringAttribute{
Description: "The ID of the certificate",
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"certificate_request_id": schema.StringAttribute{
Description: "The ID of the certificate request",
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"status": schema.StringAttribute{
Description: "The status of the certificate (pending, issued, failed)",
Computed: true,
},
"serial_number": schema.StringAttribute{
Description: "The serial number of the issued certificate",
Computed: true,
},
"not_before": schema.StringAttribute{
Description: "The not-before date of the certificate (RFC3339 format)",
Computed: true,
},
"not_after": schema.StringAttribute{
Description: "The not-after (expiration) date of the certificate (RFC3339 format)",
Computed: true,
},
"certificate": schema.StringAttribute{
Description: "The issued certificate in PEM format",
Computed: true,
},
"private_key": schema.StringAttribute{
Description: "The private key in PEM format (only available for direct field requests, not CSR-based)",
Computed: true,
Sensitive: true,
},
"certificate_chain": schema.StringAttribute{
Description: "The certificate chain in PEM format",
Computed: true,
},
},
}
}
func (r *certManagerCertificateResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
if req.ProviderData == nil {
return
}
client, ok := req.ProviderData.(*kmsclient.Client)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Resource Configure Type",
fmt.Sprintf("Expected *kmsclient.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}
r.client = client
}
func (r *certManagerCertificateResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
if !r.client.Config.IsMachineIdentityAuth {
resp.Diagnostics.AddError(
"Unable to request certificate",
"Only Machine Identity authentication is supported for this operation",
)
return
}
var plan certManagerCertificateResourceModel
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
if resp.Diagnostics.HasError() {
return
}
hasCSR := !plan.CSR.IsNull() && !plan.CSR.IsUnknown() && plan.CSR.ValueString() != ""
hasCommonName := !plan.CommonName.IsNull() && !plan.CommonName.IsUnknown() && plan.CommonName.ValueString() != ""
if !hasCSR && !hasCommonName {
resp.Diagnostics.AddError(
"Missing certificate request data",
"Either 'csr' or 'common_name' must be provided",
)
return
}
timeoutSeconds := int64(3600)
if !plan.TimeoutSeconds.IsNull() && !plan.TimeoutSeconds.IsUnknown() {
timeoutSeconds = plan.TimeoutSeconds.ValueInt64()
}
certRequest := kmsclient.RequestCertificateRequest{
ProfileId: plan.ProfileId.ValueString(),
}
if hasCSR {
csrContent := strings.ReplaceAll(plan.CSR.ValueString(), "\r\n", "\n")
csrContent = strings.TrimSpace(csrContent)
certRequest.CSR = csrContent
}
hasAttributes := false
attributes := kmsclient.CertificateAttributes{}
if !plan.CommonName.IsNull() && !plan.CommonName.IsUnknown() {
attributes.CommonName = plan.CommonName.ValueString()
hasAttributes = true
}
if !plan.Organization.IsNull() && !plan.Organization.IsUnknown() {
attributes.Organization = plan.Organization.ValueString()
hasAttributes = true
}
if !plan.OU.IsNull() && !plan.OU.IsUnknown() {
attributes.OU = plan.OU.ValueString()
hasAttributes = true
}
if !plan.Country.IsNull() && !plan.Country.IsUnknown() {
attributes.Country = plan.Country.ValueString()
hasAttributes = true
}
if !plan.Province.IsNull() && !plan.Province.IsUnknown() {
attributes.Province = plan.Province.ValueString()
hasAttributes = true
}
if !plan.Locality.IsNull() && !plan.Locality.IsUnknown() {
attributes.Locality = plan.Locality.ValueString()
hasAttributes = true
}
if !plan.AltNames.IsNull() && !plan.AltNames.IsUnknown() {
altNamesStr := make([]string, 0)
resp.Diagnostics.Append(plan.AltNames.ElementsAs(ctx, &altNamesStr, false)...)
if resp.Diagnostics.HasError() {
return
}
altNames := make([]kmsclient.CertificateAltName, 0, len(altNamesStr))
for _, altName := range altNamesStr {
altNames = append(altNames, kmsclient.CertificateAltName{
Type: "dns_name",
Value: altName,
})
}
attributes.AltNames = altNames
hasAttributes = true
}
if !plan.KeyAlgorithm.IsNull() && !plan.KeyAlgorithm.IsUnknown() {
attributes.KeyAlgorithm = plan.KeyAlgorithm.ValueString()
hasAttributes = true
}
if !plan.SignatureAlgorithm.IsNull() && !plan.SignatureAlgorithm.IsUnknown() {
attributes.SignatureAlgorithm = plan.SignatureAlgorithm.ValueString()
hasAttributes = true
}
if !plan.KeyUsages.IsNull() && !plan.KeyUsages.IsUnknown() {
keyUsages := make([]string, 0)
resp.Diagnostics.Append(plan.KeyUsages.ElementsAs(ctx, &keyUsages, false)...)
if resp.Diagnostics.HasError() {
return
}
attributes.KeyUsages = keyUsages
hasAttributes = true
}
if !plan.ExtendedKeyUsages.IsNull() && !plan.ExtendedKeyUsages.IsUnknown() {
extKeyUsages := make([]string, 0)
resp.Diagnostics.Append(plan.ExtendedKeyUsages.ElementsAs(ctx, &extKeyUsages, false)...)
if resp.Diagnostics.HasError() {
return
}
attributes.ExtendedKeyUsages = extKeyUsages
hasAttributes = true
}
if !plan.TTL.IsNull() && !plan.TTL.IsUnknown() {
attributes.TTL = plan.TTL.ValueString()
hasAttributes = true
}
if hasAttributes {
certRequest.Attributes = &attributes
}
requestResponse, err := r.client.RequestCertificate(certRequest)
if err != nil {
resp.Diagnostics.AddError("Error requesting certificate", err.Error())
return
}
certificateRequestId := requestResponse.CertificateRequestId
plan.CertificateRequestId = types.StringValue(certificateRequestId)
if requestResponse.Certificate.CertificateId != "" && requestResponse.Certificate.Certificate != "" {
r.populatePlanFromImmediateResponse(ctx, &plan, requestResponse.Certificate)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(resp.State.Set(ctx, plan)...)
return
}
r.pollCertificateRequest(ctx, &plan, certificateRequestId, timeoutSeconds, resp)
if resp.Diagnostics.HasError() {
return
}
resp.Diagnostics.Append(resp.State.Set(ctx, plan)...)
}
func (r *certManagerCertificateResource) populatePlanFromImmediateResponse(ctx context.Context, plan *certManagerCertificateResourceModel, certResponse kmsclient.CertificateResponse) {
plan.Id = types.StringValue(certResponse.CertificateId)
plan.Status = types.StringValue("issued")
if certResponse.Certificate != "" {
plan.Certificate = types.StringValue(certResponse.Certificate)
} else {
plan.Certificate = types.StringValue("")
}
if certResponse.PrivateKey != "" {
plan.PrivateKey = types.StringValue(certResponse.PrivateKey)
} else {
plan.PrivateKey = types.StringValue("")
}
if certResponse.CertificateChain != "" {
plan.CertificateChain = types.StringValue(certResponse.CertificateChain)
} else {
plan.CertificateChain = types.StringValue("")
}
if certResponse.SerialNumber != "" {
plan.SerialNumber = types.StringValue(certResponse.SerialNumber)
} else {
plan.SerialNumber = types.StringValue("")
}
r.populateCertificateDetails(ctx, plan, certResponse.CertificateId)
}
func (r *certManagerCertificateResource) populateCertificateDetails(ctx context.Context, plan *certManagerCertificateResourceModel, certificateId string) {
certDetails, err := r.client.GetCertificate(kmsclient.GetCertificateRequest{
CertificateId: certificateId,
})
if err != nil {
tflog.Warn(ctx, "Failed to fetch certificate details", map[string]interface{}{
"certificate_id": certificateId,
"error": err.Error(),
})
plan.NotBefore = types.StringValue("")
plan.NotAfter = types.StringValue("")
return
}
cert := certDetails.Certificate
plan.CommonName = types.StringValue(cert.CommonName)
plan.KeyAlgorithm = types.StringValue(cert.KeyAlgorithm)
plan.SignatureAlgorithm = types.StringValue(cert.SignatureAlgorithm)
plan.AltNames = r.parseAltNames(ctx, cert.AltNames)
plan.NotBefore = types.StringValue(cert.NotBefore)
plan.NotAfter = types.StringValue(cert.NotAfter)
if cert.CertificateChain != "" {
plan.CertificateChain = types.StringValue(cert.CertificateChain)
}
}
func (r *certManagerCertificateResource) parseAltNames(ctx context.Context, altNames interface{}) types.List {
if altNames == nil {
return types.ListNull(types.StringType)
}
var altNamesStrings []string
switch v := altNames.(type) {
case []interface{}:
for _, item := range v {
switch itemVal := item.(type) {
case string:
altNamesStrings = append(altNamesStrings, itemVal)
case map[string]interface{}:
if value, ok := itemVal["value"].(string); ok {
altNamesStrings = append(altNamesStrings, value)
}
}
}
case []string:
altNamesStrings = v
case string:
if strings.Contains(v, ",") {
for _, part := range strings.Split(v, ",") {
if trimmed := strings.TrimSpace(part); trimmed != "" {
altNamesStrings = append(altNamesStrings, trimmed)
}
}
} else if v != "" {
altNamesStrings = []string{v}
}
}
if len(altNamesStrings) == 0 {
return types.ListNull(types.StringType)
}
altNamesList, diags := types.ListValueFrom(ctx, types.StringType, altNamesStrings)
if diags.HasError() {
tflog.Warn(ctx, "Failed to parse alt names list", map[string]interface{}{
"error": diags.Errors(),
})
return types.ListNull(types.StringType)
}
return altNamesList
}
func (r *certManagerCertificateResource) pollCertificateRequest(ctx context.Context, plan *certManagerCertificateResourceModel, certificateRequestId string, timeoutSeconds int64, resp *resource.CreateResponse) {
timeout := time.Duration(timeoutSeconds) * time.Second
startTime := time.Now()
// Exponential backoff: start at 2s, max 30s
minInterval := 2 * time.Second
maxInterval := 30 * time.Second
currentInterval := minInterval
for {
if ctx.Err() != nil {
resp.Diagnostics.AddError("Operation cancelled", ctx.Err().Error())
return
}
if time.Since(startTime) > timeout {
resp.Diagnostics.AddError(
"Certificate issuance timeout",
fmt.Sprintf("Certificate issuance did not complete within %d seconds. Request ID: %s", timeoutSeconds, certificateRequestId),
)
return
}
statusResponse, err := r.client.GetCertificateRequestStatus(kmsclient.GetCertificateRequestStatusRequest{
RequestId: certificateRequestId,
})
if err != nil {
if err == kmsclient.ErrNotFound {
select {
case <-ctx.Done():
resp.Diagnostics.AddError("Operation cancelled", ctx.Err().Error())
return
case <-time.After(currentInterval):
currentInterval = r.nextBackoffInterval(currentInterval, maxInterval)
continue
}
}
resp.Diagnostics.AddError(
"Error checking certificate request status",
fmt.Sprintf("Failed to check certificate request status: %v. Request ID: %s", err, certificateRequestId),
)
return
}
status := strings.ToLower(statusResponse.Status)
switch status {
case "issued":
r.handleIssuedCertificate(ctx, plan, &statusResponse)
return
case "failed":
errorMsg := "Certificate issuance failed"
if statusResponse.ErrorMessage != nil && *statusResponse.ErrorMessage != "" {
errorMsg = fmt.Sprintf("Certificate issuance failed: %s", *statusResponse.ErrorMessage)
}
resp.Diagnostics.AddError(
"Certificate issuance failed",
fmt.Sprintf("%s. Request ID: %s", errorMsg, certificateRequestId),
)
return
case "pending":
select {
case <-ctx.Done():
resp.Diagnostics.AddError("Operation cancelled", ctx.Err().Error())
return
case <-time.After(currentInterval):
currentInterval = r.nextBackoffInterval(currentInterval, maxInterval)
continue
}
default:
if statusResponse.CertificateId != nil && *statusResponse.CertificateId != "" &&
statusResponse.Certificate != nil && *statusResponse.Certificate != "" {
r.handleIssuedCertificate(ctx, plan, &statusResponse)
return
}
select {
case <-ctx.Done():
resp.Diagnostics.AddError("Operation cancelled", ctx.Err().Error())
return
case <-time.After(currentInterval):
currentInterval = r.nextBackoffInterval(currentInterval, maxInterval)
continue
}
}
}
}
func (r *certManagerCertificateResource) nextBackoffInterval(current, maxInterval time.Duration) time.Duration {
next := time.Duration(float64(current) * 1.5)
if next > maxInterval {
return maxInterval
}
return next
}
func (r *certManagerCertificateResource) handleIssuedCertificate(ctx context.Context, plan *certManagerCertificateResourceModel, statusResponse *kmsclient.GetCertificateRequestStatusResponse) {
plan.Status = types.StringValue("issued")
certificateId := ""
if statusResponse.CertificateId != nil && *statusResponse.CertificateId != "" {
certificateId = *statusResponse.CertificateId
plan.Id = types.StringValue(certificateId)
}
if statusResponse.Certificate != nil && *statusResponse.Certificate != "" {
plan.Certificate = types.StringValue(*statusResponse.Certificate)
} else {
plan.Certificate = types.StringValue("")
}
if statusResponse.PrivateKey != nil && *statusResponse.PrivateKey != "" {
plan.PrivateKey = types.StringValue(*statusResponse.PrivateKey)
} else {
plan.PrivateKey = types.StringValue("")
}
if statusResponse.CertificateChain != nil && *statusResponse.CertificateChain != "" {
plan.CertificateChain = types.StringValue(*statusResponse.CertificateChain)
} else {
plan.CertificateChain = types.StringValue("")
}
if statusResponse.SerialNumber != nil && *statusResponse.SerialNumber != "" {
plan.SerialNumber = types.StringValue(*statusResponse.SerialNumber)
} else {
plan.SerialNumber = types.StringValue("")
}
if certificateId != "" {
r.populateCertificateDetails(ctx, plan, certificateId)
}
}
func (r *certManagerCertificateResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
if !r.client.Config.IsMachineIdentityAuth {
resp.Diagnostics.AddError(
"Unable to read certificate",
"Only Machine Identity authentication is supported for this operation",
)
return
}
var state certManagerCertificateResourceModel
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
if resp.Diagnostics.HasError() {
return
}
certResponse, err := r.client.GetCertificate(kmsclient.GetCertificateRequest{
CertificateId: state.Id.ValueString(),
})
if err != nil {
if err == kmsclient.ErrNotFound {
resp.State.RemoveResource(ctx)
return
}
resp.Diagnostics.AddError("Error reading certificate", err.Error())
return
}
cert := certResponse.Certificate
state.Status = types.StringValue(cert.Status)
state.CommonName = types.StringValue(cert.CommonName)
state.AltNames = r.parseAltNames(ctx, cert.AltNames)
state.SerialNumber = types.StringValue(cert.SerialNumber)
state.NotBefore = types.StringValue(cert.NotBefore)
state.NotAfter = types.StringValue(cert.NotAfter)
state.KeyAlgorithm = types.StringValue(cert.KeyAlgorithm)
state.SignatureAlgorithm = types.StringValue(cert.SignatureAlgorithm)
resp.Diagnostics.Append(resp.State.Set(ctx, state)...)
}
func (r *certManagerCertificateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
resp.Diagnostics.AddError(
"Certificate updates not supported",
"Certificates cannot be updated. To change certificate attributes, destroy and recreate the resource.",
)
}
func (r *certManagerCertificateResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
// Certificates cannot be deleted from Kms. Only removed from state.
}
func (r *certManagerCertificateResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}