EC2インスタンスの停止・起動を行う[SDK for Java]

# はじめに
SDKを使ってのインスタンスの停止・起動方法を紹介します。
2014/11/05：タグを使ってインスタンスの一覧を取得する方法を追記しました。

# EC2インスタンスの停止方法
ステータスがRunningになっているEC2のインスタンスのインスタンスIDを取得し、停止します。
<pre><code class=java>
import java.util.ArrayList;
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.DescribeInstancesResult;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceStateName;
import com.amazonaws.services.ec2.model.Reservation;
import com.amazonaws.services.ec2.model.StopInstancesRequest;

public class Ec2InstanceStopSample {

	public static void main(String[] args) {

		AWSCredentialsProvider provider = new ProfileCredentialsProvider("uzr");

		AmazonEC2 ec2_tokyo = Region.getRegion(Regions.AP_NORTHEAST_1)
				.createClient(AmazonEC2Client.class, provider,
						new ClientConfiguration());
		DescribeInstancesResult instanceResult = ec2_tokyo.describeInstances();

		List<String> runningIncetanceIds = new ArrayList<String>();
		for (Reservation reservation : instanceResult.getReservations()) {
			for (Instance instance : reservation.getInstances()) {
				// インスタンスのステータスがRunningのインスタンスIDを取得する。
				if (InstanceStateName.Running.toString().equals(instance.getState().getName())) {
					String instanceId = instance.getInstanceId();
					System.out.println(instanceId + "is Running");
					runningIncetanceIds.add(instanceId);
				}
			}
		}
		// インスタンスを停止する。
		ec2_tokyo.stopInstances(new StopInstancesRequest(runningIncetanceIds));
	}
}
</code></pre>

# EC2インスタンスの起動方法
ステータスがStoppedになっているEC2のインスタンスを取得し、起動します。
停止することと実装はほぼ同じでつまらないので、5秒おきにステータスをチェックするようにしています。
<pre><code class="java">
import java.util.ArrayList;
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.DescribeInstancesResult;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceStateName;
import com.amazonaws.services.ec2.model.Reservation;
import com.amazonaws.services.ec2.model.StartInstancesRequest;

public class Ec2InstanceRestartSample {

	public static void main(String[] args) throws Exception {

		AWSCredentialsProvider provider = new ProfileCredentialsProvider("uzr");

		AmazonEC2 ec2_tokyo = Region.getRegion(Regions.AP_NORTHEAST_1)
				.createClient(AmazonEC2Client.class, provider,
						new ClientConfiguration());
		DescribeInstancesResult instanceResult = ec2_tokyo.describeInstances();

		List<String> stoppedIncetanceIds = new ArrayList<String>();
		for (Reservation reservation : instanceResult.getReservations()) {
			for (Instance instance : reservation.getInstances()) {
				
				if (InstanceStateName.Stopped.toString().equals(instance.getState().getName())) {
					String instanceId = instance.getInstanceId();
					System.out.println(instanceId + "is Stopped");
					stoppedIncetanceIds.add(instanceId);
				}
			}
		}
		// インスタンスを起動する
		ec2_tokyo.startInstances(new StartInstancesRequest(stoppedIncetanceIds));
		
		System.out.println("状況を確認します。");
		
		boolean pending = true;
		

		while(pending) {
			AmazonEC2 ec2 = Region.getRegion(Regions.AP_NORTHEAST_1)
					.createClient(AmazonEC2Client.class, provider,
							new ClientConfiguration());

			for (Reservation reservation : ec2.describeInstances().getReservations()) {
				
				int runningInstanceCnt = 0;
				for (Instance instance : reservation.getInstances()) {
					String instanceId = instance.getInstanceId();
					System.out.println(instanceId + " is " + instance.getState().getName());
					
					if (InstanceStateName.Running.toString().equals(instance.getState().getName())) {
						runningInstanceCnt++;
					}
				}
				
				if (runningInstanceCnt == reservation.getInstances().size()) {
					pending = false;
				}
			}
			Thread.sleep(5000);
		}
		System.out.println("EC2のインスタンスが起動しました。");
	}
}
</code></pre>

# タグを使ってインスタンスを停止する。

AAA、BBBそれぞれで運用されているシステムがあり、AAAに所属するサーバ群をまとめて停止したい。といった場合にはタグを使うとよさそうです。
ここではKeyにSystem,Valueにシステム名が記載されたEC2のインスタンスを使って、SystemがAAAに所属するサーバ群を抽出するには下記のように実装します。
<pre><code class="java">
		List<String> systemAAAResourceIds = new ArrayList<String>();

		DescribeTagsResult tagsResult = ec2_tokyo
				.describeTags(new DescribeTagsRequest());
		for (TagDescription description : tagsResult.getTags()) {
			if ("System".equals(description.getKey()) && "AAA".equals(description.getValue())) {
				systemAAAResourceIds.add(description.getResourceId());
			}
		}
		System.out.println("System AAA ResourceIds : " + systemAAAResourceIds);
</code></pre>
ここで取得されたResourceIdはEC2のインスタンスIDと同じですので、これを使えばインスタンスの一括停止も可能ですね。

# 補足事項

## AWSCredentialProviderについて
CredentialProviderには他にもProviderがあります。
１．[EnvironmentVariableCredentialsProvider](http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/EnvironmentVariableCredentialsProvider.html)
AWS_ACCESS_KEY_ID と AWS_SECRET_ACCESS_KEYをexportしておくことでそれを読んでくれるProviderです。
２．[ProfileCredentialProvider](http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/profile/ProfileCredentialsProvider.html)
~/.aws/credentialsに記載されたプロファイル名を指定します。今回使ったのはこれです。プロファイル名はcredentialファイルに記載された値（uzr)を指定します。
<pre>
air:.aws xxxx$ cat credentials 
[uzr]
aws_access_key_id=XXXXXXXXXXXXXXXXX
aws_secret_access_key=XXXXXXXXXXXXXXXXXXXXX

</pre>
３．[SystemPropertiesCredentialProvider](http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/SystemPropertiesCredentialsProvider.html)
aws.accessKeyId と aws.secretKeyを起動引数で指定することでそれを読んでくれるProviderです。

## 状況確認について
SDKではEC2のクライアントインスタンスを作るタイミングで通信しているようでして、状況確認の間、毎回Credentialを取得するようにしています。
このようにしないとインスタンスはPending→Runningに変わっていっているのにプログラムで取得したステータスはStoppedのままになってしまいます。

## 参考
http://docs.aws.amazon.com/AWSSdkDocsJava/latest/DeveloperGuide/credentials.html