diff --git a/builder/common/state.go b/builder/common/state.go index 48b5f0e05..bc57b982a 100644 --- a/builder/common/state.go +++ b/builder/common/state.go @@ -206,6 +206,18 @@ func (w *AWSPollingConfig) WaitUntilImageImported(ctx aws.Context, conn *ec2.EC2 return err } +func (w *AWSPollingConfig) WaitUntilSnapshotImported(ctx aws.Context, conn *ec2.EC2, taskID string) error { + importInput := ec2.DescribeImportSnapshotTasksInput{ + ImportTaskIds: []*string{&taskID}, + } + + err := WaitForSnapshotToBeImported(conn, + ctx, + &importInput, + w.getWaiterOptions()...) + return err +} + // Custom waiters using AWS's request.Waiter func WaitForVolumeToBeAttached(c *ec2.EC2, ctx aws.Context, input *ec2.DescribeVolumesInput, opts ...request.WaiterOption) error { @@ -307,6 +319,43 @@ func WaitForImageToBeImported(c *ec2.EC2, ctx aws.Context, input *ec2.DescribeIm return w.WaitWithContext(ctx) } +func WaitForSnapshotToBeImported(c *ec2.EC2, ctx aws.Context, input *ec2.DescribeImportSnapshotTasksInput, opts ...request.WaiterOption) error { + w := request.Waiter{ + Name: "DescribeSnapshot", + MaxAttempts: 720, + Delay: request.ConstantWaiterDelay(5 * time.Second), + Acceptors: []request.WaiterAcceptor{ + { + State: request.SuccessWaiterState, + Matcher: request.PathAllWaiterMatch, + Argument: "ImportSnapshotTasks[].SnapshotTaskDetail.Status", + Expected: "completed", + }, + { + State: request.FailureWaiterState, + Matcher: request.PathAnyWaiterMatch, + Argument: "ImportSnapshotTasks[].SnapshotTaskDetail.Status", + Expected: "deleted", + }, + }, + Logger: c.Config.Logger, + NewRequest: func(opts []request.Option) (*request.Request, error) { + var inCpy *ec2.DescribeImportSnapshotTasksInput + if input != nil { + tmp := *input + inCpy = &tmp + } + req, _ := c.DescribeImportSnapshotTasksRequest(inCpy) + req.SetContext(ctx) + req.ApplyOptions(opts...) + return req, nil + }, + } + w.ApplyOptions(opts...) + + return w.WaitWithContext(ctx) +} + // This helper function uses the environment variables AWS_TIMEOUT_SECONDS and // AWS_POLL_DELAY_SECONDS to generate waiter options that can be passed into any // request.Waiter function. These options will control how many times the waiter diff --git a/post-processor/import/post-processor.go b/post-processor/import/post-processor.go index 20ea991d5..080b5fd97 100644 --- a/post-processor/import/post-processor.go +++ b/post-processor/import/post-processor.go @@ -31,21 +31,23 @@ type Config struct { awscommon.AccessConfig `mapstructure:",squash"` // Variables specific to this post processor - S3Bucket string `mapstructure:"s3_bucket_name"` - S3Key string `mapstructure:"s3_key_name"` - S3Encryption string `mapstructure:"s3_encryption"` - S3EncryptionKey string `mapstructure:"s3_encryption_key"` - SkipClean bool `mapstructure:"skip_clean"` - Tags map[string]string `mapstructure:"tags"` - Name string `mapstructure:"ami_name"` - Description string `mapstructure:"ami_description"` - Users []string `mapstructure:"ami_users"` - Groups []string `mapstructure:"ami_groups"` - Encrypt bool `mapstructure:"ami_encrypt"` - KMSKey string `mapstructure:"ami_kms_key"` - LicenseType string `mapstructure:"license_type"` - RoleName string `mapstructure:"role_name"` - Format string `mapstructure:"format"` + S3Bucket string `mapstructure:"s3_bucket_name"` + S3Key string `mapstructure:"s3_key_name"` + S3Encryption string `mapstructure:"s3_encryption"` + S3EncryptionKey string `mapstructure:"s3_encryption_key"` + SkipClean bool `mapstructure:"skip_clean"` + Tags map[string]string `mapstructure:"tags"` + Name string `mapstructure:"ami_name"` + Description string `mapstructure:"ami_description"` + Users []string `mapstructure:"ami_users"` + Groups []string `mapstructure:"ami_groups"` + Encrypt bool `mapstructure:"ami_encrypt"` + KMSKey string `mapstructure:"ami_kms_key"` + LicenseType string `mapstructure:"license_type"` + RoleName string `mapstructure:"role_name"` + Format string `mapstructure:"format"` + FromSnapshot bool `mapstructure:"from_snapshot"` + FromSnapshotDeviceName string `mapstructure:"from_snapshot_device_name"` ctx interpolate.Context } @@ -81,6 +83,10 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { p.config.S3Key = "packer-import-{{timestamp}}." + p.config.Format } + if p.config.FromSnapshotDeviceName == "" { + p.config.FromSnapshotDeviceName = "/dev/sda" + } + errs := new(packersdk.MultiError) // Check and render s3_key_name @@ -111,6 +117,17 @@ func (p *PostProcessor) Configure(raws ...interface{}) error { errs, fmt.Errorf("invalid format '%s'. Only 'ova', 'raw', 'vhd', 'vhdx', or 'vmdk' are allowed", p.config.Format)) } + // If importing to snapshot, only 3 formats are allowed + if p.config.FromSnapshot { + switch p.config.Format { + case "vhd", "vmdk", "raw": + default: + errs = packersdk.MultiErrorAppend( + errs, fmt.Errorf( + "invalid format '%s' for snapshot import. Only 'raw', 'vhd', or 'vmdk' are allowed", p.config.Format)) + } + } + if p.config.S3Encryption != "" && p.config.S3Encryption != "AES256" && p.config.S3Encryption != "aws:kms" { errs = packersdk.MultiErrorAppend( errs, fmt.Errorf("invalid s3 encryption format '%s'. Only 'AES256' and 'aws:kms' are allowed", p.config.S3Encryption)) @@ -210,90 +227,22 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifa // Call EC2 image import process log.Printf("Calling EC2 to import from s3://%s/%s", p.config.S3Bucket, p.config.S3Key) + // Split into snapshot or image import + var createdami string ec2conn := ec2.New(session) - params := &ec2.ImportImageInput{ - Encrypted: &p.config.Encrypt, - DiskContainers: []*ec2.ImageDiskContainer{ - { - Format: &p.config.Format, - UserBucket: &ec2.UserBucket{ - S3Bucket: &p.config.S3Bucket, - S3Key: &p.config.S3Key, - }, - }, - }, - } - - if p.config.Encrypt && p.config.KMSKey != "" { - params.KmsKeyId = &p.config.KMSKey - } - - if p.config.RoleName != "" { - params.SetRoleName(p.config.RoleName) - } - if p.config.LicenseType != "" { - ui.Message(fmt.Sprintf("Setting license type to '%s'", p.config.LicenseType)) - params.LicenseType = &p.config.LicenseType - } - - var import_start *ec2.ImportImageOutput - err = retry.Config{ - Tries: 11, - RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear, - }.Run(ctx, func(ctx context.Context) error { - import_start, err = ec2conn.ImportImage(params) - return err - }) - - if err != nil { - return nil, false, false, fmt.Errorf("Failed to start import from s3://%s/%s: %s", p.config.S3Bucket, p.config.S3Key, err) + if p.config.FromSnapshot { + createdami, err = p.importSnapshot(ui, ctx, ec2conn) + } else { + createdami, err = p.importImage(ui, ctx, ec2conn) } - ui.Message(fmt.Sprintf("Started import of s3://%s/%s, task id %s", p.config.S3Bucket, p.config.S3Key, *import_start.ImportTaskId)) - - // Wait for import process to complete, this takes a while - ui.Message(fmt.Sprintf("Waiting for task %s to complete (may take a while)", *import_start.ImportTaskId)) - err = p.config.PollingConfig.WaitUntilImageImported(aws.BackgroundContext(), ec2conn, *import_start.ImportTaskId) if err != nil { - - // Retrieve the status message - import_result, err2 := ec2conn.DescribeImportImageTasks(&ec2.DescribeImportImageTasksInput{ - ImportTaskIds: []*string{ - import_start.ImportTaskId, - }, - }) - - statusMessage := "Error retrieving status message" - - if err2 == nil { - statusMessage = *import_result.ImportImageTasks[0].StatusMessage - } - return nil, false, false, fmt.Errorf("Import task %s failed with status message: %s, error: %s", *import_start.ImportTaskId, statusMessage, err) - } - - // Retrieve what the outcome was for the import task - import_result, err := ec2conn.DescribeImportImageTasks(&ec2.DescribeImportImageTasksInput{ - ImportTaskIds: []*string{ - import_start.ImportTaskId, - }, - }) - - if err != nil { - return nil, false, false, fmt.Errorf("Failed to find import task %s: %s", *import_start.ImportTaskId, err) - } - // Check it was actually completed - if *import_result.ImportImageTasks[0].Status != "completed" { - // The most useful error message is from the job itself - return nil, false, false, fmt.Errorf("Import task %s failed: %s", *import_start.ImportTaskId, *import_result.ImportImageTasks[0].StatusMessage) + return nil, false, false, err } - ui.Message(fmt.Sprintf("Import task %s complete", *import_start.ImportTaskId)) - - // Pull AMI ID out of the completed job - createdami := *import_result.ImportImageTasks[0].ImageId - - if p.config.Name != "" { + // Dont rename on snapshot as we set the name on creation + if p.config.Name != "" && !p.config.FromSnapshot { ui.Message(fmt.Sprintf("Starting rename of AMI (%s)", createdami)) @@ -466,3 +415,209 @@ func (p *PostProcessor) PostProcess(ctx context.Context, ui packersdk.Ui, artifa return artifact, false, false, nil } + +func (p *PostProcessor) importSnapshot(ui packersdk.Ui, ctx context.Context, ec2conn *ec2.EC2) (string, error) { + var err error + var err2 error + var importResult *ec2.DescribeImportSnapshotTasksOutput + + params := &ec2.ImportSnapshotInput{ + Encrypted: &p.config.Encrypt, + DiskContainer: &ec2.SnapshotDiskContainer{ + Format: &p.config.Format, + UserBucket: &ec2.UserBucket{ + S3Bucket: &p.config.S3Bucket, + S3Key: &p.config.S3Key, + }, + }, + } + + if p.config.Encrypt && p.config.KMSKey != "" { + params.KmsKeyId = &p.config.KMSKey + } + + if p.config.RoleName != "" { + params.SetRoleName(p.config.RoleName) + } + + var importStart *ec2.ImportSnapshotOutput + err = retry.Config{ + Tries: 11, + RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear, + }.Run(ctx, func(ctx context.Context) error { + importStart, err = ec2conn.ImportSnapshot(params) + return err + }) + + if err != nil { + return "", fmt.Errorf("Failed to start import from s3://%s/%s: %s", p.config.S3Bucket, p.config.S3Key, err) + } + + importTaskId := importStart.ImportTaskId + + ui.Message(fmt.Sprintf("Started import of s3://%s/%s, task id %s", p.config.S3Bucket, p.config.S3Key, *importTaskId)) + + // Wait for import process to complete, this takes a while + ui.Message(fmt.Sprintf("Waiting for task %s to complete (may take a while)", *importTaskId)) + + err = p.config.PollingConfig.WaitUntilSnapshotImported(aws.BackgroundContext(), ec2conn, *importTaskId) + if err != nil { + // Retrieve the status message + importResult, err2 = ec2conn.DescribeImportSnapshotTasks(&ec2.DescribeImportSnapshotTasksInput{ + ImportTaskIds: []*string{ + importTaskId, + }, + }) + + statusMessage := "Error retrieving status message" + + if err2 == nil { + statusMessage = *importResult.ImportSnapshotTasks[0].SnapshotTaskDetail.StatusMessage + } + return "", fmt.Errorf("Import task %s failed with status message: %s, error: %s", *importTaskId, statusMessage, err) + } + + // Retrieve what the outcome was for the import task + importResult, err = ec2conn.DescribeImportSnapshotTasks(&ec2.DescribeImportSnapshotTasksInput{ + ImportTaskIds: []*string{ + importTaskId, + }, + }) + + if err != nil { + return "", fmt.Errorf("Failed to find import task %s: %s", *importTaskId, err) + } + + snapshotId := importResult.ImportSnapshotTasks[0].SnapshotTaskDetail.SnapshotId + + // Check it was actually completed + if *importResult.ImportSnapshotTasks[0].SnapshotTaskDetail.Status != "completed" { + // The most useful error message is from the job itself + return "", fmt.Errorf("Import task %s failed: %s", *importTaskId, *importResult.ImportSnapshotTasks[0].SnapshotTaskDetail.StatusMessage) + } + + ui.Message(fmt.Sprintf("Import task %s complete", *importTaskId)) + + ebsDevice := ec2.EbsBlockDevice{ + SnapshotId: snapshotId, + } + + blockDevice := ec2.BlockDeviceMapping{ + DeviceName: &p.config.FromSnapshotDeviceName, + Ebs: &ebsDevice, + } + + var imageName string + + // Unfortunately when importing from snapshot we need to give a name to the ami so either get the name + // from the config or set a name based on the source s3 object + if p.config.Name != "" { + imageName = p.config.Name + } else { + imageName = fmt.Sprintf("packer-import-from-%s", p.config.S3Key) + } + + registerImageOutput, err := ec2conn.RegisterImage(&ec2.RegisterImageInput{ + BlockDeviceMappings: []*ec2.BlockDeviceMapping{ + &blockDevice, + }, + RootDeviceName: &p.config.FromSnapshotDeviceName, + Name: &imageName, + }) + + if err != nil { + return "", fmt.Errorf("Failed to register snapshot %s as AMI: %s", snapshotId, err) + } + + // Pull AMI ID out of the completed job + createdAmi := *registerImageOutput.ImageId + return createdAmi, err +} + +func (p *PostProcessor) importImage(ui packersdk.Ui, ctx context.Context, ec2conn *ec2.EC2) (string, error) { + var err error + var value string + params := &ec2.ImportImageInput{ + Encrypted: &p.config.Encrypt, + DiskContainers: []*ec2.ImageDiskContainer{ + { + Format: &p.config.Format, + UserBucket: &ec2.UserBucket{ + S3Bucket: &p.config.S3Bucket, + S3Key: &p.config.S3Key, + }, + }, + }, + } + + if p.config.Encrypt && p.config.KMSKey != "" { + params.KmsKeyId = &p.config.KMSKey + } + + if p.config.RoleName != "" { + params.SetRoleName(p.config.RoleName) + } + + if p.config.LicenseType != "" { + ui.Message(fmt.Sprintf("Setting license type to '%s'", p.config.LicenseType)) + params.LicenseType = &p.config.LicenseType + } + + var importStart *ec2.ImportImageOutput + err = retry.Config{ + Tries: 11, + RetryDelay: (&retry.Backoff{InitialBackoff: 200 * time.Millisecond, MaxBackoff: 30 * time.Second, Multiplier: 2}).Linear, + }.Run(ctx, func(ctx context.Context) error { + importStart, err = ec2conn.ImportImage(params) + return err + }) + + if err != nil { + return value, fmt.Errorf("Failed to start import from s3://%s/%s: %s", p.config.S3Bucket, p.config.S3Key, err) + } + + importTaskId := importStart.ImportTaskId + + ui.Message(fmt.Sprintf("Started import of s3://%s/%s, task id %s", p.config.S3Bucket, p.config.S3Key, *importTaskId)) + + // Wait for import process to complete, this takes a while + ui.Message(fmt.Sprintf("Waiting for task %s to complete (may take a while)", *importTaskId)) + err = p.config.PollingConfig.WaitUntilImageImported(aws.BackgroundContext(), ec2conn, *importTaskId) + if err != nil { + // Retrieve the status message + importResult, err2 := ec2conn.DescribeImportImageTasks(&ec2.DescribeImportImageTasksInput{ + ImportTaskIds: []*string{ + importTaskId, + }, + }) + + statusMessage := "Error retrieving status message" + + if err2 == nil { + statusMessage = *importResult.ImportImageTasks[0].StatusMessage + } + return value, fmt.Errorf("Import task %s failed with status message: %s, error: %s", importTaskId, statusMessage, err) + } + + // Retrieve what the outcome was for the import task + importResult, err := ec2conn.DescribeImportImageTasks(&ec2.DescribeImportImageTasksInput{ + ImportTaskIds: []*string{ + importTaskId, + }, + }) + + if err != nil { + return value, fmt.Errorf("Failed to find import task %s: %s", *importTaskId, err) + } + // Check it was actually completed + if *importResult.ImportImageTasks[0].Status != "completed" { + // The most useful error message is from the job itself + return value, fmt.Errorf("Import task %s failed: %s", *importTaskId, *importResult.ImportImageTasks[0].StatusMessage) + } + + ui.Message(fmt.Sprintf("Import task %s complete", *importTaskId)) + + // Pull AMI ID out of the completed job + createdAmi := *importResult.ImportImageTasks[0].ImageId + return createdAmi, err +} diff --git a/post-processor/import/post-processor.hcl2spec.go b/post-processor/import/post-processor.hcl2spec.go index d0bf007a4..80fe8214c 100644 --- a/post-processor/import/post-processor.hcl2spec.go +++ b/post-processor/import/post-processor.hcl2spec.go @@ -11,45 +11,47 @@ import ( // FlatConfig is an auto-generated flat version of Config. // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. type FlatConfig struct { - PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` - PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` - PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` - PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` - PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` - PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` - PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` - PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` - AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"` - AssumeRole *common.FlatAssumeRoleConfig `mapstructure:"assume_role" required:"false" cty:"assume_role" hcl:"assume_role"` - CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2" hcl:"custom_endpoint_ec2"` - CredsFilename *string `mapstructure:"shared_credentials_file" required:"false" cty:"shared_credentials_file" hcl:"shared_credentials_file"` - DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages" hcl:"decode_authorization_messages"` - InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"` - MaxRetries *int `mapstructure:"max_retries" required:"false" cty:"max_retries" hcl:"max_retries"` - MFACode *string `mapstructure:"mfa_code" required:"false" cty:"mfa_code" hcl:"mfa_code"` - ProfileName *string `mapstructure:"profile" required:"false" cty:"profile" hcl:"profile"` - RawRegion *string `mapstructure:"region" required:"true" cty:"region" hcl:"region"` - SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"` - SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"` - SkipCredsValidation *bool `mapstructure:"skip_credential_validation" cty:"skip_credential_validation" hcl:"skip_credential_validation"` - Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"` - VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"` - PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"` - S3Bucket *string `mapstructure:"s3_bucket_name" cty:"s3_bucket_name" hcl:"s3_bucket_name"` - S3Key *string `mapstructure:"s3_key_name" cty:"s3_key_name" hcl:"s3_key_name"` - S3Encryption *string `mapstructure:"s3_encryption" cty:"s3_encryption" hcl:"s3_encryption"` - S3EncryptionKey *string `mapstructure:"s3_encryption_key" cty:"s3_encryption_key" hcl:"s3_encryption_key"` - SkipClean *bool `mapstructure:"skip_clean" cty:"skip_clean" hcl:"skip_clean"` - Tags map[string]string `mapstructure:"tags" cty:"tags" hcl:"tags"` - Name *string `mapstructure:"ami_name" cty:"ami_name" hcl:"ami_name"` - Description *string `mapstructure:"ami_description" cty:"ami_description" hcl:"ami_description"` - Users []string `mapstructure:"ami_users" cty:"ami_users" hcl:"ami_users"` - Groups []string `mapstructure:"ami_groups" cty:"ami_groups" hcl:"ami_groups"` - Encrypt *bool `mapstructure:"ami_encrypt" cty:"ami_encrypt" hcl:"ami_encrypt"` - KMSKey *string `mapstructure:"ami_kms_key" cty:"ami_kms_key" hcl:"ami_kms_key"` - LicenseType *string `mapstructure:"license_type" cty:"license_type" hcl:"license_type"` - RoleName *string `mapstructure:"role_name" cty:"role_name" hcl:"role_name"` - Format *string `mapstructure:"format" cty:"format" hcl:"format"` + PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"` + PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"` + PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"` + PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"` + PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"` + PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"` + PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"` + PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"` + AccessKey *string `mapstructure:"access_key" required:"true" cty:"access_key" hcl:"access_key"` + AssumeRole *common.FlatAssumeRoleConfig `mapstructure:"assume_role" required:"false" cty:"assume_role" hcl:"assume_role"` + CustomEndpointEc2 *string `mapstructure:"custom_endpoint_ec2" required:"false" cty:"custom_endpoint_ec2" hcl:"custom_endpoint_ec2"` + CredsFilename *string `mapstructure:"shared_credentials_file" required:"false" cty:"shared_credentials_file" hcl:"shared_credentials_file"` + DecodeAuthZMessages *bool `mapstructure:"decode_authorization_messages" required:"false" cty:"decode_authorization_messages" hcl:"decode_authorization_messages"` + InsecureSkipTLSVerify *bool `mapstructure:"insecure_skip_tls_verify" required:"false" cty:"insecure_skip_tls_verify" hcl:"insecure_skip_tls_verify"` + MaxRetries *int `mapstructure:"max_retries" required:"false" cty:"max_retries" hcl:"max_retries"` + MFACode *string `mapstructure:"mfa_code" required:"false" cty:"mfa_code" hcl:"mfa_code"` + ProfileName *string `mapstructure:"profile" required:"false" cty:"profile" hcl:"profile"` + RawRegion *string `mapstructure:"region" required:"true" cty:"region" hcl:"region"` + SecretKey *string `mapstructure:"secret_key" required:"true" cty:"secret_key" hcl:"secret_key"` + SkipMetadataApiCheck *bool `mapstructure:"skip_metadata_api_check" cty:"skip_metadata_api_check" hcl:"skip_metadata_api_check"` + SkipCredsValidation *bool `mapstructure:"skip_credential_validation" cty:"skip_credential_validation" hcl:"skip_credential_validation"` + Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"` + VaultAWSEngine *common.FlatVaultAWSEngineOptions `mapstructure:"vault_aws_engine" required:"false" cty:"vault_aws_engine" hcl:"vault_aws_engine"` + PollingConfig *common.FlatAWSPollingConfig `mapstructure:"aws_polling" required:"false" cty:"aws_polling" hcl:"aws_polling"` + S3Bucket *string `mapstructure:"s3_bucket_name" cty:"s3_bucket_name" hcl:"s3_bucket_name"` + S3Key *string `mapstructure:"s3_key_name" cty:"s3_key_name" hcl:"s3_key_name"` + S3Encryption *string `mapstructure:"s3_encryption" cty:"s3_encryption" hcl:"s3_encryption"` + S3EncryptionKey *string `mapstructure:"s3_encryption_key" cty:"s3_encryption_key" hcl:"s3_encryption_key"` + SkipClean *bool `mapstructure:"skip_clean" cty:"skip_clean" hcl:"skip_clean"` + Tags map[string]string `mapstructure:"tags" cty:"tags" hcl:"tags"` + Name *string `mapstructure:"ami_name" cty:"ami_name" hcl:"ami_name"` + Description *string `mapstructure:"ami_description" cty:"ami_description" hcl:"ami_description"` + Users []string `mapstructure:"ami_users" cty:"ami_users" hcl:"ami_users"` + Groups []string `mapstructure:"ami_groups" cty:"ami_groups" hcl:"ami_groups"` + Encrypt *bool `mapstructure:"ami_encrypt" cty:"ami_encrypt" hcl:"ami_encrypt"` + KMSKey *string `mapstructure:"ami_kms_key" cty:"ami_kms_key" hcl:"ami_kms_key"` + LicenseType *string `mapstructure:"license_type" cty:"license_type" hcl:"license_type"` + RoleName *string `mapstructure:"role_name" cty:"role_name" hcl:"role_name"` + Format *string `mapstructure:"format" cty:"format" hcl:"format"` + FromSnapshot *bool `mapstructure:"from_snapshot" cty:"from_snapshot" hcl:"from_snapshot"` + FromSnapshotDeviceName *string `mapstructure:"from_snapshot_device_name" cty:"from_snapshot_device_name" hcl:"from_snapshot_device_name"` } // FlatMapstructure returns a new FlatConfig. @@ -103,6 +105,8 @@ func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { "license_type": &hcldec.AttrSpec{Name: "license_type", Type: cty.String, Required: false}, "role_name": &hcldec.AttrSpec{Name: "role_name", Type: cty.String, Required: false}, "format": &hcldec.AttrSpec{Name: "format", Type: cty.String, Required: false}, + "from_snapshot": &hcldec.AttrSpec{Name: "from_snapshot", Type: cty.Bool, Required: false}, + "from_snapshot_device_name": &hcldec.AttrSpec{Name: "from_snapshot_device_name", Type: cty.String, Required: false}, } return s }