Merge pull request #672 from plan3-labs/heroku_cert_support
Heroku SSL certificate support
This commit is contained in:
commit
f923368fc5
|
@ -30,6 +30,7 @@ func Provider() terraform.ResourceProvider {
|
|||
"heroku_addon": resourceHerokuAddon(),
|
||||
"heroku_domain": resourceHerokuDomain(),
|
||||
"heroku_drain": resourceHerokuDrain(),
|
||||
"heroku_cert": resourceHerokuCert(),
|
||||
},
|
||||
|
||||
ConfigureFunc: providerConfigure,
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
package heroku
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/cyberdelia/heroku-go/v3"
|
||||
"github.com/hashicorp/terraform/helper/schema"
|
||||
)
|
||||
|
||||
func resourceHerokuCert() *schema.Resource {
|
||||
return &schema.Resource{
|
||||
Create: resourceHerokuCertCreate,
|
||||
Read: resourceHerokuCertRead,
|
||||
Update: resourceHerokuCertUpdate,
|
||||
Delete: resourceHerokuCertDelete,
|
||||
|
||||
Schema: map[string]*schema.Schema{
|
||||
"app": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
ForceNew: true,
|
||||
},
|
||||
|
||||
"certificate_chain": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"private_key": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Required: true,
|
||||
},
|
||||
|
||||
"cname": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
|
||||
"name": &schema.Schema{
|
||||
Type: schema.TypeString,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func resourceHerokuCertCreate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*heroku.Service)
|
||||
|
||||
app := d.Get("app").(string)
|
||||
preprocess := true
|
||||
opts := heroku.SSLEndpointCreateOpts{
|
||||
CertificateChain: d.Get("certificate_chain").(string),
|
||||
Preprocess: &preprocess,
|
||||
PrivateKey: d.Get("private_key").(string)}
|
||||
|
||||
log.Printf("[DEBUG] SSL Certificate create configuration: %#v, %#v", app, opts)
|
||||
a, err := client.SSLEndpointCreate(app, opts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
d.SetId(a.ID)
|
||||
log.Printf("[INFO] SSL Certificate ID: %s", d.Id())
|
||||
|
||||
return resourceHerokuCertRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceHerokuCertRead(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*heroku.Service)
|
||||
|
||||
cert, err := resourceHerokuSSLCertRetrieve(
|
||||
d.Get("app").(string), d.Id(), client)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
d.Set("certificate_chain", cert.CertificateChain)
|
||||
d.Set("name", cert.Name)
|
||||
d.Set("cname", cert.CName)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceHerokuCertUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*heroku.Service)
|
||||
|
||||
app := d.Get("app").(string)
|
||||
|
||||
if d.HasChange("certificate_chain") {
|
||||
preprocess := true
|
||||
rollback := false
|
||||
ad, err := client.SSLEndpointUpdate(
|
||||
app, d.Id(), heroku.SSLEndpointUpdateOpts{
|
||||
CertificateChain: d.Get("certificate_chain").(*string),
|
||||
Preprocess: &preprocess,
|
||||
PrivateKey: d.Get("private_key").(*string),
|
||||
Rollback: &rollback})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Store the new ID
|
||||
d.SetId(ad.ID)
|
||||
}
|
||||
|
||||
return resourceHerokuCertRead(d, meta)
|
||||
}
|
||||
|
||||
func resourceHerokuCertDelete(d *schema.ResourceData, meta interface{}) error {
|
||||
client := meta.(*heroku.Service)
|
||||
|
||||
log.Printf("[INFO] Deleting SSL Cert: %s", d.Id())
|
||||
|
||||
// Destroy the app
|
||||
err := client.SSLEndpointDelete(d.Get("app").(string), d.Id())
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error deleting SSL Cert: %s", err)
|
||||
}
|
||||
|
||||
d.SetId("")
|
||||
return nil
|
||||
}
|
||||
|
||||
func resourceHerokuSSLCertRetrieve(app string, id string, client *heroku.Service) (*heroku.SSLEndpoint, error) {
|
||||
addon, err := client.SSLEndpointInfo(app, id)
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error retrieving SSL Cert: %s", err)
|
||||
}
|
||||
|
||||
return addon, nil
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package heroku
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"os"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/cyberdelia/heroku-go/v3"
|
||||
"github.com/hashicorp/terraform/helper/resource"
|
||||
"github.com/hashicorp/terraform/terraform"
|
||||
)
|
||||
|
||||
func TestAccHerokuCert_Basic(t *testing.T) {
|
||||
var endpoint heroku.SSLEndpoint
|
||||
wd, _ := os.Getwd()
|
||||
certificateChainFile := wd + "/test-fixtures/terraform.cert"
|
||||
certificateChainBytes, _ := ioutil.ReadFile(certificateChainFile)
|
||||
certificateChain := string(certificateChainBytes)
|
||||
testAccCheckHerokuCertConfig_basic := `
|
||||
resource "heroku_app" "foobar" {
|
||||
name = "terraform-test-cert-app"
|
||||
region = "eu"
|
||||
}
|
||||
|
||||
resource "heroku_addon" "ssl" {
|
||||
app = "${heroku_app.foobar.name}"
|
||||
plan = "ssl:endpoint"
|
||||
}
|
||||
|
||||
resource "heroku_cert" "ssl_certificate" {
|
||||
app = "${heroku_app.foobar.name}"
|
||||
depends_on = "heroku_addon.ssl"
|
||||
certificate_chain="${file("` + certificateChainFile + `")}"
|
||||
private_key="${file("` + wd + `/test-fixtures/terraform.key")}"
|
||||
}
|
||||
`
|
||||
|
||||
resource.Test(t, resource.TestCase{
|
||||
PreCheck: func() { testAccPreCheck(t) },
|
||||
Providers: testAccProviders,
|
||||
CheckDestroy: testAccCheckHerokuCertDestroy,
|
||||
Steps: []resource.TestStep{
|
||||
resource.TestStep{
|
||||
Config: testAccCheckHerokuCertConfig_basic,
|
||||
Check: resource.ComposeTestCheckFunc(
|
||||
testAccCheckHerokuCertExists("heroku_cert.ssl_certificate", &endpoint),
|
||||
testAccCheckHerokuCertificateChain(&endpoint, certificateChain),
|
||||
resource.TestCheckResourceAttr(
|
||||
"heroku_cert.ssl_certificate", "cname", "terraform-test-cert-app.herokuapp.com"),
|
||||
),
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func testAccCheckHerokuCertDestroy(s *terraform.State) error {
|
||||
client := testAccProvider.Meta().(*heroku.Service)
|
||||
|
||||
for _, rs := range s.RootModule().Resources {
|
||||
if rs.Type != "heroku_cert" {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err := client.SSLEndpointInfo(rs.Primary.Attributes["app"], rs.Primary.ID)
|
||||
|
||||
if err == nil {
|
||||
return fmt.Errorf("Cerfificate still exists")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAccCheckHerokuCertificateChain(endpoint *heroku.SSLEndpoint, chain string) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
|
||||
if endpoint.CertificateChain != chain {
|
||||
return fmt.Errorf("Bad certificate chain: %s", endpoint.CertificateChain)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func testAccCheckHerokuCertExists(n string, endpoint *heroku.SSLEndpoint) resource.TestCheckFunc {
|
||||
return func(s *terraform.State) error {
|
||||
rs, ok := s.RootModule().Resources[n]
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("Not found: %s", n)
|
||||
}
|
||||
|
||||
if rs.Primary.ID == "" {
|
||||
return fmt.Errorf("No SSL endpoint ID is set")
|
||||
}
|
||||
|
||||
client := testAccProvider.Meta().(*heroku.Service)
|
||||
|
||||
foundEndpoint, err := client.SSLEndpointInfo(rs.Primary.Attributes["app"], rs.Primary.ID)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if foundEndpoint.ID != rs.Primary.ID {
|
||||
return fmt.Errorf("SSL endpoint not found")
|
||||
}
|
||||
|
||||
*endpoint = *foundEndpoint
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDCzCCAfOgAwIBAgIJAIUuu5XX/tCRMA0GCSqGSIb3DQEBBQUAMBwxGjAYBgNV
|
||||
BAMMEXd3dy50ZXJyYWZvcm0ub3JnMB4XDTE0MTIxNDIwMzA0MFoXDTI0MTIxMTIw
|
||||
MzA0MFowHDEaMBgGA1UEAwwRd3d3LnRlcnJhZm9ybS5vcmcwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQC7sp6oJ6czdRpl5azB7jaLCwQ38eqV2TRFPVVj
|
||||
PD7cWyhV2REFtqd7vEF/AUrp3+ACvc6mLdTjDuaGVVga4oA42Qgqz5Wzkl3tnBSB
|
||||
DlxFXXg+p4UjJWZPLUiOMbvHWNGthO9G1dp5h9rhqV7wJhyAlTqlnV7aaeSWcJgY
|
||||
fh7xMe50BlAmh6ywpnnlZzsy4eJiJwgbglG8OU0JK+1OxdOUDe/1eUOhFPPx1U/p
|
||||
25t8Z6qaI8FDLPwTVZzrvOZ0vTQSKyeA0ZhBTH1GhUqroogDlPETgkne6YqvZoxl
|
||||
o8+9Wdjln2bjYe/nRWYKR5BxC46PnNJMPPJFI/VNLPYanAu5AgMBAAGjUDBOMB0G
|
||||
A1UdDgQWBBQaJeROghiSQGVn30ARllECycYsczAfBgNVHSMEGDAWgBQaJeROghiS
|
||||
QGVn30ARllECycYsczAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQCa
|
||||
iRpZ0b4KVqDT3bqc9UZV491UdBVF9BN0CV4BLvg9KPyRcftujZu0lKFu+wGlAlYr
|
||||
bV6DjqHgFXltBzIFM/y790EivkgePcFv+0HKy1O8ELLduQcigYT5AC7h34xxWBy7
|
||||
96VW6qD7/OOjvexVdKmTfXO/njdmot38/uO9TdfJPQzCHrgpzjBCcI+eBFnvQwzb
|
||||
gOpMlh04U4nDeITOTbraLur1zWQjzSA9DjaGGA+IQ556MUPAS85YmJ4Jf+f8UW3o
|
||||
sZmzFojLFd8EhVRZDE4tZzqo/vN0plGv/Kh/oob5Cnp6EJ3BbG6y9tzATMWz/hRo
|
||||
oeTtQe3gt6gPKBS+UeBf
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAu7KeqCenM3UaZeWswe42iwsEN/Hqldk0RT1VYzw+3FsoVdkR
|
||||
Bbane7xBfwFK6d/gAr3Opi3U4w7mhlVYGuKAONkIKs+Vs5Jd7ZwUgQ5cRV14PqeF
|
||||
IyVmTy1IjjG7x1jRrYTvRtXaeYfa4ale8CYcgJU6pZ1e2mnklnCYGH4e8THudAZQ
|
||||
JoessKZ55Wc7MuHiYicIG4JRvDlNCSvtTsXTlA3v9XlDoRTz8dVP6dubfGeqmiPB
|
||||
Qyz8E1Wc67zmdL00EisngNGYQUx9RoVKq6KIA5TxE4JJ3umKr2aMZaPPvVnY5Z9m
|
||||
42Hv50VmCkeQcQuOj5zSTDzyRSP1TSz2GpwLuQIDAQABAoIBAQCethLiLWV8ZXDE
|
||||
6MiD02HbgJ04kR7DRr6kLZCeMLsWqR4aOUnjgudsAWuAcR9fUyagKs8qRWbV+CuF
|
||||
O3UchpnVd+8oBA+ZoBI8cNYFqpbrMHYUxKIXbfBs0uWfFv6pOblS+C07wGjUisPS
|
||||
PN1CQ3emYokMsV0bYp8fdmWlkD+pwhHH3vsPm4sYwbabaURRxZYLNyYd/4Czafqz
|
||||
UBWbAJ+xap6t/WLJCR7goHCVX0DNtpNfzoYK5/rKpQzw0H+L1pB5yghx5GbslthB
|
||||
xtTb1LjMMl4AUuU5Bv1XLzDZlS/HUYQyefhljlqJPC678KNlegRLjn2YZeZF07X1
|
||||
b/KSKDrhAoGBAPVST4JYGS9OoW4Fvu4SV7AJGW1ewHhAmzO1XFrYD8DvSjf3Cinn
|
||||
ylTYwkQK9ayLLM405Sof2J55NbEakDs5sahN93mqGrk9bWPZrFHgDf0NiMoyI/0N
|
||||
/ZBXhkBeg9LitBvmEWiBlGK55At0zePWVDcUtXg+d0tSJC4o0y1DoGo7AoGBAMPe
|
||||
ML95QKabWsCRpGKVwhOFrEp68rZlugwJEjzubC8EXHX8dNy3IURl1j8tSM0kDGay
|
||||
CDMpxOjqzVyLmLqfIghiG1nkQU7EnJdx86k3AaesHoJff3Ywi+9DwC5r/T3zg20U
|
||||
Qkr1c4Yxv0Lk3IluHwjPaT/uMd4utlPB6EpAvY6bAoGAdcNBb6yiylbQn2Qat2YO
|
||||
ue5kSmBFvHQnDLdu0h0N0uwLkLoCIwOl2P0EpG0uadmVdJdnusT203wUDiRWQFf9
|
||||
tHFY7wp9MZcPP/NqCROpI2Sv2YAgToW8xuF9DMFSPpWdKBdVG/m4JXxewDEd9NUa
|
||||
MCa8xjAWTA3uWEo4tW3VP6kCgYAKAYvT/EnFOSKFu+r97lCf1rBajbVghAnhG4WG
|
||||
/1cff8WJcYA21lQovlsXlySk9jZ7+JRaqMOacoRTOf5vajm+2+Qxz2tWrsyhH/0m
|
||||
o9y9yBk259IHI6vCaV+j/3hMdeg85lAMrEVekaQHstFhY/LJ7G6gCXcatqAx3zIS
|
||||
uQP2CQKBgQDGx31nzcrYcJz0EtZPJ5n4JMKrx9S4ZQ2+C7p6oDYXJl2Woh0f+c3K
|
||||
X1oW1MLFChq+5Or9IVh11cePtV3a6X10Q9xSLQnXdPC9X4QsZoWOUBh5fzw301g1
|
||||
WB9ncXqqew3EPHm1G5j4hZ+Gjz/P53TLRYTEYW8fm1Rv9ga5wiJpiw==
|
||||
-----END RSA PRIVATE KEY-----
|
|
@ -0,0 +1,51 @@
|
|||
---
|
||||
layout: "heroku"
|
||||
page_title: "Heroku: heroku_cert"
|
||||
sidebar_current: "docs-heroku-resource-cert"
|
||||
description: |-
|
||||
Provides a Heroku SSL certificate resource. It allows to set a given certificate for a Heroku app.
|
||||
---
|
||||
|
||||
# heroku\_cert
|
||||
|
||||
Provides a Heroku SSL certificate resource. It allows to set a given certificate for a Heroku app.
|
||||
|
||||
## Example Usage
|
||||
|
||||
```
|
||||
# Create a new heroku app
|
||||
resource "heroku_app" "default" {
|
||||
name = "test-app"
|
||||
}
|
||||
|
||||
# Add-on SSL to application
|
||||
resource "heroku_addon" "ssl" {
|
||||
app = "${heroku_app.service.name}"
|
||||
plan = "ssl"
|
||||
}
|
||||
|
||||
# Establish certificate for a given application
|
||||
resource "heroku_cert" "ssl_certificate" {
|
||||
app = "${heroku_app.service.name}"
|
||||
certificate_chain = "${file("server.crt")}"
|
||||
private_key = "${file("server.key")}"
|
||||
depends_on = "heroku_addon.ssl"
|
||||
}
|
||||
```
|
||||
|
||||
## Argument Reference
|
||||
|
||||
The following arguments are supported:
|
||||
|
||||
* `app` - (Required) The Heroku app to add to.
|
||||
* `certificate_chain` - (Required) The certificate chain to add
|
||||
* `private_key` - (Required) The private key for a given certificate chain
|
||||
|
||||
## Attributes Reference
|
||||
|
||||
The following attributes are exported:
|
||||
|
||||
* `id` - The ID of the add-on
|
||||
* `cname` - The CNAME of ssl endpoint
|
||||
* `name` - The name of the SSL certificate
|
||||
|
Loading…
Reference in New Issue