Merge pull request #4243 from rickard-von-essen/aws-snapshot-users-groups

WIP: amazon: Add snapshot_users and snapshot_groups
This commit is contained in:
Rickard von Essen 2016-12-06 10:24:07 +01:00 committed by GitHub
commit 56f2bfa5ff
13 changed files with 210 additions and 58 deletions

View File

@ -257,10 +257,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Name: b.config.AMIName, Name: b.config.AMIName,
}, },
&awscommon.StepModifyAMIAttributes{ &awscommon.StepModifyAMIAttributes{
Description: b.config.AMIDescription, Description: b.config.AMIDescription,
Users: b.config.AMIUsers, Users: b.config.AMIUsers,
Groups: b.config.AMIGroups, Groups: b.config.AMIGroups,
ProductCodes: b.config.AMIProductCodes, ProductCodes: b.config.AMIProductCodes,
SnapshotUsers: b.config.SnapshotUsers,
SnapshotGroups: b.config.SnapshotGroups,
}, },
&awscommon.StepCreateTags{ &awscommon.StepCreateTags{
Tags: b.config.AMITags, Tags: b.config.AMITags,

View File

@ -23,6 +23,8 @@ type AMIConfig struct {
AMIEncryptBootVolume bool `mapstructure:"encrypt_boot"` AMIEncryptBootVolume bool `mapstructure:"encrypt_boot"`
AMIKmsKeyId string `mapstructure:"kms_key_id"` AMIKmsKeyId string `mapstructure:"kms_key_id"`
SnapshotTags map[string]string `mapstructure:"snapshot_tags"` SnapshotTags map[string]string `mapstructure:"snapshot_tags"`
SnapshotUsers []string `mapstructure:"snapshot_users"`
SnapshotGroups []string `mapstructure:"snapshot_groups"`
} }
func (c *AMIConfig) Prepare(ctx *interpolate.Context) []error { func (c *AMIConfig) Prepare(ctx *interpolate.Context) []error {
@ -62,6 +64,10 @@ func (c *AMIConfig) Prepare(ctx *interpolate.Context) []error {
errs = append(errs, fmt.Errorf("Cannot share AMI with encrypted boot volume")) errs = append(errs, fmt.Errorf("Cannot share AMI with encrypted boot volume"))
} }
if len(c.SnapshotUsers) > 0 && len(c.AMIKmsKeyId) == 0 && c.AMIEncryptBootVolume {
errs = append(errs, fmt.Errorf("Cannot share snapshot encrypted with default KMS key"))
}
if len(errs) > 0 { if len(errs) > 0 {
return errs return errs
} }

View File

@ -23,6 +23,7 @@ func (s *StepAMIRegionCopy) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2) ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
amis := state.Get("amis").(map[string]string) amis := state.Get("amis").(map[string]string)
snapshots := state.Get("snapshots").(map[string][]string)
ami := amis[*ec2conn.Config.Region] ami := amis[*ec2conn.Config.Region]
if len(s.Regions) == 0 { if len(s.Regions) == 0 {
@ -46,11 +47,12 @@ func (s *StepAMIRegionCopy) Run(state multistep.StateBag) multistep.StepAction {
go func(region string) { go func(region string) {
defer wg.Done() defer wg.Done()
id, err := amiRegionCopy(state, s.AccessConfig, s.Name, ami, region, *ec2conn.Config.Region) id, snapshotIds, err := amiRegionCopy(state, s.AccessConfig, s.Name, ami, region, *ec2conn.Config.Region)
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
amis[region] = id amis[region] = id
snapshots[region] = snapshotIds
if err != nil { if err != nil {
errs = packer.MultiErrorAppend(errs, err) errs = packer.MultiErrorAppend(errs, err)
} }
@ -77,20 +79,21 @@ func (s *StepAMIRegionCopy) Cleanup(state multistep.StateBag) {
} }
// amiRegionCopy does a copy for the given AMI to the target region and // amiRegionCopy does a copy for the given AMI to the target region and
// returns the resulting ID or error. // returns the resulting ID and snapshot IDs, or error.
func amiRegionCopy(state multistep.StateBag, config *AccessConfig, name string, imageId string, func amiRegionCopy(state multistep.StateBag, config *AccessConfig, name string, imageId string,
target string, source string) (string, error) { target string, source string) (string, []string, error) {
snapshotIds := []string{}
// Connect to the region where the AMI will be copied to // Connect to the region where the AMI will be copied to
awsConfig, err := config.Config() awsConfig, err := config.Config()
if err != nil { if err != nil {
return "", err return "", snapshotIds, err
} }
awsConfig.Region = aws.String(target) awsConfig.Region = aws.String(target)
session, err := session.NewSession(awsConfig) session, err := session.NewSession(awsConfig)
if err != nil { if err != nil {
return "", err return "", snapshotIds, err
} }
regionconn := ec2.New(session) regionconn := ec2.New(session)
@ -101,7 +104,7 @@ func amiRegionCopy(state multistep.StateBag, config *AccessConfig, name string,
}) })
if err != nil { if err != nil {
return "", fmt.Errorf("Error Copying AMI (%s) to region (%s): %s", return "", snapshotIds, fmt.Errorf("Error Copying AMI (%s) to region (%s): %s",
imageId, target, err) imageId, target, err)
} }
@ -113,9 +116,22 @@ func amiRegionCopy(state multistep.StateBag, config *AccessConfig, name string,
} }
if _, err := WaitForState(&stateChange); err != nil { if _, err := WaitForState(&stateChange); err != nil {
return "", fmt.Errorf("Error waiting for AMI (%s) in region (%s): %s", return "", snapshotIds, fmt.Errorf("Error waiting for AMI (%s) in region (%s): %s",
*resp.ImageId, target, err) *resp.ImageId, target, err)
} }
return *resp.ImageId, nil // Getting snapshot IDs out of the copied AMI
describeImageResp, err := regionconn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{resp.ImageId}})
if err != nil {
return "", snapshotIds, fmt.Errorf("Error describing copied AMI (%s) in region (%s): %s",
imageId, target, err)
}
for _, blockDeviceMapping := range describeImageResp.Images[0].BlockDeviceMappings {
if blockDeviceMapping.Ebs != nil {
snapshotIds = append(snapshotIds, *blockDeviceMapping.Ebs.SnapshotId)
}
}
return *resp.ImageId, snapshotIds, nil
} }

