管理者以外も Chocolatey でインストールできるようにする

2020年11月

はじめに

Chocolatey 自体はとても便利で気に入っているのですが、管理者権限で PowerShell を実行してから都度 choco install ~ するのが面倒すぎて、何か良い解決策は無いかと調べていました。:mag:

一応公式サイトにも 管理者以外でインストール可能にする手順 は掲載されているのですが、私の手元の環境だとエラーが発生してしまいセットアップできませんでした。。 :persevere:

しかし、お手軽に管理者以外でもインストール可能にする方法を他に見つけましたので、備忘録も兼ねて記事として残しておくことにしました :writing_hand:

対処法

  1. PowerShell を起動して powershell start-process cmd -verb runas を実行する
  2. 別ウインドウでコマンドプロンプトが起動したら choco install gsudo を実行する
  3. 先ほどの PowerShell で gsudo choco install ~ でインストールが成功するか確認する

powershell start-process cmd -verb runas コマンドを実行すると、別ウインドウで管理者として実行したコマンドプロンプトが起動します。そこで choco install gsudo を実行することで、Windows 版の sudo である gsudo をインストールします。

これで gsudo choco install ~ のような形で PowerShell からコマンドを打ち込むことで、管理者として choco install ~ が実行できるようになりました。今後は管理者として PowerShell を起動せずとも各種ソフトウェアのインストールが Chocolatey で可能になります! :relaxed:

おわりに

PowerShell については最近使い始めたせいもあって不慣れなため、本記事タイトルの内容をどう実現すればよいか良く分かりませんでした。。

ただ sudo のような仕組みが Windows にもあれば、それだけでサクッと解決できそうだなーと思いググっていたら、非常に楽な解決策を見つけることが出来て満足です :laughing:

参考リンク

GitHub 接続時の ~/.ssh/config の書き方

2020年11月
本記事ではタイトルの内容に加えて ~/.ssh/config に対する基本的な理解を深めるため、各種設定項目に関する説明も付随して行います。GitHub を例に挙げてはいるものの、Bitbucket や GitLab、Git サーバへ接続する際にも利用可能な ~/.ssh/config の設定について記載しております。
WRITER
Soko Aoki
TAGS
git, GitHub, SSH

~/.ssh/config の書き方

SSH キーが未登録の場合は 公式サイトの手順 に従って鍵の生成・登録までを事前に行っておきます。

まず GitHub へ接続する ~/.ssh/config の設定を見ていきます。GitHub で認証に使用する SSH キーは登録済みで秘密鍵は ~/.ssh/github へ配置している想定です。

~/.ssh/config
Host github.com
IdentityFile ~/.ssh/github
User git

~/.ssh/config の各種設定項目は下記になります。

項目 説明
Host ホスト名を指定する
IdentityFile 接続時に使用する秘密鍵を指定する
User 接続時のユーザ名を指定する

設定を行うことで github.com に SSH 接続する際、ユーザ名に git が指定され、~/.ssh/github に存在する秘密鍵を用いて SSH 接続を試みるようになります。
早速 github.com 接続時に正しく認証が通っているか確認するため、適当なプライベートリポジトリを git clone してみます。

プライベートリポジトリを git clone した実行結果 (成功)。無事に git clone できることが確認できたら成功です。

接続先に応じて秘密鍵を使い分けたい

GitHub で常に同一の秘密鍵を用いて認証を行う際は問題ないのですが、例えば GitHub アカウントをプライベート用と仕事用で使い分けていて、リポジトリ先に応じて秘密鍵を使い分けたいというケースはあると思います。

その場合は ~/.ssh/config の設定と git のリモートリポジトリへの接続情報を変更することで対応可能です。
例えばプライベート用の GitHub アカウント A と仕事用の GitHub アカウント B が存在するとします。その際 github-A がホストの時は A の秘密鍵を、github-B がホストの時は B の秘密鍵を利用するための設定は下記になります。

