Skip to content

Commit

Permalink
Domain sni_endpoint_id cooperation with ACM (#361)
Browse files Browse the repository at this point in the history
* Domain sni_endpoint_id should not be computed, so that it remains unset for ACM apps

* Domain should not read/set sni_endpoint_id for ACM apps

* Update Domain docs with ACM vs manual cert management differences
  • Loading branch information
mars authored Feb 21, 2023
1 parent 5cf20ba commit 2215717
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 13 deletions.
16 changes: 10 additions & 6 deletions docs/resources/domain.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,29 @@ resource "heroku_app" "default" {
# Associate a custom domain
resource "heroku_domain" "default" {
app_id = heroku_app.default.id
hostname = "terraform.example.com"
hostname = "www.example.com"
}
```

## Argument Reference

The following arguments are supported:

* `hostname` - (Required) The hostname to serve requests from.
* `app_id` - (Required) Heroku app ID (do not use app name)

For apps with ACM enabled (automated certificate management):

* `hostname` - (Required) The hostname to setup via ACM.

For apps with `heroku_ssl` (SNI Endpoint) resources (manual certificate management):

* `hostname` - (Required) Must match common name or a subject alternative name of certificate in the `heroku_ssl` resource references by `sni_endpoint_id`.
* `sni_endpoint_id` - (Required) The ID of the `heroku_ssl` resource to associate the domain with.

## Attributes Reference

The following attributes are exported:

* `id` - The ID of the domain record.
* `hostname` - The hostname traffic will be served as.
* `cname` - The CNAME traffic should route to.
* `sni_endpoint_id` - The ID of the heroku_ssl resource to associate the domain with.

## Importing

Expand Down
31 changes: 24 additions & 7 deletions heroku/resource_heroku_domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ func resourceHerokuDomain() *schema.Resource {

"sni_endpoint_id": {
Type: schema.TypeString,
Computed: true,
Optional: true,
},
},
Expand Down Expand Up @@ -72,7 +71,10 @@ func resourceHerokuDomainImport(d *schema.ResourceData, meta interface{}) ([]*sc
return nil, err
}

populateResource(d, do)
err = populateResource(d, do, client)
if err != nil {
return nil, fmt.Errorf("Error populating domain attributes: %w", err)
}

return []*schema.ResourceData{d}, nil
}
Expand All @@ -94,7 +96,10 @@ func resourceHerokuDomainCreate(d *schema.ResourceData, meta interface{}) error
if err != nil {
return err
}
populateResource(d, do)
err = populateResource(d, do, client)
if err != nil {
return fmt.Errorf("Error populating domain attributes: %w", err)
}

config := meta.(*Config)
time.Sleep(time.Duration(config.PostDomainCreateDelay) * time.Second)
Expand All @@ -117,7 +122,10 @@ func resourceHerokuDomainUpdate(d *schema.ResourceData, meta interface{}) error
return err
}

populateResource(d, do)
err = populateResource(d, do, client)
if err != nil {
return fmt.Errorf("Error populating domain attributes: %w", err)
}

return nil
}
Expand Down Expand Up @@ -146,19 +154,28 @@ func resourceHerokuDomainRead(d *schema.ResourceData, meta interface{}) error {
}

log.Printf("[INFO] Reading Domain: %s", d.Id())
populateResource(d, do)
err = populateResource(d, do, client)
if err != nil {
return fmt.Errorf("Error populating domain attributes: %w", err)
}

return nil
}

func populateResource(d *schema.ResourceData, do *heroku.Domain) {
func populateResource(d *schema.ResourceData, do *heroku.Domain, client *heroku.Service) error {
d.SetId(do.ID)
d.Set("app_id", do.App.ID)
d.Set("hostname", do.Hostname)
d.Set("cname", do.CName)
if v := do.SniEndpoint; v != nil {
// Do not capture SNI Endpoint when ACM is active
hasACM, err := retrieveAcm(do.App.ID, client)
if err != nil {
return err
}
if v := do.SniEndpoint; !hasACM && v != nil {
d.Set("sni_endpoint_id", v.ID)
}
return nil
}

func resourceHerokuDomainV0() *schema.Resource {
Expand Down
38 changes: 38 additions & 0 deletions heroku/resource_heroku_domain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,31 @@ func TestAccHerokuDomain_Basic(t *testing.T) {
})
}

func TestAccHerokuDomain_ACM(t *testing.T) {
var domain heroku.Domain
var endpoint heroku.SniEndpoint
randString := acctest.RandString(10)
appName := fmt.Sprintf("tftest-%s", randString)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckHerokuDomainDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckHerokuDomainConfig_ACM(appName),
Check: resource.ComposeTestCheckFunc(
testAccCheckHerokuDomainExists("heroku_domain.one", &domain),
testAccCheckHerokuDomainAttributes(&domain, &endpoint),
resource.TestCheckResourceAttr("heroku_domain.one", "hostname", "terraform-tftest-"+randString+".example.com"),
resource.TestCheckResourceAttrSet("heroku_domain.one", "app_id"),
resource.TestCheckNoResourceAttr("heroku_domain.one", "sni_endpoint_id"),
),
},
},
})
}

func TestAccHerokuDomain_No_SSL_Change(t *testing.T) {
var domain heroku.Domain
var endpoint heroku.SniEndpoint
Expand Down Expand Up @@ -338,3 +363,16 @@ resource "heroku_domain" "one" {
hostname = "terraform-%s.example.com"
}`, appName, appName)
}

func testAccCheckHerokuDomainConfig_ACM(appName string) string {
return fmt.Sprintf(`resource "heroku_app" "one" {
name = "%s"
region = "us"
acm = true
}
resource "heroku_domain" "one" {
app_id = heroku_app.one.id
hostname = "terraform-%s.example.com"
}`, appName, appName)
}

0 comments on commit 2215717

Please sign in to comment.