View File

@ -11,16 +11,19 @@ import (
) )
type StepModifyAMIAttributes struct { type StepModifyAMIAttributes struct {
Users []string Users []string
Groups []string Groups []string
ProductCodes []string SnapshotUsers []string
Description string SnapshotGroups []string
ProductCodes []string
Description string
} }
func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAction { func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAction {
ec2conn := state.Get("ec2").(*ec2.EC2) ec2conn := state.Get("ec2").(*ec2.EC2)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
amis := state.Get("amis").(map[string]string) amis := state.Get("amis").(map[string]string)
snapshots := state.Get("snapshots").(map[string][]string)
// Determine if there is any work to do. // Determine if there is any work to do.
valid := false valid := false
@ -28,51 +31,89 @@ func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAc
valid = valid || (s.Users != nil && len(s.Users) > 0) valid = valid || (s.Users != nil && len(s.Users) > 0)
valid = valid || (s.Groups != nil && len(s.Groups) > 0) valid = valid || (s.Groups != nil && len(s.Groups) > 0)
valid = valid || (s.ProductCodes != nil && len(s.ProductCodes) > 0) valid = valid || (s.ProductCodes != nil && len(s.ProductCodes) > 0)
valid = valid || (s.SnapshotUsers != nil && len(s.SnapshotUsers) > 0)
valid = valid || (s.SnapshotGroups != nil && len(s.SnapshotGroups) > 0)
if !valid { if !valid {
return multistep.ActionContinue return multistep.ActionContinue
} }
// Construct the modify image attribute requests we're going to make. // Construct the modify image and snapshot attribute requests we're going
// We need to make each separately since the EC2 API only allows changing // to make. We need to make each separately since the EC2 API only allows
// one type at a kind currently. // changing one type at a kind currently.
options := make(map[string]*ec2.ModifyImageAttributeInput) options := make(map[string]*ec2.ModifyImageAttributeInput)
if s.Description != "" { if s.Description != "" {
options["description"] = &ec2.ModifyImageAttributeInput{ options["description"] = &ec2.ModifyImageAttributeInput{
Description: &ec2.AttributeValue{Value: &s.Description}, Description: &ec2.AttributeValue{Value: &s.Description},
} }
} }
snapshotOptions := make(map[string]*ec2.ModifySnapshotAttributeInput)
if len(s.Groups) > 0 { if len(s.Groups) > 0 {
groups := make([]*string, len(s.Groups)) groups := make([]*string, len(s.Groups))
adds := make([]*ec2.LaunchPermission, len(s.Groups)) addsImage := make([]*ec2.LaunchPermission, len(s.Groups))
addGroups := &ec2.ModifyImageAttributeInput{ addGroups := &ec2.ModifyImageAttributeInput{
LaunchPermission: &ec2.LaunchPermissionModifications{}, LaunchPermission: &ec2.LaunchPermissionModifications{},
} }
for i, g := range s.Groups { for i, g := range s.Groups {
groups[i] = aws.String(g) groups[i] = aws.String(g)
adds[i] = &ec2.LaunchPermission{ addsImage[i] = &ec2.LaunchPermission{
Group: aws.String(g), Group: aws.String(g),
} }
} }
addGroups.UserGroups = groups
addGroups.LaunchPermission.Add = adds
addGroups.UserGroups = groups
addGroups.LaunchPermission.Add = addsImage
options["groups"] = addGroups options["groups"] = addGroups
} }
if len(s.SnapshotGroups) > 0 {
groups := make([]*string, len(s.SnapshotGroups))
addsSnapshot := make([]*ec2.CreateVolumePermission, len(s.SnapshotGroups))
addSnapshotGroups := &ec2.ModifySnapshotAttributeInput{
CreateVolumePermission: &ec2.CreateVolumePermissionModifications{},
}
for i, g := range s.SnapshotGroups {
groups[i] = aws.String(g)
addsSnapshot[i] = &ec2.CreateVolumePermission{
Group: aws.String(g),
}
}
addSnapshotGroups.GroupNames = groups
addSnapshotGroups.CreateVolumePermission.Add = addsSnapshot
snapshotOptions["groups"] = addSnapshotGroups
}
if len(s.Users) > 0 { if len(s.Users) > 0 {
users := make([]*string, len(s.Users)) users := make([]*string, len(s.Users))
adds := make([]*ec2.LaunchPermission, len(s.Users)) addsImage := make([]*ec2.LaunchPermission, len(s.Users))
for i, u := range s.Users { for i, u := range s.Users {
users[i] = aws.String(u) users[i] = aws.String(u)
adds[i] = &ec2.LaunchPermission{UserId: aws.String(u)} addsImage[i] = &ec2.LaunchPermission{UserId: aws.String(u)}
} }
options["users"] = &ec2.ModifyImageAttributeInput{ options["users"] = &ec2.ModifyImageAttributeInput{
UserIds: users, UserIds: users,
LaunchPermission: &ec2.LaunchPermissionModifications{ LaunchPermission: &ec2.LaunchPermissionModifications{
Add: adds, Add: addsImage,
},
}
}
if len(s.SnapshotUsers) > 0 {
users := make([]*string, len(s.SnapshotUsers))
addsSnapshot := make([]*ec2.CreateVolumePermission, len(s.SnapshotUsers))
for i, u := range s.SnapshotUsers {
users[i] = aws.String(u)
addsSnapshot[i] = &ec2.CreateVolumePermission{UserId: aws.String(u)}
}
snapshotOptions["users"] = &ec2.ModifySnapshotAttributeInput{
UserIds: users,
CreateVolumePermission: &ec2.CreateVolumePermissionModifications{
Add: addsSnapshot,
}, },
} }
} }
@ -87,6 +128,7 @@ func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAc
} }
} }
// Modifying image attributes
for region, ami := range amis { for region, ami := range amis {
ui.Say(fmt.Sprintf("Modifying attributes on AMI (%s)...", ami)) ui.Say(fmt.Sprintf("Modifying attributes on AMI (%s)...", ami))
awsConfig := aws.Config{ awsConfig := aws.Config{
@ -114,6 +156,30 @@ func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAc
} }
} }
// Modifying snapshot attributes
for region, region_snapshots := range snapshots {
for _, snapshot := range region_snapshots {
ui.Say(fmt.Sprintf("Modifying attributes on snapshot (%s)...", snapshot))
awsConfig := aws.Config{
Credentials: ec2conn.Config.Credentials,
Region: aws.String(region),
}
session := session.New(&awsConfig)
regionconn := ec2.New(session)
for name, input := range snapshotOptions {
ui.Message(fmt.Sprintf("Modifying: %s", name))
input.SnapshotId = &snapshot
_, err := regionconn.ModifySnapshotAttribute(input)
if err != nil {
err := fmt.Errorf("Error modify snapshot attributes: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
}
}
}
return multistep.ActionContinue return multistep.ActionContinue
} }

