Azure Service Operatorを試す

# この記事について

Kubernetesと連携して使うAzureのリソースを、Azure Service Operator (ASO) を使ってデプロイしてみた。

# 背景
- KubernetesはManifest、AzureリソースはBicepでIaC書いてたが、統一したい
- Bicepはデプロイするためのワークフローが必要だが、ワークフローを管理したくない

# 前提条件
- azure cli, kubectl, helmがインストール済み

# 環境
- Kunbernetes
    - Kubernetes Version: 1.28.5
- Azure CLI
    - version: 2.59.0

# AKSのデプロイ

AKSをデプロイする

```bash
export AKS_CLUSTER_NAME="myAKSCluster"
export AKS_RG_NAME="myResourceGroup"
export LOCATION="japaneast"

az group create -n ${AKS_RG_NAME} -l ${LOCATION}
az aks create -n ${AKS_CLUSTER_NAME} -g ${AKS_RG_NAME} -l ${LOCATION} \
  --enable-oidc-issuer \
  --enable-workload-identity \
  --network-plugin azure \
  --node-vm-size Standard_B2ms \
  --node-count 1
```

kubeconfigを取得する

```bash
az aks get-credentials --resource-group ${AKS_RG_NAME} --name ${AKS_CLUSTER_NAME} \
  --overwrite-existing
```

# Azure Service Operatorのインストール

ほとんど公式の手順そのまま

https://azure.github.io/azure-service-operator/

## cert-managerのインストール

```bash
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.14.1/cert-manager.yaml
```

## federated credentialのセットアップ

ASOがAzureのサービスをデプロイできるように権限を付与する必要がある。

まずはUser Assigned Identityをデプロイする。
権限はサブスクリプションのOwnerにする（つよいので注意）。

```bash
export USER_ASSIGNED_ID_NAME="myIdentity"
export USER_ASSIGNED_ID_RG="myResourceGroup"
export SUBSCRIPTION_ID="$(az account show --query id --output tsv)"

az identity create --name "${USER_ASSIGNED_ID_NAME}" \
  --resource-group "${USER_ASSIGNED_ID_RG}" \
  --location "${LOCATION}" \
  --subscription "${SUBSCRIPTION_ID}"

export USER_ASSIGNED_CLIENT_ID="$(az identity show --resource-group "${USER_ASSIGNED_ID_RG}" --name "${USER_ASSIGNED_ID_NAME}" --query 'clientId' -otsv)"
export IDENTITY_TENANT=$(az aks show --name ${AKS_CLUSTER_NAME} --resource-group ${AKS_RG_NAME} --query identity.tenantId -o tsv)

az role assignment create --assignee ${USER_ASSIGNED_CLIENT_ID} \
  --role Contributor --scope /subscriptions/${SUBSCRIPTION_ID}
```

クラスタのOIDC issuerのアドレスを取得する。

```bash
export AKS_OIDC_ISSUER="$(az aks show -n "${AKS_CLUSTER_NAME}" -g "${AKS_RG_NAME}" --query "oidcIssuerProfile.issuerUrl" -otsv)"
```

federated credentialを作成する。

```bash
az identity federated-credential create --name aso-federated-credential \
  --identity-name ${USER_ASSIGNED_ID_NAME} --resource-group ${USER_ASSIGNED_ID_RG} \
  --issuer ${AKS_OIDC_ISSUER} \
  --subject "system:serviceaccount:azureserviceoperator-system:azureserviceoperator-default" \
  --audiences "api://AzureADTokenExchange"
```

## ASOのインストール

helmのリポジトリを追加する。

```bash
helm repo add aso2 https://raw.githubusercontent.com/Azure/azure-service-operator/main/v2/charts
helm repo update
```

helmのvalues.yamlを作成する。
認証はWorkload Identityで行うようにする。

https://azure.github.io/azure-service-operator/guide/authentication/credential-format/#azure-workload-identity

```bash
cat - << EOF > values.yaml
azureSubscriptionID: "${SUBSCRIPTION_ID}"
azureTenantID: "${IDENTITY_TENANT}"
azureClientID: "${USER_ASSIGNED_CLIENT_ID}"
useWorkloadIdentityAuth: true
crdPattern: 'resources.azure.com/*;containerservice.azure.com/*;keyvault.azure.com/*;managedidentity.azure.com/*;eventhub.azure.com/*'
EOF
```

