CloudFormationをもっと便利にするGo言語用ライブラリ – GoFormationをさわってみた #reinvent

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんばんは。shoitoです。
ラスベガスは日本と-17時間の時差があるので、まだ火曜夜です。

昨日、こちらのセッションに参加して、AdvancedなCloudFormationの使い方について聞きました。

[レポート]上級者への道: Advanced Infrastructure as Code Programming on AWS #reinvent #DEV327

上記セッションで紹介されていた「troposphere - Python library to create AWS CloudFormation descriptions」を弊社中山が紹介しました。

re:Invent 2018のセッションで紹介されてたtroposphereをさわってみた(CloudFormationをもっと便利に) #reinvent

今回はそれに被せるように「GoFormation - Go library for working with CloudFormation templates」を紹介します。

GoFormationって何?

AWS CloudFormationとAWS Serverless Application Model(SAM)のテンプレートを操作するためのGo言語ライブラリです。

できることとしては以下の2つです。

  • テンプレートをGoの構造体で記述して、テンプレートのJSON/YAMLへ変換
  • 逆にテンプレートを解析し、Goの構造体へ変換

ではさっそく試してみましょう

Goのライブラリなので go get コマンドでインストールします。

$ go get github.com/awslabs/goformation

まずはGoからJSON/YAMLへの変換

ソースコードはaws/goformationexample/go-to-yaml/main.go を使います。

※ コメントは日本語の説明に書き換えています。

package main

import (
    "fmt"
    "strconv"
    "time"

    "github.com/awslabs/goformation/cloudformation"
)

func main() {

    // CloudFormationテンプレートオブジェクトを生成する
    template := cloudformation.NewTemplate()

    // 現在時刻をベースにユニークな名前のSNSトピックを生成する
    template.Resources["MyTopic"] = &cloudformation.AWSSNSTopic{
        TopicName: "my-topic-" + strconv.FormatInt(time.Now().Unix(), 10),
    }

    // Eメールに通知するSNSサブスクリプションを生成する
    template.Resources["MyTopicSubscription"] = &cloudformation.AWSSNSSubscription{
        TopicArn: cloudformation.Ref("MyTopic"),
        Protocol: "email",
        Endpoint: "some.email@example.com",
    }

    // JSON形式でテンプレートを出力する
    j, err := template.JSON()
    if err != nil {
        fmt.Printf("Failed to generate JSON: %s\n", err)
    } else {
        fmt.Printf("%s\n", string(j))
    }

    // YAML形式でテンプレート出力する
    y, err := template.YAML()
    if err != nil {
        fmt.Printf("Failed to generate YAML: %s\n", err)
    } else {
        fmt.Printf("%s\n", string(y))
    }
}

適当なディレクトリで上記ソースコードを main.go ファイルとして作り、 go run で実行すると、以下のようにJSONとYAMLでそれぞれテンプレートが出力されます。
Go言語を自由に使えるので、柔軟にCloudFormation/SAMテンプレートを生成できそうです。

$ vi main.go
$ go run main.go
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Resources": {
    "MyTopic": {
      "Properties": {
        "TopicName": "my-topic-1543385240"
      },
      "Type": "AWS::SNS::Topic"
    },
    "MyTopicSubscription": {
      "Properties": {
        "Endpoint": "some.email@example.com",
        "Protocol": "email",
        "TopicArn": {
          "Ref": "MyTopic"
        }
      },
      "Type": "AWS::SNS::Subscription"
    }
  }
}
AWSTemplateFormatVersion: 2010-09-09
Resources:
  MyTopic:
    Properties:
      TopicName: my-topic-1543385240
    Type: AWS::SNS::Topic
  MyTopicSubscription:
    Properties:
      Endpoint: some.email@example.com
      Protocol: email
      TopicArn:
        Ref: MyTopic
    Type: AWS::SNS::Subscription

逆にYAMLからGo構造体への変換

ソースコードはaws/goformationexample/yaml-to-go/template.yamlexample/yaml-to-go/main.goを使います。

AWSTemplateFormatVersion: 2010-09-09
Transform:
- AWS::Serverless-2016-10-31
- AWS::CodeStar

Parameters:
  ProjectId:
    Type: String
    Description: AWS CodeStar projectID used to associate new resources to team members

Resources:
  GetHelloWorld:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.get
      Runtime: nodejs6.10
      Role:
        Fn::ImportValue:
          !Join ['-', [!Ref 'ProjectId', !Ref 'AWS::Region', 'LambdaTrustRole']]
      Events:
        GetEvent:
          Type: Api
          Properties:
            Path: /
            Method: get
package main

import (
    "log"

    "github.com/awslabs/goformation"
)

func main() {

    // JSONかYAML形式のテンプレートをオープンし、パースする
    template, err := goformation.Open("template.yaml")
    if err != nil {
        log.Fatalf("There was an error processing the template: %s", err)
    }

    // 特定の型の全リソースを取得できる
    // ここではサーバレスファンクションリソース
    // https://github.com/awslabs/goformation/blob/master/cloudformation/aws-serverless-function.go#L187
    // 他にはtemplate.GetAllAWSLambdaFunctionResources()などがある
    // https://github.com/awslabs/goformation/blob/master/cloudformation/aws-lambda-function.go
    functions := template.GetAllAWSServerlessFunctionResources()
    for name, function := range functions {

        // 例として Found a AWS::Serverless::Function named GetHelloWorld (runtime: nodejs6.10) が出力される
        log.Printf("Found a %s named %s (runtime: %s)\n", function.AWSCloudFormationType(), name, function.Runtime)

    }

    // AWS CloudFormationリソースの論理名を指定して検索できる
    search := "GetHelloWorld"
    function, err := template.GetAWSServerlessFunctionWithName(search)
    if err != nil {
        log.Fatalf("Function not found")
    }

    // 例として Found a AWS::Serverless::Function named GetHelloWorld (runtime: nodejs6.10) が出力される
    log.Printf("Found a %s named %s (runtime: %s)\n", function.AWSCloudFormationType(), search, function.Runtime)

}

また同じように、適当なディレクトリで上記YAMLを template.yaml ファイルとして、Goのソースコードを main.go ファイルとして作り、 go run で実行すると、テンプレートのYAMLを解析し、以下のようにメッセージが表示されます。

$ vi template.yaml
$ vi main.go
$ go run main.go
2018/11/27 22:22:44 Found a AWS::Serverless::Function named GetHelloWorld (runtime: nodejs6.10)
2018/11/27 22:22:44 Found a AWS::Serverless::Function named GetHelloWorld (runtime: nodejs6.10)

上記のmain.goからAWS::Serverless::Function をGoのstrongly-typed resourceとして操作できることが分かります。

さいごに

Python用のライブラリであるtroposphereと合わせて見て、Go用のGoFormationはいかがでしたでしょうか?
CloudFormation/SAMのテンプレートを柔軟に生成したり、テンプレートを解析するプログラムを作りたいときに便利そうです。