BLOGサブスレッドの日常

2016.04.12

EC2へのSSH接続を簡単にしたい! (2)コマンドラインでEC2のホスト名を取得してみる

mzsm

株式会社サブスレッドのみずしま a.k.a mzsm(@mzsm_j)です。こんにちは。

この記事は前回の続きになります。

前回は、SSH接続のいつもの設定を~/.ssh/configに保存するところまででした。

EC2インスタンスはいったん停止させてから起動しなおすだけでホスト名・IPアドレスが変わってしまうため、スケールアップ・ダウン等の作業をするたびに設定ファイルを書き換えないといけません。
この設定ファイルの更新をプログラムで自動的にできないか、というのが今回からの話です。

なお、個人および社内の都合により、プログラムはPythonを使って作成していきます。
Pythonはまだまだ2系と3系の両方が並立して使われている状況ですが、2016年にもなって2系で新しいプログラムを作っていくような気力はないので、ここでは3系の最新版である3.5を利用します。

AWSの情報を取得してみる

まずはAWSからEC2インスタンスの情報を取得してみましょう。

AWSのコマンドラインツールといえばAWS CLIがありますが、今回はPythonプログラムから情報を利用したいので、botoを利用します。

botoのインストール

botoはPython Package Index(PyPI)に登録されているため、pipを利用してインストールできます。

が、実はbotoにも昔からある古いbotoと、イチから作りなおした新しいboto3の両方が存在しており、pip install botoでは古いbotoのほうがインストールされます。

boto3はPython2系と3系の両方に対応していますが、botoはPython2系にしか対応していません。
今回はPython3.5を使いますので、ここではboto3のほうをインストールします。

$ pip install boto3

IAMユーザーの作成

AWSの情報をプログラムから取得するには、まずそのためのユーザー権限を作成する必要があります。

AWSのWebコンソール画面から、IAM(Identity and Access Management)メニューでIAMユーザーを作成します。

IAMユーザーを作成すると、アクセスキーIDシークレットアクセスキーが発行されます。
プログラムからAWSの情報を取得するときはこの2つの文字列を利用します。

特に、シークレットアクセスキーはこのタイミングを逃すと後から確認できないので注意してください。
認証情報を示したCSVファイルをダウンロードできるので、保存しておくとよいでしょう。

User Name,Access Key Id,Secret Access Key
"username",AKIAIXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

アクセスキーIDの前にユーザー名が表示されていますが、これはただの識別用であってアクセス時には使用しません。

権限の付与

IAMユーザーは、作成しただけでは何の権限も持っていません。
そのユーザーに対して、どういった情報を取得できるか、どういう操作が可能か、という権限を付与する必要があります。

不必要な権限を持っているとセキュリティ上好ましくないので、ここでは必要な情報を取得するために最低限必要な権限しか持たないようにします。

IAMメニューの「ユーザー」から、いま作成したユーザーを選択し、「アクセス許可」タブ内の「インラインポリシー」→「ユーザーポリシーの作成」→「カスタムポリシー」→「選択」と進み、テキストエリア内に次の内容を書き込みます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeInstanceStatus",
                "ec2:DescribeInstances",
                "ec2:DescribeKeyPairs",
                "ec2:DescribeTags"
            ],
            "Resource": "*"
        }
    ]
}

今回の記事の範囲内では利用しない権限もありますが、これらは後々利用するためのものです。

あとは「ポリシー名」に適当な名前を入れ、「ポリシーの検証」ボタンを押して内容がおかしくないか確認した上で、「ポリシーの適用」ボタンを押して、内容を保存します。

今回はユーザーに対して直接権限を付与しましたが、権限を付与したグループを作成し、そのグループにユーザーを追加するという方法もあります。
複数のユーザーに対して同様の権限を持たせたい場合は、グループを利用するほうが効率的で管理も簡単です。

インスタンスの一覧を取得する

起動中のEC2インスタンス一覧を取得して、各インスタンスのIDとパブリックホスト名を表示してみます。

Pythonのコンソールを起動し、実行していきます。

