From d26a5d4a0a8df2e1ca7619dd7a85ee3c63b6de26 Mon Sep 17 00:00:00 2001 From: Ivo Oskamp Date: Sat, 20 Jun 2026 21:30:50 +0200 Subject: [PATCH] Repository moved to Forgejo https://forgejo.oskamp.info/ivooskamp/Random_scripts.git --- .gitignore | 13 - README.md | 46 +- automation/docker/build-and-push.sh | 269 --------- automation/docker/update-and-build.sh | 232 -------- docs/conventions.md | 40 -- docs/runbooks/index.md | 45 -- output/.gitkeep | 0 scripts/content/convert-md.bat | 65 --- .../exchange/get-mailbox-with-permissions.ps1 | 65 --- scripts/m365/identity/m365-users-mfa.ps1 | 41 -- .../sharepoint/get-sharepoint-permissions.ps1 | 549 ------------------ scripts/m365/smtp/Test-SMTPRelay-M365.ps1 | 60 -- scripts/m365/smtp/Test-SMTPSend-Auth-M365.ps1 | 64 -- scripts/macos/serialToAdminPassword.sh | 31 - .../autopilot/Get-WindowsAutoPilotInfo.ps1 | 190 ------ scripts/windows/endpoint/Update rmm.ps1 | 45 -- scripts/windows/endpoint/schedule_reboot.ps1 | 35 -- .../endpoint/uninstall-MS-Visual-C++-2010.ps1 | 123 ---- scripts/windows/endpoint/uuid.ps1 | 10 - 19 files changed, 6 insertions(+), 1917 deletions(-) delete mode 100644 .gitignore delete mode 100644 automation/docker/build-and-push.sh delete mode 100644 automation/docker/update-and-build.sh delete mode 100644 docs/conventions.md delete mode 100644 docs/runbooks/index.md delete mode 100644 output/.gitkeep delete mode 100644 scripts/content/convert-md.bat delete mode 100644 scripts/m365/exchange/get-mailbox-with-permissions.ps1 delete mode 100644 scripts/m365/identity/m365-users-mfa.ps1 delete mode 100644 scripts/m365/sharepoint/get-sharepoint-permissions.ps1 delete mode 100644 scripts/m365/smtp/Test-SMTPRelay-M365.ps1 delete mode 100644 scripts/m365/smtp/Test-SMTPSend-Auth-M365.ps1 delete mode 100644 scripts/macos/serialToAdminPassword.sh delete mode 100644 scripts/windows/autopilot/Get-WindowsAutoPilotInfo.ps1 delete mode 100644 scripts/windows/endpoint/Update rmm.ps1 delete mode 100644 scripts/windows/endpoint/schedule_reboot.ps1 delete mode 100644 scripts/windows/endpoint/uninstall-MS-Visual-C++-2010.ps1 delete mode 100644 scripts/windows/endpoint/uuid.ps1 diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 6d2033e..0000000 --- a/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -# Runtime/output -output/* -!output/.gitkeep - -# PowerShell logs/transcripts -*.log -*.txt - -# OS/editor -.DS_Store -Thumbs.db -.vscode/ -.idea/ diff --git a/README.md b/README.md index cc3df38..bf44cc9 100644 --- a/README.md +++ b/README.md @@ -1,43 +1,9 @@ -# Random Scripts +# Random_scripts — moved -Collection of operational scripts for Microsoft 365, Windows endpoint management, macOS, and Docker automation. +This repository has **moved to Forgejo**. -## Structure +➡️ **New location:** https://forgejo.oskamp.info/ivooskamp/Random_scripts.git -- `scripts/m365/smtp/` - - `Test-SMTPRelay-M365.ps1` - - `Test-SMTPSend-Auth-M365.ps1` -- `scripts/m365/exchange/` - - `get-mailbox-with-permissions.ps1` -- `scripts/m365/identity/` - - `m365-users-mfa.ps1` -- `scripts/m365/sharepoint/` - - `get-sharepoint-permissions.ps1` -- `scripts/windows/autopilot/` - - `Get-WindowsAutoPilotInfo.ps1` -- `scripts/windows/endpoint/` - - `Update rmm.ps1` - - `schedule_reboot.ps1` - - `uninstall-MS-Visual-C++-2010.ps1` - - `uuid.ps1` -- `scripts/macos/` - - `serialToAdminPassword.sh` -- `scripts/content/` - - `convert-md.bat` -- `automation/docker/` - - `build-and-push.sh` - - `update-and-build.sh` -- `docs/` - - working practices and conventions -- `output/` - - generated output files (CSV/logs), not committed - -## Important Note - -Legacy script filenames are intentionally **unchanged** to preserve compatibility with existing guides and runbooks. New scripts may use consistent lowercase naming. - -## Usage - -- Run scripts from the correct environment (PowerShell/Bash/Batch). -- Check required modules/permissions per script. -- Write exports to `output/` when possible. +```bash +git remote set-url origin https://forgejo.oskamp.info/ivooskamp/Random_scripts.git +``` diff --git a/automation/docker/build-and-push.sh b/automation/docker/build-and-push.sh deleted file mode 100644 index 2665bdd..0000000 --- a/automation/docker/build-and-push.sh +++ /dev/null @@ -1,269 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# ============================================================================ -# build-and-push.sh -# Location: repo root -# -# Purpose: -# - Automatic version bump: -# 1 = patch, 2 = minor, 3 = major, t = test -# - Test builds: only update :dev (no commit/tag) -# - Release builds: update version.txt, commit, tag, push (to the current branch) -# - Build & push Docker images for each service under ./compose/* -# - Preflight checks: Docker daemon up, logged in to registry, valid names/tags -# - Summary: show all images + tags built and pushed -# - Branch visibility: -# - Shows currently checked out branch (authoritative) -# - Reads .last-branch for info (if present) when BRANCH is not set -# - Writes the current branch back to .last-branch at the end -# -# Usage: -# BRANCH= ./build-and-push.sh [bump] # BRANCH is optional; informative only -# ./build-and-push.sh [bump] -# If [bump] is omitted, you will be prompted (default = t). -# ============================================================================ - -DOCKER_REGISTRY="gitea.oskamp.info" -DOCKER_NAMESPACE="ivooskamp" - -VERSION_FILE="version.txt" -START_VERSION="v0.1.0" -COMPOSE_DIR="containers" -LAST_BRANCH_FILE=".last-branch" # stored in repo root - -# --- Input: prompt if missing ------------------------------------------------ -BUMP="${1:-}" -if [[ -z "${BUMP}" ]]; then - echo "Select bump type: [1] patch, [2] minor, [3] major, [t] test (default: t)" - read -r BUMP - BUMP="${BUMP:-t}" -fi - -if [[ "$BUMP" != "1" && "$BUMP" != "2" && "$BUMP" != "3" && "$BUMP" != "t" ]]; then - echo "[ERROR] Unknown bump type '$BUMP' (use 1, 2, 3, or t)." - exit 1 -fi - -# --- Helpers ----------------------------------------------------------------- -read_version() { - if [[ -f "$VERSION_FILE" ]]; then - tr -d ' \t\n\r' < "$VERSION_FILE" - else - echo "$START_VERSION" - fi -} - -write_version() { - echo "$1" > "$VERSION_FILE" -} - -bump_version() { - local cur="$1" - local kind="$2" - local core="${cur#v}" - IFS='.' read -r MA MI PA <<< "$core" - case "$kind" in - 1) PA=$((PA + 1));; - 2) MI=$((MI + 1)); PA=0;; - 3) MA=$((MA + 1)); MI=0; PA=0;; - *) echo "[ERROR] Unknown bump kind"; exit 1;; - esac - echo "v${MA}.${MI}.${PA}" -} - -check_docker_ready() { - if ! docker info >/dev/null 2>&1; then - echo "[ERROR] Docker daemon not reachable. Is Docker running and do you have permission to use it?" - exit 1 - fi -} - -ensure_registry_login() { - local cfg="${HOME}/.docker/config.json" - if [[ ! -f "$cfg" ]]; then - echo "[ERROR] Docker config not found at $cfg. Please login: docker login ${DOCKER_REGISTRY}" - exit 1 - fi - if ! grep -q "\"${DOCKER_REGISTRY}\"" "$cfg"; then - echo "[ERROR] No registry auth found for ${DOCKER_REGISTRY}. Please run: docker login ${DOCKER_REGISTRY}" - exit 1 - fi -} - -validate_repo_component() { - local comp="$1" - if [[ ! "$comp" =~ ^[a-z0-9]+([._-][a-z0-9]+)*$ ]]; then - echo "[ERROR] Invalid repository component '$comp'." - echo " Must match: ^[a-z0-9]+([._-][a-z0-9]+)*$ (lowercase, digits, ., _, - as separators)." - return 1 - fi -} - -validate_tag() { - local tag="$1" - local len="${#tag}" - if (( len < 1 || len > 128 )); then - echo "[ERROR] Invalid tag length ($len). Must be between 1 and 128 characters." - return 1 - fi - if [[ ! "$tag" =~ ^[A-Za-z0-9_][A-Za-z0-9_.-]*$ ]]; then - echo "[ERROR] Invalid tag '$tag'. Allowed: [A-Za-z0-9_.-], must start with alphanumeric or underscore." - return 1 - fi -} - -# --- Preflight --------------------------------------------------------------- -if [[ ! -d ".git" ]]; then - echo "[ERROR] Not a git repository (.git missing)." - exit 1 -fi - -if [[ ! -d "$COMPOSE_DIR" ]]; then - echo "[ERROR] '$COMPOSE_DIR' directory missing. Expected ./compose// with a Dockerfile." - exit 1 -fi - -check_docker_ready -ensure_registry_login -validate_repo_component "$DOCKER_NAMESPACE" - -# Detect currently checked out branch (authoritative for this script) -DETECTED_BRANCH="$(git branch --show-current 2>/dev/null || true)" -if [[ -z "$DETECTED_BRANCH" ]]; then - DETECTED_BRANCH="$(git symbolic-ref --quiet --short HEAD 2>/dev/null || true)" -fi -if [[ -z "$DETECTED_BRANCH" ]]; then - # Try to derive from upstream - UPSTREAM_REF_DERIVED="$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null || true)" - if [[ -n "$UPSTREAM_REF_DERIVED" ]]; then - DETECTED_BRANCH="${UPSTREAM_REF_DERIVED#origin/}" - fi -fi -if [[ -z "$DETECTED_BRANCH" ]]; then - DETECTED_BRANCH="main" -fi - -# Optional signals: BRANCH env and .last-branch (informational only) -ENV_BRANCH="${BRANCH:-}" -LAST_BRANCH_FILE_PATH="$(pwd)/$LAST_BRANCH_FILE" -LAST_BRANCH_VALUE="" -if [[ -z "$ENV_BRANCH" && -f "$LAST_BRANCH_FILE_PATH" ]]; then - LAST_BRANCH_VALUE="$(tr -d ' \t\n\r' < "$LAST_BRANCH_FILE_PATH")" -fi - -UPSTREAM_REF="$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2>/dev/null || echo "origin/$DETECTED_BRANCH")" -HEAD_SHA="$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")" - -echo "[INFO] Repo: $(pwd)" -echo "[INFO] Current branch: $DETECTED_BRANCH" -echo "[INFO] Upstream: $UPSTREAM_REF" -echo "[INFO] HEAD (sha): $HEAD_SHA" - -if [[ -n "$ENV_BRANCH" && "$ENV_BRANCH" != "$DETECTED_BRANCH" ]]; then - echo "[WARNING] BRANCH='$ENV_BRANCH' differs from checked out branch '$DETECTED_BRANCH'." - echo "[WARNING] This script does not switch branches; continuing on '$DETECTED_BRANCH'." -fi - -if [[ -n "$LAST_BRANCH_VALUE" && "$LAST_BRANCH_VALUE" != "$DETECTED_BRANCH" && -z "$ENV_BRANCH" ]]; then - echo "[INFO] .last-branch suggests '$LAST_BRANCH_VALUE', but current checkout is '$DETECTED_BRANCH'." - echo "[INFO] If you intended to build '$LAST_BRANCH_VALUE', switch branches first (use update-and-build.sh)." -fi - -# --- Versioning -------------------------------------------------------------- -CURRENT_VERSION="$(read_version)" -NEW_VERSION="$CURRENT_VERSION" -DO_TAG_AND_BUMP=true - -if [[ "$BUMP" == "t" ]]; then - echo "[INFO] Test build: keeping version $CURRENT_VERSION; will only update :dev." - DO_TAG_AND_BUMP=false -else - NEW_VERSION="$(bump_version "$CURRENT_VERSION" "$BUMP")" - echo "[INFO] New version: $NEW_VERSION" -fi - -if $DO_TAG_AND_BUMP; then - validate_tag "$NEW_VERSION" -fi -validate_tag "latest" - -# --- Version update + VCS ops (release builds only) -------------------------- -if $DO_TAG_AND_BUMP; then - echo "[INFO] Writing $NEW_VERSION to $VERSION_FILE" - write_version "$NEW_VERSION" - - echo "[INFO] Git add + commit (branch: $DETECTED_BRANCH)" - git add "$VERSION_FILE" - git commit -m "Release $NEW_VERSION on branch $DETECTED_BRANCH (bump type $BUMP)" - - echo "[INFO] Git tag $NEW_VERSION" - git tag -a "$NEW_VERSION" -m "Release $NEW_VERSION" - - echo "[INFO] Git push + tags" - git push origin "$DETECTED_BRANCH" - git push --tags -else - echo "[INFO] Skipping commit/tagging (test build)." -fi - -# --- Build & push per service ------------------------------------------------ -shopt -s nullglob -services=( "$COMPOSE_DIR"/* ) -if [[ ${#services[@]} -eq 0 ]]; then - echo "[ERROR] No services found under $COMPOSE_DIR" - exit 1 -fi - -BUILT_IMAGES=() - -for svc_path in "${services[@]}"; do - [[ -d "$svc_path" ]] || continue - svc="$(basename "$svc_path")" - dockerfile="$svc_path/Dockerfile" - - validate_repo_component "$svc" - - if [[ ! -f "$dockerfile" ]]; then - echo "[WARNING] Skipping '${svc}': Dockerfile not found in ${svc_path}" - continue - fi - - IMAGE_BASE="${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/${svc}" - - if $DO_TAG_AND_BUMP; then - echo "============================================================" - echo "[INFO] Building ${svc} -> tags: ${NEW_VERSION}, latest" - echo "============================================================" - docker build -t "${IMAGE_BASE}:${NEW_VERSION}" -t "${IMAGE_BASE}:dev" "$svc_path" - docker push "${IMAGE_BASE}:${NEW_VERSION}" - docker push "${IMAGE_BASE}:dev" - BUILT_IMAGES+=("${IMAGE_BASE}:${NEW_VERSION}" "${IMAGE_BASE}:dev") - else - echo "============================================================" - echo "[INFO] Test build ${svc} -> tag: latest" - echo "============================================================" - docker build -t "${IMAGE_BASE}:dev" "$svc_path" - docker push "${IMAGE_BASE}:dev" - BUILT_IMAGES+=("${IMAGE_BASE}:dev") - fi -done - -# --- Persist current branch to .last-branch ---------------------------------- -# (This helps script 1 to preselect next time, and is informative if you run script 2 standalone) -echo "$DETECTED_BRANCH" > "$LAST_BRANCH_FILE_PATH" - -# --- Summary ----------------------------------------------------------------- -echo "" -echo "============================================================" -echo "[SUMMARY] Build & push complete (branch: $DETECTED_BRANCH)" -if $DO_TAG_AND_BUMP; then - echo "[INFO] Release version: $NEW_VERSION" -else - echo "[INFO] Test build (no version bump)" -fi -echo "[INFO] Images pushed:" -for img in "${BUILT_IMAGES[@]}"; do - echo " - $img" -done -echo "============================================================" diff --git a/automation/docker/update-and-build.sh b/automation/docker/update-and-build.sh deleted file mode 100644 index 43d06c5..0000000 --- a/automation/docker/update-and-build.sh +++ /dev/null @@ -1,232 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -# ============================================================================ -# update-and-build.sh -# Location: /docker/develop -# -# Purpose: -# - Laat je een branch kiezen (of via BRANCH= meegeven) -# - Onthoudt de laatst gebruikte branch in .last-branch in de repo root -# - Commit en pusht altijd lokale wijzigingen (AUTO_COMMIT standaard aan) -# - Haalt daarna updates op uit origin/ (git pull --rebase) -# - Roept daarna het build-script in de repo aan (build-and-push.sh) met de -# gekozen bump type (1/2/3/t) -# -# Gebruik: -# BRANCH= sudo bash /docker/develop/update-and-build.sh [bump] -# (BRANCH optioneel; zonder krijg je een menu met keuze, of kun je -# handmatig een nieuwe branchnaam intypen) -# ============================================================================ - -REPO_DIR="/docker/develop/backup-v9" # pas aan indien nodig -BUILD_SCRIPT_NAME="build-and-push.sh" -LAST_BRANCH_FILE=".last-branch" # wordt in REPO_DIR opgeslagen - -# Omgeving: optionele flags voor non-interactief gebruik -AUTO_COMMIT="${AUTO_COMMIT:-1}" # 1 = altijd committen als er wijzigingen zijn -AUTO_PUSH="${AUTO_PUSH:-1}" # 1 = push naar origin/ na commit -AUTO_CREATE_BRANCH="${AUTO_CREATE_BRANCH:-1}" # 1 = nieuwe branch lokaal maken als hij nog niet bestaat - -# Commit message (optioneel prefix via COMMIT_MSG_PREFIX) -DEFAULT_COMMIT_MSG_PREFIX="${COMMIT_MSG_PREFIX:-Auto-commit local changes before build}" - -# --- Input: bump type -------------------------------------------------------- -BUMP_TYPE="${1:-}" -if [[ -z "${BUMP_TYPE}" ]]; then - echo "Select bump type: [1] patch, [2] minor, [3] major, [t] test (default: t)" - read -r BUMP_TYPE - BUMP_TYPE="${BUMP_TYPE:-t}" -fi - -if [[ "$BUMP_TYPE" != "1" && "$BUMP_TYPE" != "2" && "$BUMP_TYPE" != "3" && "$BUMP_TYPE" != "t" ]]; then - echo "[ERROR] Invalid bump type '$BUMP_TYPE' (use 1, 2, 3, or t)." - exit 1 -fi - -# --- Naar repo gaan ---------------------------------------------------------- -echo "[INFO] Switching to repo: $REPO_DIR" -cd "$REPO_DIR" - -# --- Fetch alle branches ----------------------------------------------------- -echo "[INFO] Fetching all branches (origin)..." -git fetch --all --prune - -# Lijst met origin branches (zonder origin/ prefix, HEAD gefilterd) -mapfile -t BRANCHES < <( - git for-each-ref \ - --format='%(refname:short)' refs/remotes/origin \ - | sed 's|^origin/||' \ - | grep -vE '^HEAD$' \ - | sort -u -) - -# Fallback: als er geen origin branches zijn, kijk dan naar lokale branches -if [[ "${#BRANCHES[@]}" -eq 0 ]]; then - mapfile -t BRANCHES < <( - git for-each-ref \ - --format='%(refname:short)' refs/heads \ - | sort -u - ) -fi - -if [[ "${#BRANCHES[@]}" -eq 0 ]]; then - echo "[ERROR] Geen branches gevonden (noch lokaal, noch bij origin)." - exit 1 -fi - -# --- Bepaal default branch/last branch --------------------------------------- -ENV_BRANCH="${BRANCH:-}" - -LAST_BRANCH="" -if [[ -f "$LAST_BRANCH_FILE" ]]; then - LAST_BRANCH="$(<"$LAST_BRANCH_FILE")" -fi - -CURRENT_BRANCH="$(git branch --show-current 2>/dev/null || true)" - -DEFAULT_BRANCH="$CURRENT_BRANCH" -if [[ -z "$DEFAULT_BRANCH" ]]; then - if [[ -n "$LAST_BRANCH" ]]; then - DEFAULT_BRANCH="$LAST_BRANCH" - else - DEFAULT_BRANCH="${BRANCHES[0]}" - fi -fi - -CHOSEN_BRANCH="" - -# 1) Als BRANCH= in de omgeving is gezet, gebruik die direct -if [[ -n "$ENV_BRANCH" ]]; then - CHOSEN_BRANCH="$ENV_BRANCH" - echo "[INFO] Branch via BRANCH env: $CHOSEN_BRANCH" -else - echo "[INFO] Beschikbare bestaande branches:" - i=1 - DEFAULT_INDEX=1 - for b in "${BRANCHES[@]}"; do - if [[ "$b" == "$DEFAULT_BRANCH" ]]; then - echo " [$i] $b (default)" - DEFAULT_INDEX="$i" - else - echo " [$i] $b" - fi - ((i++)) - done - - echo - echo "Je kunt een nummer kiezen voor een bestaande branch," - echo "of een nieuwe branch-naam intypen." - read -r -p "Selecteer nummer of typ nieuwe branch-naam (leeg = default): " choice - - # Leeg = default branch - if [[ -z "$choice" ]]; then - CHOSEN_BRANCH="$DEFAULT_BRANCH" - echo "[INFO] Default branch geselecteerd: $CHOSEN_BRANCH" - # Als gebruiker een nummer kiest - elif [[ "$choice" =~ ^[0-9]+$ ]] && (( choice >= 1 && choice <= ${#BRANCHES[@]} )); then - CHOSEN_BRANCH="${BRANCHES[choice-1]}" - echo "[INFO] Bestaande branch gekozen: $CHOSEN_BRANCH" - else - # Anders: dit is een nieuwe branch - CHOSEN_BRANCH="$choice" - echo "[INFO] Nieuwe branch gekozen: $CHOSEN_BRANCH (wordt aangemaakt als hij nog niet bestaat)" - fi -fi - -# Schrijf gekozen branch weg als last-branch -echo "$CHOSEN_BRANCH" > "$LAST_BRANCH_FILE" - -# --- Branch checkout / aanmaken ---------------------------------------------- -REMOTE_REF_EXISTS=0 -if git show-ref --verify --quiet "refs/remotes/origin/$CHOSEN_BRANCH"; then - REMOTE_REF_EXISTS=1 -fi - -LOCAL_REF_EXISTS=0 -if git show-ref --verify --quiet "refs/heads/$CHOSEN_BRANCH"; then - LOCAL_REF_EXISTS=1 -fi - -if [[ "$LOCAL_REF_EXISTS" -eq 1 ]]; then - echo "[INFO] Checkout bestaande lokale branch: $CHOSEN_BRANCH" - git switch "$CHOSEN_BRANCH" -else - if [[ "$REMOTE_REF_EXISTS" -eq 1 ]]; then - echo "[INFO] Nieuwe lokale branch maken vanaf origin/$CHOSEN_BRANCH" - git switch -c "$CHOSEN_BRANCH" --track "origin/$CHOSEN_BRANCH" - else - if [[ "$AUTO_CREATE_BRANCH" -eq 1 ]]; then - echo "[INFO] Nieuwe lokale branch maken (zonder bestaande remote): $CHOSEN_BRANCH" - git switch -c "$CHOSEN_BRANCH" - else - echo "[ERROR] Branch '$CHOSEN_BRANCH' bestaat niet lokaal en niet bij origin." - exit 1 - fi - fi -fi - -# --- Functie: auto-commit lokale wijzigingen -------------------------------- -auto_commit_if_needed() { - if [[ "$AUTO_COMMIT" -ne 1 ]]; then - return 0 - fi - - # Check of er iets te committen is - if [[ -z "$(git status --porcelain)" ]]; then - echo "[INFO] Geen lokale wijzigingen om te committen." - return 0 - fi - - echo "[INFO] Lokale wijzigingen gedetecteerd in branch '$CHOSEN_BRANCH':" - git status --short - - echo "[INFO] Auto-commit lokale wijzigingen..." - git add -A - - # Commit message opbouwen (met timestamp) - local ts - ts="$(date +'%Y-%m-%d %H:%M:%S')" - local msg="${DEFAULT_COMMIT_MSG_PREFIX} (${ts})" - - git commit -m "$msg" - - if [[ "$AUTO_PUSH" -eq 1 ]]; then - echo "[INFO] Pushing naar origin/$CHOSEN_BRANCH..." - git push -u origin "$CHOSEN_BRANCH" - else - echo "[INFO] AUTO_PUSH=0, push wordt overgeslagen." - fi -} - -# --- Eerst altijd lokale wijzigingen committen (voor git pull) -------------- -auto_commit_if_needed - -# --- Daarna updaten vanaf origin (als remote bestaat) ------------------------ -if git rev-parse --verify --quiet "origin/$CHOSEN_BRANCH" >/dev/null; then - echo "[INFO] Pulling latest changes from origin/$CHOSEN_BRANCH (rebase)..." - if ! git pull --rebase origin "$CHOSEN_BRANCH"; then - echo "[ERROR] git pull --rebase gaf een fout. Los eerst conflicten op en probeer opnieuw." - exit 1 - fi -else - echo "[INFO] Geen origin/$CHOSEN_BRANCH gevonden; sla git pull over." -fi - -# --- Na pull nogmaals checken of er iets open staat -------------------------- -if [[ -n "$(git status --porcelain)" ]]; then - echo "[INFO] Er zijn na git pull nog niet-gecommit-te wijzigingen." - auto_commit_if_needed -fi - -# --- Build script uitvoeren -------------------------------------------------- -if [[ ! -f "./$BUILD_SCRIPT_NAME" ]]; then - echo "[ERROR] Build script ./$BUILD_SCRIPT_NAME niet gevonden in $REPO_DIR" - exit 1 -fi - -chmod +x "./$BUILD_SCRIPT_NAME" -echo "[INFO] Running build: ./$BUILD_SCRIPT_NAME $BUMP_TYPE" -"./$BUILD_SCRIPT_NAME" "$BUMP_TYPE" - -echo "[INFO] Done." diff --git a/docs/conventions.md b/docs/conventions.md deleted file mode 100644 index 046e1ef..0000000 --- a/docs/conventions.md +++ /dev/null @@ -1,40 +0,0 @@ -# Repository Conventions - -## 1. Naming - -- Existing script filenames stay unchanged for documentation compatibility. -- New scripts should use clear, stable names and should not be renamed later without a migration plan. - -## 2. Placement - -- Place scripts by platform + domain: - - `scripts/m365/...` - - `scripts/windows/...` - - `scripts/macos/...` - - `scripts/content/...` -- Place build/deploy automation in `automation/...`. - -## 3. Script Header (minimum) - -Use these fields at the top of each script where possible: - -- Purpose/Synopsis -- Requirements (modules, permissions, OS) -- Input parameters -- Output (file/console) -- Example usage - -## 4. Secrets and Variables - -- Do not hardcode secrets. -- Use variables, prompts, or a secure vault mechanism. - -## 5. Output and Logging - -- Default output goes to `output/` unless operational requirements dictate otherwise. -- Use predictable filenames for exports. - -## 6. Change Policy - -- For file moves: update README and relevant guides. -- For any future filename changes: add a temporary compatibility layer (wrapper/alias) and define a deprecation window. diff --git a/docs/runbooks/index.md b/docs/runbooks/index.md deleted file mode 100644 index 81eec06..0000000 --- a/docs/runbooks/index.md +++ /dev/null @@ -1,45 +0,0 @@ -# Runbooks Index - -This overview helps update guides after the repository restructuring. - -## Important - -- Legacy script filenames were not changed. -- Only folder locations were updated. - -## Script Locations - -- `Get-WindowsAutoPilotInfo.ps1` - - New path: `scripts/windows/autopilot/Get-WindowsAutoPilotInfo.ps1` -- `Test-SMTPRelay-M365.ps1` - - New path: `scripts/m365/smtp/Test-SMTPRelay-M365.ps1` -- `Test-SMTPSend-Auth-M365.ps1` - - New path: `scripts/m365/smtp/Test-SMTPSend-Auth-M365.ps1` -- `Update rmm.ps1` - - New path: `scripts/windows/endpoint/Update rmm.ps1` -- `get-mailbox-with-permissions.ps1` - - New path: `scripts/m365/exchange/get-mailbox-with-permissions.ps1` -- `m365-users-mfa.ps1` - - New path: `scripts/m365/identity/m365-users-mfa.ps1` -- `Get-SharePointPermissions.ps1` - - New path: `scripts/m365/sharepoint/get-sharepoint-permissions.ps1` -- `schedule_reboot.ps1` - - New path: `scripts/windows/endpoint/schedule_reboot.ps1` -- `uninstall-MS-Visual-C++-2010.ps1` - - New path: `scripts/windows/endpoint/uninstall-MS-Visual-C++-2010.ps1` -- `uuid.ps1` - - New path: `scripts/windows/endpoint/uuid.ps1` -- `serialToAdminPassword.sh` - - New path: `scripts/macos/serialToAdminPassword.sh` -- `convert-md.bat` - - New path: `scripts/content/convert-md.bat` -- `build-and-push.sh` - - New path: `automation/docker/build-and-push.sh` -- `update-and-build.sh` - - New path: `automation/docker/update-and-build.sh` - -## Guide Update Checklist - -- Replace references to root-level paths with the new paths above. -- Check automated jobs/tasks for hardcoded script locations. -- Test at least one script per domain after path updates. diff --git a/output/.gitkeep b/output/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/scripts/content/convert-md.bat b/scripts/content/convert-md.bat deleted file mode 100644 index a90227d..0000000 --- a/scripts/content/convert-md.bat +++ /dev/null @@ -1,65 +0,0 @@ -@echo off -setlocal enabledelayedexpansion - -:: ============================================================ -:: EPUB Conversiescript - .md naar .epub met Pandoc -:: Laatste update: 2025-06-20 -:: -:: Changelog: -:: - Vervanging van verouderde 'wmic' door PowerShell Get-Date -:: - Fallback op datum van vandaag bij lege invoer -:: - Overstap van --epub-metadata XML naar --metadata vlaggen -:: - Vermeden gebruik van systeemvariabele %DATE% -:: - Invoervelden ontdaan van spaties en line breaks via PowerShell .Trim() -:: - Gebruik van !VAR! (delayed expansion) om correcte waardes te behouden -:: - PowerShell Console.Write gebruikt om onzichtbare tekens zoals '=' of '+' te voorkomen -:: - Alle metadata wordt correct opgenomen in EPUB (title, author, publisher, date) -:: - Pauzes verwijderd voor geautomatiseerd gebruik, metadata wordt alleen getoond -:: - Eindpauze toegevoegd zodat resultaat zichtbaar blijft -:: - Spaties toegevoegd bij invoer voor leesbaarheid -:: ============================================================ - -cd /d "C:\Users\ms-iv\Downloads" - -:: Datum ophalen in jjjj-mm-dd formaat -powershell -NoProfile -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = 'en-US'; Get-Date -Format 'yyyy-MM-dd'" > temp_datum.txt -set /p TODAY=$null - -# Container for MFA information -$MFAInfo = @() - -# Loop through all users to check their MFA status -foreach ($user in $AllUsers) { - # Check if MFA is enabled by looking at StrongAuthenticationRequirements - if ($user.StrongAuthenticationRequirements.State -ne $null) { - $MFAInfo += [pscustomobject]@{ - UserPrincipalName = $user.UserPrincipalName # User's UPN (email address) - DisplayName = $user.DisplayName # User's display name - MFAEnabled = $true # MFA is enabled - Method = "Conditional Access/MFA Policy" # MFA policy used - } - } else { - # If no MFA policy is found, mark MFA as disabled - $MFAInfo += [pscustomobject]@{ - UserPrincipalName = $user.UserPrincipalName - DisplayName = $user.DisplayName - MFAEnabled = $false # MFA is not enabled - Method = "None" # No MFA method applied - } - } -} - -# Define the path for the CSV file -$CsvFilePath = "C:\MFA_Overview.csv" - -# Export the MFA data to a CSV file without including type information -$MFAInfo | Export-Csv -Path $CsvFilePath -NoTypeInformation - -# Display a message indicating that the export was successful -Write-Host "MFA overview successfully exported to $CsvFilePath" diff --git a/scripts/m365/sharepoint/get-sharepoint-permissions.ps1 b/scripts/m365/sharepoint/get-sharepoint-permissions.ps1 deleted file mode 100644 index f18a1f5..0000000 --- a/scripts/m365/sharepoint/get-sharepoint-permissions.ps1 +++ /dev/null @@ -1,549 +0,0 @@ -#Requires -Modules Microsoft.Graph.Authentication, ImportExcel - -<# -.SYNOPSIS - Audits unique SharePoint permissions at library, folder, and file level across all sites. - -.DESCRIPTION - Reads site URLs from a CSV export (SharePoint Admin Center) and retrieves all - permissions via the Microsoft Graph API. Results are streamed directly to CSV - after each site to minimize memory usage. An Excel report is generated at the end. - - ========================================================================= - REQUIRED AZURE APP REGISTRATION PERMISSIONS - ========================================================================= - Create an App Registration in Azure Portal (Entra ID) with the following - APPLICATION permissions (not Delegated) and grant Admin Consent for all: - - Microsoft Graph: - - Sites.Read.All : Read all SharePoint site collections and their contents - - Files.Read.All : Read all files in all site document libraries - - User.Read.All : Resolve user IDs to display names / UPNs - - Group.Read.All : Resolve group IDs to display names - - How to set up: - 1. Go to Azure Portal > Microsoft Entra ID > App Registrations > New Registration - 2. Give it a name (e.g. "SharePoint Permissions Audit") - 3. Go to API Permissions > Add a Permission > Microsoft Graph > Application Permissions - 4. Add: Sites.Read.All, Files.Read.All, User.Read.All, Group.Read.All - 5. Click "Grant Admin Consent for " - 6. Go to Certificates & Secrets > New Client Secret - 7. Copy the SECRET VALUE (not the Secret ID) immediately after creation - 8. Note down the Application (client) ID and the Directory (tenant) ID - ========================================================================= - -.PARAMETER TenantId - Azure AD / Entra ID Tenant ID (found on the App Registration overview page). - -.PARAMETER ClientId - Application (Client) ID of the App Registration. - -.PARAMETER ClientSecret - Client Secret VALUE (not the Secret ID) of the App Registration. - -.PARAMETER SitesCsvPath - Path to the CSV file exported from the SharePoint Admin Center. - The CSV must contain a column named 'URL' with the site URLs. - Export: SharePoint Admin Center > Active Sites > Export to CSV - -.PARAMETER OutputPath - Folder where output files (CSV, Excel, log) will be saved. Defaults to current directory. - -.PARAMETER ExcludedSites - Optional array of site URLs to skip during the scan. - -.PARAMETER IncludeFileLevel - Switch to enable file-level permission scanning in addition to libraries and folders. - Warning: this significantly increases scan time on large tenants. - -.EXAMPLE - # Scan all sites using the exported CSV - .\Get-SharePointPermissions.ps1 ` - -TenantId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ` - -ClientId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ` - -ClientSecret "your-secret-value-here" ` - -SitesCsvPath "C:\scripts\Sites.csv" ` - -OutputPath "C:\scripts\Reports" - -.EXAMPLE - # Include file-level permissions and skip a specific site - .\Get-SharePointPermissions.ps1 ` - -TenantId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ` - -ClientId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ` - -ClientSecret "your-secret-value-here" ` - -SitesCsvPath "C:\scripts\Sites.csv" ` - -OutputPath "C:\scripts\Reports" ` - -IncludeFileLevel ` - -ExcludedSites @("https://contoso.sharepoint.com/sites/HR") - -.NOTES - Monitor progress: Get-Content "C:\scripts\Reports\SharePoint_Permissions_*.csv" -Wait -Tail 20 - Do NOT open the CSV in Excel while the script is running - this locks the file. - Use Notepad or Notepad++ to view the CSV during the scan. -#> - -param( - [Parameter(Mandatory)] - [string]$TenantId, - - [Parameter(Mandatory)] - [string]$ClientId, - - [Parameter(Mandatory)] - [string]$ClientSecret, - - [Parameter(Mandatory)] - [string]$SitesCsvPath, - - [string]$OutputPath = (Get-Location).Path, - - [string[]]$ExcludedSites = @(), - - [switch]$IncludeFileLevel -) - -$ErrorActionPreference = 'Stop' - -#region ── Module check ────────────────────────────────────────────────────── -# Ensure required PowerShell modules are installed and loaded. -# Microsoft.Graph.Authentication : Connect to Graph API and make REST calls -# ImportExcel : Generate formatted Excel reports without Excel installed - -foreach ($mod in @('Microsoft.Graph.Authentication', 'ImportExcel')) { - if (-not (Get-Module -ListAvailable -Name $mod)) { - Write-Host "[SETUP] Installing module '$mod'..." -ForegroundColor Yellow - Install-Module $mod -Scope CurrentUser -Force -AllowClobber - } - Import-Module $mod -ErrorAction Stop -} - -#endregion - -#region ── Output file paths ───────────────────────────────────────────────── - -$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss' -$csvOut = Join-Path $OutputPath "SharePoint_Permissions_$timestamp.csv" -$xlsxOut = Join-Path $OutputPath "SharePoint_Permissions_$timestamp.xlsx" -$logOut = Join-Path $OutputPath "SharePoint_Permissions_$timestamp.log" - -if (-not (Test-Path $OutputPath)) { - New-Item -ItemType Directory -Path $OutputPath | Out-Null -} - -#endregion - -#region ── Logging ─────────────────────────────────────────────────────────── - -function Write-Log { - param([string]$Message, [string]$Level = 'INFO') - $entry = "[{0}] [{1}] {2}" -f (Get-Date -Format 'yyyy-MM-dd HH:mm:ss'), $Level, $Message - Add-Content -Path $logOut -Value $entry - switch ($Level) { - 'ERROR' { Write-Host $entry -ForegroundColor Red } - 'WARNING' { Write-Host $entry -ForegroundColor Yellow } - 'SUCCESS' { Write-Host $entry -ForegroundColor Green } - default { Write-Host $entry } - } -} - -#endregion - -#region ── Authentication ──────────────────────────────────────────────────── -# Uses the OAuth2 client credentials flow directly via Invoke-RestMethod. -# This avoids MSAL.PS compatibility issues with PowerShell strict mode. - -Write-Log "Script started." - -try { - $tokenBody = @{ - grant_type = 'client_credentials' - client_id = $ClientId - client_secret = $ClientSecret - scope = 'https://graph.microsoft.com/.default' - } - $tokenResponse = Invoke-RestMethod ` - -Uri "https://login.microsoftonline.com/$TenantId/oauth2/v2.0/token" ` - -Method POST -Body $tokenBody -ContentType 'application/x-www-form-urlencoded' - - Connect-MgGraph ` - -AccessToken ($tokenResponse.access_token | ConvertTo-SecureString -AsPlainText -Force) ` - -NoWelcome - - Write-Log "Authentication successful." -Level 'SUCCESS' -} catch { - Write-Log "Authentication failed: $_" -Level 'ERROR' - exit 1 -} - -#endregion - -#region ── Graph REST helper with automatic pagination ─────────────────────── -# Graph API returns results in pages (default 200 items per page). -# This function follows @odata.nextLink until all results are retrieved. - -function Invoke-GraphGet { - param([string]$Uri) - $results = [System.Collections.Generic.List[object]]::new() - $nextUri = $Uri - while ($nextUri) { - $resp = Invoke-MgGraphRequest -Uri $nextUri -Method GET -OutputType PSObject - $nextUri = $null - if ($resp.PSObject.Properties.Name -contains '@odata.nextLink') { - $nextUri = $resp.'@odata.nextLink' - } - if ($resp.PSObject.Properties.Name -contains 'value') { - foreach ($item in $resp.value) { $results.Add($item) } - } elseif ($null -ne $resp) { - $results.Add($resp) - } - } - return $results -} - -#endregion - -#region ── Recursive drive item retrieval ──────────────────────────────────── -# Recursively fetches all folders (and optionally files) within a drive. -# Recurses into subfolders to build a complete tree. - -function Get-AllDriveItems { - param([string]$DriveId, [string]$ItemId = 'root') - $all = [System.Collections.Generic.List[object]]::new() - try { - $children = Invoke-GraphGet -Uri "https://graph.microsoft.com/v1.0/drives/$DriveId/items/$ItemId/children" - foreach ($child in $children) { - $all.Add($child) - if ($child.PSObject.Properties.Name -contains 'folder') { - $sub = Get-AllDriveItems -DriveId $DriveId -ItemId $child.id - foreach ($s in $sub) { $all.Add($s) } - } - } - } catch { - Write-Log " Error retrieving children of '$ItemId': $_" -Level 'WARNING' - } - return $all -} - -#endregion - -#region ── Principal name lookup with caching ──────────────────────────────── -# Resolves Azure AD user/group IDs to readable display names. -# Results are cached to avoid duplicate API calls for the same principal. - -$principalCache = @{} - -function Get-PrincipalName { - param([string]$Id, [string]$Type) - if (-not $Id) { return 'Unknown' } - $key = "$Type|$Id" - if ($principalCache.ContainsKey($key)) { return $principalCache[$key] } - $name = "$Type`: $Id" - try { - if ($Type -eq 'user') { - $u = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/users/${Id}?`$select=displayName,userPrincipalName" -Method GET -OutputType PSObject - $name = "$($u.displayName) ($($u.userPrincipalName))" - } elseif ($Type -eq 'group') { - $g = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/groups/${Id}?`$select=displayName" -Method GET -OutputType PSObject - $name = $g.displayName - } - } catch { } - $principalCache[$key] = $name - return $name -} - -#endregion - -#region ── Permission record builder ───────────────────────────────────────── -# Retrieves permissions for a single drive item and returns structured records. -# ForceInclude: always return records even if only inherited permissions exist (used for library root). -# Without ForceInclude: only returns records if explicit (non-inherited) grants are found. - -function Get-ItemPermissionRecords { - param( - [string]$DriveId, - [string]$ItemId, - [string]$ItemName, - [string]$ItemPath, - [string]$ItemType, # 'Library', 'Folder', or 'File' - [string]$SiteName, - [string]$SiteUrl, - [string]$LibraryName, - [switch]$ForceInclude - ) - - $records = [System.Collections.Generic.List[object]]::new() - - try { - $perms = Invoke-GraphGet -Uri "https://graph.microsoft.com/v1.0/drives/$DriveId/items/$ItemId/permissions" - } catch { - Write-Log " Error retrieving permissions for '$ItemPath': $_" -Level 'WARNING' - return $records - } - - if (-not $perms -or $perms.Count -eq 0) { return $records } - - # Only include items with explicit direct grants (not just sharing links), unless ForceInclude - $hasExplicitGrants = $perms | Where-Object { - ($_.PSObject.Properties.Name -contains 'grantedToV2' -and $_.grantedToV2) -or - ($_.PSObject.Properties.Name -contains 'grantedTo' -and $_.grantedTo) - } - if (-not $ForceInclude -and -not $hasExplicitGrants) { return $records } - - foreach ($perm in $perms) { - $roles = if ($perm.PSObject.Properties.Name -contains 'roles' -and $perm.roles) { $perm.roles -join ', ' } else { 'read' } - $grantedTo = 'Unknown' - $linkType = '' - - # Resolve who the permission is granted to (Graph API v2 properties take priority) - if ($perm.PSObject.Properties.Name -contains 'grantedToV2' -and $perm.grantedToV2) { - $g = $perm.grantedToV2 - if ($g.PSObject.Properties.Name -contains 'user' -and $g.user) { $grantedTo = Get-PrincipalName -Id $g.user.id -Type 'user' } - elseif ($g.PSObject.Properties.Name -contains 'group' -and $g.group) { $grantedTo = Get-PrincipalName -Id $g.group.id -Type 'group' } - elseif ($g.PSObject.Properties.Name -contains 'siteUser' -and $g.siteUser) { $grantedTo = $g.siteUser.displayName } - elseif ($g.PSObject.Properties.Name -contains 'siteGroup' -and $g.siteGroup) { $grantedTo = $g.siteGroup.displayName } - } elseif ($perm.PSObject.Properties.Name -contains 'grantedToIdentitiesV2' -and $perm.grantedToIdentitiesV2) { - # Multiple recipients (e.g. sharing link used by multiple people) - $names = foreach ($identity in $perm.grantedToIdentitiesV2) { - if ($identity.PSObject.Properties.Name -contains 'user' -and $identity.user) { Get-PrincipalName -Id $identity.user.id -Type 'user' } - elseif ($identity.PSObject.Properties.Name -contains 'group' -and $identity.group) { Get-PrincipalName -Id $identity.group.id -Type 'group' } - elseif ($identity.PSObject.Properties.Name -contains 'siteUser' -and $identity.siteUser) { $identity.siteUser.displayName } - else { 'Unknown' } - } - $grantedTo = ($names | Where-Object { $_ }) -join '; ' - } elseif ($perm.PSObject.Properties.Name -contains 'grantedTo' -and $perm.grantedTo) { - # Fallback: older grantedTo property - $g = $perm.grantedTo - if ($g.PSObject.Properties.Name -contains 'user' -and $g.user) { $grantedTo = "$($g.user.displayName) ($($g.user.email))" } - elseif ($g.PSObject.Properties.Name -contains 'group' -and $g.group) { $grantedTo = $g.group.displayName } - } - - # Detect sharing links (anonymous, organization-wide, or specific people links) - if ($perm.PSObject.Properties.Name -contains 'link' -and $perm.link) { - $linkType = "SharingLink ($($perm.link.type), $($perm.link.scope))" - if ($grantedTo -eq 'Unknown') { $grantedTo = $linkType } - } - - $records.Add([PSCustomObject]@{ - SiteName = $SiteName - SiteUrl = $SiteUrl - Library = $LibraryName - ItemType = $ItemType - ItemName = $ItemName - ItemPath = $ItemPath - GrantedTo = $grantedTo - Permissions = $roles - LinkType = $linkType - ScannedAt = (Get-Date -Format 'yyyy-MM-dd HH:mm') - }) - } - - return $records -} - -#endregion - -#region ── Load site list from CSV ─────────────────────────────────────────── -# Reads site URLs from the SharePoint Admin Center export CSV. -# Export location: SharePoint Admin Center > Active Sites > Export - -Write-Log "Loading sites from CSV: $SitesCsvPath" - -if (-not (Test-Path $SitesCsvPath)) { - Write-Log "CSV file not found: $SitesCsvPath" -Level 'ERROR' - exit 1 -} - -$csvSites = Import-Csv -Path $SitesCsvPath -$urlColumn = $csvSites[0].PSObject.Properties.Name | Where-Object { $_ -match '^url$' } | Select-Object -First 1 - -if (-not $urlColumn) { - Write-Log "No 'URL' column found in CSV. Available columns: $($csvSites[0].PSObject.Properties.Name -join ', ')" -Level 'ERROR' - exit 1 -} - -$siteUrls = $csvSites.$urlColumn | Where-Object { - $_ -and $_ -notmatch '/personal/' -and $_ -notin $ExcludedSites -} - -Write-Log "Sites loaded from CSV: $($siteUrls.Count)" -Level 'SUCCESS' - -#endregion - -#region ── Main scan loop ──────────────────────────────────────────────────── -# Streams permission records directly to CSV after each site to keep memory usage low. -# The CSV file is written incrementally - do NOT open it in Excel during the scan. - -$csvHeaders = 'SiteName;SiteUrl;Library;ItemType;ItemName;ItemPath;GrantedTo;Permissions;LinkType;ScannedAt' -Set-Content -Path $csvOut -Value $csvHeaders -Encoding UTF8 -$recordCount = 0 - -function Write-RecordToCsv { - # Appends one or more permission records to the CSV file immediately. - param([object[]]$Records) - foreach ($rec in $Records) { - $line = '"{0}";"{1}";"{2}";"{3}";"{4}";"{5}";"{6}";"{7}";"{8}";"{9}"' -f ` - $rec.SiteName, $rec.SiteUrl, $rec.Library, $rec.ItemType, ` - $rec.ItemName, $rec.ItemPath, $rec.GrantedTo, $rec.Permissions, ` - $rec.LinkType, $rec.ScannedAt - Add-Content -Path $csvOut -Value $line -Encoding UTF8 - $script:recordCount++ - } -} - -$siteIndex = 0 -$total = $siteUrls.Count - -foreach ($siteUrl in $siteUrls) { - $siteIndex++ - $pct = [math]::Round(($siteIndex / $total) * 100) - Write-Progress -Activity "SharePoint Permissions Audit" ` - -Status "[$siteIndex/$total] $siteUrl | Total records: $recordCount" ` - -PercentComplete $pct - - # Resolve site URL to Graph site ID using the hostname:/path format - try { - $uri = [Uri]$siteUrl - $hostPart = $uri.Host - $pathPart = $uri.AbsolutePath.TrimStart('/') - if ($pathPart) { - $siteObj = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/sites/${hostPart}:/${pathPart}" -Method GET -OutputType PSObject - } else { - $siteObj = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/sites/${hostPart}" -Method GET -OutputType PSObject - } - $siteId = $siteObj.id - $siteName = if ($siteObj.PSObject.Properties.Name -contains 'displayName' -and $siteObj.displayName) { $siteObj.displayName } else { $siteObj.name } - } catch { - Write-Log "[$siteIndex/$total] Failed to resolve site ID for '$siteUrl': $_" -Level 'WARNING' - continue - } - - Write-Log "[$siteIndex/$total] $siteName" - - # Get all document libraries for this site (excludes system lists) - try { - $drives = Invoke-GraphGet -Uri "https://graph.microsoft.com/v1.0/sites/$siteId/drives" - $drives = $drives | Where-Object { $_.driveType -eq 'documentLibrary' } - } catch { - Write-Log " Error retrieving document libraries: $_" -Level 'WARNING' - continue - } - - $recordsBefore = $recordCount - - foreach ($drive in $drives) { - $libName = $drive.name - $driveId = $drive.id - Write-Log " Library: $libName" - - # Scan library root permissions (always included) - try { - $root = Invoke-MgGraphRequest -Uri "https://graph.microsoft.com/v1.0/drives/$driveId/root" -Method GET -OutputType PSObject - $records = Get-ItemPermissionRecords -DriveId $driveId -ItemId $root.id ` - -ItemName $libName -ItemPath "/" -ItemType 'Library' ` - -SiteName $siteName -SiteUrl $siteUrl -LibraryName $libName -ForceInclude - Write-RecordToCsv -Records $records - } catch { - Write-Log " Error scanning library root '$libName': $_" -Level 'WARNING' - } - - # Scan all folders (and files if -IncludeFileLevel is set) - # Only items with unique (broken) permissions are included - try { - $items = Get-AllDriveItems -DriveId $driveId - foreach ($item in $items) { - $isFolder = $item.PSObject.Properties.Name -contains 'folder' - $isFile = $item.PSObject.Properties.Name -contains 'file' - if (-not $isFolder -and -not ($IncludeFileLevel -and $isFile)) { continue } - - $itemType = if ($isFolder) { 'Folder' } else { 'File' } - $parentPath = '' - if ($item.PSObject.Properties.Name -contains 'parentReference' -and - $item.parentReference.PSObject.Properties.Name -contains 'path') { - $parentPath = $item.parentReference.path -replace '.*?/root:', '' - } - $cleanPath = "$parentPath/$($item.name)" - - $records = Get-ItemPermissionRecords -DriveId $driveId -ItemId $item.id ` - -ItemName $item.name -ItemPath $cleanPath -ItemType $itemType ` - -SiteName $siteName -SiteUrl $siteUrl -LibraryName $libName - Write-RecordToCsv -Records $records - } - } catch { - Write-Log " Error scanning items in '$libName': $_" -Level 'WARNING' - } - } - - $added = $recordCount - $recordsBefore - if ($added -gt 0) { - Write-Log " -> $added new records written (total: $recordCount)" -Level 'SUCCESS' - } -} - -Write-Progress -Activity "SharePoint Permissions Audit" -Completed -Write-Log "Scan complete. $recordCount permission records found." -Level 'SUCCESS' - -#endregion - -#region ── Build Excel report from CSV ─────────────────────────────────────── -# Reads the completed CSV and generates a multi-sheet Excel report. -# Sheet 1 - All Permissions : full dataset -# Sheet 2 - Summary by Site : record counts grouped per site -# Sheet 3 - Top 20 Risk Items: items with the most unique permission grants -# Sheet 4 - Sharing Links : all items shared via external/anonymous links - -Write-Log "Building Excel report from CSV..." - -if ($recordCount -gt 0) { - $allRecords = Import-Csv -Path $csvOut -Delimiter ';' -Encoding UTF8 - - # Sheet 1: Full permission list - $allRecords | Export-Excel -Path $xlsxOut -WorksheetName 'All Permissions' ` - -TableName 'TblAllPermissions' -TableStyle Medium9 -AutoSize -FreezeTopRow -BoldTopRow - - # Sheet 2: Summary grouped by site - $allRecords | Group-Object SiteName | Select-Object ` - @{N='Site'; E={$_.Name}}, - @{N='Total'; E={$_.Count}}, - @{N='Libraries'; E={($_.Group.Library | Sort-Object -Unique).Count}}, - @{N='Folders'; E={($_.Group | Where-Object ItemType -eq 'Folder').Count}}, - @{N='Files'; E={($_.Group | Where-Object ItemType -eq 'File').Count}} | - Sort-Object Total -Descending | - Export-Excel -Path $xlsxOut -WorksheetName 'Summary by Site' ` - -TableName 'TblSummary' -TableStyle Medium2 -AutoSize -FreezeTopRow -BoldTopRow -Append - - # Sheet 3: Top 20 items with most unique grants (highest permission fragmentation risk) - $allRecords | Group-Object ItemPath | Select-Object ` - @{N='Site'; E={($_.Group | Select-Object -First 1).SiteName}}, - @{N='Library'; E={($_.Group | Select-Object -First 1).Library}}, - @{N='Type'; E={($_.Group | Select-Object -First 1).ItemType}}, - @{N='Item'; E={($_.Group | Select-Object -First 1).ItemName}}, - @{N='Path'; E={($_.Group | Select-Object -First 1).ItemPath}}, - @{N='Unique Grants'; E={$_.Count}} | - Sort-Object 'Unique Grants' -Descending | Select-Object -First 20 | - Export-Excel -Path $xlsxOut -WorksheetName 'Top 20 Risk Items' ` - -TableName 'TblTopRisk' -TableStyle Medium6 -AutoSize -FreezeTopRow -BoldTopRow -Append - - # Sheet 4: Sharing links (potential external exposure) - $links = @($allRecords | Where-Object { $_.LinkType -ne '' }) - if ($links.Count -gt 0) { - $links | Export-Excel -Path $xlsxOut -WorksheetName 'Sharing Links' ` - -TableName 'TblSharingLinks' -TableStyle Medium3 -AutoSize -FreezeTopRow -BoldTopRow -Append - } - - Write-Log "Excel report saved: $xlsxOut" -Level 'SUCCESS' -} else { - Write-Log "No records found - Excel report not created." -Level 'WARNING' -} - -#endregion - -Disconnect-MgGraph | Out-Null - -Write-Host "`n========================================" -ForegroundColor Cyan -Write-Host " Scan complete!" -ForegroundColor Cyan -Write-Host "========================================" -ForegroundColor Cyan -Write-Host " Records : $recordCount" -Write-Host " CSV : $csvOut" -Write-Host " Excel : $xlsxOut" -Write-Host " Log : $logOut" -Write-Host "========================================`n" -ForegroundColor Cyan diff --git a/scripts/m365/smtp/Test-SMTPRelay-M365.ps1 b/scripts/m365/smtp/Test-SMTPRelay-M365.ps1 deleted file mode 100644 index 1e078c1..0000000 --- a/scripts/m365/smtp/Test-SMTPRelay-M365.ps1 +++ /dev/null @@ -1,60 +0,0 @@ -<# -.SYNOPSIS -Test unauthenticated SMTP relay via Microsoft 365 using MX record and IP-based connector. - -.DESCRIPTION -This script sends a test email through Microsoft 365 (Exchange Online) using a connector -that allows mail relay based on the sender's IP address. It does not require authentication -and is useful for testing printers, scanners, or any other device that cannot use modern SMTP auth. - -.AUTHOR -Generic / Public version (sanitized for open-source use) - -.NOTES -Requires that your public IP is allowed in an Exchange Online connector. -Use the MX hostname (e.g. yourdomain-nl.mail.protection.outlook.com) as the SMTP endpoint. - -# CHANGELOG --------------------------------------------------------------------------------- -Date | Version | Author | Description -------------|---------|----------------|------------------------------------------ -2025-07-18 | 1.0.0 | Public / GPT | Initial release for generic SMTP relay test --------------------------------------------------------------------------------- -#> - -# Variables (edit to match your domain and connector setup) -$from = "relay@yourdomain.com" -$to = "relay@yourdomain.com" -$smtpServer = "yourdomain-nl.mail.protection.outlook.com" # Replace with your actual MX record -$smtpPort = 25 -$subject = "SMTP Relay Test via Microsoft 365 Connector" -$body = "This message was sent without authentication using an IP-allowed connector." - -# Create email message -$message = New-Object system.net.mail.mailmessage -$message.From = $from -$message.To.Add($to) -$message.Subject = $subject -$message.Body = $body -$message.IsBodyHtml = $false - -# Configure SMTP client (unauthenticated, no TLS) -$smtp = New-Object Net.Mail.SmtpClient($smtpServer, $smtpPort) -$smtp.EnableSsl = $false -$smtp.DeliveryMethod = "Network" -$smtp.UseDefaultCredentials = $true - -Write-Host "⏳ Attempting SMTP relay via $smtpServer..." - -try { - $smtp.Send($message) - Write-Host "`n✅ Message successfully sent via connector." -} -catch { - Write-Host "`n❌ Failed to send mail:" - Write-Host $_.Exception.Message - if ($_.Exception.InnerException) { - Write-Host "`nDetails:" - Write-Host $_.Exception.InnerException.Message - } -} diff --git a/scripts/m365/smtp/Test-SMTPSend-Auth-M365.ps1 b/scripts/m365/smtp/Test-SMTPSend-Auth-M365.ps1 deleted file mode 100644 index bf3259e..0000000 --- a/scripts/m365/smtp/Test-SMTPSend-Auth-M365.ps1 +++ /dev/null @@ -1,64 +0,0 @@ -<# -.SYNOPSIS -Test script to verify SMTP (Microsoft 365) connectivity using an app password. - -.DESCRIPTION -This script attempts to send a test message using Authenticated SMTP with Microsoft 365. -Useful for diagnosing SMTP authentication issues with accounts that have MFA enabled. - -.AUTHOR -Generic / Public version (original author removed for portability) - -.NOTES -This script is intended for test purposes and should not be used in production automation. - -# CHANGELOG --------------------------------------------------------------------------------- -Date | Version | Author | Description -------------|---------|---------------|------------------------------------------ -2025-07-18 | 1.0.0 | Public/GPT | Initial version - generic SMTP test script --------------------------------------------------------------------------------- -#> - -Clear-Host - -# SMTP settings -$smtpServer = "smtp.office365.com" -$smtpPort = 587 -$from = "youruser@yourdomain.com" -$to = "youruser@yourdomain.com" -$subject = "SMTP Test" -$body = "This is a test message sent via smtp.office365.com with an app password." - -# Secure password prompt -$securePassword = Read-Host "Enter your app password" -AsSecureString -$credential = New-Object System.Management.Automation.PSCredential($from, $securePassword) - -# Create email -$mail = New-Object system.net.mail.mailmessage -$mail.From = $from -$mail.To.Add($to) -$mail.Subject = $subject -$mail.Body = $body -$mail.IsBodyHtml = $false - -# Create SMTP client -$smtp = New-Object Net.Mail.SmtpClient($smtpServer, $smtpPort) -$smtp.EnableSsl = $true -$smtp.Credentials = $credential -$smtp.Timeout = 10000 - -Write-Host "⏳ Sending mail via ${smtpServer}:${smtpPort} as ${from}..." - -try { - $smtp.Send($mail) - Write-Host "`n✅ Message sent successfully to $to" -} -catch { - Write-Host "`n❌ Failed to send message:" - Write-Host $_.Exception.Message - if ($_.Exception.InnerException) { - Write-Host "`nDetails:" - Write-Host $_.Exception.InnerException.Message - } -} diff --git a/scripts/macos/serialToAdminPassword.sh b/scripts/macos/serialToAdminPassword.sh deleted file mode 100644 index 8d2d090..0000000 --- a/scripts/macos/serialToAdminPassword.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash -#set -x - -############################################################################################ -## -## Script to generate Admin Password from Serial Number -## -########################################### - -## Copyright (c) 2020 Microsoft Corp. All rights reserved. -## Scripts are not supported under any Microsoft standard support program or service. The scripts are provided AS IS without warranty of any kind. -## Microsoft disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a -## particular purpose. The entire risk arising out of the use or performance of the scripts and documentation remains with you. In no event shall -## Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever -## (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary -## loss) arising out of the use of or inability to use the sample scripts or documentation, even if Microsoft has been advised of the possibility -## of such damages. -## Feedback: neiljohn@microsoft.com - -## -## Notes -## -## This script is to support createAdminAccount.sh the cipher used in that script must match the cipher used here to generate the correct password -## i.e. ABCDEF000009 becomes S0xNTk9QNDQ0NDQzCg== -## -## WARNING: It is strongly recommended to change the cipher on line 45 before deploying into production - -echo -ne "Enter device serial number :" -read serial -password=`echo $serial | tr '[A-Z]' '[K-ZA-J]' | tr 0-9 4-90-3 | base64` -echo "Password: $password" diff --git a/scripts/windows/autopilot/Get-WindowsAutoPilotInfo.ps1 b/scripts/windows/autopilot/Get-WindowsAutoPilotInfo.ps1 deleted file mode 100644 index 133a057..0000000 --- a/scripts/windows/autopilot/Get-WindowsAutoPilotInfo.ps1 +++ /dev/null @@ -1,190 +0,0 @@ -<#PSScriptInfo - -.VERSION 1.3 - -.GUID ebf446a3-3362-4774-83c0-b7299410b63f - -.AUTHOR Michael Niehaus - -.COMPANYNAME Microsoft - -.COPYRIGHT - -.TAGS Windows AutoPilot - -.LICENSEURI - -.PROJECTURI - -.ICONURI - -.EXTERNALMODULEDEPENDENCIES - -.REQUIREDSCRIPTS - -.EXTERNALSCRIPTDEPENDENCIES - -.RELEASENOTES -Version 1.0: Original published version. -Version 1.1: Added -Append switch. -Version 1.2: Added -Credential switch. -Version 1.3: Added -Partner switch. - -#> - -<# -.SYNOPSIS -Retrieves the Windows AutoPilot deployment details from one or more computers -.DESCRIPTION -This script uses WMI to retrieve properties needed by the Microsoft Store for Business to support Windows AutoPilot deployment. -.PARAMETER Name -The names of the computers. These can be provided via the pipeline (property name Name or one of the available aliases, DNSHostName, ComputerName, and Computer). -.PARAMETER OutputFile -The name of the CSV file to be created with the details for the computers. If not specified, the details will be returned to the PowerShell -pipeline. -.PARAMETER Append -Switch to specify that new computer details should be appended to the specified output file, instead of overwriting the existing file. -.PARAMETER Credential -Credentials that should be used when connecting to a remote computer (not supported when gathering details from the local computer). -.PARAMETER Partner -Switch to specify that the created CSV file should use the schema for Partner Center (using serial number, make, and model). -.EXAMPLE -.\Get-WindowsAutoPilotInfo.ps1 -ComputerName MYCOMPUTER -OutputFile .\MyComputer.csv -.EXAMPLE -.\Get-WindowsAutoPilotInfo.ps1 -ComputerName MYCOMPUTER -OutputFile .\MyComputer.csv -Append -.EXAMPLE -.\Get-WindowsAutoPilotInfo.ps1 -ComputerName MYCOMPUTER1,MYCOMPUTER2 -OutputFile .\MyComputers.csv -.EXAMPLE -Get-ADComputer -Filter * | .\GetWindowsAutoPilotInfo.ps1 -OutputFile .\MyComputers.csv -.EXAMPLE -Get-CMCollectionMember -CollectionName "All Systems" | .\GetWindowsAutoPilotInfo.ps1 -OutputFile .\MyComputers.csv -.EXAMPLE -.\Get-WindowsAutoPilotInfo.ps1 -ComputerName MYCOMPUTER1,MYCOMPUTER2 -OutputFile .\MyComputers.csv -Partner - -#> - -[CmdletBinding()] -param( - [Parameter(Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True,Position=0)][alias("DNSHostName","ComputerName","Computer")] [String[]] $Name = @($env:ComputerName), - [Parameter(Mandatory=$False)] [String] $OutputFile = "", - [Parameter(Mandatory=$False)] [Switch] $Append = $false, - [Parameter(Mandatory=$False)] [System.Management.Automation.PSCredential] $Credential = $null, - [Parameter(Mandatory=$False)] [Switch] $Partner = $false, - [Parameter(Mandatory=$False)] [Switch] $Force = $false -) - -Begin -{ - # Initialize empty list - $computers = @() -} - -Process -{ - foreach ($comp in $Name) - { - $bad = $false - - # Get the common properties. - Write-Verbose "Checking $comp" - $serial = (Get-WmiObject -ComputerName $comp -Credential $Credential -Class Win32_BIOS).SerialNumber - - # Get the hash (if available) - $devDetail = (Get-WMIObject -ComputerName $comp -Credential $Credential -Namespace root/cimv2/mdm/dmmap -Class MDM_DevDetail_Ext01 -Filter "InstanceID='Ext' AND ParentID='./DevDetail'") - if ($devDetail -and (-not $Force)) - { - $hash = $devDetail.DeviceHardwareData - } - else - { - $bad = $true - $hash = "" - } - - # If the hash isn't available, get the make and model - if ($bad -or $Force) - { - $cs = Get-WmiObject -ComputerName $comp -Credential $Credential -Class Win32_ComputerSystem - $make = $cs.Manufacturer.Trim() - $model = $cs.Model.Trim() - if ($Partner) - { - $bad = $false - } - } - else - { - $make = "" - $model = "" - } - - # Getting the PKID is generally problematic for anyone other than OEMs, so let's skip it here - $product = "" - - # Depending on the format requested, create the necessary object - if ($Partner) - { - # Create a pipeline object - $c = New-Object psobject -Property @{ - "Device Serial Number" = $serial - "Windows Product ID" = $product - "Hardware Hash" = $hash - "Manufacturer name" = $make - "Device model" = $model - } - # From spec: - # "Manufacturer Name" = $make - # "Device Name" = $model - - } - else - { - # Create a pipeline object - $c = New-Object psobject -Property @{ - "Device Serial Number" = $serial - "Windows Product ID" = $product - "Hardware Hash" = $hash - } - } - - # Write the object to the pipeline or array - if ($bad) - { - # Report an error when the hash isn't available - Write-Error -Message "Unable to retrieve device hardware data (hash) from computer $comp" -Category DeviceError - } - elseif ($OutputFile -eq "") - { - $c - } - else - { - $computers += $c - } - - } -} - -End -{ - if ($OutputFile -ne "") - { - if ($Append) - { - if (Test-Path $OutputFile) - { - $computers += Import-CSV -Path $OutputFile - } - } - if ($Partner) - { - $computers | Select "Device Serial Number", "Windows Product ID", "Hardware Hash", "Manufacturer name", "Device model" | ConvertTo-CSV -NoTypeInformation | % {$_ -replace '"',''} | Out-File $OutputFile - # From spec: - # $computers | Select "Device Serial Number", "Windows Product ID", "Hardware Hash", "Manufacturer Name", "Device Name" | ConvertTo-CSV -NoTypeInformation | % {$_ -replace '"',''} | Out-File $OutputFile - } - else - { - $computers | Select "Device Serial Number", "Windows Product ID", "Hardware Hash" | ConvertTo-CSV -NoTypeInformation | % {$_ -replace '"',''} | Out-File $OutputFile - } - } -} \ No newline at end of file diff --git a/scripts/windows/endpoint/Update rmm.ps1 b/scripts/windows/endpoint/Update rmm.ps1 deleted file mode 100644 index 6ebfe6f..0000000 --- a/scripts/windows/endpoint/Update rmm.ps1 +++ /dev/null @@ -1,45 +0,0 @@ -# URL to the application's installer -$downloadUrl = "https://pinotage.centrastage.net/csm/profile/downloadAgent/key-site" -# Path where the installer will be saved -$downloadPath = "C:\Windows\Temp\agentsetup.exe" - -# Path to the uninstaller of the current application -$uninstallPath = "C:\Program Files (x86)\CentraStage\uninst.exe" -# Optional arguments for the uninstallation -$uninstallArgs = "/uninstall /quiet" - -# Optional arguments for the installation -$installArgs = "/silent" - -# Download the installer -Write-Output "Downloading the installer..." -Invoke-WebRequest -Uri $downloadUrl -OutFile $downloadPath - -Write-Output "The installer has been downloaded to $downloadPath." - -# Start the uninstallation -$uninstallProcess = Start-Process -FilePath $uninstallPath -ArgumentList $uninstallArgs -PassThru - -# Wait until the uninstallation is completed -Write-Output "Waiting for the uninstallation to complete..." -Wait-Process -Id $uninstallProcess.Id - -Write-Output "The uninstallation is complete." - -# Start the installation -$installProcess = Start-Process -FilePath $downloadPath -ArgumentList $installArgs -PassThru - -# Wait until the installation is completed -Write-Output "Waiting for the installation to complete..." -Wait-Process -Id $installProcess.Id - -Write-Output "The installation is complete." - -# Remove the downloaded installer -Write-Output "Removing the downloaded installer..." -Remove-Item -Path $downloadPath -Force - -Write-Output "The downloaded installer has been removed." - - -Write-Output "De installatie is voltooid." diff --git a/scripts/windows/endpoint/schedule_reboot.ps1 b/scripts/windows/endpoint/schedule_reboot.ps1 deleted file mode 100644 index 2dfb645..0000000 --- a/scripts/windows/endpoint/schedule_reboot.ps1 +++ /dev/null @@ -1,35 +0,0 @@ -# Define the reboot time -$rebootTime = "21:00" - -# Get the current date and time -$currentDate = Get-Date - -# Parse the reboot time into a DateTime object for today -$rebootDateTime = [datetime]::ParseExact("$($currentDate.ToString('yyyy-MM-dd')) $rebootTime", "yyyy-MM-dd HH:mm", $null) - -# Check if the reboot time is in the past for today; if so, schedule it for tomorrow -if ($rebootDateTime -lt $currentDate) { - $rebootDateTime = $rebootDateTime.AddDays(1) -} - -# Format the DateTime object for the task scheduler -$taskTime = $rebootDateTime.ToString("HH:mm") - -# Create a scheduled task action for a forced reboot -$taskName = "ForcedReboot" -$action = New-ScheduledTaskAction -Execute "shutdown.exe" -Argument "/r /f /t 0" - -# Create a trigger for the specified time -$trigger = New-ScheduledTaskTrigger -Once -At $rebootDateTime - -# Configure settings to allow the task to run when no user is logged in -$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable ` - -RunOnlyIfIdle:$false -WakeToRun - -# Set the task to run with the highest privileges and even when no user is logged in -$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest - -# Register the task -Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Settings $settings -Principal $principal -Force - -Write-Host "Scheduled reboot at $rebootDateTime, even if no user is logged in." diff --git a/scripts/windows/endpoint/uninstall-MS-Visual-C++-2010.ps1 b/scripts/windows/endpoint/uninstall-MS-Visual-C++-2010.ps1 deleted file mode 100644 index 9aeff20..0000000 --- a/scripts/windows/endpoint/uninstall-MS-Visual-C++-2010.ps1 +++ /dev/null @@ -1,123 +0,0 @@ -# This script is designed for use in Datto RMM (CentraStage). -# Purpose: Uninstall all "Microsoft Visual C++ 2010 ... Redistributable" (x86/x64), version independent. -# -# Example run / expected output: -# Uninstall Microsoft Visual C++ 2010 -# Found: Microsoft Visual C++ 2010 x64 Redistributable - 10.0.40219, Microsoft Visual C++ 2010 x86 Redistributable - 10.0.40219 -# Uninstall (MSI) Microsoft Visual C++ 2010 x64 Redistributable - 10.0.40219 -> msiexec /X{1D8E6291-B0D5-35EC-8441-6616F567A0F7} /quiet /norestart -# Uninstall (MSI) Microsoft Visual C++ 2010 x86 Redistributable - 10.0.40219 -> msiexec /X{F0C3E5D1-1ADE-321E-8167-68EF0DE699A5} /quiet /norestart -# Completed: all target items successfully removed (or not present). -# -# Usage in Datto RMM: -# - Create a new Component → Script (PowerShell). -# - Paste this script in the code editor. -# - Run As: System -# - Architecture: 64-bit -# - Test on one device before wide rollout. -# - Deploy via Job or Policy at the Site level to target all devices. -# -# Exit codes: -# 0 = OK (nothing found or everything removed successfully) -# 1 = Failures occurred - -$ErrorActionPreference = 'Stop' -$fails = 0 - -function Invoke-Uninstall { - param( - [string]$CmdLine, - [string]$AppName - ) - - if (-not $CmdLine) { - Write-Warning ("No UninstallString found for " + $AppName) - $script:fails++ - return - } - - # Normalize to exe + args - $exe = $null - $args = "" - if ($CmdLine -match '^\s*"(.+?)"\s*(.*)$') { - $exe = $Matches[1] - $args = $Matches[2] - } elseif ($CmdLine -match '^\s*(\S+)\s*(.*)$') { - $exe = $Matches[1] - $args = $Matches[2] - } - - # MSI: force silent uninstall - if ($exe -match '(?i)msiexec\.exe') { - if ($args -match '(?i)/I\s*{([0-9A-F\-]+)}') { - $guid = $Matches[1] - $args = "/x {" + $guid + "} /quiet /norestart" - } else { - if ($args -notmatch '(?i)/x') { $args = "/x " + $args } - if ($args -notmatch '(?i)/quiet|/qn') { $args = $args + " /quiet" } - if ($args -notmatch '(?i)/norestart') { $args = $args + " /norestart" } - } - Write-Output ("Uninstall (MSI) " + $AppName + " -> msiexec " + $args) - } else { - # Non-MSI: try to enforce silent switches - if ($args -notmatch '(?i)/quiet|/qn|/silent|/s') { $args = $args + " /quiet" } - if ($args -notmatch '(?i)/norestart') { $args = $args + " /norestart" } - Write-Output ("Uninstall (EXE) " + $AppName + " -> " + $exe + " " + $args) - } - - try { - $p = Start-Process -FilePath $exe -ArgumentList $args -Wait -PassThru -WindowStyle Hidden - $code = $p.ExitCode - # Common msiexec codes: 0=success, 3010=reboot required, 1605=not installed - if ($exe -match '(?i)msiexec\.exe') { - if ($code -in 0,3010,1605) { - if ($code -eq 3010) { Write-Output ("Note: reboot required (3010) for " + $AppName) } - elseif ($code -eq 1605) { Write-Output ("Not installed (1605) for " + $AppName + ", skipping.") } - return - } - } else { - if ($code -eq 0) { return } - } - Write-Warning ("Uninstall failed for " + $AppName + " with exit code " + $code) - $script:fails++ - } catch { - $msg = $_.Exception.Message - Write-Warning ("Error while uninstalling " + $AppName + ": " + $msg) - $script:fails++ - } -} - -# Pre-check: look for target apps -$uninstallKeys = @( - "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*", - "HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" -) - -$targets = foreach ($key in $uninstallKeys) { - Get-ItemProperty -Path $key -ErrorAction SilentlyContinue | - Where-Object { - $_.DisplayName -and ($_.DisplayName -like "Microsoft Visual C++ 2010*Redistributable*") - } -} - -if (-not $targets) { - Write-Output "No Microsoft Visual C++ 2010 Redistributables found. Nothing to do." - exit 0 -} - -$foundList = ($targets.DisplayName | Sort-Object -Unique) -join ", " -Write-Output ("Found: " + $foundList) - -# Uninstall each item -foreach ($app in $targets) { - $name = $app.DisplayName - $cmd = if ($app.QuietUninstallString) { $app.QuietUninstallString } else { $app.UninstallString } - Invoke-Uninstall -CmdLine $cmd -AppName $name -} - -if ($fails -gt 0) { - Write-Output ("Completed with errors: " + $fails + " item(s) failed to uninstall.") - exit 1 -} else { - Write-Output "Completed: all target items successfully removed (or not present)." - exit 0 -} diff --git a/scripts/windows/endpoint/uuid.ps1 b/scripts/windows/endpoint/uuid.ps1 deleted file mode 100644 index 28fafbc..0000000 --- a/scripts/windows/endpoint/uuid.ps1 +++ /dev/null @@ -1,10 +0,0 @@ -#UUID list -#Maakt een Excel lijst met UUID en serienummer -cls -$UUID = (Get-WmiObject Win32_ComputerSystemProduct).UUID -$Serial = (Get-WmiObject Win32_BIOS).SerialNumber -$a = New-object PSobject -$a | Add-Member NoteProperty "UUID" $UUID -$a | Add-Member NoteProperty "Serial" $Serial -$a | Format-Table -$a | Export-Csv -Path list.csv -Append \ No newline at end of file