first load
Some checks failed
Build, Push, Publish / Build & Release (push) Failing after 2s

This commit is contained in:
2025-12-16 04:41:33 -03:00
parent 67a1ea6d3b
commit 8a51a27411
11 changed files with 1298 additions and 1 deletions

240
.github/workflows/release_build.yml vendored Normal file
View File

@@ -0,0 +1,240 @@
name: Build, Push, Publish
on:
push:
branches:
- main
workflow_dispatch:
schedule:
- cron: '28 5 * * *'
workflow_run:
workflows: ["Sync Repo"]
types:
- completed
jobs:
release:
name: Build & Release
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
steps:
- name: 📥 Checkout code with full history and tags
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Check if any tags exist
id: check_tags_exist
run: |
git fetch --tags
TAG_COUNT=$(git tag | wc -l)
if [ "$TAG_COUNT" -eq 0 ]; then
echo "has_tags=false" >> "$GITHUB_OUTPUT"
echo "latest_tag=v0.0.0" >> "$GITHUB_OUTPUT"
else
echo "has_tags=true" >> "$GITHUB_OUTPUT"
LATEST_TAG=$(git describe --tags --abbrev=0)
echo "latest_tag=$LATEST_TAG" >> "$GITHUB_OUTPUT"
fi
- name: Check if meaningful commits exist since latest tag
id: check_commits
run: |
if [ "${{ steps.check_tags_exist.outputs.has_tags }}" = "false" ]; then
# No tags exist, so we should create first release
echo "commit_count=1" >> "$GITHUB_OUTPUT"
CHANGED_FILES=$(git ls-files | grep -v '^manifest.json$' || true)
if [ -n "$CHANGED_FILES" ]; then
echo "changed_files<<EOF" >> "$GITHUB_OUTPUT"
printf '%s\n' "$CHANGED_FILES" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
else
echo "changed_files=Initial release" >> "$GITHUB_OUTPUT"
fi
else
LATEST_TAG="${{ steps.check_tags_exist.outputs.latest_tag }}"
CHANGED_FILES="$(git diff --name-only "${LATEST_TAG}..HEAD" | grep -v '^manifest.json$' || true)"
if [ -n "$CHANGED_FILES" ]; then
echo "commit_count=1" >> "$GITHUB_OUTPUT"
echo "changed_files<<EOF" >> "$GITHUB_OUTPUT"
printf '%s\n' "$CHANGED_FILES" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
else
echo "commit_count=0" >> "$GITHUB_OUTPUT"
fi
fi
- name: Get latest release tag (from GitHub API)
id: get_latest_release
run: |
LATEST_RELEASE_TAG=$(curl -sL -H "Accept: application/vnd.github+json" \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/latest" | jq -r .tag_name)
if [ -z "$LATEST_RELEASE_TAG" ] || [ "$LATEST_RELEASE_TAG" = "null" ]; then
LATEST_RELEASE_TAG="v1.0.0"
fi
echo "latest_release_tag=$LATEST_RELEASE_TAG" >> "$GITHUB_OUTPUT"
echo "latest_release_version=${LATEST_RELEASE_TAG#v}" >> "$GITHUB_OUTPUT"
# -------------------------------
# Sync manifest.json to last release version if behind (only when no meaningful commits)
# -------------------------------
- name: 🛠 Ensure manifest.json matches latest release version
if: steps.check_commits.outputs.commit_count == '0'
run: |
if [ -f manifest.json ]; then
MANIFEST_VERSION=$(jq -r '.version // empty' manifest.json)
else
MANIFEST_VERSION=""
fi
LATEST_RELEASE_VERSION="${{ steps.get_latest_release.outputs.latest_release_version }}"
PYTHON_CODE="from packaging import version; \
print(version.parse('$LATEST_RELEASE_VERSION') > version.parse('$MANIFEST_VERSION') if '$MANIFEST_VERSION' else True)"
NEED_UPDATE=$(python3 -c "$PYTHON_CODE")
if [ "$NEED_UPDATE" = "True" ]; then
echo "Updating manifest.json to version $LATEST_RELEASE_VERSION (sync with release)"
jq --arg v "$LATEST_RELEASE_VERSION" '.version = $v' manifest.json > tmp.json && mv tmp.json manifest.json
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git add manifest.json
git commit -m "Sync manifest.json to release $LATEST_RELEASE_VERSION [🔄]" || echo "Nothing to commit"
git push origin main || true
else
echo "Manifest.json is already up-to-date with the latest release."
fi
# -------------------------------
# Continue normal workflow if commits exist
# -------------------------------
- name: 📃 Get list of changed files (Markdown bullet list)
if: steps.check_commits.outputs.commit_count != '0'
id: changed_files
run: |
BULLET_LIST="$(printf '%s\n' "${{ steps.check_commits.outputs.changed_files }}" | sed 's/^/- /')"
echo "CHANGED<<EOF" >> "$GITHUB_OUTPUT"
printf '%s\n' "$BULLET_LIST" >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
COUNT="$(printf '%s\n' "${{ steps.check_commits.outputs.changed_files }}" | wc -l)"
echo "COUNT=$COUNT" >> "$GITHUB_OUTPUT"
- name: Get manifest version
if: steps.check_commits.outputs.commit_count != '0'
id: get_manifest_version
run: |
if [ -f manifest.json ]; then
MANIFEST_VERSION=$(jq -r '.version // empty' manifest.json)
if [ -z "$MANIFEST_VERSION" ] || [ "$MANIFEST_VERSION" = "null" ]; then
MANIFEST_VERSION="1.0.0"
fi
else
MANIFEST_VERSION="1.0.0"
fi
echo "manifest_version=$MANIFEST_VERSION" >> "$GITHUB_OUTPUT"
- name: Pick base version
if: steps.check_commits.outputs.commit_count != '0'
id: pick_base_version
run: |
LATEST_RELEASE="${{ steps.get_latest_release.outputs.latest_release_version }}"
MANIFEST="${{ steps.get_manifest_version.outputs.manifest_version }}"
BASE_VERSION=$(python3 -c "from packaging import version; \
print(str(max(version.parse('$LATEST_RELEASE'), version.parse('$MANIFEST'))))")
echo "base_version=$BASE_VERSION" >> "$GITHUB_OUTPUT"
- name: 🔢 Determine version
if: steps.check_commits.outputs.commit_count != '0'
id: version
run: |
BASE_VERSION="${{ steps.pick_base_version.outputs.base_version }}"
IFS='.' read -r MAJOR MINOR PATCH <<< "$BASE_VERSION"
COUNT="${{ steps.changed_files.outputs.COUNT }}"
if [ "$COUNT" -ge 5 ]; then
MAJOR=$((MAJOR + 1))
MINOR=0
PATCH=0
elif [ "$COUNT" -ge 3 ]; then
MINOR=$((MINOR + 1))
PATCH=0
else
PATCH=$((PATCH + 1))
fi
NEW_VERSION="${MAJOR}.${MINOR}.${PATCH}"
REPO_NAME="$(basename "$GITHUB_REPOSITORY")"
ZIP_NAME="${REPO_NAME}-${NEW_VERSION}.zip"
echo "VERSION=$NEW_VERSION" >> "$GITHUB_OUTPUT"
echo "ZIP_NAME=$ZIP_NAME" >> "$GITHUB_OUTPUT"
echo "REPO_NAME=$REPO_NAME" >> "$GITHUB_OUTPUT"
- name: 🛠 Update or create manifest.json
if: steps.check_commits.outputs.commit_count != '0'
run: |
VERSION="${{ steps.version.outputs.VERSION }}"
AUTHOR="Ivan Carlos"
VERSION_FILE="manifest.json"
if [ -f "$VERSION_FILE" ]; then
jq --arg v "$VERSION" --arg a "$AUTHOR" \
'.version = $v | .author = $a' "$VERSION_FILE" > tmp.json && mv tmp.json "$VERSION_FILE"
else
echo "{ \"version\": \"$VERSION\", \"author\": \"$AUTHOR\" }" > "$VERSION_FILE"
fi
- name: 💾 Commit and push updated manifest.json
if: steps.check_commits.outputs.commit_count != '0'
run: |
git config user.name "github-actions"
git config user.email "github-actions@github.com"
git add manifest.json
git commit -m "Update manifest version to ${{ steps.version.outputs.VERSION }} [▶️]" || echo "Nothing to commit"
git push origin main
- name: 📦 Create ZIP package (excluding certain files)
if: steps.check_commits.outputs.commit_count != '0'
run: |
ZIP_NAME="${{ steps.version.outputs.ZIP_NAME }}"
zip -r "$ZIP_NAME" . -x ".git/*" ".github/*" "docker/*" ".dockerignore" "CNAME" "Dockerfile" "README.md" "LICENSE"
- name: 🚀 Create GitHub Release
if: steps.check_commits.outputs.commit_count != '0'
uses: softprops/action-gh-release@v2
with:
tag_name: "v${{ steps.version.outputs.VERSION }}"
name: "${{ steps.version.outputs.REPO_NAME }} v${{ steps.version.outputs.VERSION }}"
body: |
### Changelog
Files changed in this release:
${{ steps.changed_files.outputs.CHANGED }}
files: ${{ steps.version.outputs.ZIP_NAME }}
# ----- Docker steps -----
- name: 🔍 Check if Dockerfile exists
if: steps.check_commits.outputs.commit_count != '0'
id: dockerfile_check
run: |
if [ -f Dockerfile ]; then
echo "exists=true" >> "$GITHUB_OUTPUT"
else
echo "exists=false" >> "$GITHUB_OUTPUT"
fi
- name: 🛠 Set up Docker Buildx
if: steps.check_commits.outputs.commit_count != '0' && steps.dockerfile_check.outputs.exists == 'true'
uses: docker/setup-buildx-action@v3
- name: 🔐 Login to GitHub Container Registry
if: steps.check_commits.outputs.commit_count != '0' && steps.dockerfile_check.outputs.exists == 'true'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: 🐳 Build and Push Docker image
if: steps.check_commits.outputs.commit_count != '0' && steps.dockerfile_check.outputs.exists == 'true'
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest

78
.github/workflows/update_readme.yml vendored Normal file
View File

@@ -0,0 +1,78 @@
name: Update README
# Allow GitHub Actions to commit and push changes
permissions:
contents: write
on:
workflow_dispatch:
schedule:
- cron: '0 4 * * *' # Every day at 4 AM UTC
jobs:
update-readme:
runs-on: ubuntu-latest
env:
SOURCE_REPO: ivancarlosti/.github
SOURCE_BRANCH: main
steps:
- name: Checkout current repository
uses: actions/checkout@v4
- name: Checkout source README template
uses: actions/checkout@v4
with:
repository: ${{ env.SOURCE_REPO }}
ref: ${{ env.SOURCE_BRANCH }}
path: source_readme
- name: Update README.md (buttons and footer)
run: |
set -e
REPO_NAME="${GITHUB_REPOSITORY##*/}"
# --- Extract buttons block from source ---
BUTTONS=$(awk '/<!-- buttons -->/{flag=1;next}/<!-- endbuttons -->/{flag=0}flag' source_readme/README.md)
BUTTONS_UPDATED=$(echo "$BUTTONS" | sed "s/\.github/${REPO_NAME}/g")
# --- Extract footer block from source (everything from <!-- footer --> onward) ---
FOOTER=$(awk '/<!-- footer -->/{flag=1}flag' source_readme/README.md)
# --- Replace buttons section in README.md ---
UPDATED=$(awk -v buttons="$BUTTONS_UPDATED" '
BEGIN { skip=0 }
/<!-- buttons -->/ {
print
print buttons
skip=1
next
}
/<!-- endbuttons -->/ && skip {
print
skip=0
next
}
!skip { print }
' README.md)
# --- Replace everything after <!-- footer --> with FOOTER ---
echo "$UPDATED" | awk -v footer="$FOOTER" '
/<!-- footer -->/ {
print footer
found=1
exit
}
{ print }
' > README.tmp && mv README.tmp README.md
- name: Remove source_readme from git index
run: git rm --cached -r source_readme || true
- name: Commit and push changes
uses: stefanzweifel/git-auto-commit-action@v5
with:
file_pattern: README.md
commit_message: "Sync README from template [▶️]"
branch: ${{ github.ref_name }}

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Ivan Carlos de Almeida
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

122
POWERSHELL_ISSUE.md Normal file
View File

@@ -0,0 +1,122 @@
<img src="https://r2cdn.perplexity.ai/pplx-full-logo-primary-dark%402x.png" class="logo" width="120"/>
### How to Fix "Running Scripts is Disabled on this System" in PowerShell
PowerShell is blocking your script due to its execution policy settings, which are in place for security reasons. You can change these settings easily to allow your script to run.
#### **Recommended Steps**
1. **Open PowerShell as Administrator**
- Click Start, search for PowerShell.
- Right-click and select **Run as Administrator**.
2. **Check Current Execution Policies**
- Run:
```
Get-ExecutionPolicy -List
```
- This displays the policies for each scope (see table below for explanations).
3. **Unblock Script Execution for Your User**
- To allow scripts for your user only (safest):
```
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
```
- *RemoteSigned* allows scripts you write and scripts downloaded from the internet if they are signed.
4. **If You Need to Allow All Scripts (Less Secure)**
- Run:
```
Set-ExecutionPolicy Unrestricted -Scope CurrentUser
```
- This will allow any script to run, regardless of source. Use only if you trust the scripts youre executing.
5. **Confirm Security Prompts**
- If prompted, press **Y** to confirm or type **A** (if asked) to accept all future changes[^1_1][^1_2][^1_3].
#### **If You Still Get the Error (Group Policy Enforced)**
If changing the execution policy doesnt work, your system might have a Group Policy overriding this setting:
- Press `Win + R`, type `gpedit.msc`, and press Enter.
- Navigate to:
```
Local Computer Policy > Computer Configuration > Administrative Templates > Windows Components > Windows PowerShell
```
- Double-click **Turn on Script Execution**.
- Set it to **Enabled** and choose **Allow all scripts** (or less permissive option if desired)[^1_1][^1_4].
#### **Execution Policy Scopes Explained**
| Scope | Description |
| :-- | :-- |
| MachinePolicy | Enforced by Group Policy for entire machine |
| UserPolicy | Enforced by Group Policy for current user |
| Process | Applies only to current PowerShell session |
| CurrentUser | Applies to scripts run by current Windows user |
| LocalMachine | Applies to all users on the computer |
*Execution policies set at higher scopes (MachinePolicy, UserPolicy) override lower scopes (CurrentUser, LocalMachine)[^1_5].*
#### **Quick Troubleshooting**
- If only running a script once, you can bypass the policy by running:
```
powershell -ExecutionPolicy Bypass -File .\mainscript.ps1
```
- Use `RemoteSigned` or `Unrestricted` mindfully, as they lower script execution restrictions.
**Note:** Always revert your execution policy to the original or a safer state (such as `Restricted`) after running untrusted or experimental scripts for security[^1_6][^1_5].
**References:**
Information sourced directly from Microsoft documentation and community troubleshooting discussions[^1_1][^1_2][^1_5][^1_3].
<div style="text-align: center">⁂</div>
[^1_1]: https://stackoverflow.com/questions/4037939/powershell-says-execution-of-scripts-is-disabled-on-this-system
[^1_2]: https://www.addictivetips.com/windows-tips/fix-running-scripts-is-disabled-on-this-system-powershell-on-windows-10/
[^1_3]: https://techpress.net/powershell-running-scripts-is-disabled-on-this-system-error/
[^1_4]: https://learn.microsoft.com/en-us/answers/questions/506985/powershell-execution-setting-is-overridden-by-a-po
[^1_5]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.5
[^1_6]: https://www.youtube.com/watch?v=ChRef6Z8UD4
[^1_7]: https://answers.microsoft.com/en-us/windows/forum/all/cannot-get-powershell-script-to-run/900edc39-35e8-4896-92d0-05aad75eac87
[^1_8]: https://superuser.com/questions/106360/how-to-enable-execution-of-powershell-scripts
[^1_9]: https://learn.microsoft.com/en-us/answers/questions/3740158/cannot-get-powershell-script-to-run
[^1_10]: https://adamtheautomator.com/set-executionpolicy/
[^1_11]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy?view=powershell-7.5
[^1_12]: https://www.youtube.com/watch?v=N2Axkw00Flg
[^1_13]: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.security/set-executionpolicy?view=powershell-7.5\&rut=459b26fec52a14755fbe25de9c676e1e9b897f03730570c69e5b368ad8ae747c
[^1_14]: https://dev.to/jackfd120/resolving-npm-execution-policy-error-in-powershell-a-step-by-step-guide-for-developers-32ip
[^1_15]: https://www.softwareverify.com/blog/enabling-and-disabling-powershell-script-execution/
[^1_16]: https://learn.microsoft.com/pt-br/powershell/module/microsoft.powershell.core/about/about_execution_policies?view=powershell-7.5
[^1_17]: https://lazyadmin.nl/powershell/running-scripts-is-disabled-on-this-system/
[^1_18]: https://tecadmin.net/powershell-running-scripts-is-disabled-system/
[^1_19]: https://stackoverflow.com/questions/41117421/ps1-cannot-be-loaded-because-running-scripts-is-disabled-on-this-system
[^1_20]: https://sentry.io/answers/bypass-and-set-powershell-script-execution-policies/

View File

@@ -1,2 +1,86 @@
# gwauditor
# Google Workspace Auditor script
This script collects users, groups and Shared Drives of a Google Workspace environment on .xlsx file for audit and review purposes
<!-- buttons -->
[![Stars](https://img.shields.io/github/stars/ivancarlosti/gwauditor?label=⭐%20Stars&color=gold&style=flat)](https://github.com/ivancarlosti/gwauditor/stargazers)
[![Watchers](https://img.shields.io/github/watchers/ivancarlosti/gwauditor?label=Watchers&style=flat&color=red)](https://github.com/sponsors/ivancarlosti)
[![Forks](https://img.shields.io/github/forks/ivancarlosti/gwauditor?label=Forks&style=flat&color=ff69b4)](https://github.com/sponsors/ivancarlosti)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/ivancarlosti/gwauditor?label=Activity)](https://github.com/ivancarlosti/gwauditor/pulse)
[![GitHub Issues](https://img.shields.io/github/issues/ivancarlosti/gwauditor?label=Issues&color=orange)](https://github.com/ivancarlosti/gwauditor/issues)
[![License](https://img.shields.io/github/license/ivancarlosti/gwauditor?label=License)](LICENSE)
[![GitHub last commit](https://img.shields.io/github/last-commit/ivancarlosti/gwauditor?label=Last%20Commit)](https://github.com/ivancarlosti/gwauditor/commits)
[![Security](https://img.shields.io/badge/Security-View%20Here-purple)](https://github.com/ivancarlosti/gwauditor/security)
[![Code of Conduct](https://img.shields.io/badge/Code%20of%20Conduct-2.1-4baaaa)](https://github.com/ivancarlosti/gwauditor?tab=coc-ov-file)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/ivancarlosti?label=GitHub%20Sponsors&color=ffc0cb)][sponsor]
<!-- endbuttons -->
## Details
This script collects users, groups, mailboxes delegation, Shared Drives, YouTube accounts, Analytics accounts, policies of a [Google Workspace](https://workspace.google.com/) environment on .xlsx file for audit and review purposes, the file is archived in a .zip file including a screenshot with hash MD5 of the .xlsx file and the script executed. Note that it's prepared to run on [GAM](https://github.com/GAM-team/GAM/) configured for multiple projects, change accordly if needed. This project also offer extra features:
- Archive mailbox messages to group
- List, add or remove mailbox delegation
Set variables if different of defined:
```
$GAMpath = "C:\GAM7"
$gamsettings = "$env:USERPROFILE\.gam"
$destinationpath = (New-Object -ComObject Shell.Application).NameSpace('shell:Downloads').Self.Path
```
`$GAMpath` defines the GAM application folder
`$gamsettings` defines the settings folder of GAM
`$destinationpath` defines the location were script result is saved
Check `testing-guideline.md` file as suggestion for testing guideline
You can find scripts related to mailbox delegation and mailbox archive to group in `Other scripts` folder
## Instructions
* Save the last release version and extract files locally (download [here](https://github.com/ivancarlosti/gwauditor/releases/latest))
* Change variables of `mainscript.ps1` if needed
* Run `mainscript.ps1` on PowerShell (right-click on file > Run with PowerShell)
* Follow instructions selecting project name, option 1 to generate audit report and collect .zip file on `$destinationpath`
## Screenshots
*parts ommited on screenshots are related to project/profile name
![image](https://github.com/user-attachments/assets/489b37e0-c042-4df2-9ac9-4f5871a8d95f)
*Script startup*
![image](https://github.com/user-attachments/assets/08cb9aab-cb7a-4444-bf1e-f32a518ba190)
*Script completed*
![image](https://github.com/user-attachments/assets/6d642c0c-dfd8-4810-b674-6280b81857ce)
*.zip file content*
## Requirements
* Windows 10+ or Windows Server 2019+
* [GAM v5+](https://github.com/GAM-team/GAM/) using multiproject setup
* PowerShell
* Module `ImportExcel` on PowerShell (not required to run extra features)
<!-- footer -->
---
## 🧑‍💻 Consulting and technical support
* For personal support and queries, please submit a new issue to have it addressed.
* For commercial related questions, please [**contact me**][ivancarlos] for consulting costs.
## 🩷 Project support
| If you found this project helpful, consider |
| :---: |
[**buying me a coffee**][buymeacoffee], [**donate by paypal**][paypal], [**sponsor this project**][sponsor] or just [**leave a star**](../..)⭐
|Thanks for your support, it is much appreciated!|
[cc]: https://docs.github.com/en/communities/setting-up-your-project-for-healthy-contributions/adding-a-code-of-conduct-to-your-project
[contributing]: https://docs.github.com/en/articles/setting-guidelines-for-repository-contributors
[security]: https://docs.github.com/en/code-security/getting-started/adding-a-security-policy-to-your-repository
[support]: https://docs.github.com/en/articles/adding-support-resources-to-your-project
[it]: https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/configuring-issue-templates-for-your-repository#configuring-the-template-chooser
[prt]: https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/creating-a-pull-request-template-for-your-repository
[funding]: https://docs.github.com/en/articles/displaying-a-sponsor-button-in-your-repository
[ivancarlos]: https://ivancarlos.it
[buymeacoffee]: https://www.buymeacoffee.com/ivancarlos
[paypal]: https://icc.gg/donate
[sponsor]: https://github.com/sponsors/ivancarlosti

View File

@@ -0,0 +1,170 @@
# Archive Mailbox Messages Script
param (
[string]$clientName,
[string]$GAMpath,
[string]$gamsettings,
[string]$datetime
)
[console]::OutputEncoding = [System.Text.Encoding]::UTF8
cls
Write-Host "### SCRIPT TO ARCHIVE GOOGLE WORKSPACE MAILBOX TO A GROUP, PLEASE FOLLOW INSTRUCTIONS ###"
Write-Host
Write-Host "GAM project selected: $clientName"
Write-Host "GAM application path: $GAMpath"
Write-Host "Project path: $gamsettings"
Write-Host "Date and time: $datetime"
Write-Host
function pause{ $null = Read-Host 'Press ENTER key to end script' }
Write-Host
function Check-AdminAddress {
param (
[string]$adminAddress
)
# Run GAM command to check if the admin address exists
$output = gam info user $adminAddress 2>&1
# Check the output for errors
if ($output -match "Does not exist" -or $output -match "Show Info Failed" -or $output -match "ERROR" -or $output -match "Super Admin: False") {
return $false
} else {
return $true
}
}
while ($true) {
# Prompt for the admin account
$adminAddress = Read-Host "Please enter the admin account"
# Check if the input is empty
if ([string]::IsNullOrWhiteSpace($adminAddress)) {
continue
}
# Check if the admin account exists
if (Check-AdminAddress -adminAddress $adminAddress) {
break
} else {
Write-Host "The admin account $adminAddress does not exist, or we have an ERROR. Please check credentials and try again."
}
}
function Check-AdminAuth {
param (
[string]$adminAddress
)
# Run GAM command to check if the admin account have auth
$output = gam user $adminAddress check serviceaccount 2>&1
# Check the output for errors
if ($output -match "Some scopes failed") {
return $false
} else {
return $true
}
}
while ($true) {
# Check if the admin address exists
if (Check-AdminAuth -adminAddress $adminAddress) {
break
} else {
Write-Host "The admin account $adminAddress do not have proper authorization, we will run again the command to let you authorize it:"
gam user $adminAddress check serviceaccount
}
}
# Function to check if a mailbox address exists
function Check-EmailAddress {
param (
[string]$sourceAddress
)
# Run GAM command to check if the mailbox address exists
$output = gam info user $sourceAddress 2>&1
# Check the output for errors
if ($output -match "Does not exist" -or $output -match "Show Info Failed" -or $output -match "ERROR") {
return $false
} else {
return $true
}
}
while ($true) {
# Prompt for the mailbox address
$sourceAddress = Read-Host "Please enter the mailbox address"
# Check if the input is empty
if ([string]::IsNullOrWhiteSpace($sourceAddress)) {
continue
}
# Check if the mailbox address exists
if (Check-EmailAddress -sourceAddress $sourceAddress) {
break
} else {
Write-Host "The mailbox $sourceAddress does not exist, it's a group, or we have an ERROR. Please check credentials and try again."
}
}
# Function to check if a group exists
function Check-GroupAddress {
param (
[string]$targetAddress
)
# Run GAM command to check if the group address exists
$output = gam info group $targetAddress 2>&1
# Check the output for errors
if ($output -match "Does not exist" -or $output -match "Show Info Failed" -or $output -match "ERROR") {
return $false
} else {
return $true
}
}
while ($true) {
# Prompt for the group address
$targetAddress = Read-Host "Please enter the group address"
# Check if the input is empty
if ([string]::IsNullOrWhiteSpace($targetAddress)) {
continue
}
# Check if the group address exists
if (Check-GroupAddress -targetAddress $targetAddress) {
break
} else {
Write-Host "The group $targetAddress does not exist, it's a user mailbox, or we have an ERROR. Please check credentials and try again."
}
}
Write-Host
Write-Host Archiving mailbox to group using command: "gam user $sourceAddress archive messages $targetAddress max_to_archive 0 doit"
gam user $sourceAddress archive messages $targetAddress max_to_archive 0 doit
Write-Host
Write-Host "### SCRIPT TO ARCHIVE GOOGLE WORKSPACE MAILBOX TO A GROUP COMPLETED ###"
$currentdate = Get-Date
$culture = [System.Globalization.CultureInfo]::GetCultureInfo("en-US")
$currentdate = $currentdate.ToString("dddd, dd MMMM yyyy HH:mm:ss", $culture)
# show info after running script
Write-Host
Write-Host Project used by GAM: $clientName
Write-Host Actual date and time: $currentdate
Write-Host
pause
exit

230
_script_AuditReport.ps1 Normal file
View File

@@ -0,0 +1,230 @@
# Audit Report Script
param (
[string]$clientName,
[string]$GAMpath,
[string]$gamsettings,
[string]$datetime,
[string]$destinationpath
)
[console]::OutputEncoding = [System.Text.Encoding]::UTF8
cls
Write-Host "### SCRIPT TO COLLECT GOOGLE WORKSPACE DATA, PLEASE FOLLOW INSTRUCTIONS ###"
Write-Host
Write-Host "GAM project selected: $clientName"
Write-Host "GAM application path: $GAMpath"
Write-Host "Project path: $gamsettings"
Write-Host "Date and time: $datetime"
Write-Host "Destination path: $destinationpath"
Write-Host
function pause{ $null = Read-Host 'Press ENTER key to proceed' }
Write-Host
if (Get-Module -ListAvailable -Name ImportExcel) {
Write-Host "Module ImportExcel found, no additional installation required"
Write-Host
}
else {
Write-Host "Module ImportExcel do not exist, please run 'Install-Module -Name ImportExcel' as administrator"
pause
exit
}
# delete files used on this project on $GAMpath
del $GAMpath\*.csv
del $GAMpath\*.xlsx
del $GAMpath\*.bmp
del $GAMpath\*.ps1
del $GAMpath\*.zip
# copy script to $GAMpath
Copy-Item $MyInvocation.MyCommand.Name $GAMpath
function Check-AdminAddress {
param (
[string]$adminAddress
)
# Run GAM command to check if the admin address exists
$output = gam info user $adminAddress 2>&1
# Check the output for errors
if ($output -match "Does not exist" -or $output -match "Show Info Failed" -or $output -match "ERROR" -or $output -match "Super Admin: False") {
return $false
} else {
return $true
}
}
while ($true) {
# Prompt for the admin address
$adminAddress = Read-Host "Please enter the admin account"
# Check if the input is empty
if ([string]::IsNullOrWhiteSpace($adminAddress)) {
continue
}
# Check if the admin address exists
if (Check-AdminAddress -adminAddress $adminAddress) {
break
} else {
Write-Host "The admin account $adminAddress does not exist, or we have an ERROR. Please check credentials and try again, if correct, run >>>gam oauth delete && gam oauth create<<< and come back."
pause
}
}
function Check-AdminAuth {
param (
[string]$adminAddress
)
# Run GAM command to check if the admin address has auth
$output = gam user $adminAddress check serviceaccount 2>&1
# Check the output for errors
if ($output -match "Some scopes failed") {
return $false
} else {
return $true
}
}
while ($true) {
# Check if the admin address exists
if (Check-AdminAuth -adminAddress $adminAddress) {
break
} else {
Write-Host "The admin account $adminAddress does not have proper authorization, run >>>gam user $adminAddress check serviceaccount<<< and come back."
pause
}
}
function Check-PoliciesAuth {
# Run GAM command to check policies
$output = gam info policies user_takeout_status 2>&1
# Check the output for the word "insufficient"
if ($output -match "insufficient") {
return $false
} else {
return $true
}
}
while ($true) {
# Check policies authorization
if (Check-PoliciesAuth) {
break
} else {
Write-Host "The project does not have proper policies authorization, run >>>gam oauth delete && gam oauth create<<< and come back."
pause
}
}
#Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser
Import-Module -Name ImportExcel
Write-Host
Write-Host "## collect users information ##"
gam redirect csv "$GAMpath\users-report-$datetime.csv" print users fields primaryEmail creationTime id isAdmin isDelegatedAdmin isEnforcedIn2Sv isEnrolledIn2Sv lastLoginTime name suspended aliases
Write-Host
Write-Host "## collect groups information ##"
gam redirect csv "$GAMpath\groups-report-$datetime.csv" print groups fields email id name adminCreated members manager owners aliases
Write-Host
Write-Host "## collect shared drives information ##"
gam redirect csv "$GAMpath\teamdriveacls-report-$datetime.csv" print teamdriveacls oneitemperrow
Write-Host
Write-Host "## collect mailbox delegation information ##"
gam all users print delegates shownames > "$GAMpath\delegates-report-$datetime.csv"
Write-Host
Write-Host "## collect youtube channels information ##"
gam all users_ns_susp print youtubechannels fields id snippet statistics > "$GAMpath\youtube-report-$datetime.csv"
Write-Host
Write-Host "## collect analytics information ##"
gam all users_ns_susp print analyticaccountsummaries > "$GAMpath\analytics-report-$datetime.csv"
Write-Host
Write-Host "## collect policies information ##"
gam redirect csv "$GAMpath\domains-report-$datetime.csv" print domains
Write-Host
Write-Host "## collect policies information ##"
gam redirect csv "$GAMpath\policies-report-$datetime.csv" print policies
Write-Host
Write-Host "## add users report to Excel file ##"
Import-Csv $GAMpath\users-report-$datetime.csv -Delimiter ',' | Export-Excel -Path $GAMpath\audit-$clientName-$datetime.xlsx -WorksheetName users -AutoSize -TableName $sheet.Name -TableStyle Light1
Write-Host
Write-Host "## add groups report to Excel file ##"
Import-Csv $GAMpath\groups-report-$datetime.csv -Delimiter ',' | Export-Excel -Path $GAMpath\audit-$clientName-$datetime.xlsx -WorksheetName groups -AutoSize -TableName $sheet.Name -TableStyle Light1
Write-Host
Write-Host "## add shared drives report to Excel file ##"
Import-Csv $GAMpath\teamdriveacls-report-$datetime.csv -Delimiter ',' | Export-Excel -Path $GAMpath\audit-$clientName-$datetime.xlsx -WorksheetName teamdriveacls -AutoSize -TableName $sheet.Name -TableStyle Light1
Write-Host
Write-Host "## add delegates report to Excel file ##"
Import-Csv $GAMpath\delegates-report-$datetime.csv -Delimiter ',' | Export-Excel -Path $GAMpath\audit-$clientName-$datetime.xlsx -WorksheetName delegates -AutoSize -TableName $sheet.Name -TableStyle Light1
Write-Host
Write-Host "## add youtube report to Excel file ##"
Import-Csv $GAMpath\youtube-report-$datetime.csv -Delimiter ',' | Export-Excel -Path $GAMpath\audit-$clientName-$datetime.xlsx -WorksheetName youtube -AutoSize -TableName $sheet.Name -TableStyle Light1
Write-Host
Write-Host "## add analytics report to Excel file ##"
Import-Csv $GAMpath\analytics-report-$datetime.csv -Delimiter ',' | Export-Excel -Path $GAMpath\audit-$clientName-$datetime.xlsx -WorksheetName analytics -AutoSize -TableName $sheet.Name -TableStyle Light1
Write-Host
Write-Host "## add domains report to Excel file ##"
Import-Csv $GAMpath\domains-report-$datetime.csv -Delimiter ',' | Export-Excel -Path $GAMpath\audit-$clientName-$datetime.xlsx -WorksheetName domains -AutoSize -TableName $sheet.Name -TableStyle Light1
Write-Host
Write-Host "## add policies report to Excel file ##"
Import-Csv $GAMpath\policies-report-$datetime.csv -Delimiter ',' | Export-Excel -Path $GAMpath\audit-$clientName-$datetime.xlsx -WorksheetName policies -AutoSize -TableName $sheet.Name -TableStyle Light1
cls
Write-Host "### SCRIPT TO COLLECT GOOGLE WORKSPACE DATA COMPLETED ###"
# gather MD5 hash of .xlsx file for audit purposes
$hash = ((certutil -hashfile $GAMpath\audit-$clientName-$datetime.xlsx MD5).split([Environment]::NewLine))[1]
$currentdate = Get-Date
$culture = [System.Globalization.CultureInfo]::GetCultureInfo("en-US")
$currentdate = $currentdate.ToString("dddd, dd MMMM yyyy HH:mm:ss", $culture)
# show info after collect report
Write-Host
Write-Host Project used by GAM: $clientName
Write-Host Actual date and time: $currentdate
Write-Host MD5 hash of [audit-$clientName-$datetime.xlsx] file: $hash
# wait to print info on screen for print screen
Start-Sleep -Seconds 2
# print screen program
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# send alt + printscreen to capture the active window
[System.Windows.Forms.SendKeys]::SendWait("%{PRTSC}")
# create a bitmap to store the screenshot
$bitmap = New-Object System.Drawing.Bitmap([System.Windows.Forms.Clipboard]::GetImage())
# save the screenshot
$bitmap.Save("$GAMpath\audit-$clientName-$datetime.bmp")
# add files to .zip file on $GAMpath
Compress-Archive "$GAMpath\*.xlsx" -DestinationPath "$destinationpath\audit-$clientName-$datetime.zip"
Compress-Archive -Path "$GAMpath\*.bmp" -Update -DestinationPath "$destinationpath\audit-$clientName-$datetime.zip"
Compress-Archive -Path "$GAMpath\*.ps1" -Update -DestinationPath "$destinationpath\audit-$clientName-$datetime.zip"
Write-Host "Audit [audit-$clientName-$datetime.zip] file location:"$destinationpath
Write-Host
del $GAMpath\*.csv
del $GAMpath\*.xlsx
del $GAMpath\*.bmp
del $GAMpath\*.ps1
del $GAMpath\*.zip
pause
exit

View File

@@ -0,0 +1,220 @@
# Mailbox Delegation Script
param (
[string]$clientName,
[string]$GAMpath,
[string]$gamsettings,
[string]$datetime
)
[console]::OutputEncoding = [System.Text.Encoding]::UTF8
cls
Write-Host "### SCRIPT TO MANAGE MAILBOX DELEGATION, PLEASE FOLLOW INSTRUCTIONS ###"
Write-Host
Write-Host "GAM project selected: $clientName"
Write-Host "GAM application path: $GAMpath"
Write-Host "Project path: $gamsettings"
Write-Host "Date and time: $datetime"
Write-Host
function pause{ $null = Read-Host 'Press ENTER key to end script' }
Write-Host
function Check-AdminAddress {
param (
[string]$adminAddress
)
# Run GAM command to check if the admin address exists
$output = gam info user $adminAddress 2>&1
# Check the output for errors
if ($output -match "Does not exist" -or $output -match "Show Info Failed" -or $output -match "ERROR" -or $output -match "Super Admin: False") {
return $false
} else {
return $true
}
}
while ($true) {
# Prompt for the admin address
$adminAddress = Read-Host "Please enter the admin account"
# Check if the input is empty
if ([string]::IsNullOrWhiteSpace($adminAddress)) {
continue
}
# Check if the admin address exists
if (Check-AdminAddress -adminAddress $adminAddress) {
break
} else {
Write-Host "The admin account $adminAddress does not exist, or we have an ERROR. Please check credentials and try again."
}
}
function Check-AdminAuth {
param (
[string]$adminAddress
)
# Run GAM command to check if the admin address has auth
$output = gam user $adminAddress check serviceaccount 2>&1
# Check the output for errors
if ($output -match "Some scopes failed") {
return $false
} else {
return $true
}
}
while ($true) {
# Check if the admin address exists
if (Check-AdminAuth -adminAddress $adminAddress) {
break
} else {
Write-Host "The admin account $adminAddress does not have proper authorization, we will run the command again to let you authorize it:"
gam user $adminAddress check serviceaccount
}
}
# Function to check policy settings
function Check-PolicySettings {
param (
[string]$filter
)
# Run the GAM command and capture the output
$output = $(gam print policies filter "$filter" 2>&1)
# Check if the output contains the specified messages
if ($output -match "False,True,ADMIN" -or $output -match "False,False,ADMIN" -or $output -match "Got 0 Policies" -or $output -match "insufficient") {
Write-Host "WARNING: You can proceed but policies unreachable or mailbox delegation disabled."
Write-Host "Users may not be able to access the delegated mailbox."
Write-Host "Please check it in https://admin.google.com/ac/apps/gmail/usersettings"
Write-Host
return $false
} else {
Write-Host "Mailbox delegation is enabled, you are good to go."
Write-Host
return $true
}
}
# Define the filter
$filter = "setting.type.matches('.*gmail.mail_delegation')"
# Check policy settings
$policyCheck = Check-PolicySettings -filter $filter
# Function to check if a mailbox address exists
function Check-EmailAddress {
param (
[string]$sourceAddress
)
# Run GAM command to check if the mailbox address exists
$output = gam info user $sourceAddress 2>&1
# Check the output for errors
if ($output -match "Does not exist" -or $output -match "Show Info Failed" -or $output -match "ERROR") {
return $false
} else {
return $true
}
}
while ($true) {
# Prompt for the mailbox address
$sourceAddress = Read-Host "Please enter the mailbox address"
# Check if the input is empty
if ([string]::IsNullOrWhiteSpace($sourceAddress)) {
continue
}
# Check if the mailbox address exists
if (Check-EmailAddress -sourceAddress $sourceAddress) {
break
} else {
Write-Host "The mailbox $sourceAddress does not exist, it's a group, or we have an ERROR. Please check credentials and try again."
}
}
# Function to list delegates
function List-Delegates {
param (
[string]$sourceAddress
)
gam user $sourceAddress show delegates
}
# Function to add delegates
function Add-Delegates {
param (
[string]$sourceAddress
)
$delegatedAddress = Read-Host "Please enter the mailbox or group to enable access to $sourceAddress's mailbox"
gam user $sourceAddress add delegates $delegatedAddress
}
# Function to remove delegates
function Remove-Delegates {
param (
[string]$sourceAddress
)
$delegatedAddress = Read-Host "Please enter the mailbox or group to remove access to $sourceAddress's mailbox"
gam user $sourceAddress del delegates $delegatedAddress
}
# Menu options
while ($true) {
Write-Host
Write-Host "Select an option:"
Write-Host "1. List Delegates"
Write-Host "2. Add Delegates"
Write-Host "3. Remove Delegates"
Write-Host "4. Exit"
Write-Host
$choice = Read-Host "Enter your choice"
switch ($choice) {
1 {
List-Delegates -sourceAddress $sourceAddress
}
2 {
Add-Delegates -sourceAddress $sourceAddress
}
3 {
Remove-Delegates -sourceAddress $sourceAddress
}
4 {
Write-Host
Write-Host "### SCRIPT TO MANAGE MAILBOX DELEGATION COMPLETED ###"
$currentdate = Get-Date
$culture = [System.Globalization.CultureInfo]::GetCultureInfo("en-US")
$currentdate = $currentdate.ToString("dddd, dd MMMM yyyy HH:mm:ss", $culture)
# show info after running the script
Write-Host
Write-Host Project used by GAM: $clientName
Write-Host Actual date and time: $currentdate
Write-Host
pause
break
}
default {
Write-Host "Invalid option, please try again."
}
}
if ($choice -eq '4') {
break
}
}

112
mainscript.ps1 Normal file
View File

@@ -0,0 +1,112 @@
## Main script
# set variables
$GAMpath = "C:\GAM7"
$gamsettings = "$env:USERPROFILE\.gam"
$destinationpath = (New-Object -ComObject Shell.Application).NameSpace('shell:Downloads').Self.Path
# collect project folders on $gamsettings
$directories = Get-ChildItem -Path $gamsettings -Directory -Exclude "gamcache" | Select-Object -ExpandProperty Name
$datetime = get-date -f yyyy-MM-dd-HH-mm-ss
function Show-Menu {
cls
Write-Host "GAM project selected: $clientName"
Write-Host ""
Write-Host "Please choose an option:`n1. Generate audit report`n2. Archive mailbox messages to group`n3. List, add or remove mailbox delegation`n4. Change GAM project`n5. Exit script"
return (Read-Host -Prompt "Enter your choice")
}
function Select-GAMProject {
cls
# Refresh project list every time in case of changes
$directories = Get-ChildItem -Path $gamsettings -Directory -Exclude "gamcache" | Select-Object -ExpandProperty Name
Write-Host "Projects available:"
for ($i = 0; $i -lt $directories.Count; $i++) {
Write-Host "$($i + 1). $($directories[$i])"
}
Write-Host
$selectedProject = $null
while (-not $selectedProject) {
$selection = Read-Host "Please enter project number"
[int]$parsedSelection = 0
if ([int]::TryParse($selection, [ref]$parsedSelection) -and
$parsedSelection -ge 1 -and
$parsedSelection -le $directories.Count) {
$chosenProject = $directories[$parsedSelection - 1]
if ((& "$GAMpath\gam.exe" select $chosenProject 2>&1) -match "ERROR") {
Write-Host "Selected project '$chosenProject' is invalid. Please try again."
} else {
$selectedProject = $chosenProject
}
} else {
Write-Host "Invalid selection. Please enter a number between 1 and $($directories.Count)."
}
}
Write-Host "GAM project selected: $selectedProject"
& "$GAMpath\gam.exe" select $selectedProject save
$global:clientName = $selectedProject # Set globally
Write-Host "DEBUG: clientName is set to $clientName"
}
function Run-AuditReportScript {
Start-Process PowerShell -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File .\_script_AuditReport.ps1 -clientName $clientName -GAMpath $GAMpath -gamsettings $gamsettings -datetime $datetime -destinationpath $destinationpath" -Wait
}
function Run-ArchiveMailboxMessagesScript {
Start-Process PowerShell -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File .\_script_ArchiveMailboxMessages.ps1 -clientName $clientName -GAMpath $GAMpath -gamsettings $gamsettings -datetime $datetime" -Wait
}
function Run-MailboxDelegationScript {
Start-Process PowerShell -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File .\_script_MailboxDelegation.ps1 -clientName $clientName -GAMpath $GAMpath -gamsettings $gamsettings -datetime $datetime" -Wait
}
while ($true) {
if (-not $clientName) {
Select-GAMProject
Write-Host "DEBUG: After Select-GAMProject, clientName is $clientName"
}
$option = Show-Menu
try {
switch ($option) {
'1' {
# Call Google Workspace Auditor script to generate audit report
Run-AuditReportScript -clientName $clientName -GAMpath $GAMpath -gamsettings $gamsettings -datetime $datetime -destinationpath $destinationpath
}
'2' {
# Call script to archive mailbox messages to group
Run-ArchiveMailboxMessagesScript -clientName $clientName -GAMpath $GAMpath -gamsettings $gamsettings -datetime $datetime
}
'3' {
# Call script to list, add, or remove mailbox delegation
Run-MailboxDelegationScript -clientName $clientName -GAMpath $GAMpath -gamsettings $gamsettings -datetime $datetime
}
'4' {
# Change GAM project
Select-GAMProject
}
'5' {
Write-Output "Exiting script."
break
}
default {
Write-Output "Invalid option selected."
}
}
}
catch {
Write-Host "An error occurred: $_"
}
if ($option -eq '5') {
break
}
}

4
manifest.json Normal file
View File

@@ -0,0 +1,4 @@
{
"version": "1.0.31",
"author": "Ivan Carlos"
}

16
testing-guideline.md Normal file
View File

@@ -0,0 +1,16 @@
Please validate the Excel (.xlsx) document in the attached ZIP file and share your approval. Any necessary adjustments should be responded to in this email, and after the adjustments, a new document will be generated for validation and approval.
### How to validate users, groups, and access in the organization's technology environment:
1. **Users Tab**: Check if all users should exist in the environment based on their email (column A) or username (column K). Users who have been terminated can only be present in the list if column "M" is "True", indicating that the user is suspended. Column "H" shows the last login date and can help decide whether to maintain account access. Note that users who do not belong to a natural person (such as generic/non-nominal users) should not be maintained.
2. **Groups Tab**: Check if all groups should exist in the environment based on their email (column A) or group name (column C). Verify if the listed users (column F) should have access to the message history and receive messages sent to the group.
- **Note**: "abuse," "postmaster," and "security" groups are restricted for use by the technology infrastructure.
3. **TeamDriveACLs Tab**: Check if all Shared Drives should exist in the environment based on their name (column C). Each user with access to the shared drive is listed in a new row; verify if the listed user (column H) should have access to the Shared Drive indicated in the same row (column C). Also, check if each user's permission is appropriate for their role (column N).
**Permission Descriptions**:
- **organizer**: Admin permission, has full control over the Shared Drive.
- **fileOrganizer**: Content admin permission, has full control over files but cannot change Shared Drive settings.
- **writer**: Write permission, can create and modify files but some data destruction functions are restricted.
- **reader**: Read permission, cannot modify files.