func (s3Initializer S3Initializer) Initialize(remoteState *RemoteState, terragruntOptions *options.TerragruntOptions) error {
	s3ConfigExtended, err := parseExtendedS3Config(remoteState.Config)
	if err != nil {
		return err
	}
	if err := validateS3Config(s3ConfigExtended, terragruntOptions); err != nil {
		return err
	}
	var s3Config = s3ConfigExtended.remoteStateConfigS3
	s3Client, err := CreateS3Client(s3Config.GetAwsSessionConfig(), terragruntOptions)
	if err != nil {
		return err
	}
	if err := createS3BucketIfNecessary(s3Client, s3ConfigExtended, terragruntOptions); err != nil {
		return err
	}
	if !s3ConfigExtended.SkipBucketVersioning {
		if err := checkIfVersioningEnabled(s3Client, &s3Config, terragruntOptions); err != nil {
			return err
		}
	}
	if err := createLockTableIfNecessary(&s3Config, s3ConfigExtended.DynamotableTags, terragruntOptions); err != nil {
		return err
	}
	if err := UpdateLockTableSetSSEncryptionOnIfNecessary(&s3Config, s3ConfigExtended, terragruntOptions); err != nil {
		return err
	}
	return nil
}
Initialize the remote state S3 bucket specified in the given config. This function will validate the config
parameters, create the S3 bucket if it doesn't already exist, and check that versioning is enabled.
package remote
import (
	"encoding/json"
	"fmt"
	"reflect"
	"strconv"
	"time"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/gruntwork-io/terragrunt/aws_helper"
	"github.com/gruntwork-io/terragrunt/dynamodb"
	"github.com/gruntwork-io/terragrunt/errors"
	"github.com/gruntwork-io/terragrunt/options"
	"github.com/gruntwork-io/terragrunt/shell"
	"github.com/gruntwork-io/terragrunt/util"
	"github.com/mitchellh/mapstructure"
)
type ExtendedRemoteStateConfigS3 struct {
	remoteStateConfigS3 RemoteStateConfigS3
	S3BucketTags                map[string]string `mapstructure:"s3_bucket_tags"`
	DynamotableTags             map[string]string `mapstructure:"dynamodb_table_tags"`
	SkipBucketVersioning        bool              `mapstructure:"skip_bucket_versioning"`
	SkipBucketSSEncryption      bool              `mapstructure:"skip_bucket_ssencryption"`
	SkipBucketAccessLogging     bool              `mapstructure:"skip_bucket_accesslogging"`
	SkipBucketRootAccess        bool              `mapstructure:"skip_bucket_root_access"`
	EnableLockTableSSEncryption bool              `mapstructure:"enable_lock_table_ssencryption"`
}
var terragruntOnlyConfigs = []string{
	"s3_bucket_tags",
	"dynamodb_table_tags",
	"skip_bucket_versioning",
	"skip_bucket_ssencryption",
	"skip_bucket_accesslogging",
	"skip_bucket_root_access",
	"enable_lock_table_ssencryption",
}
type RemoteStateConfigS3 struct {
	Encrypt          bool   `mapstructure:"encrypt"`
	Bucket           string `mapstructure:"bucket"`
	Key              string `mapstructure:"key"`
	Region           string `mapstructure:"region"`
	Endpoint         string `mapstructure:"endpoint"`
	Profile          string `mapstructure:"profile"`
	RoleArn          string `mapstructure:"role_arn"`
	LockTable        string `mapstructure:"lock_table"`
	DynamoDBTable    string `mapstructure:"dynamodb_table"`
	CredsFilename    string `mapstructure:"shared_credentials_file"`
	S3ForcePathStyle bool   `mapstructure:"force_path_style"`
}
func (c *RemoteStateConfigS3) GetAwsSessionConfig() *aws_helper.AwsSessionConfig {
	return &aws_helper.AwsSessionConfig{
		Region:           c.Region,
		CustomS3Endpoint: c.Endpoint,
		Profile:          c.Profile,
		RoleArn:          c.RoleArn,
		CredsFilename:    c.CredsFilename,
		S3ForcePathStyle: c.S3ForcePathStyle,
	}
}
func (s3Config *RemoteStateConfigS3) GetLockTableName() string {
	if s3Config.DynamoDBTable != "" {
		return s3Config.DynamoDBTable
	}
	return s3Config.LockTable
}
const MAX_RETRIES_WAITING_FOR_S3_BUCKET = 12
const SLEEP_BETWEEN_RETRIES_WAITING_FOR_S3_BUCKET = 5 * time.Second
const s3LogDeliveryGranteeUri = "http://acs.amazonaws.com/groups/s3/LogDelivery"
type S3Initializer struct{}
func (s3Initializer S3Initializer) NeedsInitialization(remoteState *RemoteState, existingBackend *TerraformBackend, terragruntOptions *options.TerragruntOptions) (bool, error) {
	if remoteState.DisableInit {
		return false, nil
	}
	if !configValuesEqual(remoteState.Config, existingBackend, terragruntOptions) {
		return true, nil
	}
	s3Config, err := parseS3Config(remoteState.Config)
	if err != nil {
		return false, err
	}
	sessionConfig := s3Config.GetAwsSessionConfig()
	s3Client, err := CreateS3Client(sessionConfig, terragruntOptions)
	if err != nil {
		return false, err
	}
	if !DoesS3BucketExist(s3Client, s3Config) {
		return true, nil
	}
	if s3Config.GetLockTableName() != "" {
		dynamodbClient, err := dynamodb.CreateDynamoDbClient(sessionConfig, terragruntOptions)
		if err != nil {
			return false, err
		}
		tableExists, err := dynamodb.LockTableExistsAndIsActive(s3Config.GetLockTableName(), dynamodbClient)
		if err != nil {
			return false, err
		}
		if !tableExists {
			return true, nil
		}
	}
	return false, nil
}
func configValuesEqual(config map[string]interface{}, existingBackend *TerraformBackend, terragruntOptions *options.TerragruntOptions) bool {
	if existingBackend == nil {
		return len(config) == 0
	}
	if existingBackend.Type != "s3" {
		terragruntOptions.Logger.Printf("Backend type has changed from s3 to %s", existingBackend.Type)
		return false
	}
	if len(config) == 0 && len(existingBackend.Config) == 0 {
		return true
	}
	if util.KindOf(existingBackend.Config["encrypt"]) == reflect.String && util.KindOf(config["encrypt"]) == reflect.Bool {
		if value, err := strconv.ParseBool(existingBackend.Config["encrypt"].(string)); err == nil {
			existingBackend.Config["encrypt"] = value
		} else {
			terragruntOptions.Logger.Printf("Remote state configuration encrypt contains invalid value %v, should be boolean.", existingBackend.Config["encrypt"])
		}
	}
	for key, value := range existingBackend.Config {
		if util.KindOf(existingBackend.Config[key]) == reflect.String && util.KindOf(config[key]) == reflect.Bool {
			if convertedValue, err := strconv.ParseBool(value.(string)); err == nil {
				existingBackend.Config[key] = convertedValue
			}
		}
	}
	for _, key := range terragruntOnlyConfigs {
		delete(config, key)
	}
	if !terraformStateConfigEqual(existingBackend.Config, config) {
		terragruntOptions.Logger.Printf("Backend config has changed from %s to %s", existingBackend.Config, config)
		return false
	}
	return true
}