View File

@ -178,10 +178,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Name: b.config.AMIName, Name: b.config.AMIName,
}, },
&awscommon.StepModifyAMIAttributes{ &awscommon.StepModifyAMIAttributes{
Description: b.config.AMIDescription, Description: b.config.AMIDescription,
Users: b.config.AMIUsers, Users: b.config.AMIUsers,
Groups: b.config.AMIGroups, Groups: b.config.AMIGroups,
ProductCodes: b.config.AMIProductCodes, ProductCodes: b.config.AMIProductCodes,
SnapshotUsers: b.config.SnapshotUsers,
SnapshotGroups: b.config.SnapshotGroups,
}, },
&awscommon.StepCreateTags{ &awscommon.StepCreateTags{
Tags: b.config.AMITags, Tags: b.config.AMITags,

View File

@ -66,6 +66,14 @@ func (s *stepCreateAMI) Run(state multistep.StateBag) multistep.StepAction {
} }
s.image = imagesResp.Images[0] s.image = imagesResp.Images[0]
snapshots := make(map[string][]string)
for _, blockDeviceMapping := range imagesResp.Images[0].BlockDeviceMappings {
if blockDeviceMapping.Ebs != nil {
snapshots[*ec2conn.Config.Region] = append(snapshots[*ec2conn.Config.Region], *blockDeviceMapping.Ebs.SnapshotId)
}
}
state.Put("snapshots", snapshots)
return multistep.ActionContinue return multistep.ActionContinue
} }

