selmertsxの素振り日記

ひたすら日々の素振り内容を書き続けるだけの日記

gcloudコマンドを実行するGCPのアカウントを切り替える

忘れないようにメモ。 例えば、before-selmertsx プロジェクトから、test-selmertsx プロジェクトへと切り替えるとする。 そのときの手順は下記の通り

configurationsの作成

gcloud config configurations create test-selmertsx
gcloud config configurations activate test-selmertsx

プロジェクトの設定をする

下記コマンドでリージョンやアカウントの設定をします。

gcloud config set compute/region asia-northeast1
gcloud config set compute/zone asia-northeast1-a
gcloud config set core/account selmertsx@gmail.com
gcloud config set core/project test-selmertsx

そして gcloud config list で設定確認。

$ gcloud config list
[compute]
region = asia-northeast1
zone = asia-northeast1-a
[core]
account = selmertsx@gmail.com
disable_usage_reporting = True
project = test-selmertsx

Your active configuration is: [test-selmertsx]

プロジェクトを有効にする

gcloud auth login を実行するとブラウザが立ち上がって認証する。それで終了。

$ gcloud config configurations list
NAME             IS_ACTIVE  ACCOUNT                  PROJECT          DEFAULT_ZONE       DEFAULT_REGION
before-selmertsx          True       selmertsx@gmail.com  before-selmertsx           asia-northeast1-a
test-selmertsx  False      selmertsx@gmail.com  test-selmertsx  asia-northeast1-a  asia-northeast1

元のプロジェクトに戻す

$ gcloud config configurations activate before-project
Activated [before-project].

Cloud FunctionsとCloud Schedulerを利用してDatadogで監視しているホスト数を通知させてみた

作ったもの

指定された期間のDatadogの監視台数について、最小,最大,合計(ホスト数×時間)を算出してSlackに通知してくれるCloudFunctionsを作成しました。そのCloudFunctionsはCloud Pub/Subで実行され、Cloud SchedulerはそのCloud Pub/SubのTopicを一日一度実行してくれます。

スクリーンショット 2018-11-11 19.37.31.png

コードはこちらです。 https://github.com/selmertsx/datadog_slack_report

この資料に記載されていること

  • Cloud SchedulerからCloud Pub/Subを実行する方法
  • DatadogでのRollup functionの使い方
  • ※ Cloud Functionsに関する詳細な説明はしません。

CloudFunctionsを実行するための方法については、こちらの記事を参照してください。

https://qiita.com/selmertsx/items/31b10bfc4b72b05627e1 https://qiita.com/selmertsx/items/27686e51b4471eaf8c86

作った理由

  • 今いる企業では複数のプロダクトでDatadogを利用してリソースの監視を行っていません
  • Datadogでの監視はアカウントを分けていません
    • サーバーメトリクスは企業の資産であり、すべてのエンジニアが隣のプロダクトのサーバーメトリクスや監視方法を見て学ぶことができるように、敢えてアカウントは分けないようにしています
    • ちょっと声を掛けて見せて貰えばいいでないという声はあるかと思いますが、気になったときに誰の手も煩わせずに確認できるようにしたいという意図でそうしています
  • アカウントを分けないで運用をしていると、プロダクト毎のコストを集計する必要がある
  • コストの集計は自動でやってしまいたい
  • 最近でた Cloud Schedulerを使ってみたい

事前準備

タグ付けを行ったら次の準備に進んでください。

DatadogでのQueryの実行

DatadogではAPIを実行して、メトリクスを取得することができます。さらに、それらメトリクスに対して様々な計算処理を入れることも可能です。その実行方法については、こちらの資料に記載されています。

さて、シンプルに各プロダクト毎のホスト数をAPIで取得したい場合、count:system.cpu.user{*} by {product}のようなクエリになります。ECインスタンスのメトリクスは5分毎にagentから送信されているため、1日分のデータを取得してしまうと12 * 24 * プロダクト数分のデータが必要になってしまいます。Datadogの課金体系から考えても、1時間未満の粒度でのデータは取得する必要がありません。そのため、不要なデータを削ぎ落とす必要があります。今回はRollup関数を使って、データを1時間単位で丸め込んでから取得しました。これによって24*プロダクト数分のデータにすることができました。Rollup関数の説明はこちらになります。抜粋した文書が下記になります。

