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