Terraformでstateファイルの格納先を分離したいときのTips


はじめに

Terraformを使用してAWSリソースを構築する際に、コンプライアンス上の理由により、実際にAWSリソースを作成する対象となるAWSアカウントと、stateファイルを保管する対象になるS3バケットを分離したい、なんていう要件が発生することがよくあります。

実際に業務でそういった要件が発生し、どうしたものかとちょっと悩んだのですが、意外と簡単に解決することができたので、Tipsとして書き残しておきます。

環境

実装している環境は以下の通りです。

  • Terraform: 1.7.3
  • AWS Provider: 5.36.0
  • クライアントのOS: macOS Sonoma 14.3.1

Profileの設定

Profileの設定は、他のTerraform実行環境と同様に、macOSであれば ~/.aws/config に記述しますが、今回はAWS IAM Identity Center (AWS SSO)を使用してAWSアカウントに対してアクセスを行っているので、Profileは以下のように記述しています。

# IAM Identity CenterにSSOでアクセスするためのProfileを記述
[sso-session vlayusuke]
sso_start_url = https://vlayusuke.awsapps.com/start#
sso_region = ap-northeast-1
sso_registration_scopes = sso:account:access

# stateファイルを格納する対象となるAWSアカウントの情報
[profile vlayusuke-state-account]
sso_session = vlayusuke
sso_account_id = ************
sso_role_name = vlayusuke-S3Control
region = ap-northeast-1
output = json

# AWSリソースを構築する対象となるAWSアカウントの情報
[profile vlayusuke-resource-account]
sso_session = vlayusuke
sso_account_id = ************
sso_role_name = vlayusuke-ResourceAccess
region = ap-northeast-1
output = json

vlayusuke と定義したsso-sessionを利用して、stateファイルを格納する対象となるAWSアカウント( vlayusuke-state-account )と、実際にAWSリソースを構築するAWSアカウント( vlayusuke-resource-account )とを定義しています。

AWS IAM Identity Centerを使用していない場合でも、stateファイルを格納したいAWSアカウントのProfile情報と、AWSリソースを構築する対象のAWSアカウントのProfile情報を記述しておけば問題ないと思います。

terraform.tfの実装と実行

その上で、 terraform.tf には以下のように実装をします。

ポイントとなるのは、S3 Backendを定義するブロックと、Providerを定義するブロックに、それぞれProfileで定義したそれぞれのProfile情報を profile で設定してあげればOKです。

terraform {
  required_version = "1.7.3"

  backend "s3" {
    bucket  = "vlayusuke-state-bucket"
    key     = "state/production.terraform.tfstate"
    region  = "ap-northeast-1"
    profile = "vlayusuke-state-account"
  }

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "5.36.0"
    }
  }
}

data "aws_caller_identity" "current" {}
data "aws_elb_service_account" "main" {}

provider "aws" {
  region  = "ap-northeast-1"
  profile = "vlayusuke-resource-account"

  default_tags {
    tags = {
      Managed     = "terraform"
      Identifier  = local.identifier
      Project     = local.project
      Environment = local.env
      Repository  = local.repository
    }
  }
}

実行時には、以下の順序でコマンドを実行してあげれば確実です。先にstateファイルを保管するProfile情報を使用して aws sso login を実行してログインを行い、次に実際にAWSリソースを構築するProfile情報を使用して aws sso login をすることで、最後に実行したProfile情報に記述したAWSアカウント上で、Terraformを実行することが可能になります。

% aws sso login --profile vlayusuke-state-account

(WebブラウザでAWS IAM Identity Centerから認証を求める画面が表示されるので、認証を行います)

% aws sso login --profile vlayusuke-resource-account

(WebブラウザでAWS IAM Identity Centerから認証を求める画面が表示されるので、認証を行います)

% terraform init

実際に設定のコツさえわかってしまえば、構成管理のためにもかなり有用な方法なので、もしもこのような要件が出てきた場合にはぜひ試してみてください。

カテゴリー: AWS, Work | タグ: , | コメントする

BlueskyのハンドルをAmazon Route 53上のドメインと紐付ける


はじめに

Blueskyを本格的に使用し始めてからかれこれ半年近くが経過しました。つい最近、招待コードを用いた招待制から登録制に移行したことによって、なんかよくわからないけれどもユーザー数が急激に増え始めたのと同時に、招待制の時代にとりあえずアカウントだけもらっておくかという感じだった人も少しずつアクティブに使い始めている印象があります。自分が移行した時の記録も残してあります。

旧Twitterを使用していた時ほどアクティブにはPostはしていませんが、それでも新しいコミュニティの中で楽しく過ごさせてもらっていることは確かです。

Blueskyに登録すると、デフォルトのハンドルとして、@foobar.bsky.social (foobarがいわゆるアカウント名)が割り当てられるようになっているのですが、自身でドメインを保有している場合は、個人を認証するためにも、DNSサーバのTXTレコードを使用して、自身のドメインとBlueskyの分散識別子(DID)を紐づけることにより、仮に所属するサーバが変更したとしても同一のハンドルを維持することが可能です。

