AMIのコピーを行う[SDK for Java]

# はじめに

AMIのコピーをSDK for Javaで実装してみます。
AMIをリージョン間でコピーすることでちょっとしたDRが実現できますね。
まずは簡単なサンプル。次に、AMIのコピー機能ではコピーされないタグの情報・起動許可を合わせてコピーしてみます。

http://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/CopyingAMIs.html

# AMIのコピー

AMIはS3上に保管されます。S3間のリージョン間データ転送には費用がかかりますので、ここでは東京リージョン間でのコピーを実装してみました。
リージョン間にするにはcopyImageRequest#setSourceRegionに別リージョンを指定すれば対応できます。
また、別アカウントのAMIコピーは、別アカウントのcredentialをCopyImageRequestに与えてやれば実現可能です。

```
package aws.ec2.ami;

import java.util.List;

import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.ec2.model.CopyImageRequest;
import com.amazonaws.services.ec2.model.CopyImageResult;
import com.amazonaws.services.ec2.model.CreateTagsRequest;
import com.amazonaws.services.ec2.model.DescribeImagesRequest;
import com.amazonaws.services.ec2.model.DescribeImagesResult;
import com.amazonaws.services.ec2.model.Image;
import com.amazonaws.services.ec2.model.Tag;

public class Ec2AmiCopy {

	/**
	 * 第一引数で指定されたAMI imageをリージョン間コピーします。<br>
	 * tagの情報についても合わせてコピーします。
	 * 
	 * @param args
	 *            AMI image id
	 */
	public static void main(String[] args) {

		String sourceImageId = args[0];

		if (sourceImageId == null || "".equals(sourceImageId)) {
			throw new IllegalArgumentException("instance id is null or empty.");
		}

		AWSCredentialsProvider provider = new ProfileCredentialsProvider("uzr");

		AmazonEC2 ec2ClientTokyo2 = Region.getRegion(Regions.AP_NORTHEAST_1)
				.createClient(AmazonEC2Client.class, provider,
						new ClientConfiguration());

		// AMIをコピーするときのプロパティを設定します。コピー元のリージョンとAMIIDを指定しています。
		CopyImageRequest copyImageRequest = new CopyImageRequest();
		copyImageRequest.setDescription("copy sourceImageId:" + sourceImageId);
		copyImageRequest.setSourceRegion(Regions.AP_NORTHEAST_1.getName());
		copyImageRequest.setRequestCredentials(provider.getCredentials());
		copyImageRequest.setSourceImageId(sourceImageId);

		// AMIコピーの実行
		CopyImageResult copyImageResult = ec2ClientTokyo2
				.copyImage(copyImageRequest);
		String copyImageId = copyImageResult.getImageId();

		// この時点ではリクエストが受け付けられただけです。
		System.out.println(sourceImageId + "から" + copyImageId + "を作成しました。");
	}
}


```

# ユーザ定義タグのコピー

ユーザ定義タグについては、AMIコピーではコピーされませんので、コピー後にユーザ定義タグもコピーしてあげます。

```
package aws.ec2.ami;

import java.util.List;

import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.ec2.model.CopyImageRequest;
import com.amazonaws.services.ec2.model.CopyImageResult;
import com.amazonaws.services.ec2.model.CreateTagsRequest;
import com.amazonaws.services.ec2.model.DescribeImagesRequest;
import com.amazonaws.services.ec2.model.DescribeImagesResult;
import com.amazonaws.services.ec2.model.Image;
import com.amazonaws.services.ec2.model.Tag;

public class Ec2AmiCopy {

	/**
	 * 第一引数で指定されたAMI imageをリージョン間コピーします。<br>
	 * tagの情報についても合わせてコピーします。
	 * 
	 * @param args
	 *            AMI image id
	 */
	public static void main(String[] args) {

		String sourceImageId = args[0];

		if (sourceImageId == null || "".equals(sourceImageId)) {
			throw new IllegalArgumentException("instance id is null or empty.");
		}

		AWSCredentialsProvider provider = new ProfileCredentialsProvider("uzr");

		AmazonEC2 ec2ClientTokyo2 = Region.getRegion(Regions.AP_NORTHEAST_1)
				.createClient(AmazonEC2Client.class, provider,
						new ClientConfiguration());

		// AMIをコピーするときのプロパティを設定します。コピー元のリージョンとAMIIDを指定しています。
		CopyImageRequest copyImageRequest = new CopyImageRequest();
		copyImageRequest.setDescription("copy sourceImageId:" + sourceImageId);
		copyImageRequest.setSourceRegion(Regions.AP_NORTHEAST_1.getName());
		copyImageRequest.setRequestCredentials(provider.getCredentials());
		copyImageRequest.setSourceImageId(sourceImageId);

		// AMIコピーの実行
		CopyImageResult copyImageResult = ec2ClientTokyo2
				.copyImage(copyImageRequest);
		String copyImageId = copyImageResult.getImageId();

		// この時点ではリクエストが受け付けられただけです。
		System.out.println(sourceImageId + "から" + copyImageId + "を作成しました。");

		// Tagの情報をコピー元から取得して、コピーした先のAMIに紐付けます。
		AmazonEC2 ec2ClientTokyo = Region.getRegion(Regions.AP_NORTHEAST_1)
				.createClient(AmazonEC2Client.class, provider,
						new ClientConfiguration());

		// Tagの情報を取得
		DescribeImagesResult describeImagesResult = ec2ClientTokyo
				.describeImages(new DescribeImagesRequest()
						.withImageIds(sourceImageId));

		List<Image> images = describeImagesResult.getImages();
		if (images.size() != 1) {
			throw new IllegalStateException("error:" + images.size());
		}

		Image image = images.get(0);

		// AMIコピーしてできたイメージにタグを紐付けます。
		for (Tag tag : image.getTags()) {
			ec2ClientTokyo2.createTags(new CreateTagsRequest().withResources(
					copyImageId).withTags(tag));
		}
	}
}


```


