Google Apps Scriptで自動化するConfluenceのグループ追加

こちらの記事は株式会社メドレーのAdvent Calendar(2024)の14日目の記事です！

https://qiita.com/advent-calendar/2024/medley


こんにちは！コーポレートIT室の@sishoです。

コーポレートIT（情シス）として、生成AIの全社活用推進や、SlackやConfluence、Jiraなど全社SaaSの導入・企画改善、海外拠点のネットワーク立ち上げなど様々な業務を担当しています。
以前弊社ブログでご紹介した、**Slack × AppSheet × GAS を利用した ChatOps なアプリ開発** もご覧ください。

https://developer.medley.jp/entry/2022/12/23/153818

# 概要
今回は、**Google Apps Script（以降GAS）で実装するConfluenceのグループ追加**についてご紹介します。

Confluenceのスペース権限やページ権限の設定にグループを使っており、入社のたびに新入社員をAtlassianの管理画面からグループに追加していました。しかしながら、このグループ追加作業は、一人ずつ選択しグループに追加する必要があり非常に面倒です。

<img width="300" alt="mojikyo45_640-2.gif" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3939531/c3f8912b-5381-d66b-43b3-86f9ad6fea3b.png">


ということで、Google Apps Script（GAS）+ Atlassianの組織管理API（Organization REST API）を使ってグループ追加を自動化したよという話です。

なお、弊社ではAtlassian Guardを利用して、IdPからユーザープロビジョニングしています。そのため、本記事ではユーザープロビジョニング以降の処理について記載しています。