DNSサーバへのTXTレコード追加となると、ちょっと難易度が高いような気がするのですが、Amazon Route 53を使用することで、あらかじめドメインさえ取得していればあっという間にハンドルを自身のドメインに変更することができるため、小ネタとしてその手順をまとめていこうと思います。

Bluesky側の設定

まずは公式のWebクライアントの[設定]から[高度な設定]-[ハンドルを変更]をクリックします。

クリックすると、使用したいドメインの設定を行う画面が表示されるので、ここに、すでに取得済みのドメイン名を変更先のハンドルとして入力します。するとDNSサーバに登録するべきTXTレコードの完全なレコード名が表示されます。

ただし、Amazon Route 53のホストゾーンに設定するにあたって必須なのは did=did:plc:で始まるTXTレコードの値の方なので、[Copy Domain Value]をクリックしてコピーします。

Amazon Route 53側の設定

一番簡単なのは、AWSマネージメントコンソールにログインしてAmazon Route 53コンソールに入り、ホストゾーンからTXTレコードを追加してしまうことです。

  • レコード名には _atproto を設定します
  • レコードタイプは TXT を選択します。
  • 値には、先ほどコピーをした Copy Domain Value の値をそのままペーストします
  • TTLやルーティングポリシーはデフォルトのままで問題ありません

ここまで設定したら、[レコードを作成]ボタンをクリックします。

変更ステータスを確認すると、最初はステータスが PENDING の状態になりますが、大体1分くらい経てば INSYNC の状態になるはずです。その時点で、Bluesky側に戻り、[Verify DNS Record]ボタンをクリックすれば、通常であれば成功のダイアログが表示されて、無事にハンドルが、自分の取得したドメイン名に変更されているはずです。

なんとなく敷居が高そうだったのですが、実際に実行してみたら、ここまでにかかる時間はものの10分程度でした。Amazon Route 53に限らず、ほとんどのマネージドなDNSサーバ機能であれば、どこでも大体同じくらいの時間で設定されるのではないかと思います。

これで、将来的に本格的に分散化していった場合でも、全く同一のハンドルを名乗ることができますし、何よりもなりすましではないことを証明する上では現時点ではこの方法が一番確実なのではないかと思います。

自分のオリジナルのハンドルをBlueskyで持ちたい! と思った方は、ぜひお試ししてみることをお勧めします。

カテゴリー: AWS, Private | タグ: , | コメントする

Backlog World 2023 Re:Bootに参加してきた


はじめに

毎日のように使っているBacklog、もう仕事にも日々のタスクの管理にもなくてはならない存在としてすっかり定着しています。お世辞抜きでBacklogがない生活は考えられないという。そして小さなプロジェクトではありますが、最近どちらかというとプロジェクトマネジメント寄りのお仕事を任されるようになったということもあって、そろそろ自分の中でのプロジェクトマネジメントの仕方とか、うまい仕事の進め方とかをアップデートしていきたいなと思っていました。

その最中に発表されたのが、4年ぶりのオフラインでの開催となる「Backlog World 2023 Re:Boot」の案内。コロナ禍を経て、ようやくオフライン開催が戻ってきました。しかも会場は福岡。ある意味では原点回帰した感じです。開催が発表されるや否や、羽田福岡間の航空券を予約し、12/8(金)には前のりで福岡行きのNH259に搭乗したのでした。

当日の発表内容で刺さったこととか感想など

ご登壇された皆さんの発表内容、本当にどれも勉強になったり、自分の中での仕事の進め方に対する軸を再認識させてくれたり、新しい発見を与えてくれた内容ばかりでした。まずは登壇者の皆さんに感謝したいと思います。

おそらく発表内容の資料はconnpassの中にどんどん上がっていくと思いますので、今回はその中でも個人的にこれは刺さったなという内容を挙げながら、感想などを書いていこうかなと思います。

kinotone SIerが使うBacklog管理術 (株式会社ジョイゾー 大竹さん)

  • 全てをkintoneで管理するメリットがあるのか?「餅は餅屋」ではないのか
  • どれが良い悪いではなく、きちんとツールの特性を理解して使い分ける
  • 型のあるものに対してはシステムに合わせた業務にした方がいいのではないか?

ある意味Backlogの競合的な機能もあるkintoneを専門に開発している方から、そのようなポイントが出てくるとは想像していなかったので驚きでしたし、言われてみれば確かにそうだよなぁと納得でした。

よくシステムの世界では「全体最適」という言葉が使われたりしますけれども、確かにある一定の業務パターンやユースケースが存在しているものに対して、無理に全体最適をしてしまおうとすると、結果として業務を効率化するために本来システム化を図るべき軸がずれてしまうような気がするんですよね。

そういう意味で、その特性を正しくフラットに理解して、そちらの方がデメリットよりもメリットを見出せるのであれば、そちらに寄せていったほうが、結果として業務の効率化が図れるんだなということを認識することができました。

マネジメント層は特にコミュニティに関わった方がいいんじゃないかという話 (メディア総研株式会社 勝毛さん)

  • 仕事の進め方は誰かに習わなかったらアップデートできないんじゃないか?
  • コミュニティに関わらなかったら俺俺ルールで進めていたはず