Rollup The function takes two parameters, method and time: .rollup(method,time) The method can be sum/min/max/count/avg and time is in seconds. You can use either one individually, or both together like .rollup(sum,120).

この説明通りにクエリを作成してみると、count:system.cpu.user{*} by {product}.rollup(count, 3600) という形になりました。TypeScriptのコードは下記のようになります。

// https://github.com/selmertsx/datadog_slack_report/blob/master/src/DatadogClient.ts#L28
  public async countHosts(from: string, to: string): Promise<DatadogHostMetrics[]> {
    const params: CountHostRequest = {
      api_key: API_KEY,
      application_key: APP_KEY,
      from,
      query: `count:system.cpu.user{*} by {product}.rollup(count, 3600)`,
      to,
    };

    const res: DatadogQueryReponse = await this.request.get("/query", { params });
    return res.data.series.map((product: SeriesMetrics) => {
      const pointlists: PointList[] = product.pointlist.map((point: number[]) => {
        return { unixTime: point[0], count: point[1] };
      });
      return { product: product.scope, pointlists }
    });
  }

僕のコードの中にDatadogのAPIで帰ってくるレスポンスの型(の一部)を記載しておきましたので、データの中身について気になる方は目を通されると良いでしょう。

Cloud Functionsのデプロイ

Google Cloud Pub/SubをトリガーにCloud Functionsを起動する設定をしていきます。Google Consoleからdatadog_reportというtopicを作成し、下記のコマンドでCloud Functionsをデプロイしましょう。

gcloud beta functions deploy datadog_handler \
  --region=asia-northeast1 \
  --stage-bucket=datadog_report \
  --trigger-event=google.pubsub.topic.publish \
  --trigger-resource=datadog_report \
  --runtime=nodejs8

Cloud FunctionsのトリガーとしてCloud Pub/Subを利用する方法については、このドキュメントに詳細が記載されています。

Cloud Schedulerの設定

GCPのConsoleから、cloud schedulerを開いてみましょう。そこでcreate jobを選び、下記のように設定をします。ここでFrequencyが実行頻度を設定する部分です。cronと同じフォーマットで実行する頻度を設定することができます。そしてTargetがトリガーの設定部分です。ここではApp Engineのアプリケーション、Cloud Pub/Sub, HTTP Requestの3つを設定することができます。今回のCloudFunctionsはPub/Subをトリガーにしているので、ここではPub/Subを指定しておきましょう。

スクリーンショット 2018-11-11 22.53.10.png

create ボタンを押せば終了です。気になるお値段ですが、こちらのドキュメントによると1人あたり3ジョブまで無料ということです。

動作確認

スクリーンショット 2018-11-11 22.59.31.png

すぐに動作確認をしたいので、上記画面のRun now ボタンをクリックし、Pub/SubのTopicに対してメッセージを送ってみましょう。するとSlackに下記のようにメッセージが送信されます。

スクリーンショット 2018-11-11 19.37.31.png

以上で、Cloud FunctionsとCloud Schedulerを利用してDatadogで監視しているホスト数を通知させることができました。今後は、集計期間をpub/subのメッセージで設定できるようにするなど機能を追加していく予定です。もし、使ってくださる方がいて、追加で欲しい機能などございましたら、お気軽にIssue登録おねがいします〜

Azure ADとG Suiteのグループを同期させる

モチベーション

  • メーリスの設定を自動でして欲しい
  • Google Drive内の資料の閲覧権限を自動かつ適切に付与したい
  • 誰がどのようなグループに所属し、どのような権限を持つかはAzure ADで制御したい

やり方の方針

  • Azure ADとG SuiteのGroupを同期させる
  • Azure ADをマスターとし、G Suite内でグループの設定変更はしない
  • Azure ADの動的グループメンバーシップという便利機能を使って、グループに所属するメンバーを自動で追加・削除する

本資料で記載すること

  • AzureAD と G Suiteのグループを同期させるところまで
  • 動的グループメンバーシップについては次回

前提条件

  • Azure ADのEnterprise Application設定権限を持っている
  • G Suiteの特権管理者権限を持っている

やり方