# 前提
今回使用しているAPIはすべて、Organization REST APIのAPIを使用しています。この記事で使用しているAPIは、Atlassian管理が [New User Management Experience](https://community.atlassian.com/t5/Articles/User-management-for-cloud-admins-just-got-easier/ba-p/1576592) になっている場合のみ、利用可能ですので注意してください。

https://developer.atlassian.com/cloud/admin/organization/rest/intro/#about


# 処理内容
以下の3ステップで処理を進めます。
1. **ユーザーとグループの一覧データ取得**：Atlassian管理からユーザー一覧とグループ一覧を取得
2. **グループIDの特定**：追加したいグループのグループIDを特定
3. **グループへのユーザー追加**：追加したいグループへユーザーを追加

今回の記事ではAPIを使う箇所の要点だけまとめていますので、実装の際には複数のグループに追加できるようにループ処理などを実装してください。

## 1. ユーザーとグループの一覧データ取得

まずは、ユーザー一覧とグループ一覧をAPIで取得します。

ユーザー一覧の取得APIは、**Search for users in an organization**を使います。
   
https://developer.atlassian.com/cloud/admin/organization/rest/api-group-users/#api-v1-orgs-orgid-users-search-post

実際にユーザー一覧を取得するGASを実装した例です。orgIdやtokenはスクリプトプロパティに記載しています。

```
const orgId = PropertiesService.getScriptProperties().getProperty("ORG_ID");
const token = PropertiesService.getScriptProperties().getProperty("BEARER_TOKEN");

function getAllUsersFromAtlassian() {
  const url = `https://api.atlassian.com/admin/v1/orgs/${orgId}/users/search`;

  const options = {
    'method': 'post',
    'headers': {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    'payload': JSON.stringify({
      'limit': 10000, // Number of users to retrieve per request
    })
  };

  let allUsers = [];
  let nextUrl = url;

  while (nextUrl) {
    try {
      const response = UrlFetchApp.fetch(nextUrl, options);
      const responseData = JSON.parse(response.getContentText());

      allUsers = allUsers.concat(responseData.data);

      nextUrl = responseData.links.next;
      if (nextUrl) {
        options.payload = null; // Payload is not needed for subsequent pages
      }
    } catch (error) {
      console.error('An error occurred:', error);
      break; // Terminate the loop if an error occurs
    }
  }

  return allUsers;
}
```

Search for users in an organizationのAPIのレスポンスから以下のようなデータが得られます。以下のレスポンスのうち、accountId（アカウントID）とemail（メールアドレス）をこのあとの処理で使います。

```
{
    "data":[
        {
            "accountId":"123456:123e4567-890a-12d3-456b-12345678901c",
            "accountType":"atlassian",
            "accountStatus":"active",
            "name":"hoge",
            "nickname":"hogehoge",
            "email":"hoge@medley.jp",
            "emailVerified":true,
            "statusInUserbase":true,
        }
    ],
];

```

グループ一覧の取得APIは、**Search for groups within an organization**を使います。

https://developer.atlassian.com/cloud/admin/organization/rest/api-group-groups/#api-v1-orgs-orgid-groups-search-post

実際にグループ一覧を取得するGASを実装した例です。
   
```
function getAllGroupsFromAtlassian() {
  const url = `https://api.atlassian.com/admin/v1/orgs/${orgId}/groups/search`;

  const options = {
    'method': 'post',
    'headers': {
      'Authorization': `Bearer ${token}`,
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    'payload': JSON.stringify({
      'limit': 1000, // Number of users to retrieve per request
    })
  };

  let allGroups = [];
  let nextUrl = url;

  while (nextUrl) {
    try {
      const response = UrlFetchApp.fetch(nextUrl, options);
      const responseData = JSON.parse(response.getContentText());

      allGroups = allGroups.concat(responseData.data);

      nextUrl = responseData.links.next;
      if (nextUrl) {
        options.payload = null; // Payload is not needed for subsequent pages
      }
    } catch (error) {
      console.error('An error occurred:', error);
      break; // Terminate the loop if an error occurs
    }
  }

  return allGroups;
}
```

グループ一覧取得APIのレスポンスは以下のようなデータが得られます。以下のレスポンスのうち、id（グループID）とname（グループ名）をこのあとの処理で使います。

```
{
    "data":[
        {
            "id":"123e4567-e89b-12d3-a456426614174000",
            "name":"confluence-users",
            "description":"hogehoge"
        }
    ],
    "links":{
        "next":"MDNlZWRmNzMtMWYwMi00NGM5LWI4NjgtZGQ1NTIwMjIwYjhj",
        "self":"MDI0MzkwOGEtMmYyOC00NzgzLWI5YTktMDUwZTExNzJjZDU4"}
    }
];

```

## 2. グループIDの特定
弊社では以下の表のように、ユーザと部署の対比表と、部署毎にどのグループをつけるかの表の2つを事前に定義しています。

a.ユーザ/部署対比表

|ユーザー名 | 部署名 |
| --- | --- |
| hoge@medley.jp | コーポレートIT室  |

b.部署毎の所属グループ表

|部署名 | グループ1 | グループ2 | グループ3 |
| --- | --- | --- | --- |
| コーポレートIT室 | confluence-users | jira-users | corporate-it |
   
ここでの処理は以下の順で行います。

1. **1.ユーザーとグループの一覧データ取得**で取得したユーザーの**email**をもとに、a.ユーザ/部署対比表から**部署名**を取得
2. **1.で取得した部署名**をもとに、b.部署毎の所属グループ表から各ユーザーの所属部署に応じたグループ1〜グループ3の**グループ名**を取得
3. **2.で取得したグループ名**を元に、**1.ユーザーとグループの一覧データ取得**で取得したグループ一覧から対象グループのidを取得

※この処理のコードを記事に書くと長くなってしまうため、割愛します。
   
## 3. グループへのユーザー追加 
あとは、**Add user to group**のAPIを使って、指定したグループにユーザーを追加するだけです。

https://developer.atlassian.com/cloud/admin/organization/rest/api-group-directory/#api-v1-orgs-orgid-directory-groups-groupid-memberships-post

**2.で取得したid（=グループID）** をURLパラメータのgroupIdに、**1.のユーザー一覧のaccountId（=アカウントID）** をRequest Bodyに格納して、**Add user to group** にリクエストします。

```
function addGroupsToUser(accountId, groupId) {
    const url = `https://api.atlassian.com/admin/v1/orgs/${CONFLUENCE.ORG_ID}/directory/groups/${groupId}/memberships`;
    const options = {
        method: 'post',
        headers: {
            'Authorization': `Bearer ${CONFLUENCE.AUTH.BEARER_TOKEN}`,
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        },
        payload: JSON.stringify({ "account_id": accountId }),
    };
    const response = UrlFetchApp.fetch(url, options);
    return JSON.parse(response.getContentText());
}
```

あとは、GASのトリガーで、一連の処理を定期実行するようにすれば自動化の完成です。

# まとめ
この記事では、Confluenceのグループ追加を自動化する方法について、具体的なコードを元に解説しました。
Atlassian管理APIの仕様はわかりづらいため、同じように困っている方の参考になればと思います。

# 最後に

メドレーのコーポレートIT室では、いろいろなプロジェクトが絶賛進行中です！

「ルールを作らず、仕組み/仕掛けで解決する」などの価値観に基づき行動し、中長期的な改善施策に取り組んでいます。
コーポレートITで一緒に働いているメンバーのnote記事もご覧ください。

https://note.com/medley/n/n1d03708de6d3

弊社コーポレートITで働くことにご興味ある方は、ぜひ採用ページからご応募お願いします！

https://www.medley.jp/jobs/

Medley Advent Calendar 2024、明日は @ryochike さんです！
ぜひお楽しみに！

---