おっしゃる通りです! と膝を打ちたくなりました。仕事の進め方って、最終的には自分の血肉として吸収していくものではあるんですけれども、そもそものところってその人の中の暗黙知と経験則の集合体でもあるので、経験則が優先してくるとどうしても俺俺ルール化してしまうんですよね。実際に自分もそういう状態になっている傾向があるんじゃないかと認識しています。

けれども、なんらかのコミュニティに関わってひとつのプロジェクトを共同で進めていく中で、他の方の物事の進め方に対する考え方や実際の進め方を知ることで、自分の仕事の進め方をアップデートするチャンスは大いにありますよね。

もちろん実務の中でも色々な人の意見を聞くことは多いですが、仕事の進め方自体に対して、どう進めていくのか、といった部分に関しては聞く機会がなかったので、そのような観点でなんらかのコミュニティに関わっていきたいなということを感じました。

不確実な世界で成果を上げる 〜変化を抱擁するアジャイル思考 (株式会社ソニックガーデン 倉貫さん)

  • 自分の頭で考えて働くのがナレッジワーカーの真骨頂
  • 不確実なプロジェクトに対して進捗管理をするのは幸せではない
  • ロードマップを確認する(経営陣と開発陣で同じものを見る)

まずは我々エンジニアリングに関わる仕事のことをナレッジワーカーとして意識したことってこれまであまりなかったので、そこに新たな気づきを得ました。振り返ってみると、確かにプロジェクトを進めていく段階においては、開発であっても常に自分の頭の中で物事を考えながら仕事を進めています。もしもそれがなかったら単なる作業になってしまうわけで。なので、根本的に僕らは常に多くの場面で想像力を働かせつつ、過去の実績なども参考にしながら仕事を進めているんですよね。そういった意味で、ITエンジニアとしての存在価値を再認識させてもらうことができました。

そして進捗管理に関して。確かに現実として必ずしも幸せなことではなくて、色々と立て込んでくると苦しいことが多いのが現状なんですよね。特にいきなりそれなりの規模のシステムを構築しようとなると。自分の場合はAWSを中心としたパブリッククラウドの世界を使ってインフラを設計構築しているので、構成設計からTerraformを用いた実装の部分までを一貫して行っているんですが、システムのスケールが大きくなればなるほど、考えなければいけないこととか、急な構成変更によって手戻りが発生したりだとか、そういう場面は往々にしてあります。その度に進捗どう? なんて聞かれるとうーむ、となってしまったりとかって場面が蘇りました。そのためにも、開発スケールをできるだけ小さい単位で分割していって、少しずつ育てていく方が、本来のあり方としてはお客さんも開発する側の立場の人も実は幸せなんじゃないのかな? ということを思いました。これは本当に刺さりました。

そして経営者と開発者で同じロードマップを見る。これは実は本当に大切なことなのではないかなと思います。経営者が普段考えていることを開発者に共有されなければ、経営側の人間は一体会社の事業戦略や、それに対して開発者に求めていることがわからなくて疑心暗鬼になってしまったり、逆の立場になって考えた場合、経営陣にとっては開発者に対して、常に目の前の開発だけに追われていないで経営戦略とか会社が成長していくためのグランドマップのこともちょっとは気にしてくれよという考えに陥ってしまう可能性もあるわけで、そこで風通しが悪くなるというか、ある種の壁ができてしまうんですよね。それを回避して、同じ方向を向けるようにするためにも、ロードマップの存在というのは絶対に必要だなと感じました。

心が折れそうになった時に大切にしていること (株式会社ドリーム・アーツ 清水さん)

  • コロナ禍で人を信じるということが難しくなくなってきた
  • リーダーが孤独を感じやすくなってきた
  • 信念を失わないこと

本当にそうだよねと、うんうんと頷きたくなってしまうような内容でした。コロナ禍を通じて、現在は出社回帰の方向が強くなってきてはいるものの、リモートワーク中心で仕事をしている人もまだまだこの業界では多い中で、物理的に他の人の作業が可視化しにくくなってしまうがためについつい他の人のタスクの進捗はどうなんだろうとか、なんか実装の方向性間違っていない? とか、テキストベースでのやり取りはできていても、同期的ではないがために疑心暗鬼になってしまう場面がちょこちょこあるということは現実問題としてあるなと思いました、というかそれを感じることはままあります。

とはいえ、職種上常に物理的に全員が同じ場所で顔を合わせて話をするという状況が常に発生するわけでもないので、何かの制度的にどうこうすればいいという問題ではなくて、最終的にはその人の良識をひたすら信じていくしかないし、逆に信じてもらうことができるように、日頃のアウトプットを、たとえ中間成果物でもいいので積極的に見せていくことで、互いの信頼関係を維持してくことがより必要になってきたんだなということを再認識させられました。

そしてリーダーという立ち位置であったとしても、常に配下で動いている人のことを信じながらも、物事がきちんと進んでいることを確認するためにも、手段を問わずにコミュニケーションを自らとっていく姿勢が必要なのかなと思いました。よく出社がいいのかリモートがいいのかという議論にいきつきがちですが、それは手段でしかなくて、いかにしてより良いコミュニケーションをとっていくことができるかの方が大事なのかなと思っています。

