diff --git a/docs/resources/domain.md b/docs/resources/domain.md index fd161a4e..f4d76248 100644 --- a/docs/resources/domain.md +++ b/docs/resources/domain.md @@ -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 diff --git a/heroku/resource_heroku_domain.go b/heroku/resource_heroku_domain.go index 13de9e33..d8c7af53 100644 --- a/heroku/resource_heroku_domain.go +++ b/heroku/resource_heroku_domain.go @@ -43,7 +43,6 @@ func resourceHerokuDomain() *schema.Resource { "sni_endpoint_id": { Type: schema.TypeString, - Computed: true, Optional: true, }, }, @@ -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 } @@ -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) @@ -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 } @@ -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 { diff --git a/heroku/resource_heroku_domain_test.go b/heroku/resource_heroku_domain_test.go index ea086b1f..7c2417c1 100644 --- a/heroku/resource_heroku_domain_test.go +++ b/heroku/resource_heroku_domain_test.go @@ -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 @@ -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) +}