PlantUMLでそれなりにきれいなAzure構成図を描いてみる

# はじめに

PlantUML はテキストで作図できるため、作図のために別ツールを起動して、画像で出力して、という手順がなく Markdown 内でちょっとした図を作成する際に便利です。

PlantUML には AWS や Azure のアイコンライブラリがあり、クラウド環境の構成図も作成できます。AWSは PlantUML で作図した事例も多く、例えば以下のような解説記事があります。

https://qiita.com/sakai00kou/items/18e389fc85a8af59d9e0

一方でAzureはあまりQiitaで解説された事例があまり見つからなかったので、今回書いてみることにします。

# 完成図

[Microsoft Learn にあるNATゲートウェイのチュートリアル](https://learn.microsoft.com/ja-jp/azure/nat-gateway/quickstart-create-nat-gateway-portal)の構成図を参考に以下の図を作成しました。

**作成した図**

```plantuml
@startuml
!define AzurePuml https://raw.githubusercontent.com/plantuml-stdlib/Azure-PlantUML/release/2-2/dist
!includeurl AzurePuml/AzureCommon.puml
!includeurl AzurePuml/AzureSimplified.puml

!includeurl AzurePuml/Networking/all.puml
!includeurl AzurePuml/Compute/AzureVirtualMachine.puml
!includeurl AzurePuml/Management/AzureManagementPortal.puml

!include <cloudinsight/pubu>
!include <cloudinsight/user>

skinparam rectangle<<cloudinsight>> {
    BackgroundColor AZURE_BG_COLOR
    BorderColor transparent
    Shadowing false
}

!procedure $CiconObject($alias, $sprite, $label)
rectangle "<color:%lighten(AZURE_SYMBOL_COLOR,100)><$sprite{scale=1.8}></color>\n==$label" as $alias<<cloudinsight>>
!endprocedure

!procedure $AzureGroup($alias, $sprite, $label)
rectangle "<color:AZURE_SYMBOL_COLOR><$sprite{scale=0.5}></color>\n$label" as $alias
!endprocedure

skinparam linetype ortho

left to right direction

$CiconObject(user, $user, "")

AzureManagementPortal(portal, "Portal", )

$AzureGroup(vnetTest, $AzureVirtualNetwork, "vnet-1") {
    $AzureGroup(nsgBastion, $AzureNSG, "NSG") {
        $AzureGroup(snetBastion, $AzureSubnet, "AzureBastionSubnet") {
            AzurePublicIPAddress(bastionPublicIP, "public-ip",)
            AzureBastion(bastion, "bastion", )
        }
    }
    $AzureGroup(nsgSubnet, $AzureNSG, "NSG") {
        $AzureGroup(snetDefault, $AzureSubnet, "subnet-1") {
            AzureVirtualMachine(vmTest, "vm-1", )
        }
    }
    AzureNAT(nat, "nat-gateway", )
}

$CiconObject(internet, $pubu, "Internet")

user <-> portal: TLS
portal <---> bastionPublicIP: TLS\n443, Internet
bastionPublicIP -- bastion
bastion -----> vmTest: Remote protocol\l(RDP, SSH)
vmTest --> nat
nat --> internet

@enduml
```

**参考にした図**

![](https://learn.microsoft.com/ja-jp/azure/nat-gateway/media/quickstart-create-nat-gateway-portal/nat-gateway-qs-resources.png)
画像引用元: https://learn.microsoft.com/ja-jp/azure/nat-gateway/quickstart-create-nat-gateway-portal

# コード解説

今回前述の図を作成するにあたって書いたコードは以下です。

<details>
<summary>コード全体</summary>

```
@startuml
!define AzurePuml https://raw.githubusercontent.com/plantuml-stdlib/Azure-PlantUML/release/2-2/dist
!includeurl AzurePuml/AzureCommon.puml
!includeurl AzurePuml/AzureSimplified.puml

!includeurl AzurePuml/Networking/all.puml
!includeurl AzurePuml/Compute/AzureVirtualMachine.puml
!includeurl AzurePuml/Management/AzureManagementPortal.puml

!include <cloudinsight/pubu>
!include <cloudinsight/user>

skinparam rectangle<<cloudinsight>> {
    BackgroundColor AZURE_BG_COLOR
    BorderColor transparent
    Shadowing false
}

!procedure $CiconObject($alias, $sprite, $label)
rectangle "<color:%lighten(AZURE_SYMBOL_COLOR,100)><$sprite{scale=1.8}></color>\n==$label" as $alias<<cloudinsight>>
!endprocedure

!procedure $AzureGroup($alias, $sprite, $label)
rectangle "<color:AZURE_SYMBOL_COLOR><$sprite{scale=0.5}></color>\n$label" as $alias
!endprocedure

skinparam linetype ortho

left to right direction

$CiconObject(user, $user, "")

AzureManagementPortal(portal, "Portal", )

$AzureGroup(vnetTest, $AzureVirtualNetwork, "vnet-1") {
    $AzureGroup(nsgBastion, $AzureNSG, "NSG") {
        $AzureGroup(snetBastion, $AzureSubnet, "AzureBastionSubnet") {
            AzurePublicIPAddress(bastionPublicIP, "public-ip",)
            AzureBastion(bastion, "bastion", )
        }
    }
    $AzureGroup(nsgSubnet, $AzureNSG, "NSG") {
        $AzureGroup(snetDefault, $AzureSubnet, "subnet-1") {
            AzureVirtualMachine(vmTest, "vm-1", )
        }
    }
    AzureNAT(nat, "nat-gateway", )
}

$CiconObject(internet, $pubu, "Internet")

user <-> portal: TLS
portal <---> bastionPublicIP: TLS\n443, Internet
bastionPublicIP -- bastion
bastion -----> vmTest: Remote protocol\l(RDP, SSH)
vmTest --> nat
nat --> internet

@enduml
```

</details>

PlantUML には標準ライブラリとして AWS など各種アイコンが使用できるようになっており、 Azure についても用意されています。

https://plantuml.com/ja/stdlib

ただし PlantUML サーバが古いと同梱されているアイコンライブラリのバージョンも古かったりするため、この記事では GitHub 上の Azure アイコンライブラリ v2.2 を参照して作図しています。 

https://github.com/plantuml-stdlib/Azure-PlantUML/tree/master

```
!define AzurePuml https://raw.githubusercontent.com/plantuml-stdlib/Azure-PlantUML/release/2-2/dist
!includeurl AzurePuml/AzureCommon.puml
!includeurl AzurePuml/AzureSimplified.puml
```

また Azure アイコンライブラリの GitHub ページには [サンプルコード](https://github.com/plantuml-stdlib/Azure-PlantUML/tree/master/samples) や [アイコン一覧](https://github.com/plantuml-stdlib/Azure-PlantUML/blob/master/AzureSymbols.md) などがあるため、使用方法の確認でも参考にするとよいです。

PlantUMLサーバがネットワークへのアクセスを遮断されているなどで外部URLのアイコンライブラリが利用できない際には、標準ライブラリとして同梱されている方を参照するよう変更してください。

```
!include <azure/AzureCommon>
!include <azure/AzureSimplified>
```

PlantUML にはプリプロセッサや skinparam コマンドなどがあり、記述内容の再利用や、スタイルの共通化ができるため、今回の作図ではこれらの機能も使っています。

https://plantuml.com/ja/preprocessing

https://plantuml.com/ja/skinparam

## グループオブジェクトの作成

AWS のアイコンライブラリはVPCやサブネットなとがグループオブジェクトとして定義されておりいい感じに描画できるのですが、Azureはどのアイコンも同じスタイルとして定義されているので、そのまま利用するとVNetなどがかさばります。

VNet などは単純な rectangle を使用してもいいのですが、アイコンは表示したかったので、同様のスタイルの使いまわせるように procedure で共通化しました。

<table>
<thead>
<tr>
<th>見栄えの調整前</th>
<th>見栄えの調整後</th>
</tr>
</thead>

<tr>
<td>

```plantuml
@startuml
!define AzurePuml https://raw.githubusercontent.com/plantuml-stdlib/Azure-PlantUML/release/2-2/dist
!includeurl AzurePuml/AzureCommon.puml

!includeurl AzurePuml/Networking/all.puml
!includeurl AzurePuml/Compute/AzureVirtualMachine.puml
!includeurl AzurePuml/Management/AzureManagementPortal.puml

skinparam linetype ortho

left to right direction

AzureVirtualNetwork(vnetTest, "vnet-1",) {
    AzureNSG(nsgBastion, "NSG",) {
        AzureSubnet(snetBastion, "AzureBastionSubnet",) {
            AzurePublicIPAddress(bastionPublicIP, "public-ip",)
            AzureBastion(bastion, "bastion", )
        }
    }
    AzureNSG(nsgSubnet, "NSG",) {
        AzureSubnet(snetDefault, "subnet-1",) {
            AzureVirtualMachine(vmTest, "vm-1", )
        }
    }

}

bastionPublicIP -- bastion
bastion --> vmTest

@enduml
```

</td>
<td>

```plantuml
@startuml
!define AzurePuml https://raw.githubusercontent.com/plantuml-stdlib/Azure-PlantUML/release/2-2/dist
!includeurl AzurePuml/AzureCommon.puml
!includeurl AzurePuml/AzureSimplified.puml

!includeurl AzurePuml/Networking/all.puml
!includeurl AzurePuml/Compute/AzureVirtualMachine.puml
!includeurl AzurePuml/Management/AzureManagementPortal.puml

!procedure $AzureGroup($alias, $sprite, $label)
rectangle "<color:AZURE_SYMBOL_COLOR><$sprite{scale=0.5}></color>\n$label" as $alias
!endprocedure

skinparam linetype ortho

left to right direction


$AzureGroup(vnetTest, $AzureVirtualNetwork, "vnet-1") {
    $AzureGroup(nsgBastion, $AzureNSG, "NSG") {
        $AzureGroup(snetBastion, $AzureSubnet, "AzureBastionSubnet") {
            AzurePublicIPAddress(bastionPublicIP, "public-ip",)
            AzureBastion(bastion, "bastion", )
        }
    }
    $AzureGroup(nsgSubnet, $AzureNSG, "NSG") {
        $AzureGroup(snetDefault, $AzureSubnet, "subnet-1") {
            AzureVirtualMachine(vmTest, "vm-1", )
        }
    }

}

bastionPublicIP -- bastion
bastion --> vmTest

@enduml
```

</td>

</table>

共通化の定義部分と作図に最小限必要な部分を抜き出したものが以下です。

```plaintext:共通化処理と使用例
@startuml
!define AzurePuml https://raw.githubusercontent.com/plantuml-stdlib/Azure-PlantUML/release/2-2/dist
!includeurl AzurePuml/AzureCommon.puml
!includeurl AzurePuml/AzureSimplified.puml
!includeurl AzurePuml/Networking/all.puml
!includeurl AzurePuml/Compute/AzureVirtualMachine.puml

!procedure $AzureGroup($alias, $sprite, $label)
rectangle "<color:AZURE_SYMBOL_COLOR><$sprite{scale=0.5}></color>\n$label" as $alias
!endprocedure

$AzureGroup(snetDefault, $AzureSubnet, "subnet-1") {
    AzureVirtualMachine(vmTest, "vm-1", )
}
@enduml
```

## Azure 以外のアイコンの補完

Azure アイコンライブラリは Azure のサービスのアイコンしか含まれていないので、ユーザーやインターネットなどのシンボルは [Cloud Insight アイコンライブラリ](https://plantuml.com/ja/stdlib#dfa1f2b2b70eb2be)で補完しました。

Cloud Insight アイコンはそのままだと色合いとサイズ感が Azure アイコンと異なるので、 procedure と skinparam を使用して調整したアイコンを追加できるようにしました。


<table>
<thead>
<tr>
<th>見栄えの調整前</th>
<th>見栄えの調整後</th>
</tr>
</thead>

<tr>
<td>

```plantuml
@startuml
!define AzurePuml https://raw.githubusercontent.com/plantuml-stdlib/Azure-PlantUML/release/2-2/dist
!includeurl AzurePuml/AzureCommon.puml
!includeurl AzurePuml/AzureSimplified.puml

!includeurl AzurePuml/Networking/all.puml
!includeurl AzurePuml/Compute/AzureVirtualMachine.puml
!includeurl AzurePuml/Management/AzureManagementPortal.puml

!include <cloudinsight/pubu>
!include <cloudinsight/user>

!procedure $AzureGroup($alias, $sprite, $label)
rectangle "<color:AZURE_SYMBOL_COLOR><$sprite{scale=0.5}></color>\n$label" as $alias
!endprocedure

skinparam linetype ortho

left to right direction

rectangle "<$user>" as user

$AzureGroup(vnetTest, $AzureVirtualNetwork, "vnet-1") {
    $AzureGroup(nsgBastion, $AzureNSG, "NSG") {
        $AzureGroup(snetBastion, $AzureSubnet, "AzureBastionSubnet") {
            AzureVirtualMachine(bastion, "vm-1", )
            AzurePublicIPAddress(bastionPublicIP, "public-ip",)
        }
    }
}

rectangle "<$pubu>" as internet

user -- bastion
bastion -- bastionPublicIP
bastionPublicIP -- internet

@enduml
```

</td>
<td>

```plantuml
@startuml
!define AzurePuml https://raw.githubusercontent.com/plantuml-stdlib/Azure-PlantUML/release/2-2/dist
!includeurl AzurePuml/AzureCommon.puml
!includeurl AzurePuml/AzureSimplified.puml

!includeurl AzurePuml/Networking/all.puml
!includeurl AzurePuml/Compute/AzureVirtualMachine.puml
!includeurl AzurePuml/Management/AzureManagementPortal.puml

!include <cloudinsight/pubu>
!include <cloudinsight/user>

skinparam rectangle<<cloudinsight>> {
    BackgroundColor AZURE_BG_COLOR
    BorderColor transparent
    Shadowing false
}

!procedure $CiconObject($alias, $sprite, $label)
rectangle "<color:%lighten(AZURE_SYMBOL_COLOR,100)><$sprite{scale=1.8}></color>\n==$label" as $alias<<cloudinsight>>
!endprocedure

!procedure $AzureGroup($alias, $sprite, $label)
rectangle "<color:AZURE_SYMBOL_COLOR><$sprite{scale=0.5}></color>\n$label" as $alias
!endprocedure

skinparam linetype ortho

left to right direction

$CiconObject(user, $user, "")

$AzureGroup(vnetTest, $AzureVirtualNetwork, "vnet-1") {
    $AzureGroup(nsgBastion, $AzureNSG, "NSG") {
        $AzureGroup(snetBastion, $AzureSubnet, "AzureBastionSubnet") {
            AzureVirtualMachine(bastion, "vm-1", )
            AzurePublicIPAddress(bastionPublicIP, "public-ip",)
        }
    }
}

$CiconObject(internet, $pubu, "Internet")

user -- bastion
bastion -- bastionPublicIP
bastionPublicIP -- internet

@enduml
```

</td>
</table>

共通化の定義部分と作図に最小限必要な部分を抜き出したものが以下です。

```plaintext:共通化処理と使用例
@startuml
!define AzurePuml https://raw.githubusercontent.com/plantuml-stdlib/Azure-PlantUML/release/2-2/dist
!includeurl AzurePuml/AzureCommon.puml
!includeurl AzurePuml/AzureSimplified.puml
!include <cloudinsight/pubu>
!include <cloudinsight/user>

skinparam rectangle<<cloudinsight>> {
    BackgroundColor AZURE_BG_COLOR
    BorderColor transparent
    Shadowing false
}

!procedure $CiconObject($alias, $sprite, $label)
rectangle "<color:%lighten(AZURE_SYMBOL_COLOR,100)><$sprite{scale=1.8}></color>\n==$label" as $alias<<cloudinsight>>
!endprocedure

$CiconObject(user, $user, "")
$CiconObject(internet, $pubu, "")
@enduml
```

# おわりに

図が複雑になるとレイアウト調整で嵌ったりするのでこだわりすぎは禁物ですが、構成図をコードで作成できるとドキュメントと作図がテキストエディタで完結できたり、プリプロセッサや skinparam 等によりコンポーネントやスタイルを描画を共通化できたりするので、適宜活用したいなと思いました。