あとは信念を失わないこと。システム開発の現場においては、例えばローンチの瞬間だったりするわけですけれども、そこへの道筋へ辿り着くことができるように、そしてみんなが同じ方向を向くことができるようにコミュニケーションを積極的にとって、真面目に、楽しく、仕事を進めていくことがより必要とされてきているのではないかなと思いました。

終わりに

今回書かせてもらった内容は発表者の一部の方だけになってしまったのですが、他の方の発表も素晴らしかったですし、技術的な観点でいくと、やはりOpenAIの影響ってもの凄いんだなぁとか感じるところはありました。そしてある意味正しいOpenAIの使い方をしているよな、とも感じました。そこはもう、使い方の勘所をいかにして良くしていくかなのかなと思っています。

皆さんの発表を聞かせていただくことによって、日頃の仕事のバタバタの中で落ちそうになっていたモチベーションがだいぶ戻ってきたような気がします。もうそれだけでも個人的にはこのカンファレンスに参加するだけの元はとれたと思っています。そして、継続して外部の勉強会に積極的に参加していきたいなという気持ちを新たにすることができました。面白そうな勉強会があったら、物理的な距離を乗り越えて参加していきたいなと思っています。

最後に、JBUG運営の皆さんは、4年ぶりのオフライン開催に向けて事前の準備や調整など、本当に大変だったのではないかなと思います。特に運営メンバーが全国に散らばっていることで、コミュニケーションを密にとるという部分を相当工夫されたのではないかなと思います。

運営の皆さんのおかげで、気持ちの良い8時間を過ごすことができたことに、心から感謝したいと思います。

カテゴリー: Private, Work | タグ: , | コメントする

Blueskyをメインのマイクロブログ型SNSにお引越し


X (旧Twitter)を、かれこれ14年くらい使ってきました。最初はよくわからずに使っていたものの、そのうちにフォロワーも徐々に増え、同じ趣味を持つ仲間との間でリアルなイベントを開くことができたりと、だいぶ楽しませてもらうことができました。

けれどもマイクロブログ型SNSとしての性格がだんだん変わってきたなと思うようになったのは、やはり2011年頃からですね。自分の旧Twitterに対するスタンスはあまり変わらなくて、本当にライフログ的に日々思ったことや起きた出来事などをゆるゆる投稿するスタイルだったのですが、周りの変化が激しすぎたというか、いつから言論プラットフォームになってしまったんだろう? という違和感を感じ始めていました。それが良いのか悪いのかは置いておいて。

ただテキストだけのやり取りで相手を論破しようとしたり罵倒しようとしたりする動きには正直賛同できなかったし、強いていえばそういうコンテンツを自身でコントロールすることができていたので、自分なりの使い方ができていたというのは確かにあるかもしれません。

それすらもコントロールできなくなってきたのが、2022年初頭にイーロン・マスクが実質的な経営権を握るようになってからの度重なる仕様変更(まぁ、改悪といってもいいでしょう)です。閲覧できる投稿のフィルタリングが難しくなり、一部機能の有料化によって目にしたくないコンテンツまで見なければいけなくなった辛さといったら。利便性と精神的な安心感を得るために一時的に有料アカウントにしていましたが、Blueskyの存在を知って、もはや旧Twitterに縋り付く必要は無くなったなと正直感じました。

Blueskyとは

いわゆる「分散型SNS」と言われているものであり、

1. データを個人で所有でき、サーバーをいつでも好きに引っ越しできる
2. おすすめアルゴリズムやフィードを自由に選択できる
3. 好きなサーバーに所属しながら1つの巨大なサービスにいるように使える