$ python3
Python 3.5.1 (default, Dec  7 2015, 21:59:08)
[GCC 4.2.1 Compatible Apple LLVM 7.0.0 (clang-700.1.76)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import boto3
>>> aws_id = 'AKIAXXXXXXXXXXXXXXXX'
>>> aws_secret = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
>>> region = 'ap-northeast-1'
>>> client = boto3.client('ec2', aws_access_key_id=aws_id, aws_secret_access_key=aws_secret, region_name=region)

boto3.clientメソッドを呼び出し、EC2の情報にアクセスするためのEC2.Clientのインスタンスを取得しました。
今回はコンソールで実行していることもあり、アクセスキーIDとシークレットアクセスキーをプログラム内で直接指定していますが、本来は別のファイル内に認証情報を記述しておき、プログラムのファイル内に認証情報を残さないようにするほうがベターです。

インスタンス一覧を取得するにはEC2.Clientインスタンスのdescribe_instancesメソッドを呼び出します。

>>> resp = client.describe_instances(
...   Filters=[{'Name': 'instance-state-name',
...             'Values': ['running']}]
...  )

引数なしで呼び出すと、停止中のものも含めてすべてのインスタンスが取得出来ます。
今回は起動中のインスタンスのみ取得したいので、instance-state-namerunning(起動中)であるという条件を付与しています。
FiltersやらNameやらが大文字始まりでPythonicじゃないのが気持ち悪いですが、それは諦めましょう。

とりあえず、取得してきた情報をpprintで表示して確認してみます。

>>> import pprint
>>> pprint.pprint(resp)
{'Reservations': [{'Groups': [],
                   'Instances': [{'AmiLaunchIndex': 0,
                                  : (省略)
                                  'InstanceId': 'i-00000000',
                                  : (省略)
                                  'KeyName': 'eigyo-kaizen',
                                  : (省略)
                                  'PublicDnsName': 'ec2-203-0-113-23.ap-northeast-1.compute.amazonaws.com',
                                  'PublicIpAddress': '203.0.113.23',
                                  : (省略)
                                  'VpcId': 'vpc-00000000'}],
                   'OwnerId': '000000000000'},
                  : (省略)
                  {'Groups': [],
                   'Instances': [{'AmiLaunchIndex': 0,
                                  : (省略)
                                  'InstanceId': 'i-ccccaaaa',
                                  : (省略)
                                  'KeyName': 'eigyo-kaizen',
                                  : (省略)
                                  'PublicDnsName': 'ec2-203-0-113-200.ap-northeast-1.compute.amazonaws.com',
                                  'PublicIpAddress': '203.0.113.200',
                                  : (省略)
                                  'VpcId': 'vpc-00000000'}],
                   'OwnerId': '000000000000'}],
 'ResponseMetadata': {'HTTPStatusCode': 200,
                      'RequestId': '00000000-0000-0000-0000-000000000000'}}

データの構造がわかったので、あとはループをぶん回せば…

>>> for resv in resp['Reservations']:
...   for instance in resv['Instances']:
...     print(instance['InstanceId'])
...     print(instance['PublicDnsName'])
...     print(instance['KeyName'])
...     print('='*20)
...
i-00000000
ec2-203-0-113-23.ap-northeast-1.compute.amazonaws.com
eigyo-kaizen
====================
i-00001111
ec2-203-0-113-179.ap-northeast-1.compute.amazonaws.com
eigyo-kaizen
====================
i-11110000
ec2-203-0-113-10.ap-northeast-1.compute.amazonaws.com
eigyo-kaizen
====================
i-6666aaaa
ec2-203-0-113-205.ap-northeast-1.compute.amazonaws.com
eigyo-kaizen
====================
i-ccccaaaa
ec2-203-0-113-200.ap-northeast-1.compute.amazonaws.com
eigyo-kaizen
====================

すべてのインスタンスIDとパブリックホスト名、それとキーペア名を表示できました!

この次はどうする?

各インスタンスのパブリックホスト名を取得できたので、この情報を使えば~/.ssh/configを自動的に書き換えることができそうです。

しかしまだ足りない情報があります。
前回は各インスタンスに対してeigyo-kaizen-dev-1といったような名前を付けていました。
AWSの情報を使ってこのような名前を付けるには、どのようにすればいいでしょうか…?

…というわけで、次回に続きます。

この記事を書いた人

mzsm