~/.ssh/config
# プライベート用の GitHub アカウントで利用する接続設定
Host github-A
HostName github.com
IdentityFile ~/.ssh/github-A
User git
# 仕事用の GitHub アカウントで利用する接続設定
Host github-B
HostName github.com
IdentityFile ~/.ssh/github-B
User git

新たに HostName という項目を設定しました。HostName には接続先を指定します。HostName は何も指定しない場合 Host と同じ値が設定されます。
今回は同一接続先に対してアカウントを切り替えたいので、Host にはアカウント識別可能な値 (ex: github-A, github-B, etc.) を、HostName に接続先である github.com を明示的に指定してアカウント毎に設定をわけました。
次に git のリモートリポジトリへの接続情報を変更します。

~/.ssh/configUser を指定している場合は該当 Host へ接続時に自動的にユーザが設定されるため、接続 URL からユーザ名である git は取り除いても問題ありません。

# 現在のリモートリポジトリ URL
git remote -v
origin  git@github.com:nikaera/private-repository.git (fetch)
origin  git@github.com:nikaera/private-repository.git (push)
# リモートリポジトリ URL を
# git@github.com:nikaera/private-repository.git から
# github-A:nikaera/private-repository.git に変更する
# (ユーザ名の git は変更先から取り除いている)
git remote set-url origin github-A:nikaera/private-repository.git
# 新たに設定したリモートリポジトリ URL
git remote -v
origin  github-A:nikaera/private-repository.git (fetch)
origin  github-A:nikaera/private-repository.git (push)

この状態で先ほど git clone してきたプライベートリポジトリ内で git ls-remote origin コマンドを実行してみて正常に結果が取得できれば正しく設定できています。

次に B のアカウント情報で接続を試みます。

# 現在のリモートリポジトリ URL
git remote -v
origin  github-A:nikaera/private-repository.git (fetch)
origin  github-A:nikaera/private-repository.git (push)
# リモートリポジトリ URL を github-A:nikaera/private-repository.git から
# github-B:nikaera/private-repository.git に変更する
git remote set-url origin github-B:nikaera/private-repository.git
# 新たに設定したリモートリポジトリ URL
git remote -v
origin  github-B:nikaera/private-repository.git (fetch)
origin  github-B:nikaera/private-repository.git (push)

B には先ほどのプライベートリポジトリの読み取り権限が無い状態で、git ls-remote origin コマンドを実行してみると失敗するはずです。

これで今後は git のリモートリポジトリ URL を一度書き換えておくだけで、それぞれ適切な秘密鍵で GitHub 認証する設定ができました。
単に Git サーバに SSH 接続する際の設定情報としては、上記項目を抑えておけば問題無いです。しかし、他にも設定したほうが良い項目や、場合によっては設定が必要な項目も存在します。

基本的に設定しておいた方が良い項目

他に設定しておいたほうが良い項目には下記があります。

~/.ssh/config
Host github.com
IdentityFile ~/.ssh/github
User git
IdentitiesOnly yes # IdentityFile で指定した秘密鍵でのみ認証を試みる
Compression yes # Git でのファイル転送時に圧縮する
項目 説明
IdentitiesOnly IdentityFile で指定した鍵ファイルでのみ認証を行うかどうか指定する boolean (yes or no)
Compression 圧縮転送を行うかどうか指定する boolean (yes or no)

IdentityFile で指定したファイル以外で認証が通ってしまう状況だと、意図したアカウントで適切に認証が通せていない可能性が出てきてしまいます。

そのため、特別な理由がなければ IdentitiesOnly には yes を設定しておいたほうが良いです。

IdentitiesOnlyno に設定しておくと ssh-add で登録したキーの中からも認証を試みるようになります。

また、Compression についても yes に設定しておくことをオススメします。プログラミングを行っているプロジェクトでは、テキストファイルの数が大半を占めるはずなので、圧縮転送を有効にしておいたほうがファイル転送の速度向上を見込めるからです。

