既存のEC2インスタンスの設定情報からLaunchTemplatesを作成する

## はじめに
Launch Templates は EC2 インスタンスを起動するための設定情報を含むテンプレートです。
https://aws.amazon.com/jp/about-aws/whats-new/2017/11/introducing-launch-templates-for-amazon-ec2-instances/

テンプレートを活用すると起動毎にパラメータを指定する必要はありませんし、一部のパラメータのみを
変更して起動といった操作も可能になります。
コンソールで一からパラメータを入力して起動テンプレートを作成してもよいのですが、
既存のEC2インスタンスから設定情報を作成することも可能です。

## やってみる
### Launch Templates の作成
AWS CLIを使用します。
まず既存のインスタンスからテンプレートの元となるJSONを取得します。

```bash
$ aws ec2 get-launch-template-data --instance-id i-xxxxxxxxxxxxxxxxx > instance-data.json
```

取得したデータは以下のような内容になっていますので、必要に応じてAMI や Subnet 等のIDを書き換えます。

```instance-data.json
{
    "LaunchTemplateData": {
        "Monitoring": {
            "Enabled": false
        }, 
        "TagSpecifications": [
            {
                "ResourceType": "instance", 
                "Tags": [
                    {
                        "Value": "LauchTemplates-Test", 
                        "Key": "Name"
                    }
                ]
            }
        ], 
        "ImageId": "ami-a77c30c1", 
        "BlockDeviceMappings": [
            {
                "DeviceName": "/dev/xvda", 
                "Ebs": {
                    "DeleteOnTermination": true
                }
            }
        ], 
        "KeyName": "launch-templates-test-key", 
        "EbsOptimized": false, 
        "Placement": {
            "Tenancy": "default", 
            "GroupName": "", 
            "AvailabilityZone": "ap-northeast-1a"
        }, 
        "InstanceType": "t2.micro", 
        "NetworkInterfaces": [
            {
                "Description": "Primary network interface", 
                "NetworkInterfaceId": "eni-xxxxxxxx", 
                "PrivateIpAddresses": [
                    {
                        "Primary": true, 
                        "PrivateIpAddress": "xxx.xxx.xxx.xxx"
                    }
                ], 
                "SubnetId": "subnet-xxxxxxxx", 
                "DeleteOnTermination": true, 
                "PrivateIpAddress": "xxx.xxx.xxx.xxx", 
                "Groups": [
                    "sg-xxxxxxxx"
                ], 
                "Ipv6Addresses": [], 
                "AssociatePublicIpAddress": false
            }
        ]
    }
}
```
NetworkInterfaces 属性にテンプレート元のインスタンスが使用しているENI-IDやIPアドレスが
含まれており、注意が必要です。テンプレートの作成自体は成功しますが、テンプレートからEC2を
起動する際に指定されたリソースが確保できず、エラーが発生します。
NetworkInterfaces 属性には以下のように Security Group や Subnet のみを定義するのがよいと思います。

```json
        "NetworkInterfaces": [
            {
                "DeviceIndex": 0,
                "SubnetId": "subnet-xxxxxxxx", 
                "DeleteOnTermination": true, 
                "Groups": [
                    "sg-xxxxxxx"
                ], 
                "AssociatePublicIpAddress": false
            }
        ]
```

DeviceIndex の指定も必要です。指定を行わない場合、EC2インスタンス作成時にエラーがでます。
次に作成したJSONから起動テンプレートを作成します。

```bash
$ aws ec2 create-launch-template --launch-template-name launch_template_test --cli-input-json file://instance-data.json
{
    "LaunchTemplate": {
        "LatestVersionNumber": 1,
        "LaunchTemplateId": "lt-xxxxxxxxxxxxxxxxx",
        "LaunchTemplateName": "launch_template_test",
        "DefaultVersionNumber": 1,
        "CreatedBy": "arn:aws:iam::account-id:user/username",
        "CreateTime": "2018-04-03T10:52:39.000Z"
    }
}
```
コンソール上でもテンプレートが作成されていることを確認できます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/dbece35a-27f4-d561-2a75-104515b8d365.png)

### LaunchTemplatesからの起動
テンプレート作成時に LaunchTemplateId が返されますので
--launch-template オプションでIDを指定し、run-instances を実行します

```bash
$ aws ec2 run-instances --launch-template LaunchTemplateId=lt-xxxxxxxxxxxxxxxxx
出力は省略
```
テンプレートから起動したインスタンスのタグにはテンプレートの ID と Version が自動で追加されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/155f27f7-f493-2842-a801-f9612ca7f6a5.png)

