2017-03-21 18:43:31 +01:00
|
|
|
package s3
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2017-08-05 00:26:19 +02:00
|
|
|
"fmt"
|
|
|
|
"strings"
|
2017-03-21 18:43:31 +01:00
|
|
|
|
|
|
|
"github.com/aws/aws-sdk-go/service/dynamodb"
|
|
|
|
"github.com/aws/aws-sdk-go/service/s3"
|
|
|
|
"github.com/hashicorp/terraform/backend"
|
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
|
|
|
2017-06-12 16:21:39 +02:00
|
|
|
terraformAWS "github.com/terraform-providers/terraform-provider-aws/aws"
|
2017-03-21 18:43:31 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// New creates a new backend for S3 remote state.
|
|
|
|
func New() backend.Backend {
|
|
|
|
s := &schema.Backend{
|
|
|
|
Schema: map[string]*schema.Schema{
|
2017-03-31 18:59:29 +02:00
|
|
|
"bucket": {
|
2017-03-21 18:43:31 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
Description: "The name of the S3 bucket",
|
|
|
|
},
|
|
|
|
|
2017-03-31 18:59:29 +02:00
|
|
|
"key": {
|
2017-03-21 18:43:31 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
Description: "The path to the state file inside the bucket",
|
2017-08-05 00:26:19 +02:00
|
|
|
ValidateFunc: func(v interface{}, s string) ([]string, []error) {
|
|
|
|
// s3 will strip leading slashes from an object, so while this will
|
|
|
|
// technically be accepted by s3, it will break our workspace hierarchy.
|
|
|
|
if strings.HasPrefix(v.(string), "/") {
|
|
|
|
return nil, []error{fmt.Errorf("key must not start with '/'")}
|
|
|
|
}
|
|
|
|
return nil, nil
|
|
|
|
},
|
2017-03-21 18:43:31 +01:00
|
|
|
},
|
|
|
|
|
2017-03-31 18:59:29 +02:00
|
|
|
"region": {
|
2017-03-21 18:43:31 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
Description: "The region of the S3 bucket.",
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("AWS_DEFAULT_REGION", nil),
|
|
|
|
},
|
|
|
|
|
2017-03-31 18:59:29 +02:00
|
|
|
"endpoint": {
|
2017-03-21 18:43:31 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "A custom endpoint for the S3 API",
|
|
|
|
DefaultFunc: schema.EnvDefaultFunc("AWS_S3_ENDPOINT", ""),
|
|
|
|
},
|
|
|
|
|
2017-03-31 18:59:29 +02:00
|
|
|
"encrypt": {
|
2017-03-21 18:43:31 +01:00
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Description: "Whether to enable server side encryption of the state file",
|
|
|
|
Default: false,
|
|
|
|
},
|
|
|
|
|
2017-03-31 18:59:29 +02:00
|
|
|
"acl": {
|
2017-03-21 18:43:31 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "Canned ACL to be applied to the state file",
|
|
|
|
Default: "",
|
|
|
|
},
|
|
|
|
|
2017-03-31 18:59:29 +02:00
|
|
|
"access_key": {
|
2017-03-21 18:43:31 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "AWS access key",
|
|
|
|
Default: "",
|
|
|
|
},
|
|
|
|
|
2017-03-31 18:59:29 +02:00
|
|
|
"secret_key": {
|
2017-03-21 18:43:31 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "AWS secret key",
|
|
|
|
Default: "",
|
|
|
|
},
|
|
|
|
|
2017-03-31 18:59:29 +02:00
|
|
|
"kms_key_id": {
|
2017-03-21 18:43:31 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "The ARN of a KMS Key to use for encrypting the state",
|
|
|
|
Default: "",
|
|
|
|
},
|
|
|
|
|
2017-03-31 18:59:29 +02:00
|
|
|
"lock_table": {
|
2017-03-21 18:43:31 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "DynamoDB table for state locking",
|
|
|
|
Default: "",
|
2017-05-26 01:12:20 +02:00
|
|
|
Deprecated: "please use the dynamodb_table attribute",
|
|
|
|
},
|
|
|
|
|
|
|
|
"dynamodb_table": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "DynamoDB table for state locking and consistency",
|
|
|
|
Default: "",
|
2017-03-21 18:43:31 +01:00
|
|
|
},
|
|
|
|
|
2017-03-31 18:59:29 +02:00
|
|
|
"profile": {
|
2017-03-21 18:43:31 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "AWS profile name",
|
|
|
|
Default: "",
|
|
|
|
},
|
|
|
|
|
2017-03-31 18:59:29 +02:00
|
|
|
"shared_credentials_file": {
|
2017-03-21 18:43:31 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "Path to a shared credentials file",
|
|
|
|
Default: "",
|
|
|
|
},
|
|
|
|
|
2017-03-31 18:59:29 +02:00
|
|
|
"token": {
|
2017-03-21 18:43:31 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "MFA token",
|
|
|
|
Default: "",
|
|
|
|
},
|
|
|
|
|
2017-07-14 13:08:47 +02:00
|
|
|
"skip_credentials_validation": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Description: "Skip the credentials validation via STS API.",
|
|
|
|
Default: false,
|
|
|
|
},
|
|
|
|
|
|
|
|
"skip_get_ec2_platforms": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Description: "Skip getting the supported EC2 platforms.",
|
|
|
|
Default: false,
|
|
|
|
},
|
|
|
|
|
2017-12-05 20:19:36 +01:00
|
|
|
"skip_region_validation": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Description: "Skip static validation of region name.",
|
|
|
|
Default: false,
|
|
|
|
},
|
|
|
|
|
2017-07-14 13:08:47 +02:00
|
|
|
"skip_requesting_account_id": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Description: "Skip requesting the account ID.",
|
|
|
|
Default: false,
|
|
|
|
},
|
|
|
|
|
|
|
|
"skip_metadata_api_check": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Description: "Skip the AWS Metadata API check.",
|
|
|
|
Default: false,
|
|
|
|
},
|
|
|
|
|
2017-03-31 18:59:29 +02:00
|
|
|
"role_arn": {
|
2017-03-21 18:43:31 +01:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "The role to be assumed",
|
|
|
|
Default: "",
|
|
|
|
},
|
2017-03-31 18:59:29 +02:00
|
|
|
|
|
|
|
"session_name": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "The session name to use when assuming the role.",
|
|
|
|
Default: "",
|
|
|
|
},
|
|
|
|
|
|
|
|
"external_id": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "The external ID to use when assuming the role",
|
|
|
|
Default: "",
|
|
|
|
},
|
|
|
|
|
|
|
|
"assume_role_policy": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Description: "The permissions applied when assuming a role.",
|
|
|
|
Default: "",
|
|
|
|
},
|
2017-06-22 19:17:37 +02:00
|
|
|
|
|
|
|
"workspace_key_prefix": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
2017-06-27 17:32:00 +02:00
|
|
|
Description: "The prefix applied to the non-default state path inside the bucket",
|
2017-06-22 19:17:37 +02:00
|
|
|
Default: "env:",
|
|
|
|
},
|
2017-03-21 18:43:31 +01:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
result := &Backend{Backend: s}
|
|
|
|
result.Backend.ConfigureFunc = result.configure
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
type Backend struct {
|
|
|
|
*schema.Backend
|
|
|
|
|
|
|
|
// The fields below are set from configure
|
2017-03-22 20:52:55 +01:00
|
|
|
s3Client *s3.S3
|
|
|
|
dynClient *dynamodb.DynamoDB
|
|
|
|
|
|
|
|
bucketName string
|
|
|
|
keyName string
|
|
|
|
serverSideEncryption bool
|
|
|
|
acl string
|
|
|
|
kmsKeyID string
|
2017-05-26 01:12:20 +02:00
|
|
|
ddbTable string
|
2017-06-22 19:17:37 +02:00
|
|
|
workspaceKeyPrefix string
|
2017-03-21 18:43:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (b *Backend) configure(ctx context.Context) error {
|
2017-03-22 20:52:55 +01:00
|
|
|
if b.s3Client != nil {
|
2017-03-21 18:43:31 +01:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Grab the resource data
|
|
|
|
data := schema.FromContextBackendConfig(ctx)
|
|
|
|
|
2017-03-22 20:52:55 +01:00
|
|
|
b.bucketName = data.Get("bucket").(string)
|
|
|
|
b.keyName = data.Get("key").(string)
|
|
|
|
b.serverSideEncryption = data.Get("encrypt").(bool)
|
|
|
|
b.acl = data.Get("acl").(string)
|
|
|
|
b.kmsKeyID = data.Get("kms_key_id").(string)
|
2017-06-22 19:17:37 +02:00
|
|
|
b.workspaceKeyPrefix = data.Get("workspace_key_prefix").(string)
|
2017-05-26 01:12:20 +02:00
|
|
|
|
|
|
|
b.ddbTable = data.Get("dynamodb_table").(string)
|
|
|
|
if b.ddbTable == "" {
|
2017-12-19 17:43:06 +01:00
|
|
|
// try the deprecated field
|
2017-05-26 01:12:20 +02:00
|
|
|
b.ddbTable = data.Get("lock_table").(string)
|
|
|
|
}
|
2017-03-21 18:43:31 +01:00
|
|
|
|
2017-04-05 18:37:42 +02:00
|
|
|
cfg := &terraformAWS.Config{
|
2017-07-14 13:08:47 +02:00
|
|
|
AccessKey: data.Get("access_key").(string),
|
|
|
|
AssumeRoleARN: data.Get("role_arn").(string),
|
|
|
|
AssumeRoleExternalID: data.Get("external_id").(string),
|
|
|
|
AssumeRolePolicy: data.Get("assume_role_policy").(string),
|
|
|
|
AssumeRoleSessionName: data.Get("session_name").(string),
|
|
|
|
CredsFilename: data.Get("shared_credentials_file").(string),
|
|
|
|
Profile: data.Get("profile").(string),
|
|
|
|
Region: data.Get("region").(string),
|
|
|
|
S3Endpoint: data.Get("endpoint").(string),
|
|
|
|
SecretKey: data.Get("secret_key").(string),
|
|
|
|
Token: data.Get("token").(string),
|
|
|
|
SkipCredsValidation: data.Get("skip_credentials_validation").(bool),
|
|
|
|
SkipGetEC2Platforms: data.Get("skip_get_ec2_platforms").(bool),
|
2017-12-05 20:19:36 +01:00
|
|
|
SkipRegionValidation: data.Get("skip_region_validation").(bool),
|
2017-07-14 13:08:47 +02:00
|
|
|
SkipRequestingAccountId: data.Get("skip_requesting_account_id").(bool),
|
|
|
|
SkipMetadataApiCheck: data.Get("skip_metadata_api_check").(bool),
|
2017-03-21 18:43:31 +01:00
|
|
|
}
|
|
|
|
|
2017-04-05 18:37:42 +02:00
|
|
|
client, err := cfg.Client()
|
2017-03-21 18:43:31 +01:00
|
|
|
if err != nil {
|
2017-04-05 18:37:42 +02:00
|
|
|
return err
|
2017-03-21 18:43:31 +01:00
|
|
|
}
|
|
|
|
|
2017-04-05 18:37:42 +02:00
|
|
|
b.s3Client = client.(*terraformAWS.AWSClient).S3()
|
|
|
|
b.dynClient = client.(*terraformAWS.AWSClient).DynamoDB()
|
2017-03-22 20:52:55 +01:00
|
|
|
|
2017-03-21 18:43:31 +01:00
|
|
|
return nil
|
|
|
|
}
|