github.com/jd3nn1s/terraform@v0.9.6-0.20170906225847-13878347b7a1/backend/remote-state/s3/backend.go (about)

     1  package s3
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  
     8  	"github.com/aws/aws-sdk-go/service/dynamodb"
     9  	"github.com/aws/aws-sdk-go/service/s3"
    10  	"github.com/hashicorp/terraform/backend"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  
    13  	terraformAWS "github.com/terraform-providers/terraform-provider-aws/aws"
    14  )
    15  
    16  // New creates a new backend for S3 remote state.
    17  func New() backend.Backend {
    18  	s := &schema.Backend{
    19  		Schema: map[string]*schema.Schema{
    20  			"bucket": {
    21  				Type:        schema.TypeString,
    22  				Required:    true,
    23  				Description: "The name of the S3 bucket",
    24  			},
    25  
    26  			"key": {
    27  				Type:        schema.TypeString,
    28  				Required:    true,
    29  				Description: "The path to the state file inside the bucket",
    30  				ValidateFunc: func(v interface{}, s string) ([]string, []error) {
    31  					// s3 will strip leading slashes from an object, so while this will
    32  					// technically be accepted by s3, it will break our workspace hierarchy.
    33  					if strings.HasPrefix(v.(string), "/") {
    34  						return nil, []error{fmt.Errorf("key must not start with '/'")}
    35  					}
    36  					return nil, nil
    37  				},
    38  			},
    39  
    40  			"region": {
    41  				Type:        schema.TypeString,
    42  				Required:    true,
    43  				Description: "The region of the S3 bucket.",
    44  				DefaultFunc: schema.EnvDefaultFunc("AWS_DEFAULT_REGION", nil),
    45  			},
    46  
    47  			"endpoint": {
    48  				Type:        schema.TypeString,
    49  				Optional:    true,
    50  				Description: "A custom endpoint for the S3 API",
    51  				DefaultFunc: schema.EnvDefaultFunc("AWS_S3_ENDPOINT", ""),
    52  			},
    53  
    54  			"encrypt": {
    55  				Type:        schema.TypeBool,
    56  				Optional:    true,
    57  				Description: "Whether to enable server side encryption of the state file",
    58  				Default:     false,
    59  			},
    60  
    61  			"acl": {
    62  				Type:        schema.TypeString,
    63  				Optional:    true,
    64  				Description: "Canned ACL to be applied to the state file",
    65  				Default:     "",
    66  			},
    67  
    68  			"access_key": {
    69  				Type:        schema.TypeString,
    70  				Optional:    true,
    71  				Description: "AWS access key",
    72  				Default:     "",
    73  			},
    74  
    75  			"secret_key": {
    76  				Type:        schema.TypeString,
    77  				Optional:    true,
    78  				Description: "AWS secret key",
    79  				Default:     "",
    80  			},
    81  
    82  			"kms_key_id": {
    83  				Type:        schema.TypeString,
    84  				Optional:    true,
    85  				Description: "The ARN of a KMS Key to use for encrypting the state",
    86  				Default:     "",
    87  			},
    88  
    89  			"lock_table": {
    90  				Type:        schema.TypeString,
    91  				Optional:    true,
    92  				Description: "DynamoDB table for state locking",
    93  				Default:     "",
    94  				Deprecated:  "please use the dynamodb_table attribute",
    95  			},
    96  
    97  			"dynamodb_table": {
    98  				Type:        schema.TypeString,
    99  				Optional:    true,
   100  				Description: "DynamoDB table for state locking and consistency",
   101  				Default:     "",
   102  			},
   103  
   104  			"profile": {
   105  				Type:        schema.TypeString,
   106  				Optional:    true,
   107  				Description: "AWS profile name",
   108  				Default:     "",
   109  			},
   110  
   111  			"shared_credentials_file": {
   112  				Type:        schema.TypeString,
   113  				Optional:    true,
   114  				Description: "Path to a shared credentials file",
   115  				Default:     "",
   116  			},
   117  
   118  			"token": {
   119  				Type:        schema.TypeString,
   120  				Optional:    true,
   121  				Description: "MFA token",
   122  				Default:     "",
   123  			},
   124  
   125  			"role_arn": {
   126  				Type:        schema.TypeString,
   127  				Optional:    true,
   128  				Description: "The role to be assumed",
   129  				Default:     "",
   130  			},
   131  
   132  			"session_name": {
   133  				Type:        schema.TypeString,
   134  				Optional:    true,
   135  				Description: "The session name to use when assuming the role.",
   136  				Default:     "",
   137  			},
   138  
   139  			"external_id": {
   140  				Type:        schema.TypeString,
   141  				Optional:    true,
   142  				Description: "The external ID to use when assuming the role",
   143  				Default:     "",
   144  			},
   145  
   146  			"assume_role_policy": {
   147  				Type:        schema.TypeString,
   148  				Optional:    true,
   149  				Description: "The permissions applied when assuming a role.",
   150  				Default:     "",
   151  			},
   152  
   153  			"workspace_key_prefix": {
   154  				Type:        schema.TypeString,
   155  				Optional:    true,
   156  				Description: "The prefix applied to the non-default state path inside the bucket",
   157  				Default:     "env:",
   158  			},
   159  		},
   160  	}
   161  
   162  	result := &Backend{Backend: s}
   163  	result.Backend.ConfigureFunc = result.configure
   164  	return result
   165  }
   166  
   167  type Backend struct {
   168  	*schema.Backend
   169  
   170  	// The fields below are set from configure
   171  	s3Client  *s3.S3
   172  	dynClient *dynamodb.DynamoDB
   173  
   174  	bucketName           string
   175  	keyName              string
   176  	serverSideEncryption bool
   177  	acl                  string
   178  	kmsKeyID             string
   179  	ddbTable             string
   180  	workspaceKeyPrefix   string
   181  }
   182  
   183  func (b *Backend) configure(ctx context.Context) error {
   184  	if b.s3Client != nil {
   185  		return nil
   186  	}
   187  
   188  	// Grab the resource data
   189  	data := schema.FromContextBackendConfig(ctx)
   190  
   191  	b.bucketName = data.Get("bucket").(string)
   192  	b.keyName = data.Get("key").(string)
   193  	b.serverSideEncryption = data.Get("encrypt").(bool)
   194  	b.acl = data.Get("acl").(string)
   195  	b.kmsKeyID = data.Get("kms_key_id").(string)
   196  	b.workspaceKeyPrefix = data.Get("workspace_key_prefix").(string)
   197  
   198  	b.ddbTable = data.Get("dynamodb_table").(string)
   199  	if b.ddbTable == "" {
   200  		// try the depracted field
   201  		b.ddbTable = data.Get("lock_table").(string)
   202  	}
   203  
   204  	cfg := &terraformAWS.Config{
   205  		AccessKey:             data.Get("access_key").(string),
   206  		AssumeRoleARN:         data.Get("role_arn").(string),
   207  		AssumeRoleExternalID:  data.Get("external_id").(string),
   208  		AssumeRolePolicy:      data.Get("assume_role_policy").(string),
   209  		AssumeRoleSessionName: data.Get("session_name").(string),
   210  		CredsFilename:         data.Get("shared_credentials_file").(string),
   211  		Profile:               data.Get("profile").(string),
   212  		Region:                data.Get("region").(string),
   213  		S3Endpoint:            data.Get("endpoint").(string),
   214  		SecretKey:             data.Get("secret_key").(string),
   215  		Token:                 data.Get("token").(string),
   216  	}
   217  
   218  	client, err := cfg.Client()
   219  	if err != nil {
   220  		return err
   221  	}
   222  
   223  	b.s3Client = client.(*terraformAWS.AWSClient).S3()
   224  	b.dynClient = client.(*terraformAWS.AWSClient).DynamoDB()
   225  
   226  	return nil
   227  }