Google Apps Script(GAS)による共有ドライブの操作まとめ

本記事では、Google Apps Script(GAS)による共有ドライブの操作をまとめて掲載しています。

公式ドキュメントにはGoogle ドライブのリファレンスは存在するのですが、残念なことに共有ドライブについては見つかりません。（もしご存知でしたらご教示ください）

また、ネット上にも最新の共有ドライブの操作方法に関するまとめ記事はありませんでした。そのため、Google Apps Scriptで共有ドライブを操作する方法についてまとめましたのでご参考ください。

# 共有ドライブとは

引用元： [共有ドライブとは - Google Workspace 管理者 ヘルプ](https://support.google.com/a/answer/7212025?hl=ja)

> Google ドライブの共有ドライブでは、チームで使うファイルの保存、検索、および、ファイルへのアクセスができます。
> 共有ドライブ内のファイルは、個人ではなくチームが所有します。
> メンバーがいなくなってもファイルはそのまま残るため、どこにいてもどのデバイスからでも、チームで引き続き情報を共有して仕事を進めることができます。

通常のGoogleドライブでは、フォルダやファイルといったアイテムの所有者（オーナー）は個人のアカウントになりますが、共有ドライブではチームが所有者になります。

例えば、従業員が離職する際には所有者を全て移譲してアイテムを存続させますが、共有ドライブではチームが所有者であるため、移譲の操作が不要となり、共有ドライブ内にアイテムが存続します。

## 共有ドライブが利用可能なプラン（2020年10月時点)

### Google Workspace
 ※2020年10月6日よりG Suiteから名称変更されました

- Google Workspace Business Standard
- Google Workspace Business Plus
- Google Workspace 大規模ビジネス

### G Suite

- G Suite Enterprise
- G Suite Enterprise for Education
- G Suite Essentials
- G Suite Business
- G Suite for Education
- G Suite for Nonprofits


# 共有ドライブの操作

共有ドライブを操作するコード集です。主に以下の操作についてのコードを記載しています。

- 共有ドライブの作成
- フォルダの作成
- ファイルの作成
- フォルダの移動
- ファイルの移動
- 共有ドライブの削除

**Googleドライブは「DriveApp」**を、**共有ドライブは「Drive」**を、それぞれ使用します。

[Class DriveApp  |  Apps Script  |  Google Developers](https://developers.google.com/apps-script/reference/drive/drive-app)

また、**Googleドライブ(DriveApp)と共有ドライブ(Drive)とで扱うオブジェクトは型が異なります**。

例えば、共有ドライブはTeamDrive型ですが、GoogleドライブではFolder型として扱います。
実際にはファイルまで含めるとDriveAppで操作する方が多くなりますので、Folder型に変換して操作する方が便利です。

## 事前準備

共有ドライブの操作を実装する前に、スクリプトエディタで「Drive API」を有効化してください。

1. スクリプトエディタのメニュー > 「リソース」 > 「Googleの拡張サービス」 を選択
2. 「Drive API」を有効化

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581277/585da355-f2b3-5dc6-7f25-a45fa528f21f.png)


## 共有ドライブの操作

下記のコードは切り貼りして使うこともできますし、そのままライブラリ化できるようにしてあります。
複数のスクリプトで共通的に使うことになるかと思いますので、ライブラリ化することをオススメします。

ライブラリの作り方については以下をご参考ください。
[ワンストップでわかる Google Apps Script ライブラリの使い方と作り方 - Qiita](https://qiita.com/shikumiya_hata/items/5e11ff875a9dd050a722)

共有ドライブの操作にはDrive.Teamdrivesにある以下のAPIが用意されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581277/992d3378-2d39-7071-e931-a5e1170f790e.png)

※各APIのリファレンス(非公式)は本記事末文に掲載しました

### TeamDrive型からDrive型に変換

DriveAppで共有ドライブを扱えるように型を変換します。
DriveApp#getFolderById()に共有ドライブのIDを渡します。

```javascript:TeamDrive型からDrive型に変換
/**
 * GoogleドライブのFolder型に変換
 * @param {TeamDrive} teamDrive - 共有ドライブ
 * @return {Folder} - フォルダ
 */
function toFolder(teamDrive) {
  // 共有ドライブのIDからFolderオブジェクトを取得する
  return DriveApp.getFolderById(teamDrive.id)
}
```

### 共有ドライブを取得

共有ドライブを名称から検索します。
Drive.Teamdrives.get()には、IDの指定が必須であるため、list()で全てのアイテムから名称に一致するものを検索します。

```javascript:共有ドライブ名から共有ドライブを取得
/**
 * 共有ドライブ名から共有ドライブを取得
 * @param {string} teamDriveName - 共有ドライブ名
 * @return {TeamDrive} - 共有ドライブ
 */
function getByName(teamDriveName) {
  // 共有ドライブのリストから一致する名前を検索
  var teamDrives = Drive.Teamdrives.list().items
  
  for (var i in teamDrives) {
    var teamDrive = teamDrives[i]
    if (teamDrive.name === teamDriveName) {
      return teamDrive
    }
  }
}
```

### 共有ドライブを新規作成

まず、論理的な共有ドライブを作成し、共有ドライブの設定を行います。
その後、ユニークなアイテムIDを生成し、物理的に共有ドライブを追加します。

```javascript:共有ドライブを新規作成
/**
 * 新規に共有ドライブを作成する
 * @param {string} teamDriveName - 共有ドライブ名
 * @return {TeamDrive} - 新規の共有ドライブ
 */
function create(teamDriveName) {
  // 論理的な共有ドライブを作成
  var teamDrive = Drive.newTeamDrive()
  teamDrive.name = teamDriveName // 共有ドライブに名前を付ける
  var teamDriveId = Utilities.getUuid() // アイテムのIDを生成
  
  // 物理的な共有ドライブを作成
  return Drive.Teamdrives.insert(teamDrive, teamDriveId) // ここで実際に共有ドライブが作られる
}
```

### 共有ドライブにフォルダを作成

共有ドライブの直下にフォルダを作成します。Folderに変換して操作を行います。

```javascript:共有ドライブにフォルダを作成
/**
 * 共有ドライブにフォルダを作成（ID指定）
 * @param {string} teamDriveId - 共有ドライブID
 * @param {string} folderName - フォルダ名
 * @return {Folder} - 作成したフォルダ
 */
function createFolderById(teamDriveId, folderName) {
  var parentFolder = DriveApp.getFolderById(teamDriveId) // 対象の共有ドライブを取得（親フォルダとなる）
  return parentFolder.createFolder(folderName) // 子フォルダを作成
}

/**
 * 共有ドライブにフォルダを作成
 * @param {TeamDrive} teamDrive - 親となる共有ドライブ
 * @param {string} folderName - フォルダ名
 * @return {Folder} - 作成したフォルダ
 */
function createFolder(teamDrive, folderName) {
  return createFolderById(teamDrive.id, folderName)
}
```

### 共有ドライブを削除

共有ドライブを削除するには、その直下のアイテムを全て削除する必要があります。
直下のフォルダとファイルを全て削除してから、共有ドライブ自体の削除を行います。

```javascript:共有ドライブを削除
/**
 * 共有ドライブを削除（ID指定）
 * @param {string} teamDriveId - 対象の共有ドライブID
 */
function removeById(teamDriveId) {
  var teamDriveFolder = DriveApp.getFolderById(teamDriveId)
  
  // 直下のフォルダを全て削除
  // 共有ドライブを削除するには中身を空にしなければならないので削除する
  var folders = teamDriveFolder.getFolders()
  while (folders.hasNext()) {
    var folder = folders.next()
    folder.setTrashed(true)
  }
  // 直下のファイルを全て削除
  var files = teamDriveFolder.getFiles()
  while (files.hasNext()) {
    var file = files.next()
    file.setTrashed(true)
  }
  
  // 空になったと認識されるまで時間がかかるので待つ
  Utilities.sleep(10000)
  
  // 共有Driveを削除
  Drive.Teamdrives.remove(teamDriveId)
}

/**
 * 共有ドライブを削除
 * @param {TeamDrive} teamDrive - 対象の共有ドライブ
 */
function remove(teamDrive) {
  removeById(teamDrive.id)
}
```

## 共有ドライブの操作例

以下はライブラリ化せず、functionをそのまま使う形で実装するコード例です。

```javascript:control.gs
/**
 * 共有ドライブの作成
 */
function createTeamDriveItems() {

  // ------------------------------
  // 共有ドライブ、フォルダ、ファイルの作成
  // ------------------------------
  // [作成するフォルダ構造]
  // 共有ドライブ
  // └ teamDriveA
  // │  ├ file
  // │  └ folderA-1
  // │      └ file
  // │      └ file
  // └ teamDriveB
  //     └ folderB-1
  //         └ file

  // 共有ドライブにフォルダ作成
  const newTeamDriveA = create('teamDriveA')
  const folderA = toFolder(newTeamDriveA)
 
  // 共有ドライブにフォルダ作成
  const newTeamDriveB = create('teamDriveB')
  const folderB = toFolder(newTeamDriveB)
  
  // 共有ドライブのフォルダ内にフォルダ作成
  const folderA1 = folderA.createFolder('folderA-1')
  const folderB1 = folderB.createFolder('folderB-1')
  
  // 共有ドライブのフォルダ内にファイル作成
  const inputFile = DriveApp.getFileById('XXXXXXXXXXXXXXXXXXXXXXXXX')
  const mediaData = inputFile.getBlob()
  
  const newFileOnA = folderA.createFile(mediaData)
  const newFileOnA1a = folderA1.createFile(mediaData)
  const newFileOnA1b = folderA1.createFile(mediaData)
  const newFileOnB = folderB.createFile(mediaData)
  
  //------------------------------
  // ファイルとフォルダの移動操作
  //------------------------------

  // ファイルを、共有DriveA内のフォルダから共有DriveB内のフォルダに移動
  newFileOnA1a.moveTo(folderB1)
  
  // フォルダを、共有DriveA内のフォルダから共有DriveB内のフォルダに移動
  folderA1.moveTo(folderB1)

  // [移動後のフォルダ構造]
  // 共有ドライブ
  // └ teamDriveA
  // │  ├ file
  // └ teamDriveB
  //     └ folderB-1
  //         └ file
  //         └ file
  //         └ folderA-1
  //             └ file

  //------------------------------
  // 共有ドライブの更新
  //------------------------------
  // 共有ドライブAの名前を変更
  newTeamDriveA.name = 'teamDriveA2'
  newTeamDriveA = Drive.Teamdrives.update(newTeamDriveA, newTeamDriveA.id)
}

/**
 * 共有ドライブの削除
 */
function removeTeamDrives() {
  
  const teamDriveA = getByName('teamDriveA2')
  if (teamDriveA) {
    remove(teamDriveA)
  }
  
  const teamDriveB = getByName('teamDriveB')
  if (teamDriveB) {
    remove(teamDriveB)
  }
  
}
```

前述のコード集をライブラリ化した場合は、function名の前にライブラリの識別子を付与して読み替えてください。

```javascript:ライブラリ化した場合の例（識別子はTeamDriveLibrary）
   const newTeamDriveA = TeamDriveLibrary.create('teamDriveA')
```

# 非公式APIリファレンス

以下、Drive.Teamdrivesで補完されるAPIについて、リファレンスを記載します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581277/992d3378-2d39-7071-e931-a5e1170f790e.png)

optionalArgsに渡すオプションについては、以下が参考になります。
例）

```javascript:optionalArgsに設定するパラメータ例
const optionalArgs = {
  useDomainAdminAccess: true,
  q: "name contains 'teamDriveA'",
  fields: 'nextPageToken, items(id, name)',
  pageToken: 'XXXXX',
  pageSize: 1
}
```

[Manage shared drives  |  Google Drive API  |  Google Developers](https://developers.google.com/drive/api/v3/manage-shareddrives)
[Search query terms  |  Google Drive API  |  Google Developers](https://developers.google.com/drive/api/v3/ref-search-terms#drive_properties)
[drive.teamdrives.list - Drive | AnyAPI Documentation](https://any-api.com/googleapis_com/drive/docs/teamdrives/drive_teamdrives_list)
[Drive.Teamdrives.List (Drive API v3 (Rev. 197) 1.25.0)](https://developers.google.com/resources/api-libraries/documentation/drive/v3/java/latest/com/google/api/services/drive/Drive.Teamdrives.List.html#setQ-java.lang.String-)

**get(String teamDriveId) : TeamDrive**
IDを指定して共有ドライブを取得します。

[引数]
teamDriveId - 対象の共有ドライブID

[戻り値]
TeamDrive - IDに一致する共有ドライブオブジェクト

**get(String teamDriveId, Object optionalArgs) : TeamDrive**
IDとオプションを指定して共有ドライブを取得します。

[引数]
teamDriveId - 対象の共有ドライブID
optionalArgs - 指定するオプション

[戻り値]
TeamDrive - IDに一致する共有ドライブオブジェクト

**insert(TeamDrive resource, String requestId) : TeamDrive**

共有ドライブを新規に作成します。

[引数]
resource - 共有ドライブ（論理） Drive.newTeamDrive()で生成
requestId - 共有ドライブのユニークID Utilities.getUuid()で生成

[戻り値]
TeamDrive - 作成した共有ドライブオブジェクト

**list() : TeamDriveList**
共有ドライブのリストを取得します。

[引数]
なし

[戻り値]
TeamDriveList - 共有ドライブリスト

**list(Object optionalArgs) : TeamDriveList**
オプションを指定して共有ドライブのリストを取得します。

[引数]
optionalArgs - 指定するオプション

[戻り値]
TeamDriveList - 共有ドライブリスト

**remove(String teamDriveId) : void**
共有ドライブを削除します。
ただし、空でない共有ドライブは削除できません。空にしてから削除してください。

[引数]
teamDriveId - 対象の共有ドライブID

[戻り値]
なし

**update(TeamDrive resource, String teamDriveId) : TeamDrive**
共有ドライブの情報を更新します。

[引数]
resource - 更新情報のオブジェクト

[戻り値]
TeamDrive - 更新された共有ドライブ


# おわりに

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581277/94fd031b-0c09-4f37-116f-c05271fdd7f9.png" width="200">
弊社は業務効率化・自動化など、仕組みで解決するお手伝いをさせていただいております。
お仕事のご依頼はコチラ↓までお願いいたします。

[株式会社シクミヤ](https://shikumiya.co.jp/)
note: [Visionary Base編集部](https://note.com/visionarybase)
Twitter: @shikumiya_hata