https://docs.microsoft.com/ja-jp/azure/active-directory/saas-apps/google-apps-provisioning-tutorial#enable-automated-user-provisioning

基本的にこちらのドキュメントどおりに設定します。ここでは、上記ドキュメントの補足のみを行います。

Configure automatic user account provisioning

基本的にドキュメントの通り設定すれば良いです。

手順7: Admin API Privileges

手順に記載されている画像を見ると全てのチェックボックスがチェックされているように見えますが、僕が見た画面は何もチェックされていませんでした。

f:id:selmertsx:20181017153836p:plain

不安はあったものの、User画面から自分の権限を確認したら、ちゃんと全ての権限があったのでとりあえず先に進みました。それで問題なかったです。

f:id:selmertsx:20181017153855p:plain

手順10: Provisioningの設定

もし、G Suiteと同期するAzure ADのグループを絞る必要があるのであれば、 Provisioning の設定をする前に同期対象のグループを指定する必要があります。Users and groups タブからこんな形でユーザー・グループを設定しました。

f:id:selmertsx:20181017153817p:plain

手順13: Azure ADにG Suiteの権限委譲

Azure ADからの許可を求められる画面イメージが、サンプル画像と違ったので一応載せておきます。

f:id:selmertsx:20181017153532p:plain

手順17: Mappings

今回はGroupの同期がしたかったのでMappingsの設定を Synchronize Azure Active Directory Groups to GoogleApps にしました。また、SettingsのScopeについては、任意のグループのみを同期したいのであれば Sync only assigned users and groups に設定する必要があります。

f:id:selmertsx:20181017153638p:plain

結果

  • グループが問題なく作られている
  • グループ内にメンバーも所属している

f:id:selmertsx:20181017153758p:plain

疑問点

  • グループのオーナーを指定する場所が無かった
  • グループのメールアドレスをどのように指定するのか

G Suite Domain-Wide Delegationを使ってG SuiteのGroupを作成する

TL;DR

  • googleapisを使って、G Suiteの Groupを作成できるようにした
  • 公式のドキュメントにはnodejsでの実装方法が無かったので自分で実装した
    • よってこれが最適な方法かは分からない
  • G Suiteの特権管理者の権限が付与されている必要がある

https://developers.google.com/admin-sdk/directory/v1/guides/delegation 大体このドキュメントに沿って作った

手順

  • GCP上にサービスアカウントを作成する
  • G SuiteのAdmin consoleにて、GCPのサービスアカウントに G Suite APIを叩くための権限を付与する
  • Groupを作成するコードを実装する

GCP上にサービスアカウントを作成する

  • GCP consoleにてservice accountのページを開き、新しく service accountを作成する
  • Enable Google Apps Domain-wide Delegation にチェックを入れる
  • 鍵を作ってDLする
  • 鍵のjsonを開き、client_idをメモしておく

スクリーンショット 2018-10-16 16.41.37.png

GCPのサービスアカウントにG Suiteの権限を付与する

  • G Suiteのadmin consoleを開く
  • Advanced settingsを選ぶ
  • AuthenticationセクションのManage API client accessを選ぶ
  • Client Nameの部分にサービスアカウント作成時にメモしておいた client idを設定する
  • scopesの部分にhttps://www.googleapis.com/auth/admin.directory.group を設定する
  • authorize を押す

スクリーンショット 2018-10-16 20.44.57.png

Groupを作成するコード

import { google } from "googleapis";
import path from "path";

async function main() {
  const jwtClient = new google.auth.JWT({
    keyFile: path.join(__dirname, "credentials.json"),
    scopes: ['https://www.googleapis.com/auth/admin.directory.group.readonly'],
    subject: process.env.SUBJECT
  });

  await jwtClient.authorize()

  const admin = google.admin({
    version: 'directory_v1',
    auth: jwtClient
  });

  const response = await admin.groups.list({ domain: process.env.DOMAIN });
  console.log(response.data);
}

main();

結果

$ npx ts-node create_group.ts
{ kind: 'admin#directory#group',
  id: 'xxx',
  etag: '"xxx"',
  email: 'sample-group@xxx.jp',
  name: 'sample-group',
  description: 'hogehoge',
  adminCreated: true }

こんな感じでグループができました

TypeScriptで書いたCloud FunctionsをCloudBuildを使ってDeployする

