GoSpace Manager

Version v0.11.0

Manage Google Workspace resources using a developer-friendly CLI written in Go

Scripting Examples

PowerShell Module

You can find an auto-generated PowerShell module for GSM at https://github.com/hanneshayashi/gsm-powershell.
The module provides PowerShell functions for all GSM commands and should be fairly easy to use.
The module is created using Microsoft’s Crescendo.

Processing very large amounts of Data with PowerShell

While PowerShell is powerful and easy to use, it does have some shortcomings, unfortunately. Some of them are caused by the use of the .Net Framework powering its backend. One of these shortcomings is the lack of stream processing for JSON data, meaning that PowerShell has to load the entirety of a JSON object into memory, before being able to process it. This is USUALLY not an issue, but when dealing with very large amounts of data, it can actually cause PowerShell to die on you (this has happend to me more than once…). For these cases, you can try the --streamOutput flag, which will cause GSM to “stream” JSON objects from an array to stdout, instead of outputting an entire array at once. It will also imply the --compressOutput flag. For example:

[
        {
                "primaryEmail": "user1@example.org"
        },
        {
                "primaryEmail": "user2@example.org"
        },
        {
                "primaryEmail": "user3@example.org"
        }
]

Will become this:

{"primaryEmail":"user1@example.org"}
{"primaryEmail":"user2@example.org"}
{"primaryEmail":"user3@example.org"}

This may not seem like a huge difference, but this will allow you to “pipe” the stream to a commandlet, without having to wait for PowerShell to parse all of the data at once, like so:

gsm users list --streamOutput | % {$_ | ConvertFrom-Json } | % { $_.primaryEmail }

Since this is admitedly a little cumbersome to write every time, but still useful, I have made it the default for the GSM PowerShell Module, so you can just write

List-GSMUsers | % {$_.primaryEmail}

if you use the module.

Create a CSV with all Shared Drives in the domain

gsm drives list --useDomainAdminAccess | ConvertFrom-Json | Export-Csv ./drives.csv -NoClobber

Create CSV files for users per domain

$users = gsm users list --fields "users(primaryEmail),nextPageToken" | ConvertFrom-Json
foreach($user in $users) {
   $user.primaryEmail >> ($user.primaryEmail).SubString(($user.primaryEmail).IndexOf("@") + 1)
}

You can also take advantage of GSM’s batch processing by creating temporary CSV files (I may get around to implementing this natively at some point, but I haven’t found a CSV library yet that fits my needs and I don’t have time to write my own at the moment)

Rename all users in a domain to a different domain and remove the old domain’s alias (useful for migrations):

$users = gsm users list --fields "users(primaryEmail),nextPageToken" | ConvertFrom-Json
foreach($user in $users) {
   $user.primaryEmail + ";" + ($user.primaryEmail).Replace($oldDomain, $newDomain) >> ./renames.csv
}
$usersUpdated = gsm users patch batch --path ./renames.csv --userKey 1 --primaryEmail 2
$removeResults = gsm userAliases delete batch --path ./renames.csv --userKey 2 --alias 1

Since you take advantage of GSM’s built-in multithreading here, this would be way faster than calling

gsm users patch --userKey $user.primaryEmail --primaryEmail ($user.primaryEmail).Replace($oldDomain, $newDomain)
gsm userAliases delete --userKey ($user.primaryEmail).Replace($oldDomain, $newDomain) --alias $user.primaryEmail

inside the foreach loop.

Count the files in Drive for a list of users

$users = Get-Content ./users.csv
foreach($user in $users) {
    $fileCount = 0
    gsm files list --fields "files(id),nextPageToken" --corpora user --spaces drive --dwdSubject $user --q ("'" + $user + "' in owners and trashed = false") --streamOutput | Foreach-Object {$fileCount++}
    $user + ";" + $fileCount >> fileCount.csv
}

There are two things to note here:

  • The fields parameter. We only include the id of the file, because we are only interested in the number of files, but we need to include the nextPageToken, because responses to the files.list method are paged and without this field, GSM can’t retrieve the next page.
  • The query (--q ...). This is important, because otherwise we would also count files that are shared with the user. We also exclude files that are trashed.

Dealing with nested and multi-value properties

Some values, like users’ addresses or phone numbers are multi-value properties, meaning a user can have more than one address or phone number. Furthermore, the phoneNumer property is not just a list of numbers but it is a list objects. Since there is no standard way to deal with those properties, you need find a way to deal with them yourself. For example, you might want to use a simple PowerShell function to convert them to a usable format to use when exporting to CSV:

function Get-Multivalues {
    param(
        $multivalues
    )
    $returnArray = @()
    foreach ($multivalue in $multivalues) {
        $m = @()
        foreach ($property in $multivalue.psobject.properties) {
            if ([String]::IsNullOrEmpty($property.value) -eq $false) {
                $m += ($property.name + "=" + $property.value)
            }
        }
        $returnArray += ($m -join ";")
    }
    return ($returnArray -join ",")
}

You can use it like this:

$addresses = Get-Multivalues -multivalues $user.addresses

The output will look something like this:

"formatted=Some Street Address in City Country;type=work;primary=True"

Which is a format that can also be used to import the attribute again with GSM!

Find all publically shared files in your Google Workspace domain

Note that this script uses the GSM PowerShell Module to demontrate its usage. The same functionality can be achieved without the module and it is entirely optional!

This requires two different approaches, due to how the Drive API works. First, we will impersonate every user and list the files they have shared with the outside:

$safeDomains = (List-GSMDomains -Fields "domains(domainName)").domainName + (List-GSMDomainAliases -Fields "domainAliases(domainAliasName)").domainAliasName
List-GSMUsers -Fields "users(primaryEmail),nextPageToken" | Foreach-Object {
    $user = $_.primaryEmail
    List-GSMFiles -DwdSubject $user -Fields "files(id,permissions(id,type,domain))" -Q ("not visibility = 'limited' and '" + $user + "' in owners and trashed = false") -Corpora user -Spaces drive | Foreach-Object {
        foreach ($permission in $_.permissions) {
            if (($permission.type -eq "user") -or ($permission.type -eq "group") -or ($safeDomains.Contains($permission.domain))) {
                continue
            }
            New-Object PSObject -Property ([Ordered]@{
                    fileId       = $_.id
                    permissionId = $permission.id
                    type         = $permission.type
                    domain       = $permission.domain
                }) | Export-Csv -Path ("./exports/" + $user + ".csv") -NoClobber -Append
        }
    }
}

This script will create a .csv file for each user that contains a list of the files they have shared either publically or with an external domain.

Next, we will give our own user the organizer permission on every Shared Drive and scan it for publically shared files. We will keep a record of the permissions we gave our user, so we can remove them later.

$admin = (Get-GSMAbout -Fields "user(emailAddress)").user.emailAddress
List-GSMDrives -Fields "drives(id)" -UseDomainAdminAccess | Foreach-Object {
    $drive = $_.id
    $permissionAdmin = Create-GSMPermissions -EmailAddress $admin -UseDomainAdminAccess -Role organizer -FileId $drive -SendNotificationEmail:$false -Type user
    List-GSMFiles -DriveId $drive -Corpora drive -IncludeItemsFromAllDrives -Fields "files(id)" -Q "not visibility = 'limited' and trashed = false" | Foreach-Object {
        $_.id >> "./exports/drives.csv"
    }
    New-Object PSObject -Property ([Ordered]@{
            fileId       = $drive
            permissionId = $permissionAdmin.id
        }) | Export-Csv -Path "./exports/adminPermissions.csv" -NoClobber -Append
}
if (Test-Path "./exports/drives.csv") {
    List-GSMPermissionsBatch -Path "./exports/drives.csv" -Delimiter "," -FileId 1 -Fields_ALL "permissions(id,type,domain,permissionDetails(inherited))" | Foreach-Object {
        foreach($permission in $_.permissions) {
            if (($permission.permissionDetails.inherited -eq "true") -or ($permission.type -eq "user") -or ($permission.type -eq "group") -or ($safeDomains.Contains($permission.domain))) {
                continue
            }
            New-Object PSObject -Property ([Ordered]@{
                    fileId       = $_.fileId
                    permissionId = $permission.id
                    type         = $permission.type
                    domain       = $permission.domain
                }) | Export-Csv -Path "./exports/drives_details.csv" -NoClobber -Append
        }
    }
}

This script consists of two parts really. First, we list the files that are publically shared. Unfortunately, listing files on a Shared Drive does not give us the details for the permissions on the file (unlike in the example above), so we need to save the fileIds to a text file, so we can then list the permissions on those files later using a batch command. The actual permissions are saved to the “drive_details.csv” file. This approach does have the advantage of being able to “filter out” inherited permissions, however. If this is important to you, you may use the same approach for individual users, of course. The bad new is that this will probably take a little longer, because we have to call the API for each and every file in all Shared Drives (luckily GSM supports multi-threading).

Batch remove public permissions to files in users’ personal Drives

$users = gsm users list --fields "users(primaryEmail)" | ConvertFrom-Json
foreach ($user in $users.primaryEmail) {
    $fileName = ("./exports/" + $user + ".csv")
    if (Test-Path $fileName) {
        gsm permissions delete batch --path $fileName --delimiter "," --dwdSubject $user --fileId 1 --permissionId 2 --streamOutput | % { $_ | ConvertFrom-Json | Export-Csv -Path ("./exports/" + $user + "_deleted.csv") -Append -NoClobber}
    }
}

This will actually remove the permissions from the files we exported above.

Batch remove public permissions to files in Shared Drives

This is super easy, because we don’t need to impersonate anyone. We can just remove the permissions with a simple one-liner:

gsm permissions delete batch --path "./exports/drive_details.csv" --delimiter "," --skipHeader --fileId 1 --permissionId 2 --streamOutput | % { $_ | ConvertFrom-Json | Export-Csv -Path ("./exports/drives_deleted.csv") -Append -NoClobber}

Batch remove the permissions we gave our admin user

This is also easy, because we saved them in a nice CSV file. Since these are permissions to Shared Drives, we can use the –useDomainAdminAccess(_ALL) flag to utilize our Super User rights, although it shouldn’t be necessary:

$results = gsm permissions delete batch --path "./exports/adminPermissions.csv" --delimiter "," --fileId 1 --permissionId 2 --useDomainAdminAccess_ALL --compressOutput | ConvertFrom-Json

See Also