This commit is contained in:
240
.github/workflows/release_build.yml
vendored
Normal file
240
.github/workflows/release_build.yml
vendored
Normal 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
78
.github/workflows/update_readme.yml
vendored
Normal 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
21
LICENSE
Normal 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
122
POWERSHELL_ISSUE.md
Normal 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 you’re 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 doesn’t 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/
|
||||
|
||||
86
README.md
86
README.md
@@ -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 -->
|
||||
[](https://github.com/ivancarlosti/gwauditor/stargazers)
|
||||
[](https://github.com/sponsors/ivancarlosti)
|
||||
[](https://github.com/sponsors/ivancarlosti)
|
||||
[](https://github.com/ivancarlosti/gwauditor/pulse)
|
||||
[](https://github.com/ivancarlosti/gwauditor/issues)
|
||||
[](LICENSE)
|
||||
[](https://github.com/ivancarlosti/gwauditor/commits)
|
||||
[](https://github.com/ivancarlosti/gwauditor/security)
|
||||
[](https://github.com/ivancarlosti/gwauditor?tab=coc-ov-file)
|
||||
[][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
|
||||
|
||||

|
||||
*Script startup*
|
||||
|
||||

|
||||
*Script completed*
|
||||
|
||||

|
||||
*.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
|
||||
|
||||
170
_script_ArchiveMailboxMessages.ps1
Normal file
170
_script_ArchiveMailboxMessages.ps1
Normal 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
230
_script_AuditReport.ps1
Normal 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
|
||||
220
_script_MailboxDelegation.ps1
Normal file
220
_script_MailboxDelegation.ps1
Normal 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
112
mainscript.ps1
Normal 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
4
manifest.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"version": "1.0.31",
|
||||
"author": "Ivan Carlos"
|
||||
}
|
||||
16
testing-guideline.md
Normal file
16
testing-guideline.md
Normal 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.
|
||||
Reference in New Issue
Block a user