Skip to content

Commit

Permalink
Add ACM cert resource (#34)
Browse files Browse the repository at this point in the history
* add ACM cert resource

* fix variable

* update docs
  • Loading branch information
chrnorm authored Aug 5, 2024
1 parent 29678e5 commit b302c5d
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 16 deletions.
3 changes: 1 addition & 2 deletions docs/data-sources/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,5 @@ data "deploymeta_deployment" "this" {}

### Read-Only

- `default_subdomain` (String) The default DNS subdomain associated with the deployment
- `dns_zone_name` (String) The default DNS zone name associated with the deployment
- `default_app_domain` (String) The default app domain for the deployment
- `id` (String) The deployment ID
28 changes: 28 additions & 0 deletions docs/resources/aws_acm_certificate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "deploymeta_aws_acm_certificate Resource - deploymeta"
subcategory: ""
description: |-
Registers an AWS ACM certificate for a Common Fate deployment.
---

# deploymeta_aws_acm_certificate (Resource)

Registers an AWS ACM certificate for a Common Fate deployment.



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `arn` (String) The Amazon Resource Name (ARN) of the certificate
- `domain_name` (String) The domain name for the certificate, for example: 'www.example.com'
- `status` (String) The status of the certificate
- `validation_cname_name` (String) The CNAME name used for domain validation
- `validation_cname_value` (String) The CNAME value used for domain validation

### Read-Only

- `id` (String) The certificate ID
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ toolchain go1.22.1

require (
connectrpc.com/connect v1.14.0
github.com/common-fate/sdk v1.51.2-0.20240805112400-843ff371965e
github.com/common-fate/sdk v1.51.2-0.20240805171122-82f5839f67c0
github.com/hashicorp/terraform-plugin-docs v0.19.1
github.com/hashicorp/terraform-plugin-framework v1.8.0
github.com/hashicorp/terraform-plugin-go v0.22.2
github.com/hashicorp/terraform-plugin-log v0.9.0
)

Expand Down Expand Up @@ -40,6 +39,7 @@ require (
github.com/hashicorp/hc-install v0.6.4 // indirect
github.com/hashicorp/terraform-exec v0.20.0 // indirect
github.com/hashicorp/terraform-json v0.21.0 // indirect
github.com/hashicorp/terraform-plugin-go v0.22.2 // indirect
github.com/hashicorp/terraform-registry-address v0.2.3 // indirect
github.com/hashicorp/terraform-svchost v0.1.1 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZ
github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/common-fate/sdk v1.51.2-0.20240805112400-843ff371965e h1:U/Hnbj7xvxNH7FFdxcTzgqh/tvu6a9DVKkTbhKfT/2s=
github.com/common-fate/sdk v1.51.2-0.20240805112400-843ff371965e/go.mod h1:OrXhzB2Y1JSrKGHrb4qRmY+6MF2M3MFb+3edBnessXo=
github.com/common-fate/sdk v1.51.2-0.20240805171122-82f5839f67c0 h1:ToHYNnsIdNfLj/HdmxO5MyBokLHJLNSLJfzVCZH7B4s=
github.com/common-fate/sdk v1.51.2-0.20240805171122-82f5839f67c0/go.mod h1:OrXhzB2Y1JSrKGHrb4qRmY+6MF2M3MFb+3edBnessXo=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down
218 changes: 218 additions & 0 deletions internal/provider/aws_acm_certificate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
package provider

import (
"context"
"fmt"

"connectrpc.com/connect"
"github.com/common-fate/sdk/factory/service/deployment"
"github.com/common-fate/sdk/factoryconfig"
deploymentv1alpha1 "github.com/common-fate/sdk/gen/commonfate/factory/deployment/v1alpha1"
"github.com/common-fate/sdk/gen/commonfate/factory/deployment/v1alpha1/deploymentv1alpha1connect"
"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/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
)

// Ensure provider defined types fully satisfy framework interfaces.
var _ resource.Resource = &AWSACMCertificateResource{}
var _ resource.ResourceWithImportState = &AWSACMCertificateResource{}

func NewAWSACMCertificateResource() resource.Resource {
return &AWSACMCertificateResource{}
}

// AWSACMCertificateResource defines the resource implementation.
type AWSACMCertificateResource struct {
client deploymentv1alpha1connect.DeploymentServiceClient
}

type AWSACMCertificateResourceModel struct {
ID types.String `tfsdk:"id"`
ARN types.String `tfsdk:"arn"`
DomainName types.String `tfsdk:"domain_name"`
ValidationCNameName types.String `tfsdk:"validation_cname_name"`
ValidationCNameValue types.String `tfsdk:"validation_cname_value"`
Status types.String `tfsdk:"status"`
}

func (r *AWSACMCertificateResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_aws_acm_certificate"
}

func (r *AWSACMCertificateResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
resp.Schema = schema.Schema{
MarkdownDescription: "Registers an AWS ACM certificate for a Common Fate deployment.",

Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
MarkdownDescription: "The certificate ID",
Computed: true,
},
"arn": schema.StringAttribute{
MarkdownDescription: "The Amazon Resource Name (ARN) of the certificate",
Required: true,
},
"domain_name": schema.StringAttribute{
MarkdownDescription: "The domain name for the certificate, for example: 'www.example.com'",
Required: true,
},
"validation_cname_name": schema.StringAttribute{
MarkdownDescription: "The CNAME name used for domain validation",
Required: true,
},
"validation_cname_value": schema.StringAttribute{
MarkdownDescription: "The CNAME value used for domain validation",
Required: true,
},
"status": schema.StringAttribute{
MarkdownDescription: "The status of the certificate",
Required: true,
},
},
}
}

func (r *AWSACMCertificateResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}

cfg, ok := req.ProviderData.(*factoryconfig.Context)

if !ok {
resp.Diagnostics.AddError(
"Unexpected Resource Configure Type",
fmt.Sprintf("Expected *factoryconfig.Context, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)

return
}

r.client = deployment.NewFromConfig(cfg)
}

func (r *AWSACMCertificateResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data AWSACMCertificateResourceModel

// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

res, err := r.client.RegisterAWSACMCertificate(ctx, connect.NewRequest(&deploymentv1alpha1.RegisterAWSACMCertificateRequest{
Arn: data.ARN.ValueString(),
DomainName: data.DomainName.ValueString(),
ValidationCnameName: data.ValidationCNameName.ValueString(),
ValidationCnameValue: data.ValidationCNameValue.ValueString(),
Status: data.Status.ValueString(),
}))
if err != nil {
resp.Diagnostics.AddError("Common Fate Deployment API error", fmt.Sprintf("Unable to register AWS ACM certificate for the deployment, got error: %s", err.Error()))
return
}

tflog.Trace(ctx, "registered AWS ACM certificate")

// Convert from the API data model to the Terraform data model
// and set any unknown attribute values.
data.ID = types.StringValue(res.Msg.Certificate.Id)

// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *AWSACMCertificateResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var data AWSACMCertificateResourceModel

// Read Terraform prior state data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

apiRes, err := r.client.GetAWSACMCertificate(ctx, connect.NewRequest(&deploymentv1alpha1.GetAWSACMCertificateRequest{
Id: data.ID.ValueString(),
}))
if connect.CodeOf(err) == connect.CodeNotFound {
resp.State.RemoveResource(ctx)
return
} else if err != nil {
resp.Diagnostics.AddError("Client Error", fmt.Sprintf("Unable to read Common Fate DNS record, got error: %s", err))
return
}

data.ID = types.StringValue(apiRes.Msg.Certificate.Id)
data.ARN = types.StringValue(apiRes.Msg.Certificate.Arn)
data.DomainName = types.StringValue(apiRes.Msg.Certificate.DomainName)
data.ValidationCNameName = types.StringValue(apiRes.Msg.Certificate.ValidationCnameName)
data.ValidationCNameValue = types.StringValue(apiRes.Msg.Certificate.ValidationCnameValue)
data.Status = types.StringValue(apiRes.Msg.Certificate.Status)

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
func (r *AWSACMCertificateResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
var data AWSACMCertificateResourceModel

// Read Terraform plan data into the model
resp.Diagnostics.Append(req.Plan.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

res, err := r.client.UpdateAWSACMCertificate(ctx, connect.NewRequest(&deploymentv1alpha1.UpdateAWSACMCertificateRequest{
Certificate: &deploymentv1alpha1.AWSACMCertificate{
Id: data.ID.ValueString(),
Arn: data.ARN.ValueString(),
DomainName: data.DomainName.ValueString(),
ValidationCnameName: data.ValidationCNameName.ValueString(),
ValidationCnameValue: data.ValidationCNameValue.ValueString(),
Status: data.Status.ValueString(),
},
}))
if err != nil {
resp.Diagnostics.AddError("Common Fate Deployment API error", fmt.Sprintf("Unable to update AWS ACM certificate for the deployment, got error: %s", err.Error()))
return
}

tflog.Trace(ctx, "updated AWS ACM certificate")

// Convert from the API data model to the Terraform data model
// and set any unknown attribute values.
data.ID = types.StringValue(res.Msg.Certificate.Id)

// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}

func (r *AWSACMCertificateResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
var data AWSACMCertificateResourceModel

// Read Terraform plan data into the model
resp.Diagnostics.Append(req.State.Get(ctx, &data)...)

_, err := r.client.DeregisterAWSACMCertificate(ctx, connect.NewRequest(&deploymentv1alpha1.DeregisterAWSACMCertificateRequest{
Id: data.ID.ValueString(),
}))
if connect.CodeOf(err) == connect.CodeNotFound {
return
}

if err != nil {
resp.Diagnostics.AddError("Common Fate Deployment API error", fmt.Sprintf("Unable to delete ACM certificate for the deployment, got error: %s", err.Error()))
return
}

tflog.Trace(ctx, "deleted ACM cert")
}

func (r *AWSACMCertificateResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
resource.ImportStatePassthroughID(ctx, path.Root("id"), req, resp)
}
14 changes: 4 additions & 10 deletions internal/provider/deployment_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ type DeploymentDataSource struct {

// DeploymentDataSourceModel describes the data source data model.
type DeploymentDataSourceModel struct {
DNSZoneName types.String `tfsdk:"dns_zone_name"`
DefaultSubdomain types.String `tfsdk:"default_subdomain"`
Id types.String `tfsdk:"id"`
DefaultAppDomain types.String `tfsdk:"default_app_domain"`
}

func (d *DeploymentDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
Expand All @@ -47,12 +46,8 @@ func (d *DeploymentDataSource) Schema(ctx context.Context, req datasource.Schema
MarkdownDescription: "The deployment ID",
Computed: true,
},
"dns_zone_name": schema.StringAttribute{
MarkdownDescription: "The default DNS zone name associated with the deployment",
Computed: true,
},
"default_subdomain": schema.StringAttribute{
MarkdownDescription: "The default DNS subdomain associated with the deployment",
"default_app_domain": schema.StringAttribute{
MarkdownDescription: "The default app domain for the deployment",
Computed: true,
},
},
Expand Down Expand Up @@ -96,8 +91,7 @@ func (d *DeploymentDataSource) Read(ctx context.Context, req datasource.ReadRequ
}

data.Id = types.StringValue(apiRes.Msg.Deployment.Id)
data.DNSZoneName = types.StringValue(apiRes.Msg.Deployment.DnsZoneName)
data.DefaultSubdomain = types.StringValue(apiRes.Msg.Deployment.DefaultSubdomain)
data.DefaultAppDomain = types.StringValue(apiRes.Msg.Deployment.DefaultAppDomain)

tflog.Trace(ctx, "read deployment metadata")

Expand Down
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ func (p *DeploymentProvider) Resources(ctx context.Context) []func() resource.Re
return []func() resource.Resource{
NewDNSRecordResource,
NewTerraformOutputResource,
NewAWSACMCertificateResource,
}
}

Expand Down

0 comments on commit b302c5d

Please sign in to comment.