View File

@ -77,6 +77,22 @@ func (s *stepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.Ste
return multistep.ActionHalt return multistep.ActionHalt
} }
// Get the encrypted AMI image, we need the new snapshot id's
encImagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{aws.String(*copyResp.ImageId)}})
if err != nil {
err := fmt.Errorf("Error searching for AMI: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
encImage := encImagesResp.Images[0]
var encSnapshots []string
for _, blockDevice := range encImage.BlockDeviceMappings {
if blockDevice.Ebs != nil && blockDevice.Ebs.SnapshotId != nil {
encSnapshots = append(encSnapshots, *blockDevice.Ebs.SnapshotId)
}
}
// Get the unencrypted AMI image // Get the unencrypted AMI image
unencImagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{aws.String(id)}}) unencImagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{aws.String(id)}})
if err != nil { if err != nil {
@ -97,25 +113,26 @@ func (s *stepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.Ste
// Remove associated unencrypted snapshot(s) // Remove associated unencrypted snapshot(s)
ui.Say("Deleting unencrypted snapshots") ui.Say("Deleting unencrypted snapshots")
snapshots := state.Get("snapshots").(map[string][]string)
for _, blockDevice := range unencImage.BlockDeviceMappings { for _, blockDevice := range unencImage.BlockDeviceMappings {
if blockDevice.Ebs != nil { if blockDevice.Ebs != nil && blockDevice.Ebs.SnapshotId != nil {
if blockDevice.Ebs.SnapshotId != nil { ui.Message(fmt.Sprintf("Snapshot ID: %s", *blockDevice.Ebs.SnapshotId))
ui.Message(fmt.Sprintf("Snapshot ID: %s", *blockDevice.Ebs.SnapshotId)) deleteSnapOpts := &ec2.DeleteSnapshotInput{
deleteSnapOpts := &ec2.DeleteSnapshotInput{ SnapshotId: aws.String(*blockDevice.Ebs.SnapshotId),
SnapshotId: aws.String(*blockDevice.Ebs.SnapshotId), }
} if _, err := ec2conn.DeleteSnapshot(deleteSnapOpts); err != nil {
if _, err := ec2conn.DeleteSnapshot(deleteSnapOpts); err != nil { ui.Error(fmt.Sprintf("Error deleting snapshot, may still be around: %s", err))
ui.Error(fmt.Sprintf("Error deleting snapshot, may still be around: %s", err)) return multistep.ActionHalt
return multistep.ActionHalt
}
} }
} }
} }
// Replace original AMI ID with Encrypted ID in state // Replace original AMI ID with Encrypted ID in state
amis[region] = *copyResp.ImageId amis[region] = *copyResp.ImageId
snapshots[region] = encSnapshots
state.Put("amis", amis) state.Put("amis", amis)
state.Put("snapshots", snapshots)
imagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{copyResp.ImageId}}) imagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{copyResp.ImageId}})
if err != nil { if err != nil {

View File

@ -259,10 +259,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
Name: b.config.AMIName, Name: b.config.AMIName,
}, },
&awscommon.StepModifyAMIAttributes{ &awscommon.StepModifyAMIAttributes{
Description: b.config.AMIDescription, Description: b.config.AMIDescription,
Users: b.config.AMIUsers, Users: b.config.AMIUsers,
Groups: b.config.AMIGroups, Groups: b.config.AMIGroups,
ProductCodes: b.config.AMIProductCodes, ProductCodes: b.config.AMIProductCodes,
SnapshotUsers: b.config.SnapshotUsers,
SnapshotGroups: b.config.SnapshotGroups,
}, },
&awscommon.StepCreateTags{ &awscommon.StepCreateTags{
Tags: b.config.AMITags, Tags: b.config.AMITags,

View File

@ -207,6 +207,17 @@ each category, the available configuration keys are alphabetized.
- `skip_region_validation` (boolean) - Set to true if you want to skip - `skip_region_validation` (boolean) - Set to true if you want to skip
validation of the `ami_regions` configuration option. Default `false`. validation of the `ami_regions` configuration option. Default `false`.
- `snapshot_tags` (object of key/value strings) - Tags to apply to snapshot.
They will override AMI tags if already applied to snapshot.
- `snapshot_groups` (array of strings) - A list of groups that have access to
create volumes from the snapshot(s). By default no groups have permission to create
volumes form the snapshot(s). `all` will make the snapshot publicly accessible.
- `snapshot_users` (array of strings) - A list of account IDs that have access to
create volumes from the snapshot(s). By default no additional users other than the
user creating the AMI has permissions to create volumes from the backing snapshot(s).
- `source_ami_filter` (object) - Filters used to populate the `source_ami` field. - `source_ami_filter` (object) - Filters used to populate the `source_ami` field.
Example: Example:
@ -237,9 +248,6 @@ each category, the available configuration keys are alphabetized.
- `most_recent` (bool) - Selects the newest created image when true. - `most_recent` (bool) - Selects the newest created image when true.
This is most useful for selecting a daily distro build. This is most useful for selecting a daily distro build.
- `snapshot_tags` (object of key/value strings) - Tags to apply to snapshot.
They will override AMI tags if already applied to snapshot.
- `tags` (object of key/value strings) - Tags applied to the AMI. - `tags` (object of key/value strings) - Tags applied to the AMI.
## Basic Example ## Basic Example

View File

@ -114,9 +114,21 @@ builder.
described above. Note that if this is specified, you must omit the described above. Note that if this is specified, you must omit the
`security_group_id`. `security_group_id`.
- `shutdown_behaviour` (string) - Automatically terminate instances on shutdown
incase packer exits ungracefully. Possible values are "stop" and "terminate",
default is stop.
- `skip_region_validation` (boolean) - Set to true if you want to skip - `skip_region_validation` (boolean) - Set to true if you want to skip
validation of the region configuration option. Defaults to false. validation of the region configuration option. Defaults to false.
- `snapshot_groups` (array of strings) - A list of groups that have access to
create volumes from the snapshot(s). By default no groups have permission to create
volumes form the snapshot(s). `all` will make the snapshot publicly accessible.
- `snapshot_users` (array of strings) - A list of account IDs that have access to
create volumes from the snapshot(s). By default no additional users other than the
user creating the AMI has permissions to create volumes from the backing snapshot(s).
- `source_ami_filter` (object) - Filters used to populate the `source_ami` field. - `source_ami_filter` (object) - Filters used to populate the `source_ami` field.
Example: Example:
@ -196,10 +208,6 @@ builder.
- `windows_password_timeout` (string) - The timeout for waiting for a Windows - `windows_password_timeout` (string) - The timeout for waiting for a Windows
password for Windows instances. Defaults to 20 minutes. Example value: "10m" password for Windows instances. Defaults to 20 minutes. Example value: "10m"
- `shutdown_behaviour` (string) - Automatically terminate instances on shutdown
incase packer exits ungracefully. Possible values are "stop" and "terminate",
default is stop.
## Basic Example ## Basic Example
``` ```

View File

@ -190,9 +190,24 @@ builder.
described above. Note that if this is specified, you must omit the described above. Note that if this is specified, you must omit the
`security_group_id`. `security_group_id`.
- `shutdown_behaviour` (string) - Automatically terminate instances on shutdown
incase packer exits ungracefully. Possible values are "stop" and "terminate",
default is stop.
- `skip_region_validation` (boolean) - Set to true if you want to skip - `skip_region_validation` (boolean) - Set to true if you want to skip
validation of the region configuration option. Default `false`. validation of the region configuration option. Default `false`.
- `snapshot_groups` (array of strings) - A list of groups that have access to
create volumes from the snapshot(s). By default no groups have permission to create
volumes form the snapshot(s). `all` will make the snapshot publicly accessible.
- `snapshot_users` (array of strings) - A list of account IDs that have access to
create volumes from the snapshot(s). By default no additional users other than the
user creating the AMI has permissions to create volumes from the backing snapshot(s).
- `snapshot_tags` (object of key/value strings) - Tags to apply to snapshot.
They will override AMI tags if already applied to snapshot.
- `source_ami_filter` (object) - Filters used to populate the `source_ami` field. - `source_ami_filter` (object) - Filters used to populate the `source_ami` field.
Example: Example:
@ -223,9 +238,6 @@ builder.
- `most_recent` (bool) - Selects the newest created image when true. - `most_recent` (bool) - Selects the newest created image when true.
This is most useful for selecting a daily distro build. This is most useful for selecting a daily distro build.
- `snapshot_tags` (object of key/value strings) - Tags to apply to snapshot.
They will override AMI tags if already applied to snapshot.
- `spot_price` (string) - The maximum hourly price to pay for a spot instance - `spot_price` (string) - The maximum hourly price to pay for a spot instance
to create the AMI. Spot instances are a type of instance that EC2 starts to create the AMI. Spot instances are a type of instance that EC2 starts
when the current spot price is less than the maximum price you specify. Spot when the current spot price is less than the maximum price you specify. Spot
@ -287,10 +299,6 @@ builder.
- `windows_password_timeout` (string) - The timeout for waiting for a Windows - `windows_password_timeout` (string) - The timeout for waiting for a Windows
password for Windows instances. Defaults to 20 minutes. Example value: "10m" password for Windows instances. Defaults to 20 minutes. Example value: "10m"
- `shutdown_behaviour` (string) - Automatically terminate instances on shutdown
incase packer exits ungracefully. Possible values are "stop" and "terminate",
default is stop.
## Basic Example ## Basic Example
Here is a basic example. You will need to provide access keys, and may need to change the AMI IDs according to what images exist at the time the template is run: Here is a basic example. You will need to provide access keys, and may need to change the AMI IDs according to what images exist at the time the template is run:

View File

@ -207,6 +207,14 @@ builder.
- `skip_region_validation` (boolean) - Set to true if you want to skip - `skip_region_validation` (boolean) - Set to true if you want to skip
validation of the region configuration option. Default `false`. validation of the region configuration option. Default `false`.
- `snapshot_groups` (array of strings) - A list of groups that have access to
create volumes from the snapshot(s). By default no groups have permission to create
volumes form the snapshot(s). `all` will make the snapshot publicly accessible.
- `snapshot_users` (array of strings) - A list of account IDs that have access to
create volumes from the snapshot(s). By default no additional users other than the
user creating the AMI has permissions to create volumes from the backing snapshot(s).
- `source_ami_filter` (object) - Filters used to populate the `source_ami` field. - `source_ami_filter` (object) - Filters used to populate the `source_ami` field.
Example: Example:

View File

@ -129,6 +129,7 @@ Packer to work:
"ec2:GetPasswordData", "ec2:GetPasswordData",
"ec2:ModifyImageAttribute", "ec2:ModifyImageAttribute",
"ec2:ModifyInstanceAttribute", "ec2:ModifyInstanceAttribute",
"ec2:ModifySnapshotAttribute",
"ec2:RegisterImage", "ec2:RegisterImage",
"ec2:RunInstances", "ec2:RunInstances",
"ec2:StopInstances", "ec2:StopInstances",