Blueskyの始め方 (https://scrapbox.io/Bluesky/Blueskyの始め方)から引用

ことを目指して作られた、Twitter社からスピンオフして作られたプロジェクトであり、実際には現時点ではマイクロブログサービス(いわゆるSNS)として提供されています。この記事の執筆時点において全世界でのアカウント数は約130万アカウントですが、招待制のクローズドベータの状態が続いているものの、そのアカウント数は順調に伸びており、日本語を用いるユーザもかなりの勢いで増えてきています。

使い勝手はほとんどTwitterと同様です。タイムラインの仕組みが旧Twitterと少し異なりますが、その点についてはうまく”Feed”と呼ばれる絞り込み機能を用いて、自分と趣向の似ているユーザさんを探すことができます。

実際のユーザの方も、個性的かつ優しい方が多く、自身で関係性を構築していけばかなり気持ちの良いホームフィード(タイムラインと同等)を構築することができます。

Bluesky以外にも、さまざまなマイクロブログ型のSNSが乱立していてどこに引っ越せばいいの? という人も多いかもしれませんが、個人的なBlueskyの魅力は、そこにいる人の暖かさと、ちょっとした遊び心にあるのかなと思っています。やはり中にいる人の存在というのはSNSである以上は無視するわけにはいきませんから。

既存のBlueskyユーザさんのほとんどが招待コードを保持していると思いますので、もしも気になる方がいらっしゃったら、Blueskyユーザさんから招待コードを分けてもらって、是非Blueskyの世界に足を踏み入れてみてください。

私のBlueskyのアカウントは以下の通りです。

https://bsky.app/profile/vlayusuke.net

青い空が待ってくれています。

カテゴリー: Private | タグ: , | コメントする

Amazon EventBridge + AWS Lambda + Amazon DynamoDBでBlueskyのbot機能を実装してみた


皆さんはBlueskyについてはご存知でしょうか? 旧Twitterのようなマイクロブログと呼ばれるテキスト系のSNSで、現在はクローズドベータ版として運用されているため、参加は招待制ですが、イーロン・マスクがオーナーとなって以来混迷を続けている旧Twitterと比較するとシンプルではあるものの、非常に使い勝手の良いUIとどちらかという中道な雰囲気のユーザー層が多いため、安心して利用することができます。

以前、同じ構成で旧Twitterへのbot機能を実装しましたが、日々のPostを徐々に旧TwitterからBlueskyへ移行しつつあるということもあり、同じ機能をBluesky向けにカスタマイズしてみました。

構成もAmazon EventBridge + AWS Lambda + Amazon DynamoDBという全く同じ構成にしていますが、一部構成を変更した箇所があるので、その辺りを中心に書いていきたいと思います。

Amazon DynamoDB

以前はDynamoDBテーブルの属性のうち、コンテンツに該当する部分はタイトルとURLを一緒に格納していたのですが、Bluesky向けにはAPIの仕様の関係もあり、一旦タイトルとURLを分割しました。そしてidをパーティーションキーに、dateをソートキーにしているすことで、この2つのキーで主キーにすることとしました。こうすることで逆に後のコーディングで苦労することになってしまったのですが。。。

AWS Lambda向けのPythonのコード

基本的にはAmazon DynamoDBのテーブルから必要なデータを取得してBlueskyの投稿用APIに対してpostするという仕組みには変わりがないのですが、旧Twitterの場合と違って幾つかの処理に差異があるので、 lambda_handler() 以外にも幾つかの関数を追加しています。

パスワードを取得するための関数 get_app_password()

Blueskyの投稿用APIに対して投稿する場合は、Blueskyにログインする必要があるため、SSMパラメータストアにBlueskyから提供されるサードパーティアプリケーション向けのApp Passwordを生成したものを格納しています。くれぐれもBlueskyユーザーのパスワードを使用するのはもしもの時のためにやめましょう。

DIDを取得するための関数 get_did()

Blueskyは分散型SNSとしての運用が考慮されているため、ユーザアカウント以外にユーザ固有のIDを識別するためのDIDを保持しています。そのDIDを取得するための関数になります。

API Keyを取得するための関数 get_api_key()

DIDとパスワードが取得できたら、その情報を元にしてBlueskyのAPIへのアクセス用のAPI Keyを取得する必要があります。このAPI Keyを取得するための関数になります。

BlueskyのAPIを叩いてスキートをPostするための関数 post_skeet()

これまでの情報と、Amazon DynamoDBのテーブルに格納されている情報を元に、スキートを行う関数になります。

これらを実際に実装した全体像が以下の通りになります。

import os
import boto3
import urllib3
import http
import json
import re
import logging
import traceback
import random
from datetime import datetime
from botocore.exceptions import ClientError

logger = logging.getLogger()
logger.setLevel(logging.INFO)

ids = int(os.environ.get('POST_ID'))

dynamodb = boto3.client('dynamodb')


def lambda_handler(event, context):
    
    text = ""
    url = ""
    id = str(random.randint(0, ids))

    try:
        response = dynamodb.query(
            ExpressionAttributeValues={
                ':v1': {
                    'S': id,
                },
            },
            KeyConditionExpression='id = :v1',
            TableName='BlueskyBotOldBlogPost',
        )
        
        logger.info("response is %s", response)

        if ('Items' in response):
            text = text + response['Items'][0]['description']['S']
            url = url + response['Items'][0]['url']['S']
        
        logger.info("description is %s", text)
        logger.info("url is %s", url)
        
        app_password = get_app_password()
        did = get_did()
        key = get_api_key(did, app_password)
        
        response = post_skeet(did, key, text, url)
        
        return response
        
    except Exception as e:
        logger.error(e)
        logger.error(traceback.format_exc())
        return {
            "statusCode": 500,
            "message": 'An error occured at skeet old Blog post.'
        }


def get_app_password():
    ssm = boto3.client("ssm")

    app_password = ssm.get_parameter(Name="bluesky_password", WithDecryption=False)
    app_password = app_password["Parameter"]["Value"]

    return app_password
    

def get_did():
    http = urllib3.PoolManager()

    HANDLE = "vlayusuke.bsky.social"
    DID_URL = "https://bsky.social/xrpc/com.atproto.identity.resolveHandle"

    did_resolve = http.request("GET", DID_URL, fields={"handle": HANDLE})
    did_resolve = json.loads(did_resolve.data)
    did = did_resolve["did"]

    return did


def get_api_key(did, app_password):
    http = urllib3.PoolManager()

    API_KEY_URL = "https://bsky.social/xrpc/com.atproto.server.createSession"

    post_data = {"identifier": did, "password": app_password}
    headers = {"Content-Type": "application/json"}
    api_key = http.request(
        "POST",
        API_KEY_URL,
        headers = headers,
        body = bytes(json.dumps(post_data), encoding="utf-8"),
    )
    api_key = json.loads(api_key.data)

    return api_key["accessJwt"]


def post_skeet(did, key, text, url):
    http = urllib3.PoolManager()
    now = datetime.today()
    
    text = text + "\n\n" + url
    
    found_uri = find_uri_position(text)
    
    if found_uri:
        uri, start_position, end_position = found_uri
    
    post_feed_url = "https://bsky.social/xrpc/com.atproto.repo.createRecord"

    post_record = {
        "collection": "app.bsky.feed.post",
        "repo": did,
        "record": {
            "text": f"{text}",
            "facets": [{
                "index": {
                    "byteStart": start_position,
                    "byteEnd": end_position + 1
                },
                "features": [
                    {
                        "$type": "app.bsky.richtext.facet#link",
                        "uri": uri
                    }
                ]
            }],
            "createdAt": now.strftime("%Y-%m-%dT%H:%M:%S"),
        }
    }

    post_request = http.request(
        "POST",
        post_feed_url,
        body = json.dumps(post_record),
        headers = {"Content-Type": "application/json", "Authorization": f"Bearer {key}"},
    )

    post_request = json.loads(post_request.data)

    return post_request

  
def find_uri_position(text):
    pattern = r'(https?://\S+)'
    match = re.search(pattern, text)

    if match:
        uri = match.group(0)
        start_position = len(text[:text.index(uri)].encode('utf-8'))
        end_position = start_position + len(uri.encode('utf-8')) - 1
        
        return (uri, start_position, end_position)
    else:
        return None

苦労したところ

実装例はいくつかあって参考にさせていただいたものも多いので、それほど苦労したところはなかったのですが、BlueskyのAPIの仕様を元にして、スキートに含まれるURLをきちんとリンクとしてPostする箇所の実装にはだいぶ苦労しました。旧Twitterと違ってBlueskyではURLもプレーンテキストとして認識するので、ここをリッチテキストとして認識させる必要があります。もちろんBlueskyのAPIにはその実装が行われているのですが、そこでだいぶ苦労をしました。

とはいえ、旧Twitterがこの先どういう運命を辿るかわからない中で、次のテキスト系のSNSを選んでいく中で、Blueskyの存在は見逃せないでしょう。APIの内容をもっと理解して、あ様々な使い方を見出していきたいと思います。

カテゴリー: AWS, Private | タグ: , , , | コメントする

りきひさみねこヴァイオリン・ヴィオラ教室発表会 2023


今年も、りきひさみねこ先生の主宰するヴァイオリン・ヴィオラ教室の発表会の季節がやってきました。私にとっては去年に引き続き2回目の参加になりました。継続は力なり。

前回の記事の通り、私はJ.S.Bachの「無伴奏チェロ組曲ト長調 BWV1007」の「プレリュード」を、当たり前ですがピアノ伴奏なしの独奏で演奏することになりました。自分のヴィオラ人生の中で、オケや伴奏を伴わない独奏での演奏は初めてのことでした。

仕事の関係もあり、なかなか練習の時間をとることが難しかった中でなんとか日々少しずつ練習を重ねていって、いざ本番に挑んだのですが、本番前はこういうテンポで弾こうとか、ここの小節を弾く時にはポジション移動を忘れないようにしないととか、色々と考えていたところ、ステージに上がったところ、全て吹っ飛んでしまい、極度の緊張の中でとにかく目の前にある譜面をひたすらに追いかける形になってしまいました。指を間違えたり、一瞬演奏が止まってしまったところも、正直何箇所もありました。

惨敗、というか、やはり無伴奏は甘くなかった。

そんな無様な演奏の中でも、演奏自体を途中で諦めることなく、最後までなんとか弾き切るということだけは、先生のためだけではなく、同じ教室で習っている子どもさんを中心とした生徒の皆さんのためにも、どんな状態でも諦めちゃいけないということを見て欲しかったので、とにかくグダグダでもいいので弾き切りました。

ヴィオラという楽器だからというわけではないのですが、これまでパートという群れの中で弾くことが多かったので、自分の中にどこか、群れだからちょっと間違えても大丈夫だとか、そういう甘さがあったのかもしれません。それだけに独奏というのは、演奏技術をしっかり磨いていかなければいけないことはもちろん、ステージに立つ度胸も必要なんだなということを、改めて強く認識させてくれました。

いや今回ばかりは本当に悔しいですよ。心から悔しい。

けれども、悔しいからもう弾かないのではなく、まだまだ努力してレッスンを重ねていって、少しずつ上手くなって、来年の発表会ではもっと上手くなった姿を皆さんにお披露目できるようにしたいということだけは強く思ったのでした。

このままで終わるわけにはいかないですから。日々精進です。

なかなか上達しない自分を粘り強く見守って励ましてくださったみねこ先生と、暖かい拍手をくださった会場の皆さんには、本当に心から感謝したいと思います。ありがとうございます。

来年のこの場ではもっと上手くなった姿をお見せできるように、また基礎から練習に取り組んでいきたいと思います。負けませんから。

カテゴリー: Private | タグ: , , | コメントする

J.S.Bach 無伴奏チェロ組曲第1番「プレリュード」のレッスン その1


仕事の関係でちょっとだけお休みしていた、りきひさみねこ先生のヴィオラのレッスンを4月の末から本格的に再開し、7月末に行う発表会の曲目をどうするか話したところ、とんとん拍子でJ.S.Bachの無伴奏チェロ組曲第1番「プレリュード」を頑張ってみましょうかというお話になり、今日が実際に譜面を見ながらの初めてのレッスンでした。

最初はスラー無しでものすごくゆっくり音をとっていくところから開始です。録音を聴きながら譜面は眺めていたんですけれども、実際に弾いてみると指回りが結構大変なことに気づきました。普段は2の指でとっていたようなところを3の指でとったり、4の指でとっていたところをあえて開放限で弾いたり。正直頭がこんがらかりそうになりながらも、まずは全体の1/4までの音とりが完了。

先生曰く、前半と後半とでは前半の方が圧倒的に弾きやすいので、まずは前半をスラー無しでちゃんとさらっていき、全体をさらい終えたところで弾けないところをピックアップして重点的にレッスンしていきましょうとのこと。はい、承知しました。

この曲はJ.S.Bachの弦を使う曲の中でも好きなもののひとつなので、ゆっくりでもいいのでできるだけ丁寧に弾きたいなと考えています。目標にしているテンポは藤原真理さんの演奏している、割とゆったりしたものです。

可能であればこのテンポに近づけていきたいのですが、果たしてここまで近づけることができるかどうか。自分にとっては技術的にも自らかなりハードルを上げてしまった感がありますが、できる限りしっかりと弾くことができるように頑張っていきたいと思います。

ちなみにこの曲、先生によると原版はスラーがなくて、カザルスがスラー付きにしたとかなんとか。あとは1箇所だけ音の解釈(Hで弾くかBで弾くか)が異なるところがあるらしいです。初めて知りました。勉強になります。

レッスンの記録は自分自身の振り返りを含めて、発表会当日まで書き綴っていきます。

カテゴリー: Private | タグ: , , | コメントする

TerraformのtfstateファイルをTerraform Cloud上でお手軽に管理する方法


はじめに

IaC環境のひとつ、Terraformでコード管理しようとするとき、一般的にはバックエンドで生成される状態を管理するファイルである terraform.tfstate ファイルを、AWSではS3バケット上に格納することが多いですが、複数の環境のステートファイルをS3バケットで管理しようとするとなかなか煩雑になることが多々あるので、それを少し改良しようと、試しにTerraform Cloudで管理をしてみることにしました。

Terraform Cloudは、HashiCorp社が提供するTerraform向けのSaaS型のホスティングサービスで、本来はTerraformを組織の中で共同で利用するためのリポジトリを提供してくれたり、使いこなすことができれば、例えばGitHub上で管理しているTerraformのコードのプルリクエストがマージされた際に、GitHub Actionsのように、変更されたリソースの記述を環境上に反映してくれたりといったCI/CD的な機能を提供してくれたりと結構便利な機能が搭載されているのですが、そこまでのコントロールが必要でなくても、例えばステートファイルだけを管理することも可能です。

今回は、本当のスモールスタートで、ステートファイルだけをTerraform Cloud上に管理するところだけをスコープにしています。

設定手順

HashiCorp Cloud PlatformアカウントとOrganizationの作成

まずはHashiCorp Cloud Platformアカウントをトップページから作成してアカウントを取得後、Organizationの設定を行います。無料のプランであれば、5人まで組織の中にアカウントを紐づけることが可能です。

参考:

Projectとworkspaceの作成

Terraform Cloudは、Organizationの下に複数のProjectをぶら下げることができ、さらにその下に実際のステートファイルを管理するworkspaceを作成していくことができます。Terraform Cloudに作成したアカウントでログインすると、Projects & workspacesという画面に遷移するので、まずは右上の[New]ボタンをクリックしてProjectを作成します。

Projectの作成が完了したら、同じく[New]ボタンをクリックしてworkspaceを作成します。今回はコードの管理はGitHub上で行い、ローカルのターミナル環境を利用してTerraformの操作を行うので、Create a new Workspaceの画面上では[CLI-driven workflow]を選択します。すると次の画面でWorkspaceの名前と、Projectとの紐付けを行う画面に遷移するので、workspace名を入力して、先ほど作成したProjectを選択し、一旦[Create workspace]ボタンをクリックします。これでworkspaceまでが作成できています。

workspaceの設定

workspaceの設定まで完了したら、左ペインの[Settings]をクリックしてExecution Modeを”Local”に、Remote state sharingを”Share with all workspaces in this organization”に変更します。Execution Modeを”Remote”にすると、Terraformの実行に関する操作がすべてTerraform Cloud上にある仮想マシン上で実行されます。今回はステートファイルだけをTerraform Cloud上で管理したいため、”Local”に設定して、[Save settings]ボタンをクリックします。

Terraform側の設定

Terraform Cloudと連携できるようにするために、Terraformの実行環境を設定している .tf ファイルを以下のように記述します。(敢えてバックエンドをS3に設定していた時の設定をコメントアウトして残しています)

# ===============================================================================
# Terraform
# ===============================================================================
terraform {
  required_version = "1.4.6"
  #  backend "s3" {
  #    bucket = "terraform-bucket"
  #    key    = "terraform.tfstate"
  #    region = "ap-northeast-1"
  #  }

  cloud {
    organization = "vlayusuke"
    hostname     = "app.terraform.io"

    workspaces {
      name = "vlayusuke-poc-infrastructure-develop"
    }
  }

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "4.65.0"
    }
  }
}

