From 46f217f255e81059baace087a12fb8dd202078b9 Mon Sep 17 00:00:00 2001 From: Ari Aviran Date: Sun, 11 Sep 2016 15:37:24 +0300 Subject: [PATCH 1/4] amazon - Snapshot permissions correctly applied Fixes #3344 --- builder/amazon/common/step_ami_region_copy.go | 32 ++++++--- .../common/step_modify_ami_attributes.go | 71 ++++++++++++++++--- builder/amazon/ebs/step_create_ami.go | 8 +++ website/source/docs/builders/amazon.html.md | 1 + 4 files changed, 94 insertions(+), 18 deletions(-) diff --git a/builder/amazon/common/step_ami_region_copy.go b/builder/amazon/common/step_ami_region_copy.go index 108a758c2..1f4e65738 100644 --- a/builder/amazon/common/step_ami_region_copy.go +++ b/builder/amazon/common/step_ami_region_copy.go @@ -23,6 +23,7 @@ func (s *StepAMIRegionCopy) Run(state multistep.StateBag) multistep.StepAction { ec2conn := state.Get("ec2").(*ec2.EC2) ui := state.Get("ui").(packer.Ui) amis := state.Get("amis").(map[string]string) + snapshots := state.Get("snapshots").(map[string][]string) ami := amis[*ec2conn.Config.Region] if len(s.Regions) == 0 { @@ -46,11 +47,12 @@ func (s *StepAMIRegionCopy) Run(state multistep.StateBag) multistep.StepAction { go func(region string) { 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() defer lock.Unlock() amis[region] = id + snapshots[region] = snapshotIds if err != nil { 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 -// 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, - 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 awsConfig, err := config.Config() if err != nil { - return "", err + return "", snapshotIds, err } awsConfig.Region = aws.String(target) session, err := session.NewSession(awsConfig) if err != nil { - return "", err + return "", snapshotIds, err } regionconn := ec2.New(session) @@ -101,7 +104,7 @@ func amiRegionCopy(state multistep.StateBag, config *AccessConfig, name string, }) 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) } @@ -113,9 +116,22 @@ func amiRegionCopy(state multistep.StateBag, config *AccessConfig, name string, } 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) } - 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 } diff --git a/builder/amazon/common/step_modify_ami_attributes.go b/builder/amazon/common/step_modify_ami_attributes.go index 3763561f6..dff405083 100644 --- a/builder/amazon/common/step_modify_ami_attributes.go +++ b/builder/amazon/common/step_modify_ami_attributes.go @@ -21,6 +21,7 @@ func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAc ec2conn := state.Get("ec2").(*ec2.EC2) ui := state.Get("ui").(packer.Ui) amis := state.Get("amis").(map[string]string) + snapshots := state.Get("snapshots").(map[string][]string) // Determine if there is any work to do. valid := false @@ -33,46 +34,71 @@ func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAc return multistep.ActionContinue } - // Construct the modify image attribute requests we're going to make. - // We need to make each separately since the EC2 API only allows changing - // one type at a kind currently. + // Construct the modify image and snapshot attribute requests we're going + // to make. We need to make each separately since the EC2 API only allows + // changing one type at a kind currently. options := make(map[string]*ec2.ModifyImageAttributeInput) if s.Description != "" { options["description"] = &ec2.ModifyImageAttributeInput{ Description: &ec2.AttributeValue{Value: &s.Description}, } } + snapshotOptions := make(map[string]*ec2.ModifySnapshotAttributeInput) if len(s.Groups) > 0 { groups := make([]*string, len(s.Groups)) - adds := make([]*ec2.LaunchPermission, len(s.Groups)) + + addsImage := make([]*ec2.LaunchPermission, len(s.Groups)) addGroups := &ec2.ModifyImageAttributeInput{ LaunchPermission: &ec2.LaunchPermissionModifications{}, } + addsSnapshot := make([]*ec2.CreateVolumePermission, len(s.Groups)) + addSnapshotGroups := &ec2.ModifySnapshotAttributeInput{ + CreateVolumePermission: &ec2.CreateVolumePermissionModifications{}, + } + for i, g := range s.Groups { groups[i] = aws.String(g) - adds[i] = &ec2.LaunchPermission{ + addsImage[i] = &ec2.LaunchPermission{ + Group: aws.String(g), + } + + addsSnapshot[i] = &ec2.CreateVolumePermission{ Group: aws.String(g), } } - addGroups.UserGroups = groups - addGroups.LaunchPermission.Add = adds + addGroups.UserGroups = groups + addGroups.LaunchPermission.Add = addsImage options["groups"] = addGroups + + addSnapshotGroups.GroupNames = groups + addSnapshotGroups.CreateVolumePermission.Add = addsSnapshot + snapshotOptions["groups"] = addSnapshotGroups } if len(s.Users) > 0 { users := make([]*string, len(s.Users)) - adds := make([]*ec2.LaunchPermission, len(s.Users)) + addsImage := make([]*ec2.LaunchPermission, len(s.Users)) + addsSnapshot := make([]*ec2.CreateVolumePermission, len(s.Users)) for i, u := range s.Users { users[i] = aws.String(u) - adds[i] = &ec2.LaunchPermission{UserId: aws.String(u)} + addsImage[i] = &ec2.LaunchPermission{UserId: aws.String(u)} + addsSnapshot[i] = &ec2.CreateVolumePermission{UserId: aws.String(u)} } + options["users"] = &ec2.ModifyImageAttributeInput{ UserIds: users, LaunchPermission: &ec2.LaunchPermissionModifications{ - Add: adds, + Add: addsImage, + }, + } + + snapshotOptions["users"] = &ec2.ModifySnapshotAttributeInput{ + UserIds: users, + CreateVolumePermission: &ec2.CreateVolumePermissionModifications{ + Add: addsSnapshot, }, } } @@ -87,6 +113,7 @@ func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAc } } + // Modifying image attributes for region, ami := range amis { ui.Say(fmt.Sprintf("Modifying attributes on AMI (%s)...", ami)) awsConfig := aws.Config{ @@ -114,6 +141,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 } diff --git a/builder/amazon/ebs/step_create_ami.go b/builder/amazon/ebs/step_create_ami.go index e3e7e7026..b484e0692 100644 --- a/builder/amazon/ebs/step_create_ami.go +++ b/builder/amazon/ebs/step_create_ami.go @@ -66,6 +66,14 @@ func (s *stepCreateAMI) Run(state multistep.StateBag) multistep.StepAction { } 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 } diff --git a/website/source/docs/builders/amazon.html.md b/website/source/docs/builders/amazon.html.md index b1a2e8b1a..4b31e1012 100644 --- a/website/source/docs/builders/amazon.html.md +++ b/website/source/docs/builders/amazon.html.md @@ -129,6 +129,7 @@ Packer to work: "ec2:GetPasswordData", "ec2:ModifyImageAttribute", "ec2:ModifyInstanceAttribute", + "ec2:ModifySnapshotAttribute" "ec2:RegisterImage", "ec2:RunInstances", "ec2:StopInstances", From c3a352955e906e8e95eb9a7eaeeda252933e8bcd Mon Sep 17 00:00:00 2001 From: Rickard von Essen Date: Fri, 2 Dec 2016 09:49:21 +0100 Subject: [PATCH 2/4] amazon: Added snapshot_users and snapshot_groups --- builder/amazon/chroot/builder.go | 10 ++-- builder/amazon/common/ami_config.go | 2 + .../common/step_modify_ami_attributes.go | 48 ++++++++++++------- builder/amazon/ebs/builder.go | 10 ++-- builder/amazon/instance/builder.go | 10 ++-- .../docs/builders/amazon-chroot.html.md | 14 ++++-- .../docs/builders/amazon-ebs-volume.html.md | 16 +++++-- .../source/docs/builders/amazon-ebs.html.md | 22 ++++++--- .../docs/builders/amazon-instance.html.md | 8 ++++ 9 files changed, 97 insertions(+), 43 deletions(-) diff --git a/builder/amazon/chroot/builder.go b/builder/amazon/chroot/builder.go index 8239584a3..507ff46ff 100644 --- a/builder/amazon/chroot/builder.go +++ b/builder/amazon/chroot/builder.go @@ -257,10 +257,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Name: b.config.AMIName, }, &awscommon.StepModifyAMIAttributes{ - Description: b.config.AMIDescription, - Users: b.config.AMIUsers, - Groups: b.config.AMIGroups, - ProductCodes: b.config.AMIProductCodes, + Description: b.config.AMIDescription, + Users: b.config.AMIUsers, + Groups: b.config.AMIGroups, + ProductCodes: b.config.AMIProductCodes, + SnapshotUsers: b.config.SnapshotUsers, + SnapshotGroups: b.config.SnapshotGroups, }, &awscommon.StepCreateTags{ Tags: b.config.AMITags, diff --git a/builder/amazon/common/ami_config.go b/builder/amazon/common/ami_config.go index fa442cb5c..e8ab11303 100644 --- a/builder/amazon/common/ami_config.go +++ b/builder/amazon/common/ami_config.go @@ -23,6 +23,8 @@ type AMIConfig struct { AMIEncryptBootVolume bool `mapstructure:"encrypt_boot"` AMIKmsKeyId string `mapstructure:"kms_key_id"` 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 { diff --git a/builder/amazon/common/step_modify_ami_attributes.go b/builder/amazon/common/step_modify_ami_attributes.go index dff405083..3c0f1402a 100644 --- a/builder/amazon/common/step_modify_ami_attributes.go +++ b/builder/amazon/common/step_modify_ami_attributes.go @@ -11,10 +11,12 @@ import ( ) type StepModifyAMIAttributes struct { - Users []string - Groups []string - ProductCodes []string - Description string + Users []string + Groups []string + SnapshotUsers []string + SnapshotGroups []string + ProductCodes []string + Description string } func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAction { @@ -29,6 +31,8 @@ func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAc valid = valid || (s.Users != nil && len(s.Users) > 0) valid = valid || (s.Groups != nil && len(s.Groups) > 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 { return multistep.ActionContinue @@ -47,32 +51,35 @@ func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAc if len(s.Groups) > 0 { groups := make([]*string, len(s.Groups)) - addsImage := make([]*ec2.LaunchPermission, len(s.Groups)) addGroups := &ec2.ModifyImageAttributeInput{ LaunchPermission: &ec2.LaunchPermissionModifications{}, } - addsSnapshot := make([]*ec2.CreateVolumePermission, len(s.Groups)) - addSnapshotGroups := &ec2.ModifySnapshotAttributeInput{ - CreateVolumePermission: &ec2.CreateVolumePermissionModifications{}, - } - for i, g := range s.Groups { groups[i] = aws.String(g) addsImage[i] = &ec2.LaunchPermission{ Group: aws.String(g), } + } + addGroups.UserGroups = groups + 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), } } - - addGroups.UserGroups = groups - addGroups.LaunchPermission.Add = addsImage - options["groups"] = addGroups - addSnapshotGroups.GroupNames = groups addSnapshotGroups.CreateVolumePermission.Add = addsSnapshot snapshotOptions["groups"] = addSnapshotGroups @@ -81,11 +88,9 @@ func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAc if len(s.Users) > 0 { users := make([]*string, len(s.Users)) addsImage := make([]*ec2.LaunchPermission, len(s.Users)) - addsSnapshot := make([]*ec2.CreateVolumePermission, len(s.Users)) for i, u := range s.Users { users[i] = aws.String(u) addsImage[i] = &ec2.LaunchPermission{UserId: aws.String(u)} - addsSnapshot[i] = &ec2.CreateVolumePermission{UserId: aws.String(u)} } options["users"] = &ec2.ModifyImageAttributeInput{ @@ -94,6 +99,15 @@ func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAc 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, diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 8f28ba17a..725a43adc 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -178,10 +178,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Name: b.config.AMIName, }, &awscommon.StepModifyAMIAttributes{ - Description: b.config.AMIDescription, - Users: b.config.AMIUsers, - Groups: b.config.AMIGroups, - ProductCodes: b.config.AMIProductCodes, + Description: b.config.AMIDescription, + Users: b.config.AMIUsers, + Groups: b.config.AMIGroups, + ProductCodes: b.config.AMIProductCodes, + SnapshotUsers: b.config.SnapshotUsers, + SnapshotGroups: b.config.SnapshotGroups, }, &awscommon.StepCreateTags{ Tags: b.config.AMITags, diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index 6d67d6a5f..62644a2cd 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -259,10 +259,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Name: b.config.AMIName, }, &awscommon.StepModifyAMIAttributes{ - Description: b.config.AMIDescription, - Users: b.config.AMIUsers, - Groups: b.config.AMIGroups, - ProductCodes: b.config.AMIProductCodes, + Description: b.config.AMIDescription, + Users: b.config.AMIUsers, + Groups: b.config.AMIGroups, + ProductCodes: b.config.AMIProductCodes, + SnapshotUsers: b.config.SnapshotUsers, + SnapshotGroups: b.config.SnapshotGroups, }, &awscommon.StepCreateTags{ Tags: b.config.AMITags, diff --git a/website/source/docs/builders/amazon-chroot.html.md b/website/source/docs/builders/amazon-chroot.html.md index 690dd8220..ec426c75a 100644 --- a/website/source/docs/builders/amazon-chroot.html.md +++ b/website/source/docs/builders/amazon-chroot.html.md @@ -207,6 +207,17 @@ each category, the available configuration keys are alphabetized. - `skip_region_validation` (boolean) - Set to true if you want to skip 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. Example: @@ -237,9 +248,6 @@ each category, the available configuration keys are alphabetized. - `most_recent` (bool) - Selects the newest created image when true. 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. ## Basic Example diff --git a/website/source/docs/builders/amazon-ebs-volume.html.md b/website/source/docs/builders/amazon-ebs-volume.html.md index aef2e4897..bf9d670ae 100644 --- a/website/source/docs/builders/amazon-ebs-volume.html.md +++ b/website/source/docs/builders/amazon-ebs-volume.html.md @@ -114,9 +114,21 @@ builder. described above. Note that if this is specified, you must omit the `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 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. Example: @@ -196,10 +208,6 @@ builder. - `windows_password_timeout` (string) - The timeout for waiting for a Windows 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 ``` diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index 00615e2eb..11850a2d5 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -190,9 +190,24 @@ builder. described above. Note that if this is specified, you must omit the `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 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. Example: @@ -223,9 +238,6 @@ builder. - `most_recent` (bool) - Selects the newest created image when true. 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 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 @@ -287,10 +299,6 @@ builder. - `windows_password_timeout` (string) - The timeout for waiting for a Windows 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 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: diff --git a/website/source/docs/builders/amazon-instance.html.md b/website/source/docs/builders/amazon-instance.html.md index b050cad81..41572af85 100644 --- a/website/source/docs/builders/amazon-instance.html.md +++ b/website/source/docs/builders/amazon-instance.html.md @@ -207,6 +207,14 @@ builder. - `skip_region_validation` (boolean) - Set to true if you want to skip 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. Example: From 4ce3b8712af965a4d373c9143c114a8cb8de8d94 Mon Sep 17 00:00:00 2001 From: Rickard von Essen Date: Sun, 4 Dec 2016 20:18:27 +0100 Subject: [PATCH 3/4] Replace unencrypted EBS snapshots with encrypted To ensure that groups and users attributes are added to the encrypted snapshots. --- builder/amazon/common/ami_config.go | 4 +++ builder/amazon/ebs/step_encrypted_ami.go | 37 +++++++++++++++------ website/source/docs/builders/amazon.html.md | 2 +- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/builder/amazon/common/ami_config.go b/builder/amazon/common/ami_config.go index e8ab11303..c7674dbbe 100644 --- a/builder/amazon/common/ami_config.go +++ b/builder/amazon/common/ami_config.go @@ -64,6 +64,10 @@ func (c *AMIConfig) Prepare(ctx *interpolate.Context) []error { 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 { return errs } diff --git a/builder/amazon/ebs/step_encrypted_ami.go b/builder/amazon/ebs/step_encrypted_ami.go index 5721def98..59f3298bd 100644 --- a/builder/amazon/ebs/step_encrypted_ami.go +++ b/builder/amazon/ebs/step_encrypted_ami.go @@ -77,6 +77,22 @@ func (s *stepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.Ste 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 unencImagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{aws.String(id)}}) if err != nil { @@ -97,25 +113,26 @@ func (s *stepCreateEncryptedAMICopy) Run(state multistep.StateBag) multistep.Ste // Remove associated unencrypted snapshot(s) ui.Say("Deleting unencrypted snapshots") + snapshots := state.Get("snapshots").(map[string][]string) for _, blockDevice := range unencImage.BlockDeviceMappings { - if blockDevice.Ebs != nil { - if blockDevice.Ebs.SnapshotId != nil { - ui.Message(fmt.Sprintf("Snapshot ID: %s", *blockDevice.Ebs.SnapshotId)) - deleteSnapOpts := &ec2.DeleteSnapshotInput{ - SnapshotId: aws.String(*blockDevice.Ebs.SnapshotId), - } - if _, err := ec2conn.DeleteSnapshot(deleteSnapOpts); err != nil { - ui.Error(fmt.Sprintf("Error deleting snapshot, may still be around: %s", err)) - return multistep.ActionHalt - } + if blockDevice.Ebs != nil && blockDevice.Ebs.SnapshotId != nil { + ui.Message(fmt.Sprintf("Snapshot ID: %s", *blockDevice.Ebs.SnapshotId)) + deleteSnapOpts := &ec2.DeleteSnapshotInput{ + SnapshotId: aws.String(*blockDevice.Ebs.SnapshotId), + } + if _, err := ec2conn.DeleteSnapshot(deleteSnapOpts); err != nil { + ui.Error(fmt.Sprintf("Error deleting snapshot, may still be around: %s", err)) + return multistep.ActionHalt } } } // Replace original AMI ID with Encrypted ID in state amis[region] = *copyResp.ImageId + snapshots[region] = encSnapshots state.Put("amis", amis) + state.Put("snapshots", snapshots) imagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{copyResp.ImageId}}) if err != nil { diff --git a/website/source/docs/builders/amazon.html.md b/website/source/docs/builders/amazon.html.md index 4b31e1012..7cd40f8b6 100644 --- a/website/source/docs/builders/amazon.html.md +++ b/website/source/docs/builders/amazon.html.md @@ -129,7 +129,7 @@ Packer to work: "ec2:GetPasswordData", "ec2:ModifyImageAttribute", "ec2:ModifyInstanceAttribute", - "ec2:ModifySnapshotAttribute" + "ec2:ModifySnapshotAttribute", "ec2:RegisterImage", "ec2:RunInstances", "ec2:StopInstances", From 5c4d331a342053b49c0e10aa6dadf86a9de5149c Mon Sep 17 00:00:00 2001 From: Rickard von Essen Date: Tue, 6 Dec 2016 09:58:17 +0100 Subject: [PATCH 4/4] Added missing LaunchPermission --- builder/amazon/common/step_modify_ami_attributes.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builder/amazon/common/step_modify_ami_attributes.go b/builder/amazon/common/step_modify_ami_attributes.go index 3c0f1402a..514b84c86 100644 --- a/builder/amazon/common/step_modify_ami_attributes.go +++ b/builder/amazon/common/step_modify_ami_attributes.go @@ -64,6 +64,7 @@ func (s *StepModifyAMIAttributes) Run(state multistep.StateBag) multistep.StepAc } addGroups.UserGroups = groups + addGroups.LaunchPermission.Add = addsImage options["groups"] = addGroups }