# 起動許可設定のコピー

起動許可についてもコピーされませんので調子に乗ってタグと一緒にコピーしてみました。
起動許可は、ManagementConsoleではPermissionタブに表示されます。Permissionタブに表示されている情報はSDKではLaunchPermissionクラスで表現されます。

```起動許可をコピーする

package aws.ec2.ami;

import com.amazonaws.ClientConfiguration;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.profile.ProfileCredentialsProvider;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.ec2.model.DescribeImageAttributeRequest;
import com.amazonaws.services.ec2.model.DescribeImageAttributeResult;
import com.amazonaws.services.ec2.model.ImageAttributeName;
import com.amazonaws.services.ec2.model.LaunchPermission;
import com.amazonaws.services.ec2.model.LaunchPermissionModifications;
import com.amazonaws.services.ec2.model.ModifyImageAttributeRequest;

public class Ec2AmiLaunchPermissionCopy {

	/**
	 * 第一引数で指定されたAMI imageに紐づく起動許可設定を<br>
	 * 第二引数で指定されたAMI imageにコピーします。
	 */
	public static void main(String[] args) {

		String sourceImageId = args[0];
		String destImageId = args[1];

		if (sourceImageId == null || "".equals(sourceImageId)
				|| destImageId == null || "".equals(destImageId)) {
			throw new IllegalArgumentException("instance id is null or empty.");
		}

		AWSCredentialsProvider provider = new ProfileCredentialsProvider("uzr");

		AmazonEC2 ec2ClientTokyo = Region.getRegion(Regions.AP_NORTHEAST_1)
				.createClient(AmazonEC2Client.class, provider,
						new ClientConfiguration());

		// 起動許可（LaunchPermision）をコピーします。
		DescribeImageAttributeResult describeImageAttributeResult = ec2ClientTokyo
				.describeImageAttribute(new DescribeImageAttributeRequest(
						sourceImageId, ImageAttributeName.LaunchPermission));

		ModifyImageAttributeRequest modifyImageAttributeRequest = new ModifyImageAttributeRequest(
				destImageId, ImageAttributeName.LaunchPermission.toString());

		for (LaunchPermission permission : describeImageAttributeResult
				.getImageAttribute().getLaunchPermissions()) {
			modifyImageAttributeRequest
					.setLaunchPermission(new LaunchPermissionModifications()
							.withAdd(permission));
		}

		ec2ClientTokyo.modifyImageAttribute(modifyImageAttributeRequest);
	}
}


```

実行してみます。

```

Exception in thread "main" com.amazonaws.AmazonServiceException: The AMI ID 'ami-eb3802ea' is currently pending and may not be used for this operation (Service: AmazonEC2; Status Code: 400; Error Code: InvalidAMIID.Unavailable; Request ID: 236e0f90-0d2e-47e7-b2cf-5d96e1ee450a)
	at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:1071)
	at com.amazonaws.http.AmazonHttpClient.executeOneRequest(AmazonHttpClient.java:719)
	at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:454)
	at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:294)
	at com.amazonaws.services.ec2.AmazonEC2Client.invoke(AmazonEC2Client.java:9393)
	at com.amazonaws.services.ec2.AmazonEC2Client.modifyImageAttribute(AmazonEC2Client.java:3929)
	at aws.ec2.ami.Ec2AmiCopy.main(Ec2AmiCopy.java:98)

```

AMIのコピーリクエスト送信後、ステータスがpendingの間は起動許可設定はコピーできません。threadを待たせてavailableの状態までまってコピーするしかないですね。