ただし Compression は大容量の単一のファイルがアップされているなどして、圧縮効率の悪いファイルを含んでいるプロジェクトの場合は逆に転送速度が低下する恐れもあるので要注意です。

接続先によっては設定が必要な項目

接続先に応じて設定する必要がありそうな項目には下記があります。

~/.ssh/config
Host github.com
IdentityFile ~/.ssh/github
User git
Port 12345 # 接続先のポートが 22 以外の場合指定する
項目 説明
Port SSH 接続時のポート番号を設定する number (ex: 22, 22222, etc.)

例えば自前で用意した Git リポジトリに対して接続する場合、SSH のポートがセキュリティ上の理由等でウェルノウンポートの 22 ではない場合がありえます。その場合は Port を明示的に指定することで接続時のポート番号を適切な値に設定しておく必要が出てくるでしょう。

おわりに

今回は GitHub へ SSH 接続する際の例を元に ~/.ssh/config の設定方法について書きました。~/.ssh/configHost の設定は行っているものの、それに応じて git のリモートリポジトリ URL を変更すべき場合もあることを明示している記事が少なかったので書きました。
また、本記事では Git 接続時の ~/.ssh/config の設定にフォーカスを当てましたが、~/.ssh/config についてはサーバに SSH 接続して作業する際に設定しておくと良い項目等もあります。
~/.ssh/config の各種設定項目について理解していると頻繁に実行するコマンドのコスト削減ができて便利です。特に @oohira さんの こちらの記事 はとても参考になりました。

参考リンク

fish で湯婆婆を実装してみる

2020年11月

はじめに

深夜過ぎても眠れず今 5:30 くらいです。おはようございます。
眠れないので Twitter してたら Qiita で 湯婆婆を実装してみる のが流行っていることを知ったので fish で実装してみることにしました :fish:

ちなみにこの流行を巻き起こした第一人者である @Nemesis さんの 元ネタ記事はこちら です :ocean::cyclone:

コード

yubaba.fish
echo 契約書だよ。そこに名前を書きな。
# `-P` がプロンプトで入力受付を行うためのオプションで、
# `-l` が入力された内容を `current_name` 変数にセットするためのオプション
read -l -P '' current_name
echo フン。$current_nameというのかい。贅沢な名だねぇ。
# `current_name_length` の値が 1 以下だったらという条件文
set current_name_length (string length (echo $current_name))
if test $current_name_length -le 1
    set index 1
else
    # 1 から `current_name_length` までの値をランダムに取得して index 変数にセット
    # FYI: random コマンドで指定した START より小さい文字数の文字だと
    # `random: END must be greater than START` という警告が出てきてしまうので
    # 条件文で 2文字以上の場合のみランダムで文字列を取得するようにしている
    set index (random 1 (echo $current_name_length))
end
# `string split '' <分割したい文字列>` で文字列を 1文字ずつ分割して List として characters にセット
set characters (string split '' (echo $current_name))
# 文字列の中から適当な 1文字をピックアップ
set new_name $characters[$index]
echo 今からお前の名前は$new_nameだ。いいかい、$new_nameだよ。分かったら返事をするんだ、$new_name!!

fish のコードは :arrow_up: のようになります。
bash で慣れていると冗長な気がしますが、これには fish 特有の仕様が絡んでいます。

fish では string length $current_name のような形でコマンド置換ができません:x:
コマンド実行結果を set 等で変数に代入しておいた上で string length (echo $current_name) のような形で実行することは可能です:o:

コマンド実行時にわざわざ random 1 (echo $current_name_length) のような形で echo を挟んで値をセットしているのは上記が理由です。

他の部分の解説は大体ソースコード内にコメントを残してあるので省略します :bomb: :boom:

実行結果

スクリーンショット 2020-11-10 6.14.16.png
いきなり "馬" にされてしまった範馬勇次郎 :horse:

おまけ

テストで用いていたスクリプトは下記になります :pencil:

yubaba-test.fish
# yubaba.fish と同じディレクトリに配置して `fish yubaba-test.fish` を実行
set names テスト jhondoe 漢字 𠮷 🔨 \n
for val in $names
    echo $val | fish ./yubaba.fish
end

また、空白の例外処理については入れていないため、空白を入力したときの実行結果は下記になります :white_large_square: :arrow_double_down:

スクリーンショット 2020-11-10 6.23.31.png
虚無

おわりに

眠れなかったおかげで fish の書き方の勉強が出来てよかったです :raising_hand:

ただ眠くなるまでの暇つぶしのつもりで fish スクリプト & 記事を書いてたつもりが、ずっと PC ガン見してたせいで余計に目が冴えてきました :eye: :upside_down:
フルリモート環境とはいえ昼過ぎから MTG があるので一旦寝ときたいと思います (フラグ :bed: :zzz:

あと Qiita で fish のシンタックスハイライトが使えるようになりたい :sparkler:

参考リンク

Serverless のプラグインを TypeScript で作成する方法

2020年11月

はじめに

Serverless Framework を使っていて、度々デプロイ時に手動で設定していた作業内容を自動化したいなと思い、プラグイン作成の知識習得も兼ねてライブラリを作成し NPM で公開してみました。

serverless-amplify-auth 🔑

今後も開発する可能性はありそうなので Serverless のプラグインを TypeScript で作成する際の手順をまとめておきました。各手順はザックリと紹介しつつ、主にその過程でハマった点や工夫した点に重きをおいて記事を書いていきます。

動作環境

  • Node.js 12.19.0
  • Serverless Framework
    • Framework Core: 2.10.0
    • Plugin: 4.1.1
    • SDK: 2.3.2
    • Components: 3.3.0

TypeScript で Serverless Plugin を開発する環境を整える

本記事の内容を最後まで実践した際の最終的なプロジェクトのディレクトリ構造は下記になります。

tree -I node_modules -L 2 ./
./
├── example # ライブラリの動作検証用のサンプルコードを配置するフォルダ
│   ├── handler.js
│   ├── package.json
│   └── serverless.yml
├── lib     # src フォルダ内のファイルをコンパイルした結果を配置するフォルダ (ライブラリとして利用する際に含まれるソースコード群)
│   ├── index.js
│   └── index.js.map
├── package-lock.json
├── package.json
├── src     # Serverless プラグインのソースコードを配置するフォルダ
│   └── index.ts
└── tsconfig.json

基本的には TypeScriptでServerless FrameworkのPluginを書いてみる | Developers.IO の手順をなぞっていくだけで環境構築自体は可能です。そこで、ここでは自分なりに工夫した箇所について記載していきます。

まずは、開発に必要なパッケージを下記コマンドでまとめてインストールします。

# TypeScript の開発に必要なパッケージインストール
npm i -D typescript
# TypeScript の型定義ファイルのインストール
npm i -D @types/node @types/serverless
# 今回は AWS プロバイダー向けの開発を行うため SDK をインストールする
npm i --save aws-sdk

TypeScript のコンパイル時に必要となる tsconfig.json は下記のように設定しました。

tsconfig.json
{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "moduleResolution": "node",
    "strict": true,
    "strictBindCallApply": false,
    "strictNullChecks": false,
    "outDir": "lib",
    "sourceMap": true
  },
  "include": [
    "src/**/*"
  ]
}

compilerOptions.strict には true を設定しつつ、compilerOptions.strictNullChecks 等には false を設定することで、部分的に TypeScript のコンパイルチェックを外すようにしました。

outDir には lib を指定することで、コンパイルされた TypeScript ファイルは lib フォルダに出力されるよう設定しました。

include には src/**/* を明示的に指定しており、src フォルダ内の全ファイルをコンパイル対象にしております。


package.json の内容は部分的に抜粋し、説明が必要そうな項目について説明いたします。
全容を把握したい方は こちら からご確認いただけます。