実際に動かしてみる

Terraformを実行するときには、ターミナル上でまず最初に terraform login してTerraform Cloudにログインします。このときにトークンの発行を求められるので、 yes を入力するとWebブラウザに遷移してトークン発行画面が表示されます。

トークンを発行したらターミナルコマンドに貼り付けるとTerraform Cloudのログイン画面に遷移するので、あとは普通に terraform init して terraform plan して terraform apply することで、ステートファイルをTerraform Cloud上に保持しながらTerraformの操作を実行することが可能になります。

そしてステートファイルは以下のようにTerraform Cloud上に保持されます。

今後進めたいこと

これで、バックエンドに terraform.tfstate ファイルを管理することなく、Terraformを用いた環境の構築と構成管理を進めていくことが可能になりました。

とはいえTerraform Cloudではまだまだ色々なことができそうなので、効率的に環境構築と構成管理をできるように、Terraform Cloudの機能を色々と試していきたいと思います。

カテゴリー: Work | タグ: | コメントする

MacBook Air (M2,2022)を購入して6か月経過


先代のMacBook Air (M1, 2020)は、ディスプレイの修理をしながらも快適に使用していたのですが、会社の上司がMacBook Air (M2, 2022)を購入して楽しそうに作業していたのを見て、これはやっぱり自分も買い替えるしかない! と勝手に思ってしまい、半年前につい買い替えをしてしまいました。自分の中ではMacに関する減価償却は3年と考えていて3年おきに買い替えをしていたのですが、異例の速さです。買ってしまったものは仕方がないのでもう使い倒すしかないですね。

