てくなべ (tekunabe)

ansible / network automation / 学習メモ

YAML におけるノルウェー問題(The Norway Problem)

いろいろな書き方ができる真偽値

たとえば Ansible の Playbook では、真偽値を指定する時に、true/falseyes/no などいろいろな書き方ができてしまいます。

去年、Ansible のコミュニティでは、ドキュメント上どう統一するのがいいかの投票がありました。

github.com

現在 true/false への書き換えが進んでいます。yamllint のデフォルトでも true/false を正としています。

不都合

ただ統一感がなくて混乱する、以上の不都合が起こることがあります。

たとえば、ネットワーク機器の操作で対話的に yes というコマンドを実行する場合に

comamnds:
  - yes

のようにしてしまう場合です。文字列として扱ってほしい場合はクォーテーションで囲う必要があります。

The Norway Problem

最近知ったのですが、ノルウェー問題(The Norway Problem)と呼ぶことがあるようです。

hitchdev.com

Norway の 略称 NO をそのまま指定してしまうと False になるという話です。

[Ansible] インベントリの警告「[WARNING]: No inventory was parsed (略)」の表示有無を切り替える設定 INVENTORY_UNPARSED_WARNING

はじめに

ansible-core 2.14.0 の changelog を眺めてて始めて気がついたのですが、INVENTORY_UNPARSED_WARNING という設定項目が追加されていました。

インベントリファイルを読み込めなかったときに表示される警告、[WARNING]: No inventory was parsed, only implicit localhost is available (略) を表示するかどうかという設定項目です。デフォルトは今までとおりの挙動である True です。

実際どんな感じだろうと思って確かめてみました。

  • 動作確認環境
    • ansible core 2.14.3

元PR: Hide "[WARNING]: No inventory was parsed" message by JonTheNiceGuy · Pull Request #65499 · ansible/ansible · GitHub

True の場合 (デフォルト)

そのそもどんな感じだっけ?ということで、デフォルトのまず試します。

Playbookはこちら。hosts: localhost としています。

---
- name: Test Play
  hosts: localhost
  gather_facts: false

  tasks:
    - name: Debug
      ansible.builtin.debug:
        msg: "{{ inventory_hostname }}"

ansible-playbook コマンドでインベントリファイルを指定せずに実行します。

% ansible-playbook test.yml
[WARNING]: No inventory was parsed, only implicit localhost is available
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not
match 'all'

PLAY [Test Play] ****************************************************************************************************

TASK [Debug] ********************************************************************************************************
ok: [localhost] => {
    "msg": "localhost"
}

PLAY RECAP **********************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0  

例の警告が表示されました。

False の場合

INVENTORY_UNPARSED_WARNING は、環境変数 ANSIBLE_INVENTORY_UNPARSED_WARNINGansible.cfg で設定できるようです。

今回は、ansible.cfg で設定してみます。defaults セクションではなく inventory セクションです。

[inventory]
inventory_unparsed_warning=False

おなじく、インベントリファイルを指定せずに実行します。

% ansible-playbook test.yml
[WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not
match 'all'

PLAY [Test Play] ****************************************************************************************************

TASK [Debug] ********************************************************************************************************
ok: [localhost] => {
    "msg": "localhost"
}

PLAY RECAP **********************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

[WARNING]: No inventory was parsed, 略 が表示されなくなりました。

おわりに

あまり設定変更したいときは多くないかもしれませんが、少しでも警告表示を抑えたい場合にはいいかもしれません。

JANOG51 Meeting 参加レポート

はじめに

2023/01/25-27 に山梨県富士吉田市で開催(現地とオンラインのハイブリッド)された JANOG51 Meeting にオンラインで参加しました。

はじめて JANOG Meeting に参加したのが JANOG41 だったので、今回で10回目となりました。

現地は、満席で立ち見禁止というケースもあって、質疑したい人優先、という呼びかけもあったようです。盛り上がったみたいですね。

当日参加したプログラム、現時点でアーカイブ拝聴したプログラムや、全体的な所感についてレポートします。

なお、各プログラム詳細ページからアーカイブ動画が見れます。2023年2月28日までだそうです。


■ Day1

NMSの第一歩 ~OSSによるLab環境の改善~

www.janog.gr.jp

Cisco CX Delivery CEインターンシッププログラムの過程で作成された、NMS(Network Management System)の内容やデモ、トラブルなどの紹介がされていました。

監視はZabbix、コンフィグ管理はOxidizedを利用されていました。

Git を正としていれば、実機からコンフィグを取得してバックアップする行為自体が不要という考え方もありますが、今回のようにラボ環境だと実機を直越設定変更する機会も多く「実機が正」とする事情があるのだと思いました。

コンフィグのロールバックは、コンフィグの性質上難しい(no コマンドの生成が複雑)場合もあると思いますが、今回は configure replace を使っているようでした。

Oxidizedで現状対応していない機種に対応させるための model 作りは思ったより簡単そうでした。

ここがヘンだよ日本人の英語コミュニケーション 〜日本人特有の落とし穴を知り、グローバル社会でも活躍できるエンジニアになろう〜

www.janog.gr.jp

言語自体の壁は確かに高いですが、その先にある習慣や文化の壁がさらに高いのだなと思う内容でした。

他にも、

  • 「議論=成長に必要なもの」
  • 質問は簡潔に
  • 「ベストプラクティスはなんですか?」など。自分が答えづらい質問は相手も答えづらい
  • 「謙遜」「遠慮」で損している。面白い仕事は、存在感のある人のところにやってくる。

などなど、日本で仕事している中でも意識すると良さそうな内容がたくさんありました。

[2023/03/06 追記] YouTubeshow int さんの動画としてアップされました。

www.youtube.com


■ Day2

ムーアの法則による高速インターフェース展開予測 2025/2026

www.janog.gr.jp

物理層の話はあまり詳しくないんですが、インターフェースはどんどん早くなっていきますね・・。

「ブレークアウトアウト」自体、はじめて聞きました。

[LT] 故障対応完全自動化への道

www.janog.gr.jp

データセンター内の機器の故障時に、各種手配や復旧オペレーションの自動化を進めているお話でした。

  • 非定形作業が発生しうる状況
  • 手配のような対外手続きを含む
  • 物理作業を含む

という点で、完全自動化はなかなかハードルが高そうだなと思いました。

[LT] ロボットを活用したDCオペレーションについて

www.janog.gr.jp

リモートハンドでサーバーの電源をON/OFFしたり、ランプチェックをしたり、刺激的な世界観のお話でした。

超ネットワーク作ってみた ~「ニコニコ超会議2022」in幕張メッセ~

www.janog.gr.jp

ニコニコ超会議2022を支えたネットワークの、設計やキッティングの自動化、NOC等のお話でした。

キッティングの自動化が一番身近に感じました。ネットワークエンジニア歴ゼロの状態から、大量の NEC IX 2215 へのキッティング作業をAnsibleを使って自動化されたそうです。キッティング作業なので、最初はシリアル接続で作業する必要があり、コンソールサーバー SmartCS をフル活用。

IX に対応した(少なくとも公式の)Ansibleモジュールがなく、expect を使っていくところは、ちょっとつらそうなところがありました。

処理速度の都合上、SSH接続の設定まではコンソールで行って、そのとはSSH経由で設定するというのは、よく聞きます。

「皆さん仮想ルータ使ってますか?」仮想ルータの使いどころを語る会

www.janog.gr.jp

仮想ルータは、私自身は検証用途くらいでしか使ったことがないのですが、柔軟性があったり色々便利なイメージがあります。

一方で、活用するにはそれはそれでスキルが必要なのだなと思いました。


■ Day3

システム開発とネットワーク運用の両立に向けた取り組みと課題

www.janog.gr.jp

弊社のエンジニアがお客様と共同登壇させていただいたプログラムです。

自分たちと向き合い、課題を見極め、手を打っていくという、ある種シンプルなことを丁寧にされているという印象でした。

スキル習熟のための手段として、プログラミングスクールの採用が挙げられていました。会社負担の負担で就業時間内に。個人の情熱に任せず、組織として取り組んでいるのが素敵だなと思いました。

フレーズとしては「価値基準を理解する」と「思考領域の拡⼤と⼿段の多様化」が響きました。

領域を広げる手段として、弊社エンジニアが異文化を持ち込むためたのは、嬉しく思いました。SIer である弊社が、このような関わり方ができたのは新鮮でした。

弊社エンジニアのパートでも触れられていますが、彼が新卒1年目のときに、研修として私から「アウトプットしよう」などの話をしました(公開資料:向き合うエンジニア)。その時のことが、活かされたのが嬉しかったです。

CUEとKubernetesカスタムオペレータを用いた新しいネットワークコントローラをつくってみた

www.janog.gr.jp

Kubernetes は詳しくないのですが、カスタムオペレータという仕組みは魅力的に感じました。

あと、ネットワークの自動化やIaCは難しいなぁとは思っているものの、うまく説明できていないのですが、資料のP38に出てくる「ネットワークIaCはManyToManyと戦わなければならない」という言葉が気になりました。自分の思考がまだまだ浅いことを感じました。

CUEはずっと気になってはいるのですが、なかなか手が出せていないです。

どうやってつくる? ネットワーク自動化開発チーム

www.janog.gr.jp

私は部署内の自動化トレーニングの講師もすることもある関係で、とても気になっていたプログラムです。

採用状況は厳しいようです。衝撃だったのが LINE さんがとった対策のお話。学生から応募がなかったので、自分たちのシステムを、流行ってて学生も使える Kubrenetes ベースのシステムにしたとのこと。そしてアルバイトの学生が NFV Rolling Update の仕組みを作ってくれたそうです。発想がすごいです・・。

以下、ディスカッションパートからポイントです。

[1] ネットワーク自動化システム、だれが開発するの?

  • トータル的には、ソフトウェアエンジニアからという意見が優勢だった印象です。

[3] 開発業務を内製する?それとも外注する?

[5] ネットワーク自動化ができる人を育てる?中途採用する?

  • 応募はなく、厳しい。なので育てることになる

[6] ネットワーク自動化に必要なスキルや経験、どうやって学んでもらう?

  • キュメントは重要
  • そのうえで、業務に直結したタスクをやってもらう(浮き輪を付けて突き落とす)。

[9] ネットワーク自動化開発にスーパーヒーローは必要?

  • 一番最初に始めた人がスーパーヒーロー
    • 最初はそう。その後チームとしてヒーローを無くしていく(いい意味で)
  • ナンバー2を育てたり、体制の維持も難しい
    • スケールさせたいかどうか重要、

最後、メーカーの方から、教育と最初の一歩をメーカーとしてどう助けられるか、という逆質問がありました。様々な立場が参加して同じ課題に向き合っていく、JANOGらしい一面だなと思いました。私としては、トレーニングや検証用途に利用しやすいライセンスの仮想ルーターなどの提供はありがたいなと思っています。

NETCON Wrap-up

www.janog.gr.jp

問題環境のセットアップに時間が掛かるなどの課題をContainerlabを利用して解決を試みたそうです。

今回の JANGO では他にも Containerlab について厚かった(Containerlabを使用した商用環境と同等な検証環境の作成とユースケースについて)というプログラムがありました。気になってきました。


アーカイブ視聴予定

アーカイブが公開されているうちに以下のプログラムを視聴しようと思います。

熱が冷めないうちにブログを書くというのを優先しているため、見るのはちょっと後回しにしちゃっています。


おわりに

今回は私に馴染みがあるテーマのプログラムが多かったように思います。とても興味深かかったです。

登壇者、スタッフ、スポンサーのみなさまありがとうございました!

参加された方でアンケートがまだの方はぜひこちらから。

JANOG51 Meeting アンケート - JANOG51 Meeting

次回 JANOG52 Meeting は 2023年7月に長崎県長崎市で開催予定です。世の中が今より落ち着き、安心して開催できる状態になっていることを願っています。

参考

show int さんの動画

www.youtube.com

www.youtube.com

togetter

JANOGに関連する120件のまとめ - Togetter

私と JANOG

[Azure] いろんなツールでNSGを作ってみた(Azure CLI / Bicep / ARM テンプレート / Ansible / Terraform / CDKTF)

はじめに

Azure の操作を自動化するとき、みなさんどんなツール使うことが多いのかなと思い、先日こんなアンケートをとってみました。

Terraform が多いですね。

候補に上げてみたものの、触ってみたことが無いものもあったので、色々触ってみようと思いました。

今回は、ネットワークセキュリティグループとそのルールを定義する簡単な内容を以下の6個のツール類でやってみました。

ツールごとに別々のリソースグループを予め作成していた状態からはじめました。ツールのインストールや認証情報などの設定も済んでいる状態です。

かんたんなサンプルを掲載します。まだちょっと触っただけなので、うまく有効活用した書き方になっていないところもあると思います。また構文の説明も省略します。あくまで雰囲気、ということでご了承ください。

1. Azure CLI

まず、普段遣いとしてもとても便利な印象の Azure CLIでやってみます。

  • 環境
    • Azure CLI 2.43.0

NSG 自体の作成を az network nsg create で、ルールの作成を az network nsg rule create で行いました。

サンプルと実行

NSG の作成:

% az network nsg create -g sakana-cli -n testnsg01
{
  "NewNSG": {
    "defaultSecurityRules": [
      {
        "access": "Allow",
  // ...(略)...

ルールの作成:

% az network nsg rule create -g sakana-cli --nsg-name testnsg01 \
                           --name "Allow 443" \
                           --direction Inbound \
                           --priority 110 \
                           --destination-port-ranges 443 \
                           --protocol Tcp \
                           --destination-address-prefixes "*" \
                           --source-address-prefixes 10.0.0.0/16 \
                           --access Allow

{
  "access": "Allow",
  "destinationAddressPrefix": "*",
  // ...(略)...
}

手軽に使えて、繰り返し実行もしやすくて便利です。

2. Bicep

ARM テンプレートより簡単に書ける構文で書けるもので、今回初めて書きました。

内部的には、一度 ARM テンプレートに変換されるようです。

サンプル

param location string = resourceGroup().location

resource testnsg01 'Microsoft.Network/networkSecurityGroups@2022-07-01' = {
  name: 'testnsg01'
  location: location
}

resource Allow443 'Microsoft.Network/networkSecurityGroups/securityRules@2022-07-01' = {
  name: 'Allow443'
  parent: testnsg01
  properties: {
    direction: 'Inbound'
    priority: 110
    destinationPortRange: '443'
    protocol: 'Tcp'
    destinationAddressPrefix: '*'
    sourcePortRange: '*'
    sourceAddressPrefix: '10.0.0.0/16'
    access: 'Allow'
  }
}

VS Code の拡張「Bicep」を利用して書きましたが、補完が効いてとても書きやすかったです。

実行

ARM テンプレートと同じく Azure CLIaz deployment group create でデプロイします。Azure CLI の内部的には、指定したファイル名の拡張子が .bicep だと ARM テンプレートに変換(az bicep build -f ファイル名.bicep --stdout)する処理を挟むようでした。

% az deployment group create -f nsg.bicep -g sakana-bicep
{
  "id": "/subscriptions/xxxx/resourceGroups/sakana-bicep/providers/Microsoft.Resources/deployments/nsg",
  "location": null,
  "name": "nsg",
  "properties": {
...(略)...

参考URL

3. ARM テンプレート

一番 Azure ネイティブな方法と言えるかなと思います。

サンプル

一から書くのが手間に感じたため、先程作成した Bicep のファイルから生成します。az bicep build -f nsg.bicep コマンドで nsg.json というファイル名の ARM テンテプレートができます。

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "metadata": {
    "_generator": {
      "name": "bicep",
      "version": "0.13.1.58284",
      "templateHash": "4078294825981448537"
    }
  },
  "parameters": {
    "location": {
      "type": "string",
      "defaultValue": "[resourceGroup().location]"
    }
  },
  "resources": [
    {
      "type": "Microsoft.Network/networkSecurityGroups",
      "apiVersion": "2022-07-01",
      "name": "testnsg01",
      "location": "[parameters('location')]"
    },
    {
      "type": "Microsoft.Network/networkSecurityGroups/securityRules",
      "apiVersion": "2022-07-01",
      "name": "[format('{0}/{1}', 'testnsg01', 'Allow443')]",
      "properties": {
        "direction": "Inbound",
        "priority": 110,
        "destinationPortRange": "443",
        "protocol": "Tcp",
        "destinationAddressPrefix": "*",
        "sourcePortRange": "*",
        "sourceAddressPrefix": "10.0.0.0/16",
        "access": "Allow"
      },
      "dependsOn": [
        "[resourceId('Microsoft.Network/networkSecurityGroups', 'testnsg01')]"
      ]
    }
  ]
}

どうしても少し長くなります。Bicep の有り難みが分かりました。

実行

構文は、Bicep と同じです。今回はツールごとにリソースグループを分けているので -g は異なります。

$ az deployment group create -f nsg.json -g sakana-arm
{
  "id": "/subscriptions/xxxx/resourceGroups/sakana-arm/providers/Microsoft.Resources/deployments/nsg",
  "location": null,
  "name": "nsg",
  "properties": {
...(略)...

4. Ansible

azure.azcollection というコレクションの中に、さまざまなリソースを操作するうモジュールがあります。

NSG の作成自体も、ルールの作成も同じく azure/azcollection/azure_rm_securitygroupモジュールを利用します。

  • 環境
    • ansible-core 2.12.1
    • azure.azcollection 1.14.0

サンプル

---
- hosts: localhost
  gather_facts: false
  connection: local

  vars:
    resource_group: sakana-ansible
    ansible_python_interpreter: "{{ ansible_playbook_python }}"

  tasks:
    - name: Create NSG
      azure.azcollection.azure_rm_securitygroup:
        resource_group: "{{ resource_group }}"
        name: testnsg01
        rules:
          - name: Allow 443
            direction: Inbound
            priority: 110
            destination_port_range: 443
            protocol: Tcp
            source_address_prefix: 10.0.0.0/16
            access: Allow

実行

% ansible-playbook -i localhost, nsg.yml 

PLAY [localhost] ***********************************************************************************

TASK [Create NSG] **********************************************************************************
changed: [localhost]

PLAY RECAP *****************************************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

5. Terraform

次は、今回のアンケートでは使っている人が多かった Terraform です。

  • 環境
    • Terraform v1.3.7

サンプル

provider "azurerm" {
  features {}
}

resource "azurerm_network_security_group" "testnsg01" {
  name                = "testnsg01"
  location            = "japaneast"
  resource_group_name = "sakana-tf"
}

resource "azurerm_network_security_rule" "Allow443" {
  name                        = "Allow 443"
  network_security_group_name = azurerm_network_security_group.testnsg01.name
  resource_group_name         = "sakana-tf"
  direction                   = "Inbound"
  priority                    = 110
  destination_port_range      = "443"
  protocol                    = "Tcp"
  source_address_prefix       = "10.0.0.0/16"
  source_port_range           = "*"
  destination_address_prefix  = "*"
  access                      = "Allow"
}

実行

init:

% terraform init

Initializing the backend...

Initializing provider plugins...
- Finding latest version of hashicorp/azurerm...
- Installing hashicorp/azurerm v3.39.1...
- Installed hashicorp/azurerm v3.39.1 (signed by HashiCorp)

...(略)...

plan:

 % terraform plan

Terraform used the selected providers to generate the following execution plan. Resource actions
are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azurerm_network_security_group.testnsg01 will be created
  + resource "azurerm_network_security_group" "testnsg01" {
      + id                  = (known after apply)
      + location            = "japaneast"
      + name                = "testnsg01"
      + resource_group_name = "sakana-tf"
      + security_rule       = (known after apply)
    }

  # azurerm_network_security_rule.Allow443 will be created
  + resource "azurerm_network_security_rule" "Allow443" {
      + access                      = "Allow"
      + destination_address_prefix  = "*"
      + destination_port_range      = "443"
      + direction                   = "Inbound"
      + id                          = (known after apply)
      + name                        = "Allow 443"
      + network_security_group_name = "testnsg01"
      + priority                    = 110
      + protocol                    = "Tcp"
      + resource_group_name         = "sakana-tf"
      + source_address_prefix       = "10.0.0.0/16"
      + source_port_range           = "*"
    }

Plan: 2 to add, 0 to change, 0 to destroy.

───────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take
exactly these actions if you run "terraform apply" now.

apply:

% terraform apply

Terraform used the selected providers to generate the following execution plan. Resource actions
are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # azurerm_network_security_group.testnsg01 will be created
  + resource "azurerm_network_security_group" "testnsg01" {
      + id                  = (known after apply)
      + location            = "japaneast"
      + name                = "testnsg01"
      + resource_group_name = "sakana-tf"
      + security_rule       = (known after apply)
    }

  # azurerm_network_security_rule.Allow443 will be created
  + resource "azurerm_network_security_rule" "Allow443" {
      + access                      = "Allow"
      + destination_address_prefix  = "*"
      + destination_port_range      = "443"
      + direction                   = "Inbound"
      + id                          = (known after apply)
      + name                        = "Allow 443"
      + network_security_group_name = "testnsg01"
      + priority                    = 110
      + protocol                    = "Tcp"
      + resource_group_name         = "sakana-tf"
      + source_address_prefix       = "10.0.0.0/16"
      + source_port_range           = "*"
    }

Plan: 2 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

azurerm_network_security_group.testnsg01: Creating...
azurerm_network_security_group.testnsg01: Creation complete after 4s [id=/subscriptions/xxxx/resourceGroups/sakana-tf/providers/Microsoft.Network/networkSecurityGroups/testnsg01]
azurerm_network_security_rule.Allow443: Creating...
azurerm_network_security_rule.Allow443: Still creating... [10s elapsed]
azurerm_network_security_rule.Allow443: Creation complete after 10s [id=/subscriptions/xxxx/resourceGroups/sakana-tf/providers/Microsoft.Network/networkSecurityGroups/testnsg01/securityRules/Allow 443]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

ステートを持つのが特徴ですね、

6. CDKTF (CDK for Terraform)

CDK 経由で Terraform を書けるようなツールです。言語は一番サンプルが多そうな TypeScript にしました。

できるまで苦戦してしまいました。何か指摘があれば Twittter までご連絡いただけるとありがたいです。

  • 環境
    • cdktf 0.14.3

サンプル

main.ts (cdktf init で生成されたものに追記):

// Copyright (c) HashiCorp, Inc
// SPDX-License-Identifier: MPL-2.0
import { Construct } from "constructs";
import { App, TerraformStack } from "cdktf";
import { AzurermProvider } from '@cdktf/provider-azurerm/lib/provider';
import { NetworkSecurityGroup } from '@cdktf/provider-azurerm/lib/network-security-group';
import { NetworkSecurityRule } from '@cdktf/provider-azurerm/lib/network-security-rule';

class MyStack extends TerraformStack {
  constructor(scope: Construct, id: string) {
    super(scope, id);

    // define resources here
    new AzurermProvider(this, "azure", { features: {} })

    const nsg = new NetworkSecurityGroup(this, "nsg", {
      location: "japaneast",
      resourceGroupName: "sakana-cdktf",
      name: "sakana-cdktf"
    })

    new NetworkSecurityRule(this, "rule", {
      name: "allow 443",
      networkSecurityGroupName: nsg.name,
      resourceGroupName: "sakana-cdktf",
      direction: "Inbound",
      priority: 110,
      destinationPortRange: "443",
      protocol: "Tcp",
      sourceAddressPrefix: "10.0.0.0/16",
      sourcePortRange: "*",
      destinationAddressPrefix: "*",
      access: "Allow"
    })
  }
}

const app = new App();
new MyStack(app, "cdk");
app.synth();

実行

synth:

% cdktf synth 

Generated Terraform code for the stacks: cdk

deploy:

% cdktf deploy
cdk  Initializing the backend...
cdk  
     Successfully configured the backend "local"! Terraform will automatically
     use this backend unless the backend configuration changes.

     Initializing provider plugins...
     - Finding hashicorp/azurerm versions matching "3.39.1"...
cdk  - Using hashicorp/azurerm v3.39.1 from the shared cache directory
cdk  Terraform has created a lock file .terraform.lock.hcl to record the provider
     selections it made above. Include this file in your version control repository
     so that Terraform can guarantee to make the same selections by default when
     you run "terraform init" in the future.
cdk  ╷
     │ Warning: Incomplete lock file information for providers
     │ 
     │ Due to your customized provider installation methods, Terraform was forced
     │ to calculate lock file checksums locally for the following providers:
     │   - hashicorp/azurerm
     │ 
     │ The current .terraform.lock.hcl file only includes checksums for
     │ darwin_amd64, so Terraform running on another platform will fail to install
     │ these providers.
     │ 
     │ To calculate additional checksums for another platform, run:
     │   terraform providers lock -platform=linux_amd64
     │ (where linux_amd64 is the platform to generate)
     ╵
     
     Terraform has been successfully initialized!
     
     You may now begin working with Terraform. Try running "terraform plan" to see
     any changes that are required for your infrastructure. All Terraform commands
     should now work.

     If you ever set or change modules or backend configuration for Terraform,
     rerun this command to reinitialize your working directory. If you forget, other
     commands will detect it and remind you to do so if necessary.
cdk  Terraform used the selected providers to generate the following execution
     plan. Resource actions are indicated with the following symbols:
       + create
     
     Terraform will perform the following actions:
cdk    # azurerm_network_security_group.nsg (nsg) will be created
       + resource "azurerm_network_security_group" "nsg" {
           + id                  = (known after apply)
           + location            = "japaneast"
           + name                = "sakana-cdktf"
           + resource_group_name = "sakana-cdktf"
           + security_rule       = (known after apply)
         }

       # azurerm_network_security_rule.rule (rule) will be created
       + resource "azurerm_network_security_rule" "rule" {
           + access                      = "Allow"
           + destination_address_prefix  = "*"
           + destination_port_range      = "443"
           + direction                   = "Inbound"
           + id                          = (known after apply)
           + name                        = "allow 443"
           + network_security_group_name = "sakana-cdktf"
           + priority                    = 110
           + protocol                    = "Tcp"
           + resource_group_name         = "sakana-cdktf"
           + source_address_prefix       = "10.0.0.0/16"
           + source_port_range           = "*"
         }

     Plan: 2 to add, 0 to change, 0 to destroy.
     
     ─────────────────────────────────────────────────────────────────────────────

     Saved the plan to: plan

     To perform exactly these actions, run the following command to apply:
         terraform apply "plan"

(ここで Approve を選択)

     To perform exactly these actions, run the following command to apply:
         terraform apply "plan"
cdk  azurerm_network_security_group.nsg (nsg): Creating...
cdk  azurerm_network_security_group.nsg (nsg): Creation complete after 4s [id=/subscriptions/xxxx/resourceGroups/sakana-cdktf/providers/Microsoft.Network/networkSecurityGroups/sakana-cdktf]
cdk  azurerm_network_security_rule.rule (rule): Creating...
cdk  azurerm_network_security_rule.rule (rule): Still creating... [10s elapsed]
cdk  azurerm_network_security_rule.rule (rule): Creation complete after 10s [id=/subscriptions/xxxx/resourceGroups/sakana-cdktf/providers/Microsoft.Network/networkSecurityGroups/sakana-cdktf/securityRules/allow 443]
cdk  
     Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
     

No outputs found.

参考URL

今回 CDKTF を触ったの初めてでした。以下のページを参考にさせていただきました。

Terraform 直接の場合と同じくステートを持ちます。

おわりに

とても簡単ではありますが、一通り触ってみました。触ったことがないものも少しだけ雰囲気を感じることができました。

もちろんコード類の書きっぷりだけでは簡単に比較はできず、運用スタイルや課題、組織が持っているスキルるなどにもよるかと思います。

第一印象としては、Azure 限定でよいなら、Bicep は良いなと思いました。

[Azure/Ansible] 関連付けされていない NSG を Ansible で洗い出してまとめ削除する

はじめに

先日、関連付けされていない NSG を Azure CLI で洗い出してまとめ削除するという記事を投稿しました。

tekunabe.hatenablog.jp

これを Ansible でもやってみようとおもって、やってみました。

  • 環境
    • ansible-core 2.12.1
    • azure.azcollection 1.14.0

前提環境

前回の記事と同じです。

どのサブネット、どのネットワークインターフェースにも関連づいていない NSG は以下の 2つです。

  • nsg_free01
  • nsg_free02

Playbook

以下のような Playbook を書きました。

---
- hosts: localhost
  gather_facts: false
  connection: local

  vars:
    resource_group: sakana

  tasks:
    # 指定したリソースグループ内の NSG を一通り取得
    - name: Get all NSG
      azure.azcollection.azure_rm_securitygroup_info:
        resource_group: "{{ resource_group }}"
      register: res_free_nsg

    # 関連付けされていない NSG を抽出
    - name: Get free NSG
      ansible.builtin.set_fact:
        free_nsg_list: "{{ res_free_nsg.securitygroups | json_query(query_string) }}"
      vars:
        query_string: "[?!network_interfaces && !subnets].name"
    
    # 関連付けされていない NSG の名前をデバッグ表示
    - name: Debug free NSG
      ansible.builtin.debug:
        msg: "{{ free_nsg_list }}"

    # 関連付けされていない NSG を削除
    - name: Delete free NSG
      azure.azcollection.azure_rm_securitygroup:
        resource_group: "{{ resource_group }}"
        name: "{{ item }}"
        state: absent
      loop: "{{ free_nsg_list }}"

Azure CLI との大きな違いは以下の2つかと思います。

  • NSG の情報のキーはキャメルケースではなくアンダーバーつなぎなので networkInterfaces ではなく network_interfaces
  • 削除対象の NSGid ではなく name で指定可能

一方、JMESPATH (Ansibleフィルターとしては json_query)を使う点は同じにしました。

実行

PLAY [localhost] ******************************************************************

TASK [Get all NSG] ***************************************************************
ok: [localhost]

TASK [Get free NSG] ***************************************************************
ok: [localhost]

TASK [Debug free NSG] *************************************************************
ok: [localhost] => {
    "msg": [
        "nsg_free01",
        "nsg_free02"
    ]
}

TASK [Delete free NSG] ************************************************************
changed: [localhost] => (item=nsg_free01)
changed: [localhost] => (item=nsg_free02)

PLAY RECAP ************************************************************************
localhost                  : ok=4    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

できました。

[Azure] 関連付けされていない NSG を Azure CLI で洗い出してまとめ削除する

はじめに

セキュリティグループ(NSG)の設定の自動化を試しているうちに、どのサブネット、どのネットワークインターフェースにも関連づいていない不要な NSG がちらほらできてしまいました。これらを洗い出したり、まとめ削除したいなと思うことがありました。

Azure CLI で割とさくっとできそうだったので試しました。

  • 環境
    • Azure CLI 2.43.0

前提環境

リソースグループ sakana に 6つある状態から始めます。

NSG 関連付け状態
nsg01 サブネットに関連付け
vm0201, vm9901 ネットワークインターフェースに関連付け
nsg02 サブネット、ネットワークインターフェースに関連付け
nsg_free01, nsg_free02 関連付けなし(洗い出し、削除対象)

一覧表示(az network nsg list コマンド

az network nsg list -g sakana 
[
  {
    // ...(略)...
    "name": "nsg01",
    // ...(略)...
    "subnets": [
      {
        "id": "/subscriptions/xxx/resourceGroups/sakana/providers/Microsoft.Network/virtualNetworks/vnet01/subnets/subnet01",
      }
    ],
    // ...(略)...
  },
  {
    // ...(略)...
    "name": "nsg02",
    "networkInterfaces": [
      {
        "id": "/subscriptions/xxx/resourceGroups/sakana/providers/Microsoft.Network/networkInterfaces/sakana-rhel82-test2995",
        "resourceGroup": "sakana"
      }
    ],
    // ...(略)...
    "subnets": [
      {
        "id": "/subscriptions/xxx/resourceGroups/sakana/providers/Microsoft.Network/virtualNetworks/vnet01/subnets/subnet-vm",
        "resourceGroup": "sakana"
      }
    ],
    // ...(略)...
  },
  {
    // ...(略)...
    "name": "nsg_free01",
    // ...(略)...
  },
  {
    // ...(略)...
    "name": "nsg_free02",
    // ...(略)...
  },
  {
    // ...(略)...
    "name": "vm0201",
    "networkInterfaces": [
      {
        "id": "/subscriptions/xxx/resourceGroups/sakana/providers/Microsoft.Network/networkInterfaces/vm0201",
        "resourceGroup": "sakana"
      }
    ],
    // ...(略)...
  },
  {
    // ...(略)...
    "name": "vm9901",
    "networkInterfaces": [
      {
        "id": "/subscriptions/xxx/resourceGroups/sakana/providers/Microsoft.Network/networkInterfaces/vm9901",
        "resourceGroup": "sakana"
      }
    ],
    // ...(略)...
  }
]

洗い出し

何かに関連付けしている NSGsubnetsnetworkInterfaces というキーがあることが分かりました。

なので、これらのキーがないものと言う条件を --query に指定します。見やすいように名前だけ表示するようにします。条件の書式は JMESPATH によるものです。

コマンド

az network nsg list -g sakana --query "[?!networkInterfaces && !subnets].name"

結果

[
  "nsg_free01",
  "nsg_free02"
]

狙い通りの対象の NSG が洗い出せました。

まとめて削除

今度は、洗い出した NSG をまとめて削除します。

削除のコマンドは az network nsg delete です。複数の NSG を指定するには -n ではなく、--ids を利用するといいようです。

なので、洗い出しコマンド(az network nsg list) の --query では、name ではなく id を指定します。

また、az network nsg delete--ids ではスペース区切りの ID を指定する必要あるので、洗い出しの az network nsg list 側のフォーマットは -o tsv にします。Azure CLI では、-@ を使うとパイプで渡された値を取得できるようです。今回はこれを使ってみます。

まとめると以下のコマンドです。

az network nsg list -g sakana \
  --query "[?!networkInterfaces && !subnets].id" \
  -o tsv | az network nsg delete --ids @-

これらのやり方は、「Azure CLI を正しく使用するためのヒント)」の「別のコマンドへ値を渡す」でヒントを得ました。

結果

[
  null,
  null
]

使い方がいまいちなようで、結果もちょっとイマイチですね・・

消えたことを確認します。

コマンド

az network nsg list -g sakana --query "[].name" 

結果

[
  "nsg01",
  "nsg02",
  "vm0201",
  "vm9901"
]

無事に対象の nsg_free01nsg_free02 が消えました。

おわりに

条件にマッチした一覧を洗い出してまとめて操作、というのはいろいろ応用がききそうな気がします。

ただし、まとめて削除するのはリスクがあることがあるため、いったん洗い出すなど動作確認をしながら実施したほうがよさそうです。

参考

Ansible 版

tekunabe.hatenablog.jp

[Azure] ネットワークセキュリティグループ(NSG)のルールのプロトコルの大文字小文字事情

はじめに

先日、以下の記事を拝見しました。

zenn.dev

そこで、ネットワークセキュリティグループのルール内のプロトコルTCP など)の大文字小文字が、ポータルから設定するときと、Ansible から設定するときとで異なることを知りました。

気になったので、いくつかの設定方法でこのあたりの事情を調べてみました。

  • 環境
    • Azure ポータル 本記事執筆時点
    • Ansible azure.azcollection コレクション 1.14.0
    • Azure CLI 2.43.0

設定方法と結果

プロトコルTCPUDP 等が指定できますが TCP で試しました。

方法 入力値 ポータルで確認した結果 補足
Azure ポータル TCP TCP すべて大文字のプロトコル名を選択する
Ansible azure.azcollection.azure_rm_securitygroup モジュールprotocol オプション Tcp Tcp
同上 TCP (エラー) モジュールの仕様上、TCPとしては Tcp しか受け入れられないため設定できない
Azure CLI az network nsg rule create--protocol オプション Tcp Tcp
同上 TCP Tcp API リクエストとしては "protocol": "Tcp" に変換された
API Security Rules - Create Or Update(実際は Ansible azure.azcollection.azure_rm_resource モジュール経由) TCP TCP そのまま
同上 Tcp Tcp そのまま

Azure CLI で、全て大文字の受け入れられた点と、変換される点が意外でした。