Skip to content

Commit

Permalink
Add keep_artifact option to be able to remove the source AMI after copy
Browse files Browse the repository at this point in the history
This is a feature that give the choice whether to keep the source AMI
or not after copy.

It solves the use case where we have to copy an AMI that is using
encrypted snapshots across accounts.

We can only share snapshots encrypted with Customer Managed Keys, but
the resulting copied AMI is using the default `aws/ebs` KMS key.

For consistency across all accounts we can then use the post processor to copy the AMI on the source account itself then drop the intermediate AMI that used the CMK.
  • Loading branch information
jphuynh committed Jan 31, 2019
1 parent 59fe6e3 commit 480fd94
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 8 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@

### Description

This plugin fills a gap in a lot of AWS image bakery workflows where the source image built by any of Packer's Amazon builders (EBS, Chroot, Instance etc.) needs to be copied to a number of target accounts.
This plugin fills a gap in a lot of AWS image bakery workflows where the source image built by any of Packer's Amazon builders (EBS, Chroot, Instance etc.) needs to be copied to a number of target accounts.

For each `region:ami-id` built, the plugin will copy the image and tags, and optionally encrypt the target AMI and wait for it to become active.


### Installation

This is a packer _plugin_. Please read the plugin [documentation](https://www.packer.io/docs/extend/plugins.html).

You can download the latest binary for your architecture from the [releases page](https://github.com/martinbaillie/packer-post-processor-ami-copy/releases/latest).

### Usage

```json
"builders": [
{
Expand All @@ -44,13 +44,17 @@ You can download the latest binary for your architecture from the [releases page
```

### Configuration

Type: `ami-copy`

Required:

- `ami_users` (array of strings) - A list of account IDs to copy the images to. NOTE: you must share AMI and snapshot access in the builder through `ami_users` and `snapshot_users` respectively.

Optional:

- `copy_concurrency` (integer) - Limit the number of copies executed in parallel (default: unlimited).
- `encrypt_boot` (boolean) - create the copy with an encrypted EBS volume in the target accounts
- `kms_key_id` (string) - the ID of the KMS key to use for boot volume encryption. (default EBS KMS key used otherwise).
- `ensure_available` (boolean) - wait until the AMI becomes available in the copy target account(s)
- `keep_artifact` (boolean) - remove the original generated AMI after copy (default: true)
2 changes: 2 additions & 0 deletions amicopy/amicopy.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/aws/aws-sdk-go/service/ec2"

"errors"

"github.com/hashicorp/packer/common"
"github.com/hashicorp/packer/packer"
)
Expand All @@ -21,6 +22,7 @@ type AmiCopy struct {
Output *ec2.CopyImageOutput
SourceImage *ec2.Image
EnsureAvailable bool
KeepArtifact bool
}

// Copy will perform an EC2 copy based on the `Input` field.
Expand Down
24 changes: 18 additions & 6 deletions post-processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"errors"
"fmt"
"strconv"
"strings"
"sync"
"sync/atomic"
Expand All @@ -26,7 +27,7 @@ import (
awscommon "github.com/hashicorp/packer/builder/amazon/common"
)

// BuilderId is the ID of this post processer.
// BuilderId is the ID of this post processor.
// nolint: golint
const BuilderId = "packer.post-processor.ami-copy"

Expand All @@ -41,6 +42,7 @@ type Config struct {
RoleName string `mapstructure:"role_name"`
CopyConcurrency int `mapstructure:"copy_concurrency"`
EnsureAvailable bool `mapstructure:"ensure_available"`
KeepArtifact string `mapstructure:"keep_artifact"`

ctx interpolate.Context
}
Expand Down Expand Up @@ -68,6 +70,10 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
return errors.New("ami_users must be set")
}

if len(p.config.KeepArtifact) == 0 {
p.config.KeepArtifact = "true"
}

return nil
}

Expand All @@ -80,6 +86,12 @@ func (p *PostProcessor) Configure(raws ...interface{}) error {
// controller by `copy_concurrency`.
func (p *PostProcessor) PostProcess(
ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, error) {

keepArtifactBool, err := strconv.ParseBool(p.config.KeepArtifact)
if err != nil {
return artifact, keepArtifactBool, err
}

// Ensure we're being called from a supported builder
switch artifact.BuilderId() {
case ebs.BuilderId,
Expand All @@ -89,15 +101,15 @@ func (p *PostProcessor) PostProcess(
instance.BuilderId:
break
default:
return artifact, true,
return artifact, keepArtifactBool,
fmt.Errorf("Unexpected artifact type: %s\nCan only export from Amazon builders",
artifact.BuilderId())
}

// Current AWS session
var currSession, err = p.config.AccessConfig.Session()
currSession, err := p.config.AccessConfig.Session()
if err != nil {
return artifact, true, err
return artifact, keepArtifactBool, err
}

// Copy futures
Expand All @@ -112,7 +124,7 @@ func (p *PostProcessor) PostProcess(
ami.id,
ec2.New(currSession, aws.NewConfig().WithRegion(ami.region)),
); err != nil || source == nil {
return artifact, true, err
return artifact, keepArtifactBool, err
}

for _, user := range users {
Expand Down Expand Up @@ -208,7 +220,7 @@ func (p *PostProcessor) PostProcess(
return artifact, true, fmt.Errorf(
"%d/%d AMI copies failed, manual reconciliation may be required", copyErrs, copyCount)
}
return artifact, true, nil
return artifact, keepArtifactBool, nil
}

// ami encapsulates simplistic details about an AMI.
Expand Down

0 comments on commit 480fd94

Please sign in to comment.