ちなみに以下のスペックのモデルを購入しました。

  • 8Core CPU
  • 8Core GPU
  • 16GB Memory
  • 512GB SSD

メモリは例によってデフォルト8GBのところを16GBに拡張しています。

期待通りのパフォーマンス

そんなわけで半年間使ってみてのインプレッションなのですが、パフォーマンスに関しては期待通りですね。普段使いだけではなく一部開発にも使用しているので、時たまDockerを立ち上げたりしているのですが、メモリを食いがちのDockerを立ち上げても他のアプリケーションの動作には影響しませんし、コードを書く際には同時にVisual Studio Codeを立ち上げているのですが、同時使用していても全く苦になりません。

それでいてMacBook Air (Mi, 2020)の重量よりもさらに軽い1.24kgまで軽くなっているので、どこにでも持っていきたい派の自分としては可搬性がさらに向上して嬉しい限りです。個人的に寂しいなと思っているのは、ディスプレイが大きくなったことで、”MacBook Air”のロゴがなくなってしまったことくらいですが、最近はそれも気にならなくなってきました。

ディスプレイのノッチに関しては、これはもう慣れですね。ちょうどメニューバーにかぶさるようにしてノッチが存在しているので、メニューバーに表示させているアプリケーションが一部見えなくなってしまっているのですが、そこは妥協点と言えば妥協点かもしれません。