package.json
{
  "main": "lib/index.js",
  "files": [
    "lib"
  ],
  "scripts": {
    "build": "rm -rf lib && tsc",
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}

main には src/index.ts をコンパイルすると生成される lib/index.js を指定しました。そのため、ライブラリのエントリーポイントは lib/index.js が設定されます。

files には lib フォルダを指定することで、TypeScript をコンパイルした結果のみがライブラリのソースコードとして取り込まれるようになります。

TypeScript で Serverless Plugin の開発を進める

開発環境が整ったところで早速 Serverless Plugin のソースコードを書いていきます。TypeScript のソースコードは src/index.ts に配置します。

Serverless Plugin のプログラムを書く

src/index.ts
import * as Serverless from 'serverless'
import {
  SharedIniFileCredentials,
  config,
} from 'aws-sdk'
/**
 * serverless.yml の custom property の型定義
 */
interface Variables {
  value1: string
  value2: number
  value3: boolean
  profile?: string
}
export default class Plugin {
  serverless: Serverless
  options: Serverless.Options
  hooks: {
    [event: string]: () => Promise<void>
  }
  variables: Variables
  /**
   * プラグインの初期化関数。
   * 注意点として、初期化関数内では serverless.yml 内の変数展開が行われないので、
   * ${ssm:~} 等で設定した値を呼び出しても、適切に値が設定されない状態で呼び出すことになる。
   */
  constructor(serverless: Serverless, options: Serverless.Options) {
    this.serverless = serverless
    this.options = options
    /**
     * serverless.service.custom 内の特定プロパティを取得するための記述
     * 今回は Serverless のプラグイン名に serverless-typescript を設定したため、
     * serverless-typescript 文字列をキーとして指定する。
     */
    this.variables = serverless.service.custom['serverless-typescript']
    /**
     * プラグインがフックする関数を指定する。複数指定することも可能だが、
     * 今回は before:package:createDeploymentArtifacts を指定して、
     * パッケージングの手前の処理を定義した run 関数でフックする。
     */
    this.hooks = {
      'before:package:createDeploymentArtifacts': this.run.bind(this),
    }
  }
  /**
  * before:package:createDeploymentArtifacts 時に実行される関数
  */
  async run() {
    /**
    * プラグイン実行時に必要となるフィールドがセットされていなければ処理をスキップする
    */
    if (!this.variables) {
      this.serverless.cli.log(
        `serverless-typescript: Set the custom.serverless-typescript field to an appropriate value.`,
      )
      return
    }
    /**
     * this.serverless.getProvider 関数を用いることで、
     * デプロイ時のアカウントの各種情報について取得することが出来る
     */
    const awsProvider = this.serverless.getProvider('aws')
    const region = await awsProvider.getRegion()
    const accountId = await awsProvider.getAccountId()
    const stage = await awsProvider.getStage()
    /**
     * serverless.yml で指定した値や AWS 情報が取得できているか、
     * 確認するために標準出力する
     */
    this.serverless.cli.log(
      `serverless-typescript values: ${JSON.stringify({
        stage: stage,
        region: region,
        accountId: accountId,
        variables: this.variables,
      })}`,
    )
    /**
     * プラグイン内で処理を実行する際、別の特定 Profile を用いたい際は、
     * AWS SDK の SharedIniFileCredentials を用いて切り替えると楽に切替可能。
     * その際は process.env.AWS_SDK_LOAD_CONFIG に値を設定しておくこと
     */
    if (this.variables.profile) {
      process.env.AWS_SDK_LOAD_CONFIG = 'true'
      const credentials = new SharedIniFileCredentials({
        profile: this.variables.profile,
      })
      config.credentials = credentials
    }
  }
}
module.exports = Plugin

ソースコード内にいくつかコメントを残しましたが、何点か補足の説明をしていきます。

serverless.service.custom['serverless-typescript'] を呼び出すことで、serverless.yml 内の下記の記述内容を Object として取得できます。

serverless.yml
custom:
    # custom.serverless-typescript 内の定義を Object として取得可能
    serverless-typescript:
        value1: "value1"
        value2: 0
        value3: true
        # profile: default (optional)

this.hooks には必要に応じてフックを指定します。フックの書き方については 公式ドキュメント に詳細が記載されています。フックの種類については Gist でまとめてくださっている方がいました。

this.serverless.getProvider('aws') を用いることで、デプロイ時にアカウントの各種情報について取得することが出来ます。この記述を利用することで Serverless Pseudo Parameters のようなシンタックスを自身のプラグインに取り込むことが可能になります。
私が作成したプラグインでも serverless.ymlARN を構築する際に利用していて、index.ts 内で利用しました。

また、プラグイン内でデプロイ時とは異なる Profile を使用したいケースもあるかと存じます。それは AWS SDK の SharedIniFileCredentials を用いることで簡易に実装できました。1

それでは、次にプラグインの動作検証用コードを example フォルダに配置していきます。

Serverless Plugin の動作検証用プログラムを書く

example フォルダ内には検証用プロジェクトを作成するので、その前準備として example/package.json を作成します。

# package.json ファイルを作成する
cd example && npm init -y

example/package.json ファイルを作成したら開発用のスクリプトを example/package.json に追記します。

example/package.json
{
  "scripts": {
    "prestart": "cd ../ && npm run build",
    "start": "sls package",
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}

scripts 内の prestartstart スクリプト実行前に実行されるスクリプトです。npm start を実行すると prestart でプラグインの build タスクを実行した後、 Serverless Framework のパッケージングを行うことでプラグインの動作確認が行えます。2

次に動作検証用の serverless.ymlexample フォルダに配置します。

serverless.yml
service:
    name: serverless-typescript
    publish: false
# プラグイン内で利用する設定値を定義する
custom:
    serverless-typescript:
        value1: "value1"
        value2: 0
        value3: true
        profile: custom_profile
provider:
    name: aws
    runtime: nodejs12.x
    region: ap-northeast-1
# プラグインのパスを指定して読み込む
plugins:
    localPath: '../../'
    modules:
        - serverless-typescript
# 何でも良いので動作検証用の関数を定義する (関数の定義は後述)
functions:
    hello:
        handler: handler.hello

example フォルダ内に handler.js を配置して functions.hello.handler で用いる検証用の関数を定義します。

example/handler.js
'use strict';
// 検証用の関数。serverless.yml 内では handler.hello で参照可能
module.exports.hello = (event, context, callback) => {
  callback(null, {
    statusCode: 200,
    body: "Hello World!"
  });
};

上記作業が完了次第、cd example && npm start を実行して動作検証してみます。

cd example && npm start
> example@1.0.0 prestart /Users/nika/Desktop/serverless-typescript/example
> cd ../ && npm run build
> serverless-typescript@1.0.0 build /Users/nika/Desktop/serverless-typescript
> rm -rf lib && tsc
> example@1.0.0 start /Users/nika/Desktop/serverless-typescript/example
> sls package
Serverless: Configuration warning at 'service': unrecognized property 'publish'
Serverless:
Serverless: Learn more about configuration validation here: http://slss.io/configuration-validation
Serverless:
# src/index.ts 内の this.serverless.cli.log の出力内容
# 各種値が正常にセットされていることが確認出来る
Serverless: serverless-typescript values: {"stage":"dev","region":"ap-northeast-1","accountId":"XXXXXXXXXX","variables":{"value1":"value1","value2":0,"value3":true,"profile":"custom_profile"}}
Serverless: Packaging service...
Serverless: Excluding development dependencies...

標準出力にあるプラグイン内で出力したログから、適切に値が取得出来ていることが確認出来れば OK です。

Serverless プラグインの中で AWS Profile の切り替えが行えるか確認してみる

Serverless プラグインでの Profile の切り替えについて、動作検証がまだ出来ていないので確認していきます。

serverless.yml 内の custom.serverless-typescript.profile に設定箇所は既に用意してあるので、~/.aws/credentials に実在する Profile 名を指定します。

serverless.yml
custom:
    serverless-typescript:
        profile: <プラグイン実行時に使用したい Profile 名>

動作検証のため、src/index.ts 内にログ出力の記述を加えます。

src/index.ts
import * as Serverless from 'serverless'
import {
  SharedIniFileCredentials,
  config,
} from 'aws-sdk'
interface Variables {
  value1: string
  value2: number
  value3: boolean
  profile?: string
}
export default class Plugin {
  serverless: Serverless
  options: Serverless.Options
  hooks: {
    [event: string]: () => Promise<void>
  }
  variables: Variables
  constructor(serverless: Serverless, options: Serverless.Options) {
    this.serverless = serverless
    this.options = options
    this.variables = serverless.service.custom['serverless-typescript']
    this.hooks = {
      'before:package:createDeploymentArtifacts': this.run.bind(this),
    }
  }
  async run() {
    if (!this.variables) {
      this.serverless.cli.log(
        `serverless-typescript: Set the custom.serverless-typescript field to an appropriate value.`,
      )
      return
    }
    const awsProvider = this.serverless.getProvider('aws')
    const region = await awsProvider.getRegion()
    const accountId = await awsProvider.getAccountId()
    const stage = await awsProvider.getStage()
    this.serverless.cli.log(
      `serverless-typescript values: ${JSON.stringify({
        stage: stage,
        region: region,
        accountId: accountId,
        variables: this.variables,
      })}`,
    )
    if (this.variables.profile) {
      process.env.AWS_SDK_LOAD_CONFIG = 'true'
      const credentials = new SharedIniFileCredentials({
        profile: this.variables.profile,
      })
      config.credentials = credentials
      // Profile が切り替えられたか確認するためにログを出力する
      this.serverless.cli.log(`serverless-typescript profile: ${JSON.stringify(config.credentials)}`);
    }
  }
}
module.exports = Plugin

早速 cd example && npm start を実行して正常に profile が切り替えられていそうか確認してみます。

# 成功時の実行結果
cd example && npm start
# ...
# accessKeyId のフィールドに ~/.aws/credentials 内に存在する値が出力されている
Serverless: serverless-typescript profile: {"expired":false,"expireTime":null,"refreshCallbacks":[],"accessKeyId":"XXXXXXXXXXXXXX","profile":"XXXXXXXXXXXXXX","disableAssumeRole":false,"preferStaticCredentials":false,"tokenCodeFn":null,"httpOptions":null}
# ...

ちなみに存在しない Profile を指定した場合の出力は下記のようになります。

# 失敗時の実行結果
cd example && npm start
# ...
# accessKeyId のフィールドが存在しない時は Profile が正しく設定出来ていない
Serverless: serverless-typescript profile: {"expired":false,"expireTime":null,"refreshCallbacks":[],"profile":"custom_profile","disableAssumeRole":false,"preferStaticCredentials":false,"tokenCodeFn":null,"httpOptions":null}
# ...

おわりに

今回初めて Serverless プラグインの開発をしてみて、手軽に出来ることが分かったので自動化出来そうな作業は積極的にプラグイン化していきたいなと感じました。

プラグイン化した後は Git リポジトリにアップするだけでなく、NPM のパッケージGitHub Packages として公開しておくと、後々プラグインを利用する際に便利です。また、公開してライブラリのスタッツを見るのは案外楽しく開発のモチベーションにも繋がるのでオススメです。

参考リンク


  1. 注意点として、SharedIniFileCredentials を用いてプロファイルを切り替える時は、環境変数に AWS_SDK_LOAD_CONFIG="true" を設定する必要がありました。設定しないと ConfigError: Missing region in config というエラーが発生してしまい、プロファイルを切り替えることが出来ませんでした。。 

  2. 今回は Serverless の before:package:createDeploymentArtifacts フックを利用しているので、sls package コマンドで動作検証が可能となっている。before:deploy:deploy 等のデプロイ中に実行されるフックを利用する際は sls deploy --noDeploy コマンド等で動作検証を行う必要がある。