これを読んで出来るようになること

TypeScriptで書いたCloud Functionを、Code Buildを使ってdeployできるようになる。

前提と事前準備

前提

  • CloudFunctionsのコードはwebpackを使ってまとめている
  • Deployは serverless コマンドではなく、gcloudコマンドで行っている

事前準備

過去の記事

selmertsx.hatenablog.com

設定

ディレクトリ構造

cloud_source_repository_test git:(master) tree -I node_modules
├── README.md
├── cloudbuild.yaml
├── package-lock.json
├── package.json
├── src
│   └── index.ts
├── tsconfig.json
└── webpack.config.js

TypeScriptのサンプルコード

import { Decimal } from "decimal.js";

function subscribe(req: any, res: any){
  const x = new Decimal('0xff.f')
  return res.send(`Hello World! ${x}`);
}

export { subscribe }

cloudbuild.yamlの設定

steps:
  - name: node:8
    entrypoint: npm
    args:
    - install

  - name: node:8
    entrypoint: npm
    args:
    - run
    - build

  - name: 'gcr.io/cloud-builders/gcloud'
    args:
    - functions
    - deploy
    - subscribe
    - --stage-bucket=xxx
    - --trigger-http

buildコマンドの中では webpack --mode productionが実行されています。

tsconfig.jsonの設定

{
  "compilerOptions": {
    "target": "es2017",
    "module": "commonjs",
    "strict": true,
    "preserveConstEnums": true,
    "strictNullChecks": true,
    "sourceMap": true,
    "outDir": "./",
    "moduleResolution": "node",
    "esModuleInterop": true
  },
  "include": [
    "src/**/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}

Webpackの設定

module.exports = {
  entry: "./src/index.ts",
  target: 'node',
  output: {
    path: __dirname,
    filename: 'index.js',
    libraryTarget: 'this'
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader'
      }
    ]
  },
  resolve: {
    extensions: [ '.js', '.json' ]
  }
}

IAM Roleの設定

f:id:selmertsx:20181009192122p:plain

動作確認

$ curl https://xxx.cloudfunctions.net/subscribe
Hello World! 255.9375%

備考

webpackを利用する理由

  • node_modulesも、Cloud Functionにuploadすればwebpackを利用する必要は無い
  • CodeBuild内でBuildとdeployをするため、Development環境用のpackageもinstallは必須
  • Development環境用のpackageもgcloud コマンドでdeployするとファイルサイズが大きくなる

残タスク

GCPのCloud BuildでCloudFunctionsをデプロイする

モチベーション

GitHub上で管理しているCloud Functionsのコードについて、masterにマージされたタイミングでdeployして欲しい。

Cloud Buildとは何か?

Docker Image作ったり Cloud Functionsをdeployしたり、テストしたりできます。CircleCIみたいなものをイメージして頂けるとよいかなと思います。各タスクに必要なDocker Imageについて、GCPが提供している公式のイメージが使え、GCP内のリソースにもアクセスできるので、GCP固有の機能を使う際にはCloud Buildを用いるのが便利なのではないでしょうか。GCPを利用しているサービスでCloud Buildを利用すると、Credentialsな情報をGCP内のみで扱うことができるというメリットもあります。

今回やることの一覧

  • Cloud Source Repositoriesを作成
  • Cloud Source RepositoriesとGitHubを連携
  • Cloud Buildの Triggerを設定
  • Cloud Buildの build config fileを設定

事前準備

今回デプロイしたいコードを用意したGitHubリポジトリを作成しておきます。今回はサンプルとして下記のリポジトリを用意しました。

https://github.com/selmertsx/cloud_source_repository_test

Cloud Source Repositoriesの設定

Cloud Source Repositoriesに関して設定しなければならない項目は下記2点です。全部で10分程度で終わります。

  • Cloud Source Repositoriesを作成
  • Cloud Source RepositoriesとGitHubを連携

この設定ですが、基本的に公式で提供されているこの手順通りにやれば大丈夫です。すると、このように連携可能なGitHubリポジトリの一覧が出てくるので、ここから目的のものを選びましょう。今回はcloud_source_repository_testを選択します。

f:id:selmertsx:20181005170848p:plain