前述の通り、DeviceIndexを指定しない場合はエラーがでます。

```bash
$ aws ec2 run-instances --launch-template LaunchTemplateId=lt-xxxxxxxxxxxxxxxxx

An error occurred (InvalidParameterValue) when calling the RunInstances operation: Each network interface requires a device index.
```

またオプションで指定すれば、テンプレート内の各パラメータを上書きして起動することができます。

```bash

$ aws ec2 run-instances --launch-template LaunchTemplateId=lt-xxxxxxxxxxxxxxxxx --instance-type t2.small
出力は省略
```
### プライベートIPアドレス指定時のエラー
2018/4/4 時点で --Launch-template と --private-ip-address を同時に指定して起動するとエラーが発生します。

```bash
$ aws ec2 run-instances --launch-template LaunchTemplateId=lt-xxxxxxxxxxxxxxxxx --private-ip-address xxx.xxx.xxx.xxx

An error occurred (InvalidParameterCombination) when calling the RunInstances operation: Network interfaces and an instance-level private IP address may not be specified on the same request
```

ドキュメント上は、テンプレートパラメータは上書き可能であり、
特にプライベートIPの指定に関する言及がありません。
ドキュメントと挙動が一致していないので不具合である可能性があります。
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-launch-templates.html#launch-instance-from-launch-template
NetworkInterfaces 属性を丸ごと削除したテンプレートを作成すれば回避できますが、そうすると起動の度に
Subnet や SecurityGroup も追加で指定する必要があるのでテンプレートの使い勝手が悪くなってしまいます。

## おまけ
### CloudFormation でテンプレートを作成する
2018/3/29 の Update で CloudFormation で LaunchTemplates を作成できるようになりました。
https://aws.amazon.com/jp/about-aws/whats-new/2018/03/aws-cloudformation-now-supports-launch-templates/
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-launchtemplate.html

以下のような定義で作成ができます。

```launch-template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: LauchTemplates CloudFormation Test

Resources:
  LauchTemplateTest:
    Type: "AWS::EC2::LaunchTemplate"
    Properties:
      LaunchTemplateName: Launch_templates_test
      LaunchTemplateData:
        Monitoring: 
          Enabled: false
        TagSpecifications: 
          - ResourceType: instance
            Tags: 
              - Value: "LauchTemplates-Test"
                Key: Name
        ImageId: "ami-a77c30c1"
        BlockDeviceMappings: 
          - DeviceName: "/dev/xvda"
            Ebs: 
              DeleteOnTermination: true
        KeyName: "launch-templates-test-key"
        EbsOptimized: false
        Placement: 
          Tenancy: default
          GroupName: ""
          AvailabilityZone: "ap-northeast-1a"
        InstanceType: "t2.micro"
        NetworkInterfaces: 
          - DeviceIndex: 0
            SubnetId: "subnet-xxxxxxxx"
            DeleteOnTermination: true
            Groups: 
              - "sg-xxxxxxxx"
            AssociatePublicIpAddress: false

Outputs:
  LauchTemplateId:
    Description: The physical ID of the launch template
    Value: !Ref LauchTemplateTest
  LauchTemplateLatest:
    Description: The latest version of the launch template
    Value: !GetAtt LauchTemplateTest.LatestVersionNumber
  LauchTemplateDefault:
    Description: The default version of the launch template
    Value: !GetAtt LauchTemplateTest.DefaultVersionNumber

```

注意点としては、デフォルトバージョンは CloudFormation では指定ができないため、コンソール or CLI で
明示的に設定する必要があります。
最新バージョンについてはスタックが更新されれば自動でカウントアップされます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/125105/8ddc1f17-b45d-b8ae-b51d-d2065ef21f4e.png)

### CloudFormation から起動テンプレートを使用する
[AWS::EC2::Instance](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-instance.html) リソースでは LaunchTemplates を指定できないようです。
[AWS::AutoScaling::AutoScalingGroup](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-as-group.html) リソースには LaunchTemplate プロパティが、
AWS::EC2::SpotFleet リソースの [SpotFleetRequestConfigData プロパティ](https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-spotfleet-spotfleetrequestconfigdata.html#cfn-ec2-spotfleet-spotfleetrequestconfigdata-launchtemplateconfigs)には LaunchTemplateConfigs が
存在しますので起動テンプレートを利用できます。

以上です。
参考になれば幸いです。