Rough notes on using GAM to print all Members of all Groups in Google Workspace 1

Since google failed to provide helpful UI for the administrators, we used GAMADV to print all members of all groups.

GAMADV-XTD3 6.30.10 - https://github.com/taers232c/GAMADV-XTD3 - pyinstaller
Ross Scroggs <[email protected]>
Python 3.11.0 64-bit final
Linux Ubuntu 22.04 Jammy Jellyfish x86_64
Path: /home/bib/bin/gamadv-xtd3
Config File: /home/bib/.gam/gam.cfg, Section: DEFAULT, customer_id: my_customer, domain: 

To install:

bash <(curl -s -S -L https://git.io/fhZWP) -l;

To authorize:

gam user [email protected] check serviceaccount;

This created a link for the browser which we followed to authorize the API.
Example output:

System time status
  Your system time differs from admin.googleapis.com by less than 1 second  PASS
Service Account Private Key Authentication
  Authentication                                                            PASS
Service Account Private Key age; Google recommends rotating keys on a routine basis
  Service Account Private Key age: 350 days                                 WARN
Domain-wide Delegation authentication:, User: [email protected], Scopes: 28
  https://mail.google.com/                                                  FAIL (1/28)
  https://sites.google.com/feeds                                            FAIL (2/28)
  https://www.googleapis.com/auth/apps.alerts                               FAIL (3/28)
  https://www.googleapis.com/auth/calendar                                  FAIL (4/28)
  https://www.googleapis.com/auth/classroom.announcements                   FAIL (5/28)
  https://www.googleapis.com/auth/classroom.coursework.students             FAIL (6/28)
  https://www.googleapis.com/auth/classroom.courseworkmaterials             FAIL (7/28)
  https://www.googleapis.com/auth/classroom.profile.emails                  FAIL (8/28)
  https://www.googleapis.com/auth/classroom.rosters                         FAIL (9/28)
  https://www.googleapis.com/auth/classroom.topics                          FAIL (10/28)
  https://www.googleapis.com/auth/cloud-identity                            FAIL (11/28)
  https://www.googleapis.com/auth/cloud-platform                            FAIL (12/28)
  https://www.googleapis.com/auth/contacts                                  FAIL (13/28)
  https://www.googleapis.com/auth/contacts.other.readonly                   FAIL (14/28)
  https://www.googleapis.com/auth/datastudio                                FAIL (15/28)
  https://www.googleapis.com/auth/directory.readonly                        FAIL (16/28)
  https://www.googleapis.com/auth/documents                                 FAIL (17/28)
  https://www.googleapis.com/auth/drive                                     FAIL (18/28)
  https://www.googleapis.com/auth/drive.activity                            FAIL (19/28)
  https://www.googleapis.com/auth/drive.admin.labels                        FAIL (20/28)
  https://www.googleapis.com/auth/drive.labels                              FAIL (21/28)
  https://www.googleapis.com/auth/gmail.modify                              FAIL (22/28)
  https://www.googleapis.com/auth/gmail.settings.basic                      FAIL (23/28)
  https://www.googleapis.com/auth/gmail.settings.sharing                    FAIL (24/28)
  https://www.googleapis.com/auth/keep                                      FAIL (25/28)
  https://www.googleapis.com/auth/spreadsheets                              FAIL (26/28)
  https://www.googleapis.com/auth/tasks                                     FAIL (27/28)
  https://www.googleapis.com/auth/userinfo.profile                          FAIL (28/28)
Some scopes FAILED!
To authorize them, please go to the following link in your browser:

    https://admin.google.com/ac/owl/domainwidedelegation?clientScopeToAdd=https://mail.google.com/,https://sites.google.com/feeds,https://www.googleapis.com/auth/apps.alerts,https://www.googleapis.com/auth/calendar,https://www.googleapis.com/auth/classroom.announcements,https://www.googleapis.com/auth/classroom.coursework.students,https://www.googleapis.com/auth/classroom.courseworkmaterials,https://www.googleapis.com/auth/classroom.profile.emails,https://www.googleapis.com/auth/classroom.rosters,https://www.googleapis.com/auth/classroom.topics,https://www.googleapis.com/auth/cloud-identity,https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/contacts,https://www.googleapis.com/auth/contacts.other.readonly,https://www.googleapis.com/auth/datastudio,https://www.googleapis.com/auth/directory.readonly,https://www.googleapis.com/auth/documents,https://www.googleapis.com/auth/drive,https://www.googleapis.com/auth/drive.activity,https://www.googleapis.com/auth/drive.admin.labels,https://www.googleapis.com/auth/drive.labels,https://www.googleapis.com/auth/gmail.modify,https://www.googleapis.com/auth/gmail.settings.basic,https://www.googleapis.com/auth/gmail.settings.sharing,https://www.googleapis.com/auth/keep,https://www.googleapis.com/auth/spreadsheets,https://www.googleapis.com/auth/tasks,https://www.googleapis.com/auth/userinfo.profile,https://www.googleapis.com/auth/userinfo.email&clientIdToAdd=101337532152182481803&overwriteClientId=true&dn=bytefreaks.net&authuser=bob@bytefreaks.net

You will be directed to the Google Workspace admin console Security > API Controls > Domain-wide Delegation page
The "Add a new Client ID" box will open
Make sure that "Overwrite existing client ID" is checked
Click AUTHORIZE
When the box closes you're done
After authorizing it may take some time for this test to pass so wait a few moments and then try this command again.

After authorizing, we executed again to verify that all tests passed:

System time status
  Your system time differs from admin.googleapis.com by 1 second            PASS
Service Account Private Key Authentication
  Authentication                                                            PASS
Service Account Private Key age; Google recommends rotating keys on a routine basis
  Service Account Private Key age: 350 days                                 WARN
Domain-wide Delegation authentication:, User: [email protected], Scopes: 28
  https://mail.google.com/                                                  PASS (1/28)
  https://sites.google.com/feeds                                            PASS (2/28)
  https://www.googleapis.com/auth/apps.alerts                               PASS (3/28)
  https://www.googleapis.com/auth/calendar                                  PASS (4/28)
  https://www.googleapis.com/auth/classroom.announcements                   PASS (5/28)
  https://www.googleapis.com/auth/classroom.coursework.students             PASS (6/28)
  https://www.googleapis.com/auth/classroom.courseworkmaterials             PASS (7/28)
  https://www.googleapis.com/auth/classroom.profile.emails                  PASS (8/28)
  https://www.googleapis.com/auth/classroom.rosters                         PASS (9/28)
  https://www.googleapis.com/auth/classroom.topics                          PASS (10/28)
  https://www.googleapis.com/auth/cloud-identity                            PASS (11/28)
  https://www.googleapis.com/auth/cloud-platform                            PASS (12/28)
^[[C  https://www.googleapis.com/auth/contacts                                  PASS (13/28)
  https://www.googleapis.com/auth/contacts.other.readonly                   PASS (14/28)
  https://www.googleapis.com/auth/datastudio                                PASS (15/28)
  https://www.googleapis.com/auth/directory.readonly                        PASS (16/28)
  https://www.googleapis.com/auth/documents                                 PASS (17/28)
  https://www.googleapis.com/auth/drive                                     PASS (18/28)
  https://www.googleapis.com/auth/drive.activity                            PASS (19/28)
  https://www.googleapis.com/auth/drive.admin.labels                        PASS (20/28)
  https://www.googleapis.com/auth/drive.labels                              PASS (21/28)
  https://www.googleapis.com/auth/gmail.modify                              PASS (22/28)
  https://www.googleapis.com/auth/gmail.settings.basic                      PASS (23/28)
  https://www.googleapis.com/auth/gmail.settings.sharing                    PASS (24/28)
  https://www.googleapis.com/auth/keep                                      PASS (25/28)
  https://www.googleapis.com/auth/spreadsheets                              PASS (26/28)
  https://www.googleapis.com/auth/tasks                                     PASS (27/28)
  https://www.googleapis.com/auth/userinfo.profile                          PASS (28/28)
All scopes PASSED!

Service Account Client name: 101337532152182481803 is fully authorized.

Finally, we executed the following to create a CSV file that contained all members of all groups:

gam print group-members > emails.2022.csv;

DJI care – No activation code

We purchased a DJI Mini 3 Pro and powered it on.

After that, we purchased a DJI Care Refresh (2-Year Plan).

In the DJI controller, there was a warning that we needed to activate the service plan within 48 hours. Clicking on that option would require an activation code.

DJI did not send us an activation code with the purchase of the service plan.

We used https://service.dji.com/djicare/coverage to verify that our purchase was valid, but no information on the activation code was available.

From the controller, we want to the profile settings and unbound the drone. After we bound it again, the Replacement Service was marked as valid.


Rough notes on setting up an Ubuntu 22.04LTS server with docker and snap 1

IP allocations

First, we set up a static IP on the network device that would handle all external traffic and a DHCP on the network device that would access the management network, which is connected for maintenance.

To do so, we created the following file:

/etc/netplan/01-netcfg.yaml

using the following command:

sudo nano /etc/netplan/01-netcfg.yaml;

and added the following content to it:

# This file describes the network interfaces available on your system
# For more information, see netplan(5).
network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: no
      addresses: [192.168.45.13/24]
      gateway4: 192.168.45.1
      nameservers:
          addresses: [1.1.1.1,8.8.8.8]
    eth1:
      dhcp4: yes

To apply the changes, we executed the following:

sudo netplan apply;

Update everything (the operating system and all packages)

Usually, it is a good idea to update your system before making significant changes to it:

sudo apt update -y; sudo apt upgrade -y; sudo apt autoremove -y;

Install docker via snap

In this setup, we did not use the docker version available on the Ubuntu repositories, we went for the ones from the snap. To install it, we used the following commands:

sudo apt install snapd;
sudo snap install docker;

Increase network pool for docker daemon

To handle the following problem:

ERROR: could not find an available, non-overlapping IPv4 address pool among the defaults to assign to the network

We modified the following file

/var/snap/docker/current/config/daemon.json

using the command:

sudo nano /var/snap/docker/current/config/daemon.json;

and set the content to be as follows:

{
    "log-level":        "error",
    "storage-driver":   "overlay2",
    "default-address-pools": [
        {
            "base": "172.80.0.0/16",
            "size": 24
        },
        {
            "base": "172.90.0.0/16",
            "size": 24
        }
    ]
}

We executed the following command to restart the docker daemon and get the network changes applied:

sudo snap disable docker;
sudo snap enable docker;

Gave access to our user to manage the docker

We added our user to the docker group so that we could manage the docker daemon without sudo rights.

sudo addgroup --system docker;
sudo adduser $USER docker;
newgrp docker;
sudo snap disable docker;
sudo snap enable docker;

After that, we made sure that the access rights to the volumes were correct:

sudo chown -R www-data:www-data /volumes/*
sudo chown -R tux:tux /volumes/letsencrypt/ /volumes/reverse/private/

Deploying

After we copied everything in place, we executed the following command to create our containers and start them with the appropriate networks and volumes:

export COMPOSE_HTTP_TIMEOUT=600;
docker-compose up -d --remove-orphans;

We had to increase the timeout as we were getting the following error:

ERROR: for container_a  UnixHTTPConnectionPool(host='localhost', port=None): Read timed out. (read timeout=60)
ERROR: An HTTP request took too long to complete. Retry with --verbose to obtain debug information.
If you encounter this issue regularly because of slow network conditions, consider setting COMPOSE_HTTP_TIMEOUT to a higher value (current value: 60).

Updating the databases and performing any repairs

First, we connected to a terminal of the database container using the following command:

docker exec -it mariadb_c1 /bin/bash;

From there, we executed the following commands:

mysql_upgrade --user=root --password;
mysqlcheck -p -o --all-databases;

Bulk / Batch stopping docker containers

The following commands will help you stop many docker containers simultaneously. Of course, you can change the command stop to another, for example rm or whatever suits your needs.

You need to keep in mind that if you have dependencies between containers, you might need to execute the commands below more than once.

Stop all docker containers.

docker container stop $(docker container ls -q);
#This command creates a list of all containers.
#Using the -q parameter, we only get back the container ID and not all information about them.
#Then it will stop each container one by one.

Stop specific docker containers using a filter on their name.

docker container stop $(docker container ls -q --filter name=_web);
#This command finds all containers that their name contains _web.
#Using the -q parameter, we only get back the container ID and not all information about them.
#Then it will stop each container one by one.

A personal note

Check the system for things you might need to configure, like a crontab or other services.

A script that handles privileges on the docker volumes

To avoid access problems with the various external volumes we created the mysql user and group on the host machine as follows:

sudo groupadd -g 999 mysql;
sudo useradd -u 999 mysql -g mysql;

Then we execute the following to repair ownership issues with our containers. Please note that this script is custom to a particular installation and might not meet your needs.

#!/bin/bash

sudo chown -R www-data:www-data ~/volumes/*;
sudo chown -R bob:bob ~/volumes/letsencrypt/ ~/volumes/reverse/private/;
find ~/volumes/ -maxdepth 2 -type d -name mysql -exec sudo chown -R mysql:mysql '{}' \;;

Extend LVM space to the rest of the free space on the disk

Recently, we formatted a server with Ubuntu 22.04 LTS. While selecting the disk settings, we selected the encrypted LVM partition scheme, and even though we selected the whole disk, we did not notice that the LVM would only allocate, by default, 100GB out of the 600GB available on the raid volume.

So, we proceeded with the installation, and at some point, we noticed that we ran out of space which should not have happened.

Using the command df -h we quickly spotted the problem:

$ df -h
Filesystem                 Size  Used Avail Use% Mounted on
tmpfs                      3,2G  3,9M  3,2G   1% /run
/dev/mapper/vgubuntu-root  100G   83G   17G  83% /
tmpfs                       16G   40M   16G   1% /dev/shm
tmpfs                      5,0M  4,0K  5,0M   1% /run/lock
/dev/sda5                  703M  257M  395M  40% /boot
/dev/sda1                  511M   24K  511M   1% /boot/efi
tmpfs                       16G     0   16G   0% /run/qemu
tmpfs                      3,2G  156K  3,2G   1% /run/user/1000

/dev/mapper/vgubuntu-root was only 100GB instead of the 600GB that we would expect it to be.

Using the command vgdisplay we verified that the space allocated to the logical volume group was not what we wanted.

To fix the problem, we issued the following commands:

lvextend -l +100%FREE /dev/ubuntu-vg/ubuntu-lv;
resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv;

lvextend instructed our logical volume to consume all the available space on the hosting disk.

Then resize2fs allocated all the available space to our partition.