github.phpd.cn/hashicorp/packer@v1.3.2/builder/amazon/common/step_encrypted_ami.go (about) 1 package common 2 3 import ( 4 "context" 5 "fmt" 6 "log" 7 8 "github.com/aws/aws-sdk-go/aws" 9 "github.com/aws/aws-sdk-go/service/ec2" 10 "github.com/hashicorp/packer/helper/multistep" 11 "github.com/hashicorp/packer/packer" 12 ) 13 14 type StepCreateEncryptedAMICopy struct { 15 image *ec2.Image 16 KeyID string 17 EncryptBootVolume bool 18 Name string 19 AMIMappings []BlockDevice 20 } 21 22 func (s *StepCreateEncryptedAMICopy) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { 23 ec2conn := state.Get("ec2").(*ec2.EC2) 24 ui := state.Get("ui").(packer.Ui) 25 kmsKeyId := s.KeyID 26 27 // Encrypt boot not set, so skip step 28 if !s.EncryptBootVolume { 29 if kmsKeyId != "" { 30 log.Printf("Ignoring KMS Key ID: %s, encrypted=false", kmsKeyId) 31 } 32 return multistep.ActionContinue 33 } 34 35 ui.Say("Creating Encrypted AMI Copy") 36 37 amis := state.Get("amis").(map[string]string) 38 var region, id string 39 if amis != nil { 40 for region, id = range amis { 41 break // There is only ever one region:ami pair in this map 42 } 43 } 44 45 ui.Say(fmt.Sprintf("Copying AMI: %s(%s)", region, id)) 46 47 if kmsKeyId != "" { 48 ui.Say(fmt.Sprintf("Encrypting with KMS Key ID: %s", kmsKeyId)) 49 } 50 51 copyOpts := &ec2.CopyImageInput{ 52 Name: &s.Name, // Try to overwrite existing AMI 53 SourceImageId: aws.String(id), 54 SourceRegion: aws.String(region), 55 Encrypted: aws.Bool(true), 56 KmsKeyId: aws.String(kmsKeyId), 57 } 58 59 copyResp, err := ec2conn.CopyImage(copyOpts) 60 if err != nil { 61 err := fmt.Errorf("Error copying AMI: %s", err) 62 state.Put("error", err) 63 ui.Error(err.Error()) 64 return multistep.ActionHalt 65 } 66 67 // Wait for the copy to become ready 68 ui.Say("Waiting for AMI copy to become ready...") 69 if err := WaitUntilAMIAvailable(ctx, ec2conn, *copyResp.ImageId); err != nil { 70 err := fmt.Errorf("Error waiting for AMI Copy: %s", err) 71 state.Put("error", err) 72 ui.Error(err.Error()) 73 return multistep.ActionHalt 74 } 75 76 // Get the encrypted AMI image, we need the new snapshot id's 77 encImagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{aws.String(*copyResp.ImageId)}}) 78 if err != nil { 79 err := fmt.Errorf("Error searching for AMI: %s", err) 80 state.Put("error", err) 81 ui.Error(err.Error()) 82 return multistep.ActionHalt 83 } 84 encImage := encImagesResp.Images[0] 85 var encSnapshots []string 86 for _, blockDevice := range encImage.BlockDeviceMappings { 87 if blockDevice.Ebs != nil && blockDevice.Ebs.SnapshotId != nil { 88 encSnapshots = append(encSnapshots, *blockDevice.Ebs.SnapshotId) 89 } 90 } 91 92 // Get the unencrypted AMI image 93 unencImagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{aws.String(id)}}) 94 if err != nil { 95 err := fmt.Errorf("Error searching for AMI: %s", err) 96 state.Put("error", err) 97 ui.Error(err.Error()) 98 return multistep.ActionHalt 99 } 100 unencImage := unencImagesResp.Images[0] 101 102 // Remove unencrypted AMI 103 ui.Say("Deregistering unencrypted AMI") 104 deregisterOpts := &ec2.DeregisterImageInput{ImageId: aws.String(id)} 105 if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil { 106 ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err)) 107 return multistep.ActionHalt 108 } 109 110 // Remove associated unencrypted snapshot(s) 111 ui.Say("Deleting unencrypted snapshots") 112 snapshots := state.Get("snapshots").(map[string][]string) 113 114 OuterLoop: 115 for _, blockDevice := range unencImage.BlockDeviceMappings { 116 if blockDevice.Ebs != nil && blockDevice.Ebs.SnapshotId != nil { 117 // If this packer run didn't create it, then don't delete it 118 for _, origDevice := range s.AMIMappings { 119 if origDevice.SnapshotId == *blockDevice.Ebs.SnapshotId { 120 ui.Message(fmt.Sprintf("Keeping Snapshot ID: %s", *blockDevice.Ebs.SnapshotId)) 121 continue OuterLoop 122 } 123 } 124 125 ui.Message(fmt.Sprintf("Deleting Snapshot ID: %s", *blockDevice.Ebs.SnapshotId)) 126 deleteSnapOpts := &ec2.DeleteSnapshotInput{ 127 SnapshotId: aws.String(*blockDevice.Ebs.SnapshotId), 128 } 129 if _, err := ec2conn.DeleteSnapshot(deleteSnapOpts); err != nil { 130 ui.Error(fmt.Sprintf("Error deleting snapshot, may still be around: %s", err)) 131 return multistep.ActionHalt 132 } 133 } 134 } 135 136 // Replace original AMI ID with Encrypted ID in state 137 amis[region] = *copyResp.ImageId 138 snapshots[region] = encSnapshots 139 state.Put("amis", amis) 140 state.Put("snapshots", snapshots) 141 142 imagesResp, err := ec2conn.DescribeImages(&ec2.DescribeImagesInput{ImageIds: []*string{copyResp.ImageId}}) 143 if err != nil { 144 err := fmt.Errorf("Error searching for AMI: %s", err) 145 state.Put("error", err) 146 ui.Error(err.Error()) 147 return multistep.ActionHalt 148 } 149 s.image = imagesResp.Images[0] 150 151 return multistep.ActionContinue 152 } 153 154 func (s *StepCreateEncryptedAMICopy) Cleanup(state multistep.StateBag) { 155 if s.image == nil { 156 return 157 } 158 159 _, cancelled := state.GetOk(multistep.StateCancelled) 160 _, halted := state.GetOk(multistep.StateHalted) 161 if !cancelled && !halted { 162 return 163 } 164 165 ec2conn := state.Get("ec2").(*ec2.EC2) 166 ui := state.Get("ui").(packer.Ui) 167 168 ui.Say("Deregistering the AMI because cancellation or error...") 169 deregisterOpts := &ec2.DeregisterImageInput{ImageId: s.image.ImageId} 170 if _, err := ec2conn.DeregisterImage(deregisterOpts); err != nil { 171 ui.Error(fmt.Sprintf("Error deregistering AMI, may still be around: %s", err)) 172 return 173 } 174 }