2015-05-26 16:52:58 +02:00
|
|
|
package aws
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/sha1"
|
|
|
|
"encoding/hex"
|
|
|
|
"fmt"
|
2015-07-28 21:02:26 +02:00
|
|
|
"log"
|
2015-05-26 16:52:58 +02:00
|
|
|
"strings"
|
2015-11-12 22:15:47 +01:00
|
|
|
"time"
|
2015-05-26 16:52:58 +02:00
|
|
|
|
2015-06-03 20:36:57 +02:00
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
|
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
|
|
"github.com/aws/aws-sdk-go/service/iam"
|
2015-11-12 22:15:47 +01:00
|
|
|
"github.com/hashicorp/terraform/helper/resource"
|
2015-05-26 16:52:58 +02:00
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
|
|
)
|
|
|
|
|
|
|
|
func resourceAwsIAMServerCertificate() *schema.Resource {
|
|
|
|
return &schema.Resource{
|
|
|
|
Create: resourceAwsIAMServerCertificateCreate,
|
|
|
|
Read: resourceAwsIAMServerCertificateRead,
|
|
|
|
Delete: resourceAwsIAMServerCertificateDelete,
|
|
|
|
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"certificate_body": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
ForceNew: true,
|
|
|
|
StateFunc: normalizeCert,
|
|
|
|
},
|
|
|
|
|
|
|
|
"certificate_chain": &schema.Schema{
|
2015-06-24 07:31:24 +02:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
2015-06-21 07:40:09 +02:00
|
|
|
StateFunc: normalizeCert,
|
2015-05-26 16:52:58 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
"path": &schema.Schema{
|
2015-07-28 21:02:26 +02:00
|
|
|
Type: schema.TypeString,
|
2015-05-26 16:52:58 +02:00
|
|
|
Optional: true,
|
2015-07-28 21:02:26 +02:00
|
|
|
Default: "/",
|
2015-05-26 16:52:58 +02:00
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"private_key": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
ForceNew: true,
|
|
|
|
StateFunc: normalizeCert,
|
|
|
|
},
|
|
|
|
|
|
|
|
"name": &schema.Schema{
|
2016-02-17 23:32:54 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
ConflictsWith: []string{"name_prefix"},
|
|
|
|
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
|
|
|
value := v.(string)
|
2016-02-18 00:03:31 +01:00
|
|
|
if len(value) > 128 {
|
2016-02-17 23:32:54 +01:00
|
|
|
errors = append(errors, fmt.Errorf(
|
2016-02-18 00:03:31 +01:00
|
|
|
"%q cannot be longer than 128 characters", k))
|
2016-02-17 23:32:54 +01:00
|
|
|
}
|
|
|
|
return
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
"name_prefix": &schema.Schema{
|
2015-05-26 16:52:58 +02:00
|
|
|
Type: schema.TypeString,
|
2016-02-17 23:32:54 +01:00
|
|
|
Optional: true,
|
2015-05-26 16:52:58 +02:00
|
|
|
ForceNew: true,
|
2016-02-17 23:32:54 +01:00
|
|
|
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
|
|
|
value := v.(string)
|
2016-02-18 00:03:31 +01:00
|
|
|
if len(value) > 30 {
|
2016-02-17 23:32:54 +01:00
|
|
|
errors = append(errors, fmt.Errorf(
|
2016-02-18 00:03:31 +01:00
|
|
|
"%q cannot be longer than 30 characters, name is limited to 128", k))
|
2016-02-17 23:32:54 +01:00
|
|
|
}
|
|
|
|
return
|
|
|
|
},
|
2015-05-26 16:52:58 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
"arn": &schema.Schema{
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceAwsIAMServerCertificateCreate(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
conn := meta.(*AWSClient).iamconn
|
|
|
|
|
2016-02-17 23:32:54 +01:00
|
|
|
var sslCertName string
|
|
|
|
if v, ok := d.GetOk("name"); ok {
|
|
|
|
sslCertName = v.(string)
|
|
|
|
} else if v, ok := d.GetOk("name_prefix"); ok {
|
|
|
|
sslCertName = resource.PrefixedUniqueId(v.(string))
|
|
|
|
} else {
|
|
|
|
sslCertName = resource.UniqueId()
|
|
|
|
}
|
|
|
|
|
2015-05-26 16:52:58 +02:00
|
|
|
createOpts := &iam.UploadServerCertificateInput{
|
|
|
|
CertificateBody: aws.String(d.Get("certificate_body").(string)),
|
|
|
|
PrivateKey: aws.String(d.Get("private_key").(string)),
|
2016-02-17 23:32:54 +01:00
|
|
|
ServerCertificateName: aws.String(sslCertName),
|
2015-05-26 16:52:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := d.GetOk("certificate_chain"); ok {
|
|
|
|
createOpts.CertificateChain = aws.String(v.(string))
|
|
|
|
}
|
|
|
|
|
2015-07-28 21:02:26 +02:00
|
|
|
if v, ok := d.GetOk("path"); ok {
|
2015-05-26 16:52:58 +02:00
|
|
|
createOpts.Path = aws.String(v.(string))
|
|
|
|
}
|
|
|
|
|
2015-07-28 21:02:26 +02:00
|
|
|
log.Printf("[DEBUG] Creating IAM Server Certificate with opts: %s", createOpts)
|
2015-05-26 16:52:58 +02:00
|
|
|
resp, err := conn.UploadServerCertificate(createOpts)
|
|
|
|
if err != nil {
|
|
|
|
if awsErr, ok := err.(awserr.Error); ok {
|
|
|
|
return fmt.Errorf("[WARN] Error uploading server certificate, error: %s: %s", awsErr.Code(), awsErr.Message())
|
|
|
|
}
|
|
|
|
return fmt.Errorf("[WARN] Error uploading server certificate, error: %s", err)
|
|
|
|
}
|
|
|
|
|
2015-08-17 20:27:16 +02:00
|
|
|
d.SetId(*resp.ServerCertificateMetadata.ServerCertificateId)
|
2016-02-17 23:32:54 +01:00
|
|
|
d.Set("name", sslCertName)
|
2015-05-26 16:52:58 +02:00
|
|
|
|
|
|
|
return resourceAwsIAMServerCertificateRead(d, meta)
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceAwsIAMServerCertificateRead(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
conn := meta.(*AWSClient).iamconn
|
|
|
|
resp, err := conn.GetServerCertificate(&iam.GetServerCertificateInput{
|
|
|
|
ServerCertificateName: aws.String(d.Get("name").(string)),
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if awsErr, ok := err.(awserr.Error); ok {
|
2016-05-02 22:36:50 +02:00
|
|
|
if awsErr.Code() == "NoSuchEntity" {
|
|
|
|
log.Printf("[WARN] IAM Server Cert (%s) not found, removing from state", d.Id())
|
|
|
|
d.SetId("")
|
|
|
|
return nil
|
|
|
|
}
|
2015-05-26 16:52:58 +02:00
|
|
|
return fmt.Errorf("[WARN] Error reading IAM Server Certificate: %s: %s", awsErr.Code(), awsErr.Message())
|
|
|
|
}
|
|
|
|
return fmt.Errorf("[WARN] Error reading IAM Server Certificate: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// these values should always be present, and have a default if not set in
|
|
|
|
// configuration, and so safe to reference with nil checks
|
|
|
|
d.Set("certificate_body", normalizeCert(resp.ServerCertificate.CertificateBody))
|
2015-07-28 21:02:26 +02:00
|
|
|
|
|
|
|
c := normalizeCert(resp.ServerCertificate.CertificateChain)
|
|
|
|
if c != "" {
|
|
|
|
d.Set("certificate_chain", c)
|
|
|
|
}
|
|
|
|
|
2015-05-26 16:52:58 +02:00
|
|
|
d.Set("path", resp.ServerCertificate.ServerCertificateMetadata.Path)
|
2015-08-17 20:27:16 +02:00
|
|
|
d.Set("arn", resp.ServerCertificate.ServerCertificateMetadata.Arn)
|
2015-05-26 16:52:58 +02:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceAwsIAMServerCertificateDelete(d *schema.ResourceData, meta interface{}) error {
|
|
|
|
conn := meta.(*AWSClient).iamconn
|
2015-11-12 22:15:47 +01:00
|
|
|
log.Printf("[INFO] Deleting IAM Server Certificate: %s", d.Id())
|
2016-05-02 22:36:50 +02:00
|
|
|
err := resource.Retry(3*time.Minute, func() *resource.RetryError {
|
2015-11-12 22:15:47 +01:00
|
|
|
_, err := conn.DeleteServerCertificate(&iam.DeleteServerCertificateInput{
|
|
|
|
ServerCertificateName: aws.String(d.Get("name").(string)),
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if awsErr, ok := err.(awserr.Error); ok {
|
|
|
|
if awsErr.Code() == "DeleteConflict" && strings.Contains(awsErr.Message(), "currently in use by arn") {
|
2016-02-17 23:32:54 +01:00
|
|
|
log.Printf("[WARN] Conflict deleting server certificate: %s, retrying", awsErr.Message())
|
2016-03-09 23:53:32 +01:00
|
|
|
return resource.RetryableError(err)
|
2015-11-12 22:15:47 +01:00
|
|
|
}
|
2016-05-02 22:36:50 +02:00
|
|
|
if awsErr.Code() == "NoSuchEntity" {
|
|
|
|
log.Printf("[WARN] IAM Server Certificate (%s) not found, removing from state", d.Id())
|
|
|
|
d.SetId("")
|
|
|
|
return nil
|
|
|
|
}
|
2015-11-12 22:15:47 +01:00
|
|
|
}
|
2016-03-09 23:53:32 +01:00
|
|
|
return resource.NonRetryableError(err)
|
2015-11-12 22:15:47 +01:00
|
|
|
}
|
|
|
|
return nil
|
2015-05-26 16:52:58 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
d.SetId("")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func normalizeCert(cert interface{}) string {
|
2015-07-28 21:02:26 +02:00
|
|
|
if cert == nil || cert == (*string)(nil) {
|
2015-05-26 16:52:58 +02:00
|
|
|
return ""
|
|
|
|
}
|
2015-07-28 21:02:26 +02:00
|
|
|
|
2015-05-26 16:52:58 +02:00
|
|
|
switch cert.(type) {
|
|
|
|
case string:
|
|
|
|
hash := sha1.Sum([]byte(strings.TrimSpace(cert.(string))))
|
|
|
|
return hex.EncodeToString(hash[:])
|
|
|
|
case *string:
|
|
|
|
hash := sha1.Sum([]byte(strings.TrimSpace(*cert.(*string))))
|
|
|
|
return hex.EncodeToString(hash[:])
|
|
|
|
default:
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
}
|