今度こそは5年使えるかも

AppleCare+ for Macのラインナップの中にサブスクリプションプランが加わったのも大きいですね。これまで3年間隔で買い替えていたのは、買い切り型のAppleCareの期限が3年だったからというのもあったのですが、サブスクリプションプランであれば3年以上経過した場合でも保証が継続するので、ビンテージにならない限りは保証が効くというところが個人的にはかなり嬉しいところです。

これからもApple Siliconのチップは進化を続けるのでしょうけれども、相当な仕様変更がない限りは、今度こそはバッテリーの使用頻度を工夫しながらも5年くらい使っても期待通りのパフォーマンスは出続けてくれるのではないかなと思います。

カテゴリー: Private, Work | タグ: , , | コメントする

山形交響楽団 第307回定期演奏会


山形交響楽団が、飯森範親さんの指揮で久しぶりにブルックナーの交響曲第7番を演奏するというので、山形新幹線に飛び乗って山形まで聴きに行ってきました。

本当に素晴らしい演奏を聴くと、感想を書くために何か言葉を紡ぎ出そうとしても、それが全て野暮になってしまうのではないかというほど、素晴らしい演奏でした。

芳醇な弦の響きと重厚感のある管楽器の響きが一体になって、まるで山形テルサホール全体が、ひとつの大きなオルガンのようになったのではないかという感覚を覚えてしまったほどでした。

その質感と音の世界が、いつまでも耳の奥に残ってなかなか抜けることができません。

会心の演奏だったと思います。この忙しい日々の中で素晴らしい音楽を聴くことができることがどれだけ素晴らしいことなのかということを、今もって改めて痛感させられました。そして自分なりに継続して音楽に触れていきたいと思わせてくれる演奏会でした。

飯森範親さんと山形交響楽団の皆さんに感謝です。

カテゴリー: Private | タグ: , , | コメントする