Cloud Build Triggerの設定

  • Cloud Buildのトリガー設定ページにアクセス
  • Add Triggerを選択
  • Cloud Source Repositories のリポジトリを選択
  • 先程設定したCloud Source Repositoriesを選択
  • Triggerの設定をする
    • 今回はmaster branchの変更時にcloud buildを実行したいので、下記のように設定した

f:id:selmertsx:20181005170953p:plain

cloud functionsの実装

最初にコードをuploadする際のbucketを用意します。

$ gsutil mb gs://selmertsx-sample-bucket
Creating gs://selmertsx-sample-bucket/...
exports.subscribe = (event, callback) => {
  return callback(null, "Success");
}

今回はシンプルにこれだけです!

build config fileの設定

build config fileの設定については、下記資料を参考にして行いました。

steps:
  - name: 'gcr.io/cloud-builders/gcloud'
    args:
    - beta
    - functions
    - deploy
    - subscribe
    - --stage-bucket=selmertsx-sample-bucket
    - --trigger-topic=cloud-builds

上記ファイルをloud_source_repository_testのルートディレクトリに置いて、master branchにpushします。すると下記のようにcloud buildが実行されます。

f:id:selmertsx:20181005170801p:plain

今回のjavascriptコードはcloud buildの実行をtriggerにしているので、deployされたコードはそのまま実行されちゃいます。その結果がこちらです。

f:id:selmertsx:20181005170712p:plain

というわけで、Cloud BuildからCloud Functionsがデプロイできるところまで確認できました。

【適宜更新】IDaaS諸々

来年の技術書典でIDaaS周りの話を書きたいなと思ったので、適宜考えたことをメモってく。とりあえずガガっと書きなぐって、あとで修正してく。

背景

企業がセキュリティを強化するにあたって、エンジニアができることは何か。

日本セキュリティ協会による2017年の報告によると、アプリケーションのバグによる情報流出は全体の5%程度でしかありません。情報流出の原因の大半は、誤った操作やID&Passwordを盗まれての不正アクセス、機密情報を印刷した紙媒体やPCの紛失にあります。よって、企業のセキュリティを高める場合、「IAM管理」「端末管理」の2点が最初に着手するべきポイントになります。( 紙媒体を保持しないことも重要ですが、本書ではこの話は扱わない )

IAM (Identity and Access Management) 管理とは、従業員に対して企業側が提供する情報(email address、所属など) と、従業員が閲覧・更新可能な企業リソースを紐づけて管理することを指します。

端末管理とは、その名前の通り企業が保有している通信端末の管理を指します。これはIAM管理と紐付けられていることもあり、どこのだれがどのような端末を保有していて、社内のどのリソースにアクセスできるのか。などを管理します。

端末管理において実行可能な権限と制約はセットで考えなければなりません。例えば、社員個人が保有している端末では会社のSlackにアクセス可能だけれども、社内のファイルサーバーにはアクセスできない。会社から提供された端末は紛失時に出荷状態に戻されてしまうが、社員保有の端末はSlack等企業情報を保有するアプリケーションのアンインストールと、ブラウザのキャッシュ・履歴を削除される。などが考えられます。また、端末のアクセスログも記録し、不正なアクセスを受けている端末を発見した際に、速やかに社内のネットワークから切り離すことも行われます。

このように「IAM」と「端末」の2つを確実に管理することが、企業のセキュリティの向上につながります。

IAMの管理に関して、2010年頃からOktaやOneLoginなどIAM管理をクラウド上で行うことができるサービスが現れました。これらのクラウドは IDaaS (Identity as a Service ) と呼ばれ、ID情報の管理だけでなくSSO(Signle Sign On) などもサービスとして提供しています。

またIDaaSは端末管理と紐付くことも多く、oktaなどはjamfがインストールされていない端末では特定のサービスにアクセスできないなどの機能も提供しています。

IDaaSの導入にあたっては、企業が持つ人事マスターとの連携が重要です。大抵のケースにおいて、IAM管理は部署や役職に紐付いて行われるからです。また、その人事マスターとIDaaSは、何らかの仕組みで自動で同期されるようにする必要があります。人事情報の登録を2重に行うのはコストが掛かるし、手で設定することにはミスが発生するリスクがあるためです。

TODO