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

This commit is contained in:
2025-12-16 04:43:41 -03:00
parent d6c8176c7e
commit fb6ead25ee
12 changed files with 1144 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 }}

76
ADMIN-install-modules.ps1 Normal file
View File

@@ -0,0 +1,76 @@
# Check for administrator privileges
$currentIdentity = [Security.Principal.WindowsIdentity]::GetCurrent()
$principal = New-Object Security.Principal.WindowsPrincipal($currentIdentity)
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
Write-Host "⚠ This script must be run as Administrator. Please restart PowerShell with elevated privileges." -ForegroundColor Yellow
Read-Host -Prompt "Press Enter to exit"
exit
}
# Trust PSGallery if not already trusted
try {
if ((Get-PSRepository -Name "PSGallery").InstallationPolicy -ne "Trusted") {
Set-PSRepository -Name "PSGallery" -InstallationPolicy Trusted
}
} catch {
Write-Warning "Failed to set PSGallery as trusted: $($_.Exception.Message)"
}
# List of required modules
$modules = @(
    'MicrosoftTeams',
    'ImportExcel',
    'Microsoft.Graph',
    'ExchangeOnlineManagement'
)
$updatedModules = @()
foreach ($module in $modules) {
try {
$installed = Get-InstalledModule -Name $module -ErrorAction Stop
Write-Host "`nUpdating module: $module (Current: $($installed.Version))" -ForegroundColor Cyan
Update-Module -Name $module -Force -ErrorAction Stop
$updatedModules += $module
Write-Host "Updated $module to latest version." -ForegroundColor Green
} catch {
Write-Host "`nModule not found or update failed: $module. Attempting install..." -ForegroundColor Yellow
try {
Install-Module -Name $module -Scope AllUsers -Force -AllowClobber -ErrorAction Stop
Write-Host "Successfully installed $module." -ForegroundColor Green
$updatedModules += $module
} catch {
Write-Warning ("Failed to install {0}: {1}" -f $module, $_.Exception.Message)
}
}
}
# Ask user if they want to remove old versions
if ($updatedModules.Count -gt 0) {
$response = Read-Host "`nDo you want to remove old versions of the updated modules? (y/n)"
if ($response -match '^(y|yes)$') {
foreach ($module in $updatedModules) {
try {
$allVersions = Get-InstalledModule -Name $module -AllVersions
$latest = $allVersions | Sort-Object Version -Descending | Select-Object -First 1
$oldVersions = $allVersions | Where-Object { $_.Version -ne $latest.Version }
foreach ($old in $oldVersions) {
Write-Host "Removing old version $($old.Version) of $module..." -ForegroundColor DarkGray
Uninstall-Module -Name $module -RequiredVersion $old.Version -Force -ErrorAction Stop
}
Write-Host "Cleaned up old versions of $module." -ForegroundColor Green
} catch {
Write-Warning ("Failed to remove old versions of {0}: {1}" -f $module, $_.Exception.Message)
}
}
} else {
Write-Host "Skipping module cleanup." -ForegroundColor Yellow
}
}
Write-Host "`nAll modules processed successfully." -ForegroundColor Cyan
Read-Host -Prompt "Press Enter to exit"

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,53 @@
# m365auditor
# Microsoft 365 Auditor script
This script collects users, groups and Teams of a Microsoft 365 environment on .xlsx file for audit and review purposes
<!-- buttons -->
[![Stars](https://img.shields.io/github/stars/ivancarlosti/m365auditor?label=⭐%20Stars&color=gold&style=flat)](https://github.com/ivancarlosti/m365auditor/stargazers)
[![Watchers](https://img.shields.io/github/watchers/ivancarlosti/m365auditor?label=Watchers&style=flat&color=red)](https://github.com/sponsors/ivancarlosti)
[![Forks](https://img.shields.io/github/forks/ivancarlosti/m365auditor?label=Forks&style=flat&color=ff69b4)](https://github.com/sponsors/ivancarlosti)
[![GitHub commit activity](https://img.shields.io/github/commit-activity/m/ivancarlosti/m365auditor?label=Activity)](https://github.com/ivancarlosti/m365auditor/pulse)
[![GitHub Issues](https://img.shields.io/github/issues/ivancarlosti/m365auditor?label=Issues&color=orange)](https://github.com/ivancarlosti/m365auditor/issues)
[![License](https://img.shields.io/github/license/ivancarlosti/m365auditor?label=License)](LICENSE)
[![GitHub last commit](https://img.shields.io/github/last-commit/ivancarlosti/m365auditor?label=Last%20Commit)](https://github.com/ivancarlosti/m365auditor/commits)
[![Security](https://img.shields.io/badge/Security-View%20Here-purple)](https://github.com/ivancarlosti/m365auditor/security)
[![Code of Conduct](https://img.shields.io/badge/Code%20of%20Conduct-2.1-4baaaa)](https://github.com/ivancarlosti/m365auditor?tab=coc-ov-file)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/ivancarlosti?label=GitHub%20Sponsors&color=ffc0cb)][sponsor]
<!-- endbuttons -->
## Instructions
* Save the last release version and extract files locally (download [here](https://github.com/ivancarlosti/m365auditor/releases/latest))
* Change variables of `mainscript.ps1` if needed
* Update `tenantIds.txt` with your tenants
* Run `mainscript.ps1` on PowerShell (right-click on file > Run with PowerShell)
* Follow instructions selecting tenant, authenticate, collect .zip file on `$destinationpath`
* If needs help to install or update required modules, run `ADMIN-install-modules.ps1` as administrator
## Requirements
* Windows 10+ or Windows Server 2019+
* PowerShell 5.x (some modules still runs under .NET Framework, PowerShell 7.x uses .NET Core)
* Modules `MicrosoftTeams`, `ImportExcel`, `Microsoft.Graph`, `ImportExcel` on PowerShell
<!-- 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

513
mainscript.ps1 Normal file
View File

@@ -0,0 +1,513 @@
# Set console output encoding to UTF-8 for Latin characters
[console]::OutputEncoding = [System.Text.Encoding]::UTF8
# Specify the output directory for working files, report and other variables
$outputDirectory = (New-Object -ComObject Shell.Application).NameSpace('shell:Downloads').Self.Path
$timestamp = Get-Date -Format "yyyy-MM-dd_HH-mm-ss"
# Read tenant IDs from a text file
$tenantIdsFilePath = ".\tenantIds.txt"
if (-not (Test-Path $tenantIdsFilePath)) {
Write-Host "Tenant IDs file not found. Please create a file named 'tenantIds.txt' with the tenant IDs, one per line."
pause
exit
}
$tenantIds = Get-Content -Path $tenantIdsFilePath
# Prompt the user to select a tenant
Write-Host
Write-Host "Select a Tenant ID to connect:"
for ($i = 0; $i -lt $tenantIds.Count; $i++) {
Write-Host "$($i + 1). $($tenantIds[$i])"
}
$tenantSelection = Read-Host "Enter the number of the Tenant ID you want to connect to"
$selectedTenantId = $tenantIds[$tenantSelection - 1]
# Check if ImportExcel module is installed, if not, ask the user to install it
if (-not (Get-Module -ListAvailable -Name ImportExcel)) {
Write-Host "ImportExcel module is not installed. Please install it using the following command: Install-Module -Name ImportExcel -Force -AllowClobber"
pause
exit
}
# Check if Microsoft Teams module is installed, if not, ask the user to install it
if (-not (Get-Module -ListAvailable -Name MicrosoftTeams)) {
Write-Host "Microsoft Teams module is not installed. Please install it using the following command: Install-Module -Name MicrosoftTeams -Force -AllowClobber"
pause
exit
}
# Check if Microsoft Graph module is installed, if not, ask the user to install it
if (-not (Get-Module -ListAvailable -Name Microsoft.Graph)) {
Write-Host "Microsoft Graph module is not installed. Please install it using the following command: Install-Module -Name Microsoft.Graph -Force -AllowClobber"
pause
exit
}
# Check if Exchange Online module is installed, if not, ask the user to install it
if (-not (Get-Module -ListAvailable -Name ExchangeOnlineManagement)) {
Write-Host "ExchangeOnlineManagement module is not installed. Please install it using the following command: Install-Module -Name ExchangeOnlineManagement -Force -AllowClobber"
pause
exit
}
# Import the required modules
Import-Module Microsoft.Graph.Authentication
Import-Module Microsoft.Graph.Users # Get-MgUser Get-MgUserLicenseDetail
Import-Module Microsoft.Graph.Groups
Import-Module Microsoft.Graph.Teams # Get-MgTeam
Import-Module Microsoft.Graph.Identity.DirectoryManagement # Get-MgDomain
Import-Module MicrosoftTeams # Get-TeamChannel Get-TeamChannelUser
Import-Module ImportExcel # Export-Excel
Import-Module ExchangeOnlineManagement # Get-Mailbox
# Connect to Microsoft Graph
Write-Host
Write-Host "Connecting to Microsoft Graph..."
Connect-MgGraph -TenantId $selectedTenantId -Scopes "User.Read.All Directory.Read.All Group.Read.All Team.ReadBasic.All TeamMember.Read.All ChannelMember.Read.All Domain.Read.All" -NoWelcome
Write-Host
Write-Host "Connected to Microsoft Graph."
# Connect to Microsoft Teams
Write-Host
Write-Host "Connecting to Microsoft Teams..."
Connect-MicrosoftTeams
Write-Host "Connected to Microsoft Teams."
# Connect to Exchange Online
Write-Host
Write-Host "Connecting to Exchange Online..."
Connect-ExchangeOnline -ShowBanner:$false
Write-Host
Write-Host "Connected to Exchange Online."
# Create the output directory if it doesn't exist
if (-not (Test-Path $outputDirectory)) {
New-Item -ItemType Directory -Path $outputDirectory
}
# Export Groups with all columns available
Write-Host
Write-Host "Exporting groups..."
$groups = Get-MgGroup -All
# Export Users with account status and licenses using Graph (temporary)
# Comprehensive license mapping including Visio SKUs
$licenseMap = @{
'a403ebcc-fae0-4ca2-8c8c-7a907fd6c235' = 'Microsoft Fabric (Free)'
'f245ecc8-75af-4f8e-b61f-27d8114de5f3' = 'Microsoft 365 Business Standard'
'3b555118-da6a-4418-894f-7df1e2096870' = 'Microsoft 365 Business Basic'
'f30db892-07e9-47e9-837c-80727f46fd3d' = 'Microsoft Power Automate Free'
'4b9405b0-7788-4568-add1-99614e613b69' = 'Exchange Online (Plan 1)'
'19ec0d23-8335-4cbd-94ac-6050e30712fa' = 'Exchange Online (Plan 2)'
'cdd28e44-67e3-425e-be4c-737fab2899d3' = 'Microsoft 365 Apps for business'
'50509a35-f0bd-4c5e-89ac-22f0e16a00f8' = 'Microsoft Teams Rooms Basic without Audio Conferencing'
'6af4b3d6-14bb-4a2a-960c-6c902aad34f3' = 'Microsoft Teams Rooms Basic'
'e0dfc8b9-9531-4ec8-94b4-9fec23b05fc8' = 'Microsoft Teams Exploratory Dept (unlisted)'
'f8a1db68-be16-40ed-86d5-cb42ce701560' = 'Power BI Pro'
'cbdc14ab-d96c-4c30-b9f4-6ada7cdc1d46' = 'Microsoft 365 Business Premium'
'c1d032e0-5619-4761-9b5c-75b6831e1711' = 'Power BI Premium Per User'
'53818b1b-4a27-454b-8896-0dba576410e6' = 'Planner and Project Plan 3'
'295a8eb0-f78d-45c7-8b5b-1eed5ed02dff' = 'Microsoft Teams Shared Devices'
'c5928f49-12ba-48f7-ada3-0d743a3601d5' = 'Vision Plan 2'
'1f2f344a-700d-42c9-9427-5cea1d5d7ba6' = 'Microsoft Stream Trial'
'52ea0e27-ae73-4983-a08f-13561ebdb823' = 'Teams Premium (for Departments)'
'639dec6b-bb19-468b-871c-c5c441c4b0cb' = 'Microsoft 365 Copilot'
'5b631642-bd26-49fe-bd20-1daaa972ef80' = 'Microsoft Power Apps for Developer'
'beb6439c-caad-48d3-bf46-0c82871e12be' = 'Planner Plan 1'
'3f9f06f5-3c31-472c-985f-62d9c10ec167' = 'Power Pages vTrial for Makers'
}
# Get users and process license information
$usersGraph = Get-MgUser -All -Property `
Id,DisplayName,UserPrincipalName,Mail,AccountEnabled,UserType, `
CreatedDateTime,AssignedLicenses,Department,JobTitle,OfficeLocation, `
UserPrincipalName,ProxyAddresses
$usersGraphReport = $usersGraph | ForEach-Object {
$user = $_
$skuPartNumbers = @()
try {
$licenseDetails = Get-MgUserLicenseDetail -UserId $user.Id
foreach ($license in $licenseDetails) {
$skuPartNumbers += $license.SkuPartNumber
}
} catch {
Write-Warning "Could not get license details for $($user.UserPrincipalName)"
$skuPartNumbers += "Error"
}
[PSCustomObject]@{
Id = $user.Id
DisplayName = $user.DisplayName
UserPrincipalName = $user.UserPrincipalName
Mail = $user.Mail
AccountEnabled = $user.AccountEnabled
UserType = $user.UserType
Licenses = ($user.AssignedLicenses | ForEach-Object {
if ($licenseMap.ContainsKey($_.SkuId)) {
$licenseMap[$_.SkuId]
} else {
$_.SkuId
}
}) -join ", "
LicenseSKUs = ($user.AssignedLicenses.SkuId -join ", ")
SkuPartNumbers = $skuPartNumbers -join ", "
CreatedDateTime = $user.CreatedDateTime
Department = $user.Department
JobTitle = $user.JobTitle
OfficeLocation = $user.OfficeLocation
}
}
$usersGraphReport | Export-Csv -Path "$outputDirectory\UsersGraph.csv" -NoTypeInformation -Encoding UTF8
# Export Shared Mailboxes
Write-Host
Write-Host "Exporting Shared Mailboxes..."
$sharedMailboxes = Get-Mailbox -RecipientTypeDetails SharedMailbox -ResultSize Unlimited | Select-Object DisplayName,PrimarySmtpAddress,ExternalDirectoryObjectId
$sharedMailboxes | Export-Csv -Path "$outputDirectory\SharedMailboxes.csv" -NoTypeInformation -Encoding UTF8
Write-Host "Shared Mailboxes exported to $outputDirectory\SharedMailboxes.csv."
# Get all Teams
$teams = Get-MgTeam -All
# Export Teams using original file name
$teams | Select-Object Id, DisplayName, Description, Visibility | `
Export-Csv -Path "$outputDirectory\Teams.csv" -NoTypeInformation -Encoding UTF8 -Force
Write-Host "Teams exported to $outputDirectory\Teams.csv."
# Export Teams Channels and Memberships
Write-Host
Write-Host "Exporting Teams channels and memberships..."
$teamsChannels = @()
# Loop through Teams
foreach ($team in $teams) {
# Check if Team Id is null or empty
if ([string]::IsNullOrEmpty($team.Id)) {
Write-Host "Skipping team '$($team.DisplayName)' because Id is null or empty."
continue
}
# Get channels for the team
try {
$channels = Get-TeamChannel -GroupId $team.Id
} catch {
Write-Warning "Could not get channels for team $($team.DisplayName): $_"
continue
}
foreach ($channel in $channels) {
$channelMembers = @()
$membershipNote = ""
if ($channel.MembershipType -eq "shared") {
$membershipNote = "Shared channel membership not accessible via Teams module"
Write-Warning "Skipping members of shared channel '$($channel.DisplayName)' in team '$($team.DisplayName)'"
} else {
try {
$channelMembers = Get-TeamChannelUser -GroupId $team.Id -DisplayName $channel.DisplayName
} catch {
$membershipNote = "Error retrieving users: $_"
Write-Warning "Could not get users for channel $($channel.DisplayName) in team $($team.DisplayName): $_"
}
}
if (-not $channelMembers -or $channelMembers.Count -eq 0) {
$teamsChannels += [PSCustomObject]@{
TeamName = $team.DisplayName
TeamId = $team.Id
ChannelName = $channel.DisplayName
ChannelId = $channel.Id
MembershipType = $channel.MembershipType
UserName = ""
Role = ""
Notes = $membershipNote
}
} else {
foreach ($member in $channelMembers) {
$teamsChannels += [PSCustomObject]@{
TeamName = $team.DisplayName
TeamId = $team.Id
ChannelName = $channel.DisplayName
ChannelId = $channel.Id
MembershipType = $channel.MembershipType
UserName = $member.User
Role = $member.Role
Notes = ""
}
}
}
}
}
$teamsChannels | Export-Csv -Path "$outputDirectory\TeamsChannels.csv" -NoTypeInformation -Encoding UTF8 -Force
Write-Host "Teams channels and memberships exported to $outputDirectory\TeamsChannels.csv."
# Wait
Start-Sleep -Seconds 2
# Export Domains
Write-Host
Write-Host "Exporting domains..."
$domains = Get-MgDomain
$domains | Export-Csv -Path "$outputDirectory\Domains.csv" -NoTypeInformation -Encoding UTF8
Write-Host "Domains exported to $outputDirectory\Domains.csv."
# Convert Teams and Groups data to arrays for easier comparison
$teamsArray = $teams | ForEach-Object {
[PSCustomObject]@{
GroupId = $_.GroupId
}
}
$groupsArray = $groups | ForEach-Object {
$aliases = (Get-MgGroup -GroupId $_.Id).ProxyAddresses -join ", "
[PSCustomObject]@{
ObjectId = $_.Id
DisplayName = $_.DisplayName
MailEnabled = $_.MailEnabled
Mail = $_.Mail
SecurityEnabled = $_.SecurityEnabled
SharePointGroup = if ($teamsArray.GroupId -contains $_.Id -or $aliases -match "SPO:") { "Yes" } else { "No" }
Aliases = $aliases
}
}
$groupsArray | Export-Csv -Path "$outputDirectory\Groups.csv" -NoTypeInformation -Encoding UTF8
Write-Host "Groups exported to $outputDirectory\Groups.csv."
# Export Group Memberships to separate CSV files based on SharePoint group status
Write-Host
Write-Host "Exporting group memberships..."
$sharePointMembershipsCsvPath = "$outputDirectory\SharePointMemberships.csv"
$dlMembershipsCsvPath = "$outputDirectory\DLMemberships.csv"
$sharePointMemberships = @()
$dlMemberships = @()
foreach ($group in $groupsArray) {
$owners = Get-MgGroupOwner -GroupId $group.ObjectId
$members = Get-MgGroupMember -GroupId $group.ObjectId
foreach ($owner in $owners) {
$userInfo = $usersGraph | Where-Object { $_.Id -eq $owner.Id }
$membership = [PSCustomObject]@{
GroupName = $group.DisplayName
GroupObjectId = $group.ObjectId
ObjectId = $owner.Id
DisplayName = $userInfo.DisplayName
UserPrincipalName= $userInfo.UserPrincipalName
MembershipStatus = "Owner"
}
if ($group.SharePointGroup -eq "Yes") {
$sharePointMemberships += $membership
} else {
$dlMemberships += $membership
}
}
foreach ($member in $members) {
$userInfo = $usersGraph | Where-Object { $_.Id -eq $member.Id }
$membership = [PSCustomObject]@{
GroupName = $group.DisplayName
GroupObjectId = $group.ObjectId
ObjectId = $member.Id
DisplayName = $userInfo.DisplayName
UserPrincipalName= $userInfo.UserPrincipalName
MembershipStatus = "Member"
}
if ($group.SharePointGroup -eq "Yes") {
$sharePointMemberships += $membership
} else {
$dlMemberships += $membership
}
}
}
$sharePointMemberships | Export-Csv -Path $sharePointMembershipsCsvPath -NoTypeInformation -Encoding UTF8
Write-Host "SharePoint memberships exported to $sharePointMembershipsCsvPath."
$dlMemberships | Export-Csv -Path $dlMembershipsCsvPath -NoTypeInformation -Encoding UTF8
Write-Host "DL memberships exported to $dlMembershipsCsvPath."
# Get all distribution groups
$groups = Get-DistributionGroup -ResultSize Unlimited
# Prepare an array to store results
$dlOwnersCsvPath = "$outputDirectory\DLOwners.csv"
$dlOwners = @()
foreach ($group in $groups) {
foreach ($owner in $group.ManagedBy) {
$ownerRecipient = Get-Recipient -Identity $owner -ErrorAction SilentlyContinue
# Defensive: Only output if we get a valid recipient
if ($ownerRecipient) {
$dlOwners += [PSCustomObject]@{
GroupDisplayName = $group.DisplayName
GroupPrimarySmtp = $group.PrimarySmtpAddress
OwnerDisplayName = $ownerRecipient.DisplayName
OwnerEmail = $ownerRecipient.PrimarySmtpAddress
}
}
}
}
# Export to CSV
$dlOwners | Export-Csv -Path $dlOwnersCsvPath -NoTypeInformation -Encoding UTF8
Write-Host "Distribution list owners exported to $outputDirectory\DLOwners.csv."
# Disconnect from Microsoft Teams (if connected)
try {
Disconnect-MicrosoftTeams -ErrorAction SilentlyContinue
Write-Host "Disconnected from Microsoft Teams." -ForegroundColor Green
} catch {
Write-Host "No active Teams session to disconnect." -ForegroundColor Yellow
}
# Disconnect from Microsoft Graph (if connected)
try {
Disconnect-MgGraph -ErrorAction SilentlyContinue
Write-Host "Disconnected from Microsoft Graph." -ForegroundColor Green
} catch {
Write-Host "No active Graph session to disconnect." -ForegroundColor Yellow
}
# Disconnect from Exchange Online (if connected)
try {
Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue
Write-Host "Disconnected from Exchange Online." -ForegroundColor Green
} catch {
Write-Host "No active Exchange Online session to disconnect." -ForegroundColor Yellow
}
# Consolidate all CSVs into an Excel file with each CSV in a separate sheet
Write-Host
Write-Host "Consolidating CSVs into an Excel file..."
$excelFilePath = "$outputDirectory\$selectedTenantId-report_$timestamp.xlsx"
$excelSheets = @(
@{Name="Users"; Path="$outputDirectory\UsersGraph.csv"},
@{Name="SharedMailboxes"; Path="$outputDirectory\SharedMailboxes.csv"},
@{Name="Groups"; Path="$outputDirectory\Groups.csv"},
@{Name="SharePointMemberships"; Path="$sharePointMembershipsCsvPath"},
@{Name="DLMemberships"; Path="$dlMembershipsCsvPath"},
@{Name="DLOwners"; Path="$dlOwnersCsvPath"},
@{Name="Teams"; Path="$outputDirectory\Teams.csv"},
@{Name="TeamsChannels"; Path="$outputDirectory\TeamsChannels.csv"},
@{Name="Domains"; Path="$outputDirectory\Domains.csv"}
)
foreach ($sheet in $excelSheets) {
Import-Csv -Path $sheet.Path | Export-Excel -Path $excelFilePath -WorksheetName $sheet.Name -AutoSize -TableName $sheet.Name -TableStyle Light1
}
Write-Host "CSV consolidation complete. Excel file saved to $excelFilePath."
# Wait
Start-Sleep -Seconds 2
# Delete the CSV files after consolidation
foreach ($sheet in $excelSheets) {
Remove-Item -Path $sheet.Path -Force
Write-Host "Deleted $($sheet.Path)"
}
# Clear the prompt
Clear-Host
# Clear the prompt
cls
# Calculate MD5 hash of the Excel file
Write-Host "Calculating MD5 hash of the Excel file..."
Write-Host "File name: $selectedTenantId-report_$timestamp.xlsx"
$md5Hash = Get-FileHash -Path $excelFilePath -Algorithm MD5 | Select-Object -ExpandProperty Hash
Write-Host "MD5 hash of the Excel file: $md5Hash"
Write-Host
# 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}")
Start-Sleep -Milliseconds 500 # Give clipboard time to update
# Try to get image from clipboard
$image = [System.Windows.Forms.Clipboard]::GetImage()
if ($image -ne $null) {
$bitmap = New-Object System.Drawing.Bitmap $image
$screenshotPath = "$outputDirectory\screenshot_$timestamp.png"
$bitmap.Save($screenshotPath)
Write-Host "Screenshot saved to $screenshotPath"
} else {
Write-Host "No image found in clipboard. Screenshot not saved."
$screenshotPath = $null
}
Write-Host
Start-Sleep -Seconds 2
# Archive the Excel file, the script itself, and the screenshot in a zip file
Write-Host "Archiving the Excel file, the script itself, and the screenshot in a zip file..."
$zipFilePath = "$outputDirectory\$selectedTenantId-report_$timestamp.zip"
$scriptPath = $MyInvocation.MyCommand.Path
Write-Host "$zipFilePath"
Write-Host "$scriptPath"
Write-Host "$excelFilePath"
Write-Host
Start-Sleep -Seconds 2
# Prepare list of files to archive
$filesToArchive = @($excelFilePath, $scriptPath)
if ($screenshotPath) {
$filesToArchive += $screenshotPath
}
Compress-Archive -Path $filesToArchive -DestinationPath $zipFilePath
Write-Host "Files archived to $zipFilePath"
Start-Sleep -Seconds 2
# Delete the Excel file and the screenshot if it exists
Remove-Item -Path $excelFilePath -Force
if ($screenshotPath) {
Remove-Item -Path $screenshotPath -Force
}
Write-Host "Deleted $selectedTenantId-report_$timestamp.xlsx and screenshot (if created)"
Start-Sleep -Seconds 2
# Pause at the end
pause

4
manifest.json Normal file
View File

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

11
pendings.txt Normal file
View File

@@ -0,0 +1,11 @@
ToDo:
* ADMIN-install-modules.ps1 can warn `'PackageManagement' is currently in use`, it's safe to ignore this warning.
* Find a way to list last user login and activity, last signin call using Microsoft Graph requires AD Premium license.
* Microsoft Graph API workaround Microsoft Graph module limitations but requires app registration.
* Pending to validate Shared channels on Teams if they will add note related to unable to catch users
* Improve Shared Mailbox with members
* Create delegated mailbox TAB
* Improve DLs merging members and owners using ExchangeOnline module, owners list have issue reporting System.Object[]
* List aliases of all mail accounts (users, dls, sharepoint)
* List 2FA settings of Microsoft 365 (portal admins + enforced rule + AzureAD)

1
tenantIds.txt Normal file
View File

@@ -0,0 +1 @@
tenantID01.onmicrosoft.com

View File

@@ -0,0 +1,13 @@
Please validate the Excel (.xlsx) document attached 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 "E" is "Disabled", indicating that the user is blocked. Note that users who do not belong to a natural person (such as generic/non-nominal users) should not be maintained.
2. **SharePointMemberships Tab**: Check if all SharePoints should exist in the environment based on their name (column A). Each user with access to the SharePoint is listed in a new row; verify if the listed user (column D, E, F) should have access to the SharePoint indicated in the same row (column A). Also, check if each user's permission is appropriate for their role (column G).
3. **DLMemberships Tab**: Check if all Distributiol lists should exist in the environment based on their name (column A). Each user that receive messages from each Distribution List is listed in a new row; verify if the listed user (column D, E, F) should get e-mail messages from related Distribution List indicated in the same row (column A). Also, check if each user's permission is appropriate for their role (column G).
**Permission Descriptions**:
- **Owner**: Admin permission, has full control over the SharePoint/Distribution List.
- **Member**: Have full control over files/get messages but cannot approve, add or remove new members

View File

@@ -0,0 +1,13 @@
Por favor, valide o documento Excel (.xlsx) em anexo e compartilhe sua aprovação. Quaisquer ajustes necessários devem ser respondidos neste e-mail, e após os ajustes, um novo documento será gerado para validação e aprovação.
### Como validar usuários, grupos e acesso no ambiente tecnológico da organização:
1. **Aba Users**: Verifique se todos os usuários devem existir no ambiente com base em seu e-mail (coluna A) ou nome de usuário (coluna K). Usuários que foram desligados só podem estar presentes na lista se a coluna "E" estiver "Desativado", indicando que o usuário está bloqueado. Note que usuários que não pertencem a uma pessoa natural (como usuários genéricos/não nominais) não devem ser mantidos.
2. **Aba SharePointMemberships**: Verifique se todos os SharePoints devem existir no ambiente com base em seu nome (coluna A). Cada usuário com acesso ao SharePoint está listado em uma nova linha; verifique se o usuário listado (coluna D, E, F) deve ter acesso ao SharePoint indicado na mesma linha (coluna A). Além disso, verifique se a permissão de cada usuário é apropriada para seu papel (coluna G).
3. **Aba DLMemberships**: Verifique se todas as listas de distribuição devem existir no ambiente com base em seu nome (coluna A). Cada usuário que recebe mensagens de cada lista de distribuição está listado em uma nova linha; verifique se o usuário listado (coluna D, E, F) deve receber mensagens de e-mail da lista de distribuição relacionada indicada na mesma linha (coluna A). Além disso, verifique se a permissão de cada usuário é apropriada para seu papel (coluna G).
**Descrições de Permissão**:
- **Proprietário**: Permissão de administrador, tem controle total sobre o SharePoint/Lista de Distribuição.
- **Membro**: Tem controle total sobre arquivos/receber mensagens, mas não pode aprovar, adicionar ou remover novos membros.