AWS SDK for JavaでVPCを構築する


AWS SDK for JavaでVPCを構築するようなサンプルがあまりなかったので書いてみました。

# お題

Amazon VPCに関する[ドキュメント](http://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/UserGuide/VPC_Internet_Gateway.html#api_cli_overview)にある以下の構成でVPCを構築します。

* VPC
    * CIDR : 10.0.0.0/16
* Subnet
    * SubnetA(Public Subnet)
        * CIDR : 10.0.0.0/24
    * SubnetB(Private Subnet)
        * CIDR : 10.0.1.0/24
* EC2
    * InstanceA
        * SubnetAに所属
        * EIPあり
    * InstanceB
        * SubnetBに所属
        * EIPなし

ドキュメントの方ではEC2インスタンスは各Subnetに3インスタンス建てられていますが、今回はめんどくさいので1インスタンスにしています。

※ イメージは[ドキュメント](http://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/UserGuide/VPC_Internet_Gateway.html#api_cli_overview)を御覧ください。

# 環境・ライブラリ

* Java7
* AWS SDK for Java 1.6.1
* Guava 15.0

なおAWSのロギング制御のためにslf4j( + jcl-over-slf4j) + logback を利用していますが、
コードのほうではめんどくさかったので使っていません。

# コード

でこんな感じになりました。
所感としては普通にコンソールとほぼほぼ同じ事をしていくだけです。
AWS SDK for JavaではEC2に絡むところはだいたい`AmazonEC2Client`経由で処理を行います。
`AmazonEC2Client`には各種AWSのリソースを操作するメソッド群がいるので、それを呼び出すのですが、
細かいパラメータは`メソッドと同名 + Request`というクラスを利用して設定を行います。

その2つだけ覚えておけば、後は補完使ってメソッド名を探せば大体の設定は行えるはずです。

なお一部OpsWorks関連のクラス名とEC2関連のクラス名がかぶっているところがあるので、注意しておくといいと思います。

```java:VpcSample
package stk;

import java.util.List;

import com.amazonaws.auth.ClasspathPropertiesFileCredentialsProvider;
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.AllocateAddressRequest;
import com.amazonaws.services.ec2.model.AssociateAddressRequest;
import com.amazonaws.services.ec2.model.AssociateRouteTableRequest;
import com.amazonaws.services.ec2.model.AttachInternetGatewayRequest;
import com.amazonaws.services.ec2.model.AuthorizeSecurityGroupIngressRequest;
import com.amazonaws.services.ec2.model.CreateInternetGatewayRequest;
import com.amazonaws.services.ec2.model.CreateInternetGatewayResult;
import com.amazonaws.services.ec2.model.CreateRouteRequest;
import com.amazonaws.services.ec2.model.CreateRouteTableRequest;
import com.amazonaws.services.ec2.model.CreateSecurityGroupRequest;
import com.amazonaws.services.ec2.model.CreateSecurityGroupResult;
import com.amazonaws.services.ec2.model.CreateSubnetRequest;
import com.amazonaws.services.ec2.model.CreateVpcRequest;
import com.amazonaws.services.ec2.model.CreateVpcResult;
import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
import com.amazonaws.services.ec2.model.DescribeInstancesResult;
import com.amazonaws.services.ec2.model.DomainType;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceStateName;
import com.amazonaws.services.ec2.model.InstanceType;
import com.amazonaws.services.ec2.model.InternetGateway;
import com.amazonaws.services.ec2.model.IpPermission;
import com.amazonaws.services.ec2.model.Reservation;
import com.amazonaws.services.ec2.model.RouteTable;
import com.amazonaws.services.ec2.model.RunInstancesRequest;
import com.amazonaws.services.ec2.model.RunInstancesResult;
import com.amazonaws.services.ec2.model.Subnet;
import com.amazonaws.services.ec2.model.Tenancy;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

public class VpcSample {

	private static final String KEY_PAIR_NAME = "oohashiKey";
	private static final String AMI_AMAZON_LINUX_64BIT = "ami-0d13700c";
	private static final String ALL_IP_RANGE = "0.0.0.0/0";

	/**
	 * Amazon VPCのドキュメントと同じVPCネットワークを作成する。
	 * 
	 * @see <a href="http://docs.aws.amazon.com/ja_jp/AmazonVPC/latest/UserGuide/VPC_Internet_Gateway.html#api_cli_overview">対象ドキュメント</a>
	 * 
	 * @param args 無し
	 * @throws InterruptedException 
	 */
	public static void main(String[] args) throws InterruptedException {
		
		//クライアントの作成
		AmazonEC2 ec2 = new AmazonEC2Client(new ClasspathPropertiesFileCredentialsProvider());		
		ec2.setRegion(Region.getRegion(Regions.AP_NORTHEAST_1));
		String az = ec2.describeAvailabilityZones().getAvailabilityZones().get(0).getZoneName();
		
		//VPCの作成
		CreateVpcRequest cvr = new CreateVpcRequest();
		
		String cidrBlock = "10.0.0.0/16";
		cvr.withInstanceTenancy(Tenancy.Default)
			.withCidrBlock(cidrBlock);
		CreateVpcResult createVpcResult = ec2.createVpc(cvr);		
		String vpcId = createVpcResult.getVpc().getVpcId();
		
		System.out.println("VPC " + vpcId + " を作成しました");
		
		//サブネットの作成
		String subnetACidrBlock = "10.0.0.0/24";		
		Subnet subnetA = createSubnet(ec2, vpcId, az, subnetACidrBlock);
		
		String subnetBCidrBlock = "10.0.1.0/24";
		Subnet subnetB = createSubnet(ec2, vpcId, az, subnetBCidrBlock);		
		
		//サブネットの中に適当なEC2インスタンスを作成
		RunInstancesResult subnetAInstanceResult = runNewEC2(ec2, subnetA);
		RunInstancesResult subnetBInstanceResult = runNewEC2(ec2, subnetB);
		
		List<Instance> instanceList = Lists.newArrayList(subnetAInstanceResult.getReservation().getInstances());
		instanceList.addAll(subnetBInstanceResult.getReservation().getInstances());
		
		List<String> instanceIdList = Lists.transform(instanceList, new Function<Instance, String>() {
			public String apply(Instance input) {
				return input.getInstanceId();
			}
		});
		
		sleepUntilInstancesIsRunning(ec2, instanceIdList);
		
		//InternetGatewayの作成
		CreateInternetGatewayRequest cigr = new CreateInternetGatewayRequest();
		CreateInternetGatewayResult internetGatewayResult = ec2.createInternetGateway(cigr);		
		InternetGateway internetGateway = internetGatewayResult.getInternetGateway();
		
		System.out.println("ゲートウェイ " + internetGateway.getInternetGatewayId() + "を作成しました");
	
		//GatewayをVPCにアタッチ
		AttachInternetGatewayRequest aigr = new AttachInternetGatewayRequest();
		aigr.withInternetGatewayId(internetGateway.getInternetGatewayId())
			.withVpcId(vpcId);
		ec2.attachInternetGateway(aigr);
		System.out.printf("Attach %s to %s\n" , internetGateway.getInternetGatewayId() , vpcId);
		
		//カスタムルートテーブルの作成
		CreateRouteTableRequest crtr = new CreateRouteTableRequest();
		crtr.withVpcId(vpcId);
		
		RouteTable routeTable = ec2.createRouteTable(crtr).getRouteTable();
		System.out.printf("ルートテーブル %s を作成しました\n", routeTable.getRouteTableId());
		
		//ルートの作成
		CreateRouteRequest crr = new CreateRouteRequest();
		crr.withRouteTableId(routeTable.getRouteTableId())
			.withDestinationCidrBlock(ALL_IP_RANGE)
			.withGatewayId(internetGateway.getInternetGatewayId());
		ec2.createRoute(crr);
		
		System.out.printf("ゲートウェイ[%s]へのルーティングを作成しました\n", internetGateway.getInternetGatewayId());
		
		//ルートテーブルをサブネットに紐付け
		AssociateRouteTableRequest artr = new AssociateRouteTableRequest();
		artr.withRouteTableId(routeTable.getRouteTableId())
			.withSubnetId(subnetA.getSubnetId());
		ec2.associateRouteTable(artr);
		System.out.printf("ルートテーブル[%s]をサブネット[%s]に紐付けました。\n", routeTable.getRouteTableId(), subnetA.getSubnetId());
		
		//http/https用のSGを作成
		CreateSecurityGroupRequest csgr = new CreateSecurityGroupRequest();
		csgr.withDescription("for vpc")
			.withGroupName("VPC_SG")
			.withVpcId(vpcId);
		
		CreateSecurityGroupResult securityGroup = ec2.createSecurityGroup(csgr);
		
		IpPermission http = new IpPermission();
		http
			.withIpProtocol("tcp")
			.withFromPort(80)
			.withToPort(80)
			.withIpRanges(ALL_IP_RANGE);
		
		IpPermission https = new IpPermission();
		https
			.withIpProtocol("tcp")
			.withFromPort(443)
			.withToPort(443)
			.withIpRanges(ALL_IP_RANGE);
			
		
		AuthorizeSecurityGroupIngressRequest asger = new AuthorizeSecurityGroupIngressRequest();
		asger
			.withIpPermissions(Lists.newArrayList(http, https))
			.withGroupId(securityGroup.getGroupId());
			
		ec2.authorizeSecurityGroupIngress(asger);
		
		System.out.printf("SG[%s]を作成しました\n", securityGroup.getGroupId());
		
		//EIPの作成
		AllocateAddressRequest aar = new AllocateAddressRequest();
		aar.withDomain(DomainType.Vpc);		
		String ip = ec2.allocateAddress(aar).getPublicIp();
		System.out.printf("EIP[%s]を取得しました\n", ip);
		
		//EIPの割り当て
		AssociateAddressRequest associateRequest = new AssociateAddressRequest();
		associateRequest.withPublicIp(ip).withInstanceId(subnetAInstanceResult.getReservation().getInstances().get(0).getInstanceId());
		
		ec2.associateAddress(associateRequest);
		System.out.printf("EC2インスタンス[%s]にEIP[%s]を割り当てました。\n", subnetAInstanceResult.getReservation().getInstances().get(0).getInstanceId(), ip);
		
		System.out.println("すべての作成処理が完了しました\n");
	}

	private static void sleepUntilInstancesIsRunning(AmazonEC2 ec2,
			List<String> instanceIdList) throws InterruptedException {
		while(true) {
			DescribeInstancesResult instanceStatusResult = ec2.describeInstances(new DescribeInstancesRequest().withInstanceIds(instanceIdList));
			
			if(Iterables.all(instanceStatusResult.getReservations(), new Predicate<Reservation>() {
				@Override
				public boolean apply(Reservation input) {
					return Iterables.all(input.getInstances(), new Predicate<Instance>() {
						@Override
						public boolean apply(Instance input) {
							return input.getState().getName().equalsIgnoreCase(InstanceStateName.Running.toString());
						}
					});
				}
			})){
				break;
			} else {
				System.out.println("sleeping...");
				Thread.sleep(1000);
			}
		}
	}

	private static RunInstancesResult runNewEC2(AmazonEC2 ec2, Subnet subnet) {
		//EC2インスタンスの設定
		RunInstancesRequest request = new RunInstancesRequest()
			.withImageId(AMI_AMAZON_LINUX_64BIT) //amazon-linux 64bit
			.withMaxCount(1)
			.withMinCount(1)
			.withKeyName(KEY_PAIR_NAME)
			.withInstanceType(InstanceType.T1Micro)
			.withSubnetId(subnet.getSubnetId());
		
		RunInstancesResult result = ec2.runInstances(request);
		
		System.out.println("EC2インスタンス " + result.getReservation().getInstances().get(0).getInstanceId() + "を作成しました");
		
		return result;
	}

	private static Subnet createSubnet(AmazonEC2 ec2, String vpcId, String az, String subnetBCidrBlock) {
		CreateSubnetRequest csr = new CreateSubnetRequest();
		csr.withAvailabilityZone(az)
			.withCidrBlock(subnetBCidrBlock)
			.withVpcId(vpcId);
		
		Subnet subnet = ec2.createSubnet(csr).getSubnet();
		System.out.println("Subnet " + subnet.getSubnetId() + " を作成しました");

		return subnet;
	}

}

```