From 46f217f255e81059baace087a12fb8dd202078b9 Mon Sep 17 00:00:00 2001 From: Ari Aviran Date: Sun, 11 Sep 2016 15:37:24 +0300 Subject: [PATCH] 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",