AWS TrustedAdvisorのBacklog課題投稿を自動化してみた

#1.はじめに
今回は主にTrustedAdvisorを中心とした、AWSの最適化の取り組みをご紹介したいと思います。

ということで簡単にTrustedAdvisorを説明しますと、
「AWSのベストプラクティスとマッチしていない設定/利用法をチェックし、改善を促すサービス」
になるかと思います。

これをこれまではAWSのコンソールから目検で確認していたのですが、アカウントの増加に対応できないため、
確認と問題がある項目のBacklog投稿作業を自動化することにしました。

#2.全体の構成
普段はインフラを中心に見ているため勉強も兼ねて、サーバレスな感じの構成とすることにしました。
なのでLambdaを中心にS3,SQS,IAM,CLoudWatchなどAWSのサービスを組み合わせています。

![スクリーンショット 2017-12-18 21.46.30.png](https://qiita-image-store.s3.amazonaws.com/0/216944/cfcd9342-a5db-9152-991c-f1a4b4fa0f78.png)

#3.大まかな流れ
###CloudWatchによる実行
全体のキックはCloudWatch EventのRuleを利用し定期的に実行しています。


###アカウント情報の取得
アカウント情報はS3内のファイルに記載しておき、Lambdaがそれを読み込みSQSに登録しています。
登録後、次のTrustedAdvisorのステータス情報を取得するLambdaを呼び出します。

```python:Get_AwsAccount.py
def lambda_handler(event, context):
    s3 = boto3.resource('s3')
    #読み込むS3のファイルを指定
    s3_object = s3.Object('%s' %Bucket_Name,'%s/%s' %(Target_Dir,FileName))
    
    #ファイルを読み込み
    account_file = s3_object.get()
    #本文抽出
    account_info = account_file['Body'].read()
    #アカウント毎に別ける
    accounts = account_info.split()

    for account in accounts:
        #SQSに登録する本文を作成
        queue_body = 'arn:aws:iam::アカウント番号:role/ロール名'
        
        #SQSに情報を送信する
        responce = boto3.client('sqs').send_message(
            QueueUrl = 'キューURL',
            MessageBody = queue_body
        )
            
    lambda_client = boto3.client('lambda')
    response2 = lambda_client.invoke(
        FunctionName = 'Get_TrustedAdvisor_Detail',
        InvocationType = 'RequestResponse',
        LogType = 'Tail',
    )
```
SQSに登録する情報はAssumeRoleに利用する各アカウントのIAMRole ARNです。
なお、AssumeRoleするためには、事前に各アカウントでIAMRoleを作成しておく必要があります。

今回はSQSの利用テストも兼ねて組み込みんだので、S3に置いてあるファイルに直接ARN情報を書けば、
SQSは無くても大丈夫ですｗ

###TrustedAdvisorのステータス情報取得
SQSに登録されたIAMRoleのARNを利用して、各アカウントのTrustedAdvisorの情報を収集、
その内容をS3に吐き出します。


```python:Get_TrustedAdvisor_Detail.py
def lambda_handler(event, context):
    s3 = boto3.client('s3')
    lambda_function = boto3.client('lambda')
    sqs = boto3.client('sqs')

    #キューを取得
    get_queue = sqs.receive_message(
        QueueUrl = Sqs_URL,
        WaitTimeSeconds = 3
    )
    
    Messages = get_queue['Messages']
    ReceiptHandle = Messages[0]['ReceiptHandle']
    
    #取得済みのキューを削除
    delete_queue = sqs.delete_message(
        QueueUrl = Sqs_URL,
        ReceiptHandle = ReceiptHandle
    )
    
    target_role_arn = Messages[0]['Body']
    sts_region = "ap-northeast-1"
    
    #他のAWSAccountに接続するための認証情報を取得する
    credentials = get_sts_credentials(target_role_arn, sts_region)
    
    #取得した認証情報を使ってそのアカウントのTrustedAdvisorへの接続設定を行う
    support = boto3.client(
        'support',
        region_name = 'us-east-1',
        aws_access_key_id=credentials['AccessKeyId'],
        aws_secret_access_key=credentials['SecretAccessKey'],
        aws_session_token=credentials['SessionToken']
    )
    
    #TrustedAdvisorの詳細情報を取得する
    TACheck = support.describe_trusted_advisor_checks(language='en')

    #######################################
    ###必要な情報を整理し、S3に吐き出す処理を書く###
    #######################################
    
    #キューがなくなるまでこの処理を繰り返す
    list_queue = sqs.list_queues()
    
    if len(list_queue['QueueUrls']) != 0:
        lambda_client = boto3.client('lambda')
        response2 = lambda_client.invoke(
            FunctionName = 'Get_TrustedAdvisor_Detail',
            InvocationType = 'RequestResponse',
            LogType = 'Tail',
        )

#AssumeRoleして他アカウントの認証情報を取得
def get_sts_credentials(role_arn, sts_region):
    sts_endpoint_url = "https://sts." + sts_region + ".amazonaws.com"
    sts_client = boto3.client('sts',endpoint_url=sts_endpoint_url,use_ssl=True)
    assume_role = sts_client.assume_role(
        RoleArn=role_arn,
        RoleSessionName="セッション名"
    )
    return assume_role['Credentials']
```

###Backlogへの投稿
S3にファイルがPutされることをトリガーに、Backlogを投稿するLambdaが投稿用に情報を編集し、
[Backlog API](https://developer.nulab-inc.com/ja/docs/backlog/)を利用して投稿します。


```python:Post_Backlog.py
def lambda_handler(event, context):
    
    ##################################
    ###S3に書き出した情報を取得し、整形する###
    ##################################

    project_id = '1234567890'
    issue_type_id = '1234567890'
    priority_id = '3'
    summary = '件名'
    description = '本文'
        
    #Backlog投稿メソッド呼び出し
    r = add_issue(project_id, issue_type_id, priority_id, summary, description)

#Backlogチケット投稿
def add_issue(_project_key, _issue_type_id, _priority_id, _summary, _description):
    api = 'issues'
    url = BASE_URL.format(space_key=space_key, api=api, api_key=api_key)
    #投稿の詳細指定
    payload={
        'projectId': _project_key,
        'issueTypeId': _issue_type_id,
        'priorityId': _priority_id,
        'categoryId[]':'1234567890',
        'summary': _summary,
        'description': _description
    }
 
    #投稿実行
    r = requests.post(url, data=payload)
    r.raise_for_status()
    return r
```

あんまりコーディングしないので、こうすると良いよとかあったらご指摘下さい。

#4.こんなんなりました
手動で対応していたときと比べ、確認しているAWSアカウント数と項目数が6倍になりました。

起票は楽になりましたが、改善はあまりペースが変わっていないので、
今後は問題がある項目を自動的に改善するようなLambdaを作成していきたいと考えています。