Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug] fix fastly_tls_subscription resource to properly handle challenges for multi-SAN #435

Merged
merged 8 commits into from
Jul 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions docs/resources/tls_subscription.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Enables TLS on a domain using a certificate managed by Fastly.

DNS records need to be modified on the domain being secured, in order to respond to the ACME domain ownership challenge.

There are two options for doing this: the `managed_dns_challenge`, which is the default method; and the `managed_http_challenges`, which points production traffic to Fastly.
There are two options for doing this: the `managed_dns_challenges`, which is the default method; and the `managed_http_challenges`, which points production traffic to Fastly.

~> See the [Fastly documentation](https://docs.fastly.com/en/guides/serving-https-traffic-using-fastly-managed-certificates#verifying-domain-ownership) for more information on verifying domain ownership.

Expand Down Expand Up @@ -78,11 +78,12 @@ data "aws_route53_zone" "demo" {

# Set up DNS record for managed DNS domain validation method
resource "aws_route53_record" "domain_validation" {
name = fastly_tls_subscription.example.managed_dns_challenge.record_name
type = fastly_tls_subscription.example.managed_dns_challenge.record_type
for_each = { for domain in fastly_tls_subscription.example.managed_dns_challenges : domain.record_name => domain }
name = each.value.record_name
type = each.value.record_type
zone_id = data.aws_route53_zone.demo.id
allow_overwrite = true
records = [fastly_tls_subscription.example.managed_dns_challenge.record_value]
records = [each.value.record_value]
ttl = 60
}

Expand Down Expand Up @@ -112,14 +113,14 @@ In addition to the arguments listed above, the following attributes are exported
* `created_at` - Timestamp (GMT) when the subscription was created.
* `updated_at` - Timestamp (GMT) when the subscription was last updated.
* `state` - The current state of the subscription. The list of possible states are: `pending`, `processing`, `issued`, and `renewing`.
* `managed_dns_challenge` - The details required to configure DNS to respond to ACME DNS challenge in order to verify domain ownership. See Managed DNS Challenge below for details.
* `managed_dns_challenges` - A list of options for configuring DNS to respond to ACME DNS challenge in order to verify domain ownership. See Managed DNS Challenge below for details.
* `managed_http_challenges` - A list of options for configuring DNS to respond to ACME HTTP challenge in order to verify domain ownership. See Managed HTTP Challenges below for details.

### Managed DNS Challenge

The available attributes in the `managed_dns_challenge` block are:
The available attributes in the `managed_dns_challenges` block are:

* `record_name` - The name of the DNS record to add. For example `_acme-challenge.example.com`. Accessed like this, `fastly_tls_subscription.tls.managed_dns_challenge.record_name`.
* `record_name` - The name of the DNS record to add. For example `_acme-challenge.example.com`. Accessed like this, `fastly_tls_subscription.tls.managed_dns_challenges.record_name`.
* `record_type` - The type of DNS record to add, e.g. `A`, or `CNAME`.
* `record_value` - The value to which the DNS record should point, e.g. `xxxxx.fastly-validations.com`.

Expand Down Expand Up @@ -160,11 +161,22 @@ $ terraform import fastly_tls_subscription.demo xxxxxxxxxxx
### Read-Only

- **created_at** (String) Timestamp (GMT) when the subscription was created.
- **managed_dns_challenge** (Map of String) The details required to configure DNS to respond to ACME DNS challenge in order to verify domain ownership.
- **managed_dns_challenge** (Map of String, Deprecated) The details required to configure DNS to respond to ACME DNS challenge in order to verify domain ownership.
- **managed_dns_challenges** (Set of Object) A list of options for configuring DNS to respond to ACME DNS challenge in order to verify domain ownership. (see [below for nested schema](#nestedatt--managed_dns_challenges))
- **managed_http_challenges** (Set of Object) A list of options for configuring DNS to respond to ACME HTTP challenge in order to verify domain ownership. Best accessed through a `for` expression to filter the relevant record. (see [below for nested schema](#nestedatt--managed_http_challenges))
- **state** (String) The current state of the subscription. The list of possible states are: `pending`, `processing`, `issued`, and `renewing`.
- **updated_at** (String) Timestamp (GMT) when the subscription was updated.

<a id="nestedatt--managed_dns_challenges"></a>
### Nested Schema for `managed_dns_challenges`

Read-Only:

- **record_name** (String)
- **record_type** (String)
- **record_value** (String)


<a id="nestedatt--managed_http_challenges"></a>
### Nested Schema for `managed_http_challenges`

Expand Down
88 changes: 71 additions & 17 deletions fastly/resource_fastly_tls_subscription.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ package fastly

import (
"context"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"time"

"github.com/fastly/go-fastly/v3/fastly"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
Expand Down Expand Up @@ -73,6 +73,31 @@ func resourceFastlyTLSSubscription() *schema.Resource {
Description: "The details required to configure DNS to respond to ACME DNS challenge in order to verify domain ownership.",
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Deprecated: "Use 'managed_dns_challenges' attribute instead",
},
"managed_dns_challenges": {
smaeda-ks marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeSet,
Description: "A list of options for configuring DNS to respond to ACME DNS challenge in order to verify domain ownership.",
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"record_name": {
Type: schema.TypeString,
Description: "The name of the DNS record to add. For example `_acme-challenge.example.com`.",
Computed: true,
},
"record_type": {
Type: schema.TypeString,
Description: "The type of DNS record to add, e.g. `A`, or `CNAME`.",
Computed: true,
},
"record_value": {
Type: schema.TypeString,
Description: "The value to which the DNS record should point, e.g. `xxxxx.fastly-validations.com`.",
Computed: true,
},
},
},
},
"managed_http_challenges": {
Type: schema.TypeSet,
Expand Down Expand Up @@ -172,24 +197,53 @@ func resourceFastlyTLSSubscriptionRead(_ context.Context, d *schema.ResourceData
}

var managedHTTPChallenges []map[string]interface{}
var managedDNSChallenge map[string]string
for _, challenge := range subscription.Authorizations[0].Challenges {
if challenge.Type == "managed-dns" {
if len(challenge.Values) < 1 {
return diag.Errorf("Fastly API returned no record values for Managed DNS Challenge")
var managedDNSChallenges []map[string]interface{}
for _, domain := range subscription.Authorizations {
for _, challenge := range domain.Challenges {
if challenge.Type == "managed-dns" {
if len(challenge.Values) < 1 {
return diag.Errorf("Fastly API returned no record values for Managed DNS Challenges")
}

managedDNSChallenges = append(managedDNSChallenges, map[string]interface{}{
"record_type": challenge.RecordType,
"record_name": challenge.RecordName,
"record_value": challenge.Values[0],
})
} else {
managedHTTPChallenges = append(managedHTTPChallenges, map[string]interface{}{
"record_type": challenge.RecordType,
"record_name": challenge.RecordName,
"record_values": challenge.Values,
})
}
}
}

managedDNSChallenge = map[string]string{
"record_type": challenge.RecordType,
"record_name": challenge.RecordName,
"record_value": challenge.Values[0],
// TODO: This block of code contains a bug where the state file will only include
// the first domain's challenge data in the case of multi-SAN cert subscriptions.
// Users should use the new "managed_dns_challenges" attribute instead.
// We're leaving this for backward compatibility but is planned to be removed in v1.0.0.
// https://github.com/fastly/terraform-provider-fastly/pull/435
{
var managedDNSChallengeOld map[string]string
for _, challenge := range subscription.Authorizations[0].Challenges {
if challenge.Type == "managed-dns" {
if len(challenge.Values) < 1 {
return diag.Errorf("Fastly API returned no record values for Managed DNS Challenge")
}

managedDNSChallengeOld = map[string]string{
"record_type": challenge.RecordType,
"record_name": challenge.RecordName,
"record_value": challenge.Values[0],
}
}
} else {
managedHTTPChallenges = append(managedHTTPChallenges, map[string]interface{}{
"record_type": challenge.RecordType,
"record_name": challenge.RecordName,
"record_values": challenge.Values,
})
}

err = d.Set("managed_dns_challenge", managedDNSChallengeOld)
if err != nil {
return diag.FromErr(err)
}
}

Expand Down Expand Up @@ -221,7 +275,7 @@ func resourceFastlyTLSSubscriptionRead(_ context.Context, d *schema.ResourceData
if err != nil {
return diag.FromErr(err)
}
err = d.Set("managed_dns_challenge", managedDNSChallenge)
err = d.Set("managed_dns_challenges", managedDNSChallenges)
if err != nil {
return diag.FromErr(err)
}
Expand Down
15 changes: 8 additions & 7 deletions templates/resources/tls_subscription.md.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Enables TLS on a domain using a certificate managed by Fastly.

DNS records need to be modified on the domain being secured, in order to respond to the ACME domain ownership challenge.

There are two options for doing this: the `managed_dns_challenge`, which is the default method; and the `managed_http_challenges`, which points production traffic to Fastly.
There are two options for doing this: the `managed_dns_challenges`, which is the default method; and the `managed_http_challenges`, which points production traffic to Fastly.

~> See the [Fastly documentation](https://docs.fastly.com/en/guides/serving-https-traffic-using-fastly-managed-certificates#verifying-domain-ownership) for more information on verifying domain ownership.

Expand Down Expand Up @@ -78,11 +78,12 @@ data "aws_route53_zone" "demo" {

# Set up DNS record for managed DNS domain validation method
resource "aws_route53_record" "domain_validation" {
name = fastly_tls_subscription.example.managed_dns_challenge.record_name
type = fastly_tls_subscription.example.managed_dns_challenge.record_type
for_each = { for domain in fastly_tls_subscription.example.managed_dns_challenges : domain.record_name => domain }
name = each.value.record_name
type = each.value.record_type
zone_id = data.aws_route53_zone.demo.id
allow_overwrite = true
records = [fastly_tls_subscription.example.managed_dns_challenge.record_value]
records = [each.value.record_value]
ttl = 60
}

Expand Down Expand Up @@ -112,14 +113,14 @@ In addition to the arguments listed above, the following attributes are exported
* `created_at` - Timestamp (GMT) when the subscription was created.
* `updated_at` - Timestamp (GMT) when the subscription was last updated.
* `state` - The current state of the subscription. The list of possible states are: `pending`, `processing`, `issued`, and `renewing`.
* `managed_dns_challenge` - The details required to configure DNS to respond to ACME DNS challenge in order to verify domain ownership. See Managed DNS Challenge below for details.
smaeda-ks marked this conversation as resolved.
Show resolved Hide resolved
* `managed_dns_challenges` - A list of options for configuring DNS to respond to ACME DNS challenge in order to verify domain ownership. See Managed DNS Challenge below for details.
* `managed_http_challenges` - A list of options for configuring DNS to respond to ACME HTTP challenge in order to verify domain ownership. See Managed HTTP Challenges below for details.

### Managed DNS Challenge

The available attributes in the `managed_dns_challenge` block are:
The available attributes in the `managed_dns_challenges` block are:

* `record_name` - The name of the DNS record to add. For example `_acme-challenge.example.com`. Accessed like this, `fastly_tls_subscription.tls.managed_dns_challenge.record_name`.
* `record_name` - The name of the DNS record to add. For example `_acme-challenge.example.com`. Accessed like this, `fastly_tls_subscription.tls.managed_dns_challenges.record_name`.
* `record_type` - The type of DNS record to add, e.g. `A`, or `CNAME`.
* `record_value` - The value to which the DNS record should point, e.g. `xxxxx.fastly-validations.com`.

Expand Down