Azure Service Operatorをインストールする。
デフォルトのnamespace名が、長い…。

```bash
helm upgrade --install aso2 aso2/azure-service-operator \
    --create-namespace \
    --namespace=azureserviceoperator-system \
    -f values.yaml
```

そのうちPodが立ち上がる。

```bash
$ kubectl get pods -n azureserviceoperator-system 
NAME                                                       READY   STATUS    RESTARTS        AGE
azureserviceoperator-controller-manager-5df476889c-x2crc   1/1     Running   1 (6m28s ago)   6m44s
```

# リソースをデプロイしてみる

サポートされているリソースの一覧は、以下から確認できる。

https://azure.github.io/azure-service-operator/reference/

## Resource Group

デプロイ先のnamespaceを作成する

```bash
kubectl create ns aso-sample
```

リソースグループのCustomResourceを作成する。

```bash
cat <<EOF | kubectl apply -f -
apiVersion: resources.azure.com/v1api20200601
kind: ResourceGroup
metadata:
  name: aso-sample-rg
  namespace: aso-sample
spec:
  location: japaneast
EOF
```

kubectlでresourcegroupsを取得すると、READY=Trueと表示される。

```bash
$ kubectl get resourcegroups.resources.azure.com -n aso-sample
NAME            READY   SEVERITY   REASON      MESSAGE
aso-sample-rg   True               Succeeded
```

同様にaz cliでリソースグループが正しく作成できていることが確認できる。

```bash
$ az group list -o table --query "[?name=='aso-sample-rg']"
Name           Location
-------------  ----------
aso-sample-rg  japaneast
```

## User Assigned Identity

Manifestが簡単そうなので、User Assigned Identityをデプロイしてみる。
サンプルをほぼそのままコピペ。

```bash
cat <<EOF | kubectl apply -f -
apiVersion: managedidentity.azure.com/v1api20230131
kind: UserAssignedIdentity
metadata:
  name: sampleuserassignedidentity
  namespace: aso-sample
spec:
  location: japaneast
  owner:
    name: aso-sample-rg
  operatorSpec:
    configMaps:
      clientId:
        name: umi-cm
        key: clientId
      principalId:
        name: umi-cm
        key: principalId
      tenantId:
        name: umi-cm
        key: tenantId
    secrets:
      clientId:
        name: umi-secret
        key: clientId
      principalId:
        name: umi-secret
        key: principalId
      tenantId:
        name: umi-secret
        key: tenantId
EOF
```

同様に、az cliからもデプロイされていることを確認。

```bash
$ az identity list -g aso-sample-rg \
  -o table --query "[?name=='sampleuserassignedidentity']"

Name                        Location    TenantId                              PrincipalId                           ClientId                              ResourceGroup
--------------------------  ----------  ------------------------------------  ------------------------------------  ------------------------------------  ---------------
sampleuserassignedidentity  japaneast   454dfabb-ba50-4830-be47-c8530d997f69  30e89404-8cc4-4231-9661-67ea882a8fbe  b4833b6d-5498-4a32-acf5-f5f6aedfd90f  aso-sample-rg
```


# リソースを消してみる

リソースグループを消す（時間が、ちょっとかかる）。

```bash
$ kubectl delete resourcegroups.resources.azure.com -n aso-sample aso-sample-rg 
resourcegroup.resources.azure.com "aso-sample-rg" deleted
```

リソースグループに入っていたUser Assigned Identityのカスタムリソースも、ちゃんと消えてる！
素晴らしい！

```bash
$ kubectl get userassignedidentities.managedidentity.azure.com -n aso-sample 
No resources found in aso-sample namespace.
```

# まとめ

Azure Service OperatorをAKSにインストールし、簡単なAzureリソースを作成、削除してみた。
期待していた通り、Kubernetesのリソースを扱うが如く、Azureリソースを作成できた。

ただし、サンプルを軽く覗いた感じ、VMやDB等、よく使うリソースは設定も複雑っぽいので、個別に検証していきたい。