AWS LambdaでGmail APIの呼び出し 第1回【cloudpack 大阪 BLOG】
AWS LambdaからGmailAPIを呼び出したいと思い方法を調べてみました。当初はGmailAPIにアクセスするためのQuickstartが用意されていたため簡単にできるかと思っていました。が、サンプルがOAuth認証のみとなっていました。OAuthではプログラム実行時に手動で認証になるためLambdaではサービスアカウントを使用する必要があります。このあたりの使い方がよくわからずがっつりハマりました。また、Lambdaでもしっかり躓きなかなか大変でしたが、何とかAPIを呼び出すところまで漕ぎ着けたので手順をまとめておきたいと思います。今回は長くなりそうなので2回に分けたいと思います。
手順
1.GmailAPIの設定(第1回)
① プロジェクトの作成
② APIの有効化
③ サービスアカウントの作成
2.Lambdaの設定(第2回)
① Lambda用プロジェクトの作成
② 秘密鍵をp12からpemに変換
③ Lambdaの作成
④ 動作確認
開発環境
Amazon Linux AMI release 2015.09
Python v2.7.10
google-api-python-client v1.4.2
pycrypto v2.6.1
lambda-uploader v0.5.0
1.GmailAPIの設定
① プロジェクトの作成
前提としてGmailの2段階認証を有効にしておいてください。まずはGoogle Developer Consoleにアクセスします。
HOME画面にアクセスし、「プロジェクトの作成」を選択。初めての場合はプロジェクトの作成画面になるかもしれません。
プロジェクト名に任意の名前を入力し、作成をクリック。
③ サービスアカウントの作成
Lambdaからアクセスするためのアカウントを作成します。サーバからアクセスする場合はOAuthクライアントIDでも可能ですが、認証が必要となるためサービスアカウントを作成します。左ペインの認証情報を選択し、新しい認証情報クリック。
サービスアカウントキーを選択。
任意の名前を入力します。キーのタイプP12を選択し作成をクリック。
鍵が自動的にダウンロードされますので保管しておいてください。またパスワードも控えておいてください。
余談ですが、たまたまSafariでこのページを開いていたのですが自動でダウンロードされませんでした。
サービスアカウントの管理をクリック
GmailAPIにアクセスする際はこのメールアドレス(サービスアカウントID)とダウンロードした鍵を使用して認証を行います。
これで準備が整いましたので、次回はLambdaでGmailアクセスを行いたいと思います。
AWS IOTでDynamoDBへ登録する【cloudpack 大阪 BLOG】
仕事でAWS IOTを使って簡単なデモを作成しました。センサーから送信されたデータは、IOTからDynamoDBへ直接連携できる機能が用意されていたため、DynamoDBへ登録することにしました。今回はその手順を残しておきます。
手順(概要)
1.テーブル作成
2. AWS IOTの設定
3. 動作確認
2.AWS IOTの設定
①thingの作成
AWS IOT上で管理するデバイスの名前を作成します。
②ruleの作成
IOTで受け取ったデータをどのように処理するかのルールを作成します。
Create a ruleをクリック
③証明書の取得
認証用の証明書を取得します
秘密鍵、証明書等をダウンロードします。
また使いたいSDKを選択し、「Confirm & start connect」を選択します。
以下のページが表示されSDKのダウンロードができます。
また、下記ページからroot証明書をダウンロードします。
https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem
3. 動作確認
動作確認はnodeで1回だけpublishするサンプルを作成しました。テストだけならaws cliでも確認可能です。
aws-iot-device-sdk-js/examples以下に適当な名前で下記プログラムを作成します。
また同じディレクトリにcertsディレクトリを作成し上記で取得した証明書を格納しておきます
const deviceModule = require('..').device; const cmdLineProcess = require('./lib/cmdline'); const device = deviceModule({ keyPath: './certs/private.pem.key', certPath: './certs/certificate.pem.crt', caPath: './certs/root-CA.crt', clientId: '1', region: 'ap-northeast-1', reconnectPeriod:10 }); var thingName = 'iot_test' device.subscribe(thingName); var put_data = {}; var data = new Array(); // データ送信 var now = new Date(); var yyyymmdd = now.getFullYear()+ ( "0"+( now.getMonth()+1 ) ).slice(-2)+ ( "0"+now.getDate() ).slice(-2); var hhmmss = ("0" + now.getHours()).slice(-2) + ("0" + now.getMinutes()).slice(-2) + ("0" + now.getSeconds()).slice(-2) + ("00" + now.getMilliseconds()).slice(-3); put_data = { date: yyyymmdd, time : hhmmss, value :Math.floor( Math.random() * 11 ) }; device.publish(thingName, JSON.stringify(put_data)); console.log( JSON.stringify(put_data)); console.log( 'publish');
次のコマンドで実行します。
node put_data.js
publishしたデータ送信データが登録されましたね。
今回初めてDynamoDBを使ったのでよく分からず、IOTの設定のところでつまづきました。
また、ルール作成のところでPayload fieldにDynamoに登録したいフィールドを入力しましたが、
全項目が登録されてしまい特定のフィールドだけ選択して登録ということが出来ませんでした。ここはもう少し調べてみたいと思います。
VimでPythonのコード補完設定【cloudpack 大阪 BLOG】
最近はAWSのLambdaなどの開発でPythonを使うことが増えてきましたが、まだまだ慣れていないこともあり、ちょっとしたコードを書くにも関数名がわからないので都度ネットで調べないといけないのがすごくストレスでした。
開発環境としてはLinux上のVimで開発しているため、Vim上で動作するPluginがないか調べたところ「jedi-vim」というよさそうなpluginがあったため導入してみました。まだあまり使い込んでいませんが、コード補完だけでなく定義元への移動やPydocの閲覧もでき動作も軽いので結構気に入りました。
今回はjedi-vimの導入方法について紹介したいと思います。
導入手順
1.Neobundleのインストール
2.jedi-vimのインストール
3. 動作確認
Neobundleのインストール
以下のコマンドを実行
mkdir -p ~/.vim/bundle git clone https://github.com/Shougo/neobundle.vim ~/.vim/bundle/neobundle.vim
.vimrcに以下の設定を追加
if 0 | endif if has('vim_starting') if &compatible set nocompatible " Be iMproved endif " Required: set runtimepath+=~/.vim/bundle/neobundle.vim/ endif " Required: call neobundle#begin(expand('~/.vim/bundle/')) " Let NeoBundle manage NeoBundle " Required: NeoBundleFetch 'Shougo/neobundle.vim' " My Bundles here: " Refer to |:NeoBundle-examples|. " Note: You don't set neobundle setting in .gvimrc! call neobundle#end() " Required: filetype plugin indent on " If there are uninstalled bundles found on startup, " this will conveniently prompt you to install them. NeoBundleCheck
jedi-vimのインストール
以下のコマンドを実行
cd ~/.vim/bundle/
git clone --recursive https://github.com/davidhalter/jedi-vim.git
.vimrcに以下の設定を追加
" Jedi for python NeoBundleLazy "davidhalter/jedi-vim", { \ "autoload": { "filetypes": [ "python", "python3", "djangohtml"] }} if ! empty(neobundle#get("jedi-vim")) let g:jedi#auto_initialization = 1 let g:jedi#auto_vim_configuration = 1 nnoremap [jedi] <Nop> xnoremap [jedi] <Nop> nmap <Leader>j [jedi] xmap <Leader>j [jedi] let g:jedi#completions_command = "<C-Space>" # 補完キーの設定この場合はCtrl+Space let g:jedi#goto_assignments_command = "<C-g>" # 変数の宣言場所へジャンプ(Ctrl + g) let g:jedi#goto_definitions_command = "<C-d>" # クラス、関数定義にジャンプ(Gtrl + d) let g:jedi#documentation_command = "<C-k>" # Pydocを表示(Ctrl + k) let g:jedi#rename_command = "[jedi]r" let g:jedi#usages_command = "[jedi]n" let g:jedi#popup_select_first = 0 let g:jedi#popup_on_dot = 0 autocmd FileType python setlocal completeopt-=preview " for w/ neocomplete if ! empty(neobundle#get("neocomplete.vim")) autocmd FileType python setlocal omnifunc=jedi#completions let g:jedi#completions_enabled = 0 let g:jedi#auto_vim_configuration = 0 let g:neocomplete#force_omni_input_patterns.python = \ '\%([^. \t]\.\|^\s*@\|^\s*from\s.\+import \|^\s*from \|^\s*import \)\w*' endif endif
chrootの設定でハマった話【cloudpack 大阪 BLOG】
chrootの設定でかなりハマったので、今回はその内容をまとめたいと思います。
実現したいこと
sftp_userの接続時のホームディレクトリを「/home/sftp_user/home_dir」に制限したい。
意図は、bash_profile、bash_history、.sshなどをsftp接続ユーザーに見せたくないため
設定方法
ユーザー追加
useradd sftp_user passwd sftp_user
/etc/ssh/sshd_configの設定変更
#Subsystem sftp /usr/libexec/openssh/sftp-server
Subsystem sftp internal-sftp
Match User sftp_user
ChrootDirectory /home/sftp_user/home_dir
ForceCommand internal-sftp
PasswordAuthentication yes
sshd再起動
/usr/sbin/sshd -t ← 構文チェック service sshd restart
sshdサービス再起動は十分注意してください。再起動が失敗した場合、新たにssh接続ができなくなります。
クラウドサービスのように直接接続する手段がない場合に対応するときは、別でもう一つssh接続をしておくなど慎重に対応してください。
今回はAWSでの対応のため、起動失敗のままセッションを切ってしまうと接続できなくなってしまい、リカバリ対応がかなり大変になることが想定されました。
ディレクトリの設定
chown root:root /home/sftp_user/home_dir ←必須 chmod 755 /home/sftp_user/home_dir ←必須 mkdir /home/sftp_user/home_dir/data chmod 755 /home/sftp_user/home_dir/data
※「/home/sftp_user/home_dir/」にはsftp_userの書き込み権限がないため、書き込み可能なディレクトリを用意しておく必要があります。
接続確認
sftp sftp_user@IP_ADDR
でパスワードを入力して接続OK
これで対応完了!
としたかったのですが・・・
接続は鍵認証にしたかったようなので、接続用の鍵を作成し設定しました。
いざ接続確認してみるものの、Connection Refuse・・・
再度ユーザー作成し、sshで鍵認証ができることを確認してから上記対応を行うも接続出来ない状況でした。
/var/log/secureを確認しても芳しい情報は得られずでなかなか解決ができませんでした。
で、もう一度よく設定を見直すと今回chrootの設定先は「/home/sftp_user/home_dir」となります
そのため「/home/sftp_user」はroot権限に設定しているため、ssh接続時に鍵の参照ができてないため接続エラーとなっていることに気が付きました。
ということで上記のディレクトリ構成のままで鍵認証ができないことが分かったので、制限用のディレクトリは別途「/var/sftp/sftp_user」などにすることで解決しました。
貝塚 泉南 2015.11.29
EC2インスタンスにRaspberry Piで電源スイッチを付けてみた【cloudpack 大阪 BLOG】
久しぶりにRaspberryPiを触ってみて思いついたので早速作ってみました。以前はLチカやったところで終わっていましたが、Pythonを少し覚えたので何かAPIと連携させてみようと考えていました。最近、EC2インスタンスを触ることが多くなりましたが、サーバの電源を入れるということを全てWeb上でやっているので物理スイッチでわざわざ起動、停止をやってみようと思い作ってみました。
今回やってみたのは以下のような内容です。
・RaspberryPiでスイッチを押すとEC2が起動もしくは停止
・起動中 or 停止中はLEDを点滅させる
・起動、停止が完了したらslackで自分に通知
準備
RaspberryPi B+ (OSはraspbian 今回使用したバージョンは「Linux raspberrypi 3.12.28+ 」)
ブレッドボード
LED
タクトスイッチ
抵抗 330Ω(LED用)、1kΩ(タクトスイッチ用)
ジャンパーワイヤ
モジュールのインストール
pipのインストール
apt-get install python-pip
boto3(AWS SDK FOR Python)のインストール
pip install boto3
配線の確認
実際に部品が正しく配置されているか確認するため、下記のプログラムを実行して動作確認を行います。
LEDの動作確認
#! /usr/bin/env python import RPi.GPIO as GPIO import time LEDPIN = 21 GPIO.setwarnings(False) # Suppress warnings GPIO.setmode( GPIO.BCM ) GPIO.setup( LEDPIN, GPIO.OUT ) while 1: GPIO.output( LEDPIN, True ) time.sleep( 1.0 ) GPIO.output( LEDPIN, False ) time.sleep( 1.0 )
実行すると一定間隔でLEDが点滅します。点滅しなければ部品の配置や配線が間違えてないか見直してください
タクトスイッチの動作確認
#! /usr/bin/env python import RPi.GPIO as GPIO import time # define INPUT_PIN = 4 # init GPIO.setwarnings(False) # Suppress warnings GPIO.cleanup() GPIO.setmode(GPIO.BCM) GPIO.setup(INPUT_PIN,GPIO.IN) if __name__ == "__main__": Btn_Flag = False while True: print (GPIO.input(INPUT_PIN)) time.sleep(0.2)
実行してボタンを押すとボタンが押されている間はコンソールに「1」が表示されます
EC2インスタンスの起動・停止プログラム
上記のテストプログラムが正常に実行されれば実際にボタンを押してEC2のストップ、スタートができるプログラムを作成していきましょう。
AWSのアクセスキー、シークレットキー及びSlackのAPIキーは取得したものに変更してください。また、Slackの送信先は自身のユーザー名(@ + ユーザー名)に変更してください。
#! /usr/bin/env python import RPi.GPIO as GPIO import time import boto3 from boto3.session import Session import sys import urllib import urllib2 # define INPUT_PIN = 4 LEDPIN = 21 # init GPIO.setwarnings(False) # Suppress warnings GPIO.cleanup() GPIO.setmode(GPIO.BCM) GPIO.setup(INPUT_PIN,GPIO.IN) GPIO.setup( LEDPIN, GPIO.OUT ) ################################################################# # Function ################################################################# def getResourceEC2 (): session = Session(aws_access_key_id='ACCESS_KEY', aws_secret_access_key='SECRET_KEY', region_name='ap-northeast-1') ec2 = session.resource('ec2') return ec2 def get_ec2_state(instanceId): ec2 = getResourceEC2() instance = ec2.Instance(instanceId) if(instance.state['Name']=='running'): return True else: return False def start_ec2(instanceId): ec2 = getResourceEC2() instance = ec2.Instance(instanceId) if(instance.state['Name']=='stopped'): instance.start() # Flashing LED led_flag = False while instance.state['Name'] != 'running': GPIO.output( LEDPIN, not led_flag) led_flag = not led_flag instance.reload() time.sleep( 1.0 ) GPIO.output( LEDPIN, True) print('EC2 Start!') post_slack('EC2 Start!') else: print('EC2 already Started!') post_slack('EC2 already Started!') def stop_ec2(instanceId): ec2 = getResourceEC2() instance = ec2.Instance(instanceId) if(instance.state['Name']=='running'): instance.stop() # Flashing LED led_flag = False while instance.state['Name'] != 'stopped': GPIO.output( LEDPIN, not led_flag) led_flag = not led_flag instance.reload() time.sleep( 1.0 ) GPIO.output( LEDPIN, False) print('EC2 Stop!') post_slack('EC2 Stop!') else: print('EC2 already Stopped!') post_slack('EC2 already Stopped!') def post_slack(message): url = "https://slack.com/api/chat.postMessage" params = {'token' :'SLACK_API_KEY', 'channel':'@USER', 'text' : message } req = urllib2.Request(url) req.add_header('Content-Type', 'application/x-www-form-urlencoded') req.add_data(urllib.urlencode(params)) res = urllib2.urlopen(req) ################################################################# # Main Function ################################################################# if __name__ == "__main__": print('Switch Start') argvs = sys.argv Id = argvs[1] Btn_Flag = get_ec2_state(Id) GPIO.output( LEDPIN, Btn_Flag) while True: if(GPIO.input(INPUT_PIN)): Btn_Flag = not Btn_Flag if(Btn_Flag): start_ec2(Id) else: stop_ec2(Id) time.sleep(0.2)
※簡略化するため、ステータスチェックなど細かい処理は省いています。
泉南 2015.11.08
天候 :雨
今季初のタチウオでした。