信頼はずっと、挑戦はもっと。

お問い合わせ
TEL:03-3496-3888

BLOG コアテックの社員ブログ (毎週月曜~金曜更新中)

LIST OF ARTICLES

記事一覧

  • テクログ

    gitの仕組みとかよくわからないけど、とりあえず使ってみる人用

    はじめにこれまで、gitってあまり触れてこなかったんですが、codecommitにlambdaコードを管理してるので、デプロイとかいろいろ便利なのでやってみました。gitってしっかり使いこなそうと思うと結構複雑なんだなとー理解はあとでいいから、とにかく使う!というところを書いて行きます。登場人物codecommitjenkins(デプロイでつかう。今回はほとんど登場しないです)sourcetree使った流れcodecommit(AWS)に、リポジトリを作って、ブランチを切ります。↓sourcetreeというgitを使うためのツールを使って、そのブランチにコードを置きます。↓jenkinsに作ったジョブでデプロイをします。という流れでつかいました。今回は、gitコマンドを中心にしていきます。他のツールは説明に必要な部分だけ使いかたとか書きます。細かく知りたい方は他のページをご参考に実際にやったことcodecommitでリポジトリを作る(これは、ブランチを入れるための箱のようなものです。)その中で、ブランチを作成。(これは、コードの置き場と作業場所のようなものです。と今は思っていただければいいかと思います。)sourcetreeを使って、リポジトリを自分のローカルに持ってきます。(ツールはgitが使えて、codecommitに繋げればなんでもいいかと)で、sourcetreeのホーム画面の右上にある、端末を使います。すると、黒い画面(CUI)が出ます。GUIよりも実はとっつきやすかったりしますよ。こういうやつここで、git checkout -b 新しいブランチ名 :新しいブランチを作成して、そのブランチに移動します。 masterという名前のブランチがあると思いますが、基本そこには何も置かないし作業もしないと思ってもらえればいいと思います。(運用の仕方にもよるので一概には言えないですが、基本的にはということで)git branch -a : リモートとローカルブランチの両方のあるなしが確認できます。あと、自分が今いるブランチの場所を*で教えてくれます。 ざっくり、リモートブランチは、codecommitにあるブランチローカルブランチは自分のPCにあるという感じのイメージです。ローカルブランチにcodecommitに置きたいファイルなどを持ってきます。たとえば、ファイルダウンロードして、そのディレクトリから、ローカルブランチにコピーとかで持ってきます。そしたら、git add . します。これは、今自分がいる階層にあるファイルをaddするよって意味です。git commit -m "最初のコミット" これで、コミット(確定させるみたい感じの意味です)して、これはなにをしたのかを -mオプションをつけて書いておきます。他の人が見た時に概要が掴めるようにです。git push -n :実際にpushするときは -nを取って実行する。 最後にpushします。-nはdry-run(実際にはpushしないで、もししたらどうなるかの結果を表示してくれます)これで、codecommit上に、自分が更新、作成をしたファイルを置くことができます。そしたら、デプロイしましょう。まとめリポジトリ作成↓リポジトリクローン↓ブランチ作成↓git add↓git commit↓git push↓デプロイ現場からは以上です。
  • テクログ

    AWS【S3】再入門

    こちらからご覧ください↓https://qiita.com/s-yanada/items/f5b008cb11df85f0201b
  • テクログ

    CloudWatch Logsエージェントがローテートさせるとtimestamp is more than 2 hours in future.で止まってしまう件について

    こんにちわ。パグです。本日はCloudWatch Logsエージェントのログがtimestamp is more than 2 hours in futureで止まってしまう件について書きます。現在の環境はFuelPHPで、主にFuelPHPのローテートログをCloudWatchエージェントでPUSHさせたいって内容になっています。が、恐らく他の環境のログの場合で起きた事象もこれに近しいのではないかと思います。CloudWatchLogsエージェントは導入済みで、ローテート時にうまく動かない人向けなので、そもそもCloudWatchLogsにログが反映されていない場合は別の要因によるのではないかと思います。[/server/fuelphp/logs] datetime_format = %Y-%m-%d %H:%M:%S file = /path/to/fuel/app/logs/20*/*/* buffer_duration = 5000 log_stream_name = {instance_id} initial_position = start_of_file log_group_name = /server/fuelphp/logs 他にマルチラインの設定などが必要かと思いますが、簡単に書くとこんな感じに設定。CloudWatch側では、log_group_nameで検索ができるようになりますが、翌日「 timestamp is more than 2 hours in future.」とエラーが出ておりPUSHイベントが動いていません。色々調べていて、皆大好きStackOverflowを見て見ると、以下のような記事が。https://stackoverflow.com/questions/40604940/cloudwatch-logs-acting-weirdhttps://forums.aws.amazon.com/thread.jspa?threadID=243092この記事の人に激しく同意>Yes I'm experiencing the issue too. Is there a way to reset the state file without doing this?うん。私もそう思うわ。それやらないでResetしたいんだよぉぉぉぉ。はい。じゃあ。本題です。awslogsのPUTイベントが失敗する原因は以下です。PutLogEvents オペレーションの制約に従って、次の問題によりログイベントまたはバッチがスキップされる場合があります。以下本家のマニュアルから抜粋https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/WhatIsCloudWatchLogs.html注記1.データがスキップされた場合、CloudWatch Logs エージェントはログに警告を書き込みます。2.ログイベントのサイズが 256 KB を超過した場合、ログイベントは完全にスキップされます。3.ログイベントのタイムスタンプが 2 時間以上未来の場合、ログイベントはスキップされます。4.ログイベントのタイムスタンプが 14 日以上過去の場合、ログイベントはスキップされます。5.ログイベントがロググループの保持期間よりも古い場合、バッチはすべてスキップされます。単一の PutLogEvents リクエストでログイベントのバッチが 24 時間実行されている場合、PutLogEvents オペレーションは失敗します。上記の3がこの「 timestamp is more than 2 hours in future.」というエラーにあたります。タイムスタンプが実行時間より2時間以上未来日になっているので、スキップしますとのことで、最初はUTCと日本時間のズレのせいかと考えたのですが、どうもズレている時刻が異なります。実行タイムスタンプの調べ方はStackOverflowに書いてある通りで「/var/lib/awslogs/agent-state」をsqlite3で検索(JSON形式で保存されているので実行された体の時刻を調べます)調べるストリームIDは/var/log/awslogs.logに出ています。2019-09-28 06:01:02,041 - cwlogs.push.stream - INFO - 12879 - Thread-1 - Removing dead reader [77cbf636732d4f124469c8ccb0f71abe, /logs/2019/09/27.php] 2019-09-28 06:01:02,041 - cwlogs.push.stream - INFO - 12879 - Thread-1 - Removing dead publisher [77cbf636732d4f124469c8ccb0f71abe, /logs/2019/*/*.php] 2019-09-28 06:01:02,044 - cwlogs.push.stream - INFO - 12879 - Thread-1 - Starting publisher for [77cbf636732d4f124469c8ccb0f71abe, /logs/2019/09/28.php] 2019-09-28 06:01:02,044 - cwlogs.push.stream - INFO - 12879 - Thread-1 - Starting reader for [77cbf636732d4f124469c8ccb0f71abe, /logs/2019/09/28.php] 上記の77cbf636732d4f124469c8ccb0f71abeです。(PATHはサイトの詳細が記載してあるので少し削りました)これを検索します。[root@server]# sqlite3 /var/lib/awslogs/agent-state SQLite version 3.7.17 2013-05-20 00:56:22 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> select * from push_state where k="8deaef1856dda2abe912ceedc4180f53"; 上記のkの部分にストリームIDを入れると、JSONからPUSHした時にイベントが出てきます。8deaef1856dda2abe912ceedc4180f53|{"start_position": 248, "source_id": "8deaef1856dda2abe912ceedc4180f53", "first_timestamp": 1570412666000, "first_timestamp_status": 1, "sequence_token": "49599891918873079124975725871068904036184047788058782002", "batch_timestamp": 1570412666866,  "end_position": 369}|2019-10-06T21:00:13|2019-10-07T01:44:32 このFirstTimestampとbatchtimestampが明らかに前日になっています。前日になっていますが、9時間ズレとかではありません。なのでUTCの問題ではありません。なんでかなーと調べていくと、書き込まれない時に、ローテートされた後のログストリームIDがずっと変わらないではありませんか。2019-09-28 06:00:58,041 - cwlogs.push.reader - INFO - 12879 - Thread-1346 - Reader is leaving as requested... 2019-09-28 06:01:02,041 - cwlogs.push.stream - INFO - 12879 - Thread-1 - Removing dead reader [77cbf636732d4f124469c8ccb0f71abe, /logs/2019/09/27.php] 2019-09-28 06:01:02,041 - cwlogs.push.stream - INFO - 12879 - Thread-1 - Removing dead publisher [77cbf636732d4f124469c8ccb0f71abe, /logs/2019/*/*.php] 2019-09-28 06:01:02,044 - cwlogs.push.stream - INFO - 12879 - Thread-1 - Starting publisher for [77cbf636732d4f124469c8ccb0f71abe, /logs/2019/09/28.php] 2019-09-28 06:01:02,044 - cwlogs.push.stream - INFO - 12879 - Thread-1 - Starting reader for [77cbf636732d4f124469c8ccb0f71abe, /logs/2019/09/28.php] 2019-09-28 06:01:02,045 - cwlogs.push.reader - INFO - 12879 - Thread-1348 - Replay events end at 384. 2019-09-28 06:01:02,045 - cwlogs.push.reader - INFO - 12879 - Thread-1348 - Start reading file from 74. 2019-09-28 06:01:02,045 - cwlogs.push.batch - WARNING - 12879 - Thread-1348 - Skip event: {'timestamp': 1569618001000, 'start_position': 74L, 'end_position': 153L}, reason: timestamp is more than 2 hours in future. 2019-09-28 06:09:30,216 - cwlogs.push.batch - WARNING - 12879 - Thread-1348 - Skip event: {'timestamp': 1569618569000, 'start_position': 153L, 'end_position': 229L}, reason: timestamp is more than 2 hours in future. ダメだった時のログはこんな感じで。Removing dead publisherしてるのにStarting publisherのストリームIDが変わらない。77cbf636732d4f124469c8ccb0f71abeこの場合、翌日になっているのにずっとタイムスタンプ上9月27日の朝6時とかになっているので、9月28日のPUSHとならずに、スキップされてしまいます。原因は、FuelPHPのログって、一番上位に<?php defined('COREPATH') or exit('No direct script access allowed'); ?> こんな感じの固定文言が出ているんですけど、こいつが前日とまったく同じなものだから、Startを前日のログから検索してしまって、日付がリセットされないーって内容でした。なので、FuelPHPはCoreの LogsClassをOverwriteして2019-10-01 08:26:02<?php defined('COREPATH') or exit('No direct script access allowed'); ?>タイムスタンプを突っ込んでやりました。2019-10-07 06:00:08,256 - cwlogs.push.stream - INFO - 14088 - Thread-1 - Removing dead reader [729a61c49dafeeb9472f9bc030510546, /logs/2019/10/06.php] 2019-10-07 06:00:08,261 - cwlogs.push.stream - INFO - 14088 - Thread-1 - Starting reader for [8deaef1856dda2abe912ceedc4180f53, /logs/2019/10/07.php] すると、ログに設定した日付を判別して、勝手にローテートされるようになりました。うまくローテーションがかからなくてログの一部が翌日になったら送れなくなった人はお試しください。(Sitemapって、エラーじゃないやんけとか、いわない。)ではでは〜。
  • テクログ

    slackコマンドっぽいことをchatworkでしたい!

    そろそろ夏も終わりですね。はやく涼しくなってほしいですね。そういう気持ちからslackコマンドっぽいことをchatworkでしたい!ということになりました。-----------------------◆本当の経緯もとからslackでのchatops的なものは結構やっていたんですが、弊社slackだけではなくchatworkも使うんですね(ぼやかしで、chatworkもWebhookがあって、slackよりも機能や設定できることは少ないけど、なんとか似たことができそうだな、となったので作りました。-----------------------◆構成chatwork→APIGateway→Tokenやコマンドを検証し、振り分けるLambdaFunction→(SNS)→コマンドの実態LambdaFunction+chatworkへ結果返答わかりやすくするためTokenやコマンドを検証し、振り分けるLambdaFunction を [振り分けLambda]コマンドの実態Lambda を [実態Lambda]とします-----------------------◆設定を簡単に解説細かい設定は書きませんが、いろいろ試行錯誤するのも経験のうちですよね。(1)APIGatewayを作成chatworkのWebhookが叩く入り口です。URLがあればひとまずOKです。パス設定とかはおまかせします。(2)[振り分けLambda]の殻を作成APIGatewayの先のLambdaを作成します。(3)APIGatewayを設定(1)と(2)をつなぎまーす叩くとレスポンスが来るだけ確認しておくのもいいですね。(4)chatworkのWebhook設定chatworkにボットアカウントを作り、そのアカウントでWebhookの設定をします参考:https://help.chatwork.com/hc/ja/articles/115000169541-Webhook%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8BURLはAPIGatewayのURLで、イベントはルームイベントにしました。(なぜなら、目に見えないところではなく、その部屋だけで使ってほしいからです。)メッセージ作成のみ、にしました。(5)[振り分けLambda]を実装ソース!だよ!何もライブラリが要らないってのを重視。なんか貼ったら改行があれなのでまあそこはいいかんじにしてください。何かあってもこちらはサンプルですのでそこはいつものとおりご了承ください。見直すと入ってきた文字整形とか無理矢理感。chatworkはクライアントごとにメッセージの形式が微妙に違ったりしてね…必要な環境変数はCHATWORK_API_TOKEN (KMSで暗号化してある、postに使うchatworkのTokenWEBHOOK_TOKEN (渡ってきたchatworkのメッセージが正しいものかを判定するためのTokenです。# -*- coding: utf-8 -*- # chatwork commanderの入り口 import boto3 import json import logging import os import sys from datetime import datetime from botocore.exceptions import ClientError import copy import urllib.request, urllib.error from base64 import b64decode, b64encode import hmac import hashlib valid_command_list = ['こまんど1','こまんど2','こまんど3'] to_str = "[To:1111111111]" usage_str = "【コマンド一覧】\n" + \ "※それぞれのコマンドの使い方については[コマンド usage]を打って確認してください\n\n" + \ "こまんど1\n" + \ "こまんど2\n" + \ "こまんど3\n" SNS_ARN_PREFIX = "arn:aws:sns:ap-northeast-1:11111111111:chat_command_" #### #メイン #### def lambda_handler(event, context):   #先にイベントは取っておく(判定とかに使うから   eventjson = json.loads(json.dumps(event))   bodyjson = json.loads(eventjson['body'])   room_id    = str(bodyjson['webhook_event']['room_id'])   message_id = str(bodyjson['webhook_event']['message_id'])   body      = str(bodyjson['webhook_event']['body'])   account_id = str(bodyjson['webhook_event']['account_id'])   #まず   #[To:1111111111]   #がないなら無視(全メッセージ流れてくるから   if not to_str in body:     print('not me.END')     return {'statusCode': 200,'body': json.dumps('not me.END')}   #毎回kms使うのがやだからここでやってます   ENCRYPTED_WEBHOOK_TOKEN = os.environ['WEBHOOK_TOKEN']   WEBHOOK_TOKEN = boto3.client('kms').decrypt(CiphertextBlob=b64decode(ENCRYPTED_WEBHOOK_TOKEN))['Plaintext']   ENCRYPTED_CHATWORK_API_TOKEN = os.environ['CHATWORK_API_TOKEN']   CHATWORK_API_TOKEN = boto3.client('kms').decrypt(CiphertextBlob=b64decode(ENCRYPTED_CHATWORK_API_TOKEN))['Plaintext']   #Tokenの確認   if check_request(event, WEBHOOK_TOKEN) == True:     print("Token OK")     #body整形     command_text = body.replace('\n', '').replace('[To:1111111]', '').replace('aaaaa さん', '').replace('aaaaaさん', '').strip()     command_args = command_text.split(" ")     #コマンドの確認     command = command_args[0]     nextStatus = checkCommandStrAndMakeNextStatus(command, room_id, account_id, message_id, command_text, CHATWORK_API_TOKEN)     #nextStatusがTrue(続行)の場合、次のlambdaをSNSで呼ぶ     #続行ではない場合は特に何もせずreturnして終わり     if nextStatus == True:       message = {"room_id"   : room_id,             "command_args": command_args,             "account_id" : account_id,             "message_id" : message_id       }       #SNS_ARN_PREFIX+command がARNになるようになっている       sendSNS(message, SNS_ARN_PREFIX+command)     return {       'statusCode': 200,       'body': json.dumps('OK')     }   else:     print("Token NG")     return {       'statusCode': 403,       'body': json.dumps('NG')     } ##----------------------- def sendSNS(message, sns_TOPIC):   sns_client = boto3.client(     'sns'   )   #メッセージ整形   message=json.dumps(message)   message=json.dumps({'default': message, 'lambda': message})   response = sns_client.publish(     TopicArn=sns_TOPIC,     Subject='/lambda',     MessageStructure='json',     Message=message   )   print(response) #### #コマンドの内容を確認してchatwork通知 #また、今後の続行ステータスを返す #### def checkCommandStrAndMakeNextStatus(command, room_id, account_id, message_id, command_text, CHATWORK_API_TOKEN):   if command == 'usage':     postChatwork(usage_str,room_id, CHATWORK_API_TOKEN)     return False#終了   else:     if not command in valid_command_list:       postChatwork("対象コマンドがありません。\n"+usage_str,room_id, CHATWORK_API_TOKEN)       return False#終了     else:       #chatworkに受付状況を通知       postChatwork(         makeReplyMessage(account_id, room_id, message_id, command_text),         room_id,         CHATWORK_API_TOKEN       )       return True#続行 #### #メッセージをつけるだけ #### def makeReplyMessage(account_id, room_id, message_id, command_text):   return "コマンド:" + command_text + "\nを受け付けました。(コマンドの実態に渡しました)\nお待ち下さい。" #### #chatworkに通知 #### def postChatwork(message, room_id, CHATWORK_API_TOKEN):   print("in postChatwork")   END_POINT_BASE = "https://api.chatwork.com/v2"   END_POINT   = "/rooms/"   ACTION     = "/messages"   headers = { 'X-ChatWorkToken': CHATWORK_API_TOKEN }   data = { 'body': message }   data = urllib.parse.urlencode(data)   data = data.encode('utf-8')   # リクエストの生成と送信   post_message_url = END_POINT_BASE + END_POINT + room_id + ACTION   request = urllib.request.Request(post_message_url, data=data, method="POST", headers=headers)   with urllib.request.urlopen(request) as response:     response_body = response.read().decode("utf-8")     print(response_body) #参考 #https://qiita.com/ikedaosushi/items/b01183f207ea1db6a8d3 def check_request(event, WEBHOOK_TOKEN):   webhook_sig = event['headers']['X-ChatWorkWebhookSignature'].encode('utf-8')   sig = build_sig(event['body'], WEBHOOK_TOKEN)   is_valid = webhook_sig == sig   return is_valid def build_sig(body, WEBHOOK_TOKEN):   token_decode = b64decode(WEBHOOK_TOKEN)   sig = hmac.new(token_decode, body.encode('utf-8'), hashlib.sha256).digest()   sig_encoded = b64encode(sig)   return sig_encoded だいたいでいえば・メッセージはルームのものがすべて来るので、処理対象メッセージなのかを判定・tokenの確認・コマンドの確認・受け付けたことを返答・SNSで次のLambdaを呼ぶ(このSNSを一定の形式に統一することによって、後ろのコマンドを増やしやすくなってます。(6)実態Lambdaを呼ぶためのSNSを作るarn:aws:sns:ap-northeast-1:11111111111:chat_command_ ってなっているようにSNSをまず作りますたとえばmake_EC2みたいなコマンドだった場合、SNS名はchat_command_make_EC2になりますね。(7)[実態Lambda]を作るここは自分でつくってね!SNSを受け取って、分解して、処理をすればいいです。終わったらchatworkに通知したり、いろいろ内部でやればいいですね。夢が広がる。(8)SNSの先に[実態Lambda]を追加まあ追加するだけですあとはchatworkでボットにはなしかけたりしてテストをしてみよう!-----------------------コマンドを増やすときも、許可するコマンドを追加して、SNS作成コマンド実態Lambda作成だけでぽんぽんふえるよ!ルームを分けることによって、重要度ごとに使える人を分けられたりもします。以上夏休みの自由研究にどうぞ。もうおわってますか。-----------------------<以下はただの一覧用の画像でーす>
  • テクログ

    AWS CLIを使ってIAMユーザーの登録から二段階認証をオンにするまでのスクリプト

    नमस्ते! आप कैसे(कैसी) हैं?ナマステー!こんにちは。インフラ担当のまつやです。弊社ではWebサービスの基盤としてAWS(Amazon Web Service)を主に使っています。エンジニアはAWSコンソールからログインして作業することが多いため、メンバーが増えるたびにアカウントを発行する必要があります。このアカウント作成はAWSコンソールにログインし、IAM(Identity and Access Management)から作成できますが、発行頻度が増えてくるとGUIからのアカウントの発行・通知がやや大変になってきました。そこで、AWS CLI(コマンドラインインターフェイス)を利用して、ユーザー発行に必要なコマンド操作を一括で行えるシェルスクリプトを作成することにしました。また弊社ではAWSコンソールからのログインにパスワード設定の上、MFAデバイスによる二段階認証を必須としています。この二段階認証も同時に設定してしまい、パスワード情報と二段階認証用のQRコードをユーザーに通知するだけで済むようにしたいと思います。Amazon Linuxのbash環境で作成しています。python(2.7)aws-cli(最新版)oathtoolが必要になります。以下が本体のシェルスクリプトです。#!/bin/bash set -e # 環境変数設定 export BASE_DIR=${HOME}/iam # # usage # usage_exit() {  echo "usage:"  echo "${0} -u USERNAME [-g GROUP] [-a] [-h]" 1>&2  echo "-a: create API key for each user." 1>&2  echo "-h: this help." 1>&2  exit 1 } #パスワード生成コマンド password_command="cat /dev/urandom | tr -dc '12345678abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ+\-!' | fold -w 16 | grep -E '[123456789]' | grep -E '[+\-\!]' | grep -E '^[123456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ]' |head -n 1" #引数の設定 while getopts "u:g:h" OPT do    case $OPT in        "u") CREATE_USER="true"; username=${OPTARG}            ;;        "g") ADD_TO_GROUP="true"; group=${OPTARG}            ;;        "h") usage_exit            ;;        \?) usage_exit            ;;    esac done # 引数でユーザーを指定しない場合終了する。 [ "${CREATE_USER}" != "true" ] && usage_exit #IAMユーザーのクレデンシャル情報格納用のディレクトリを作成。  if [ "${CREATE_USER}" = "true" ]; then      if [ ! -e "${BASE_DIR}/account/${username}" ]; then        mkdir -p ${BASE_DIR}/account/${username}      else       echo "Directory ${username} already exists."      fi #IAMユーザー作成   aws iam create-user --user-name ${username} --profile default   aws iam wait user-exists --user-name ${username} #二段階認証の仮想MFAデバイスを作成   aws iam create-virtual-mfa-device --virtual-mfa-device-name ${username} \   --bootstrap-method Base32StringSeed \   --outfile ${BASE_DIR}/account/${username}/${username}_secret.txt #クレデンシャル情報ファイルに出力する内容  secret_key=`cat ${BASE_DIR}/account/${username}/${username}_secret.txt`  signin_url="https://AWSアカウント名.signin.aws.amazon.com/console"  password=$(eval ${password_command}) #ログインパスワードの設定&初回ログイン時のパスワード強制変更をオンにする   aws iam create-login-profile --user-name $username \   --password ${password} --password-reset-required --profile default #クレデンシャル情報をファイルに出力 echo "User name,Password,Secret access key,Console login link ${username},${password},${secret_key},${signin_url}" > ${BASE_DIR}/account/${username}/${username}_password.csv #二段階認証用のQRコードを生成  python /home/ec2-user/iam/qrcodegen.py "otpauth://totp/Amazon%20Web%20Services:${username}@AWSアカウント名?secret=${secret_key}&issuer=Amazon%20Web%20Services" ${BASE_DIR}/account/${username}/${username}_qr.png # Login Profile が完全にできているか確認してから次へ進む  aws iam wait user-exists --user-name ${username} #ワンタイムパスワードを発行する code="oathtool --totp -d 6 --time-step-size=30s --base32 ${secret_key}"        code1=$(eval ${code})        while :         do           code2=$(eval ${code})            if [ $code2 != $code1 ]; then                 break;            fi         done #二段階認証をオンにする  aws iam enable-mfa-device --user-name ${username} \  --serial-number arn:aws:iam::XXXXXXXXXXXX:mfa/${username} \  --authentication-code1 ${code1} --authentication-code2 ${code2} #(注: arn:aws:iam::XXXXXXXXXXXX:mfa の12桁のXXXXXXXXXXXXにはAWSアカウントIDが入ります)  else  usage_exit  fi # 引数で-gを指定した場合 ユーザー ${username} にグループ ${group} を割り当てる  if [ "${ADD_TO_GROUP}" = "true" ]; then     aws iam wait user-exists --user-name ${username}     aws iam add-user-to-group --group-name ${group} \     --user-name ${username} \     --profile default  fi -u username の形式でユーザー名の指定を必須にしています。ここで指定したユーザー名を使って処理が進みます。まず、基本のIAMユーザー作成を行います。aws iam wait user-existsでユーザーが作成されたことを確認して先に進みます。次に二段階認証用の仮想MFAデバイスを作成します。AWSコンソールから作成する場合はIAMユーザーと同じ名前で作成されますが、CLIで作成する場合はデバイス名が重複しない限りはIAMユーザー名と異なっていても別の名前のデバイスを作成し、関連付けをすることができます。ここでは管理が面倒になるので、IAMユーザ名と一致させます。MFAデバイス作成と同時にシークレットキーを発行します。ここではQRコードの形でも発行できますが、シークレットキーと同時に発行することができないため、シークレットキーを発行し、そのシークレットキーを元に別途QRコードを作成します。次にログインパスワードを設定します。初回ログイン時に強制的にパスワード変更させるオプションを有効にします。この時発行するパスワードですが、cat /dev/urandomして tr -dcとかで切り出して、16文字のものを生成しています。小文字のエル、数字の0と大文字小文字のオーは除外しています。パスワードコマンドはこれに限らずpwgenとかが使えるならそれでいいかと思います。次にQRコードの生成です。pythonのqrcodeライブラリを使います。pipでインストールします。pip install qrcode インストールしたら、中間プログラムを作成します。ここではqrcodegen.pyとしています。qrcodegen.pyimport qrcode import sys import os args = sys.argv qr = qrcode.QRCode(  version=4,  error_correction=qrcode.constants.ERROR_CORRECT_M,  box_size=6,  border=4, ) qr.data = args[1] qr.save_path = args[2] qr.add_data(qr.data, 20) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") img.save(qr.save_path) これを使ってQRコードを生成します。MFAデバイスを作成したときの ${username}_secret.txt を${secret_key}に指定します。${username}@AWSアカウントの箇所はご自身の環境に合わせて読み替えて下さい。これでIAMユーザー毎のクレデンシャル情報格納ディレクトリにQRコードが生成されます。二段階認証を有効にします。oathtoolを使用してワンタイムパスワードを発行します。これを都合2回入力することで二段階認証が有効になります。Amazon Linuxでは基本のリポジトリには存在しないので、epelリポジトリからインストールします。epelリポジトリを有効にする方法はここでは割愛します。発行したシークレットキーを元に6桁のワンタイムパスワードを発行します。whileでこのパスワード発行コマンドを実行し、初めにに発行した値と異なる値になるまで回します。ここでcode1とcode2を取得します。code1とcode2を使って二段階認証をオンにします。最後に-gでグループを引数に指定した場合の処理です。指定したグループをユーザーに割当てます。指定しなければスルーします。以上のようにして、事前に二段階認証をオンにした状態でIAMユーザー情報を各エンジニアに展開しています。エンジニアは発行されたパスワードとQRコード、あるいはシークレットキーを元にAWSコンソールにログインできます。この他に、二段階認証がオフになっているユーザーがいないかを監視するLambdaのスクリプトも作成していますが、それについてはまたの機会に。・参考にした記事 https://dev.classmethod.jp/etc/aws-cli-iamuser-bulk-create/ https://dev.classmethod.jp/cloud/aws/virtual-mfa-by-aws-cli/それではまた!फिर मिलेंगे!
  • テクログ

    データレイクを知って一発で好きになりました

    皆さん初めまして。初めましてじゃない方も初めまして。しゃむぽんです。先日、AWS Summit Tokyo 2019に行ってきました。超絶にわか知識な状態で挑みましたが、結果とても有意義な3日間になりました。様々な用語やサービスを知りました。今更知ったのかよって思われるものもあると思いますが、少なくとも自分にはどれもこれも新鮮ですごくタメになりました。その中でも一番個人的に気に入ったのがデータレイクという概念です。ビッグデータを整理するための概念で、AWS社の説明では「規模にかかわらず、すべての構造化データと非構造化データを保存できる一元化されたリポジトリ」だそうです。AWSだとS3ですね。いや純粋にすごくないですか?今まで自分の感覚ではデータベースの構造化データと画像や動画のような非構造化データの間にはなんかこう絶対的な超えられない壁みたいなものを感じてましたからね。ほんと衝撃でした。っていうだけの話なんですけどね。データレイクで調べるとすごいタメになる記事いっぱい出てきますのでもっと知りたいという方は是非調べてみてください!逆にいやもうそんなんとっくに知っとるしって方は生暖かい目で見ていてくだてか教えてください。にわか知識なのであんまり深掘りもできそうにないので今回はこの辺で。ほいでは!