add reload button
This commit is contained in:
1 parent
910555f363
commit
b642f2af7d
4 files changed
+217
-9
No files matched your search
@@ -0,0 +1,137 @@
|
|||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 1. Set up environment
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
$ComputerName = "FRRF1LLC1103011"
|
||||||
|
$printDebug = $false
|
||||||
|
|
||||||
|
Import-Module GroupPolicy -ErrorAction Stop
|
||||||
|
Import-Module ActiveDirectory -ErrorAction Stop
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 2. Resolve the computer’s DN and build the OU chain (leaf → root)
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
$comp = Get-ADComputer -Identity $ComputerName -Properties DistinguishedName
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "Cannot find computer '$ComputerName' in AD: $_"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$ouDN = $comp.DistinguishedName -replace '^CN=.*?,', ''
|
||||||
|
$ouChain = @()
|
||||||
|
$currentDN = $ouDN
|
||||||
|
|
||||||
|
while ($currentDN -match '^OU=') {
|
||||||
|
$ouChain += $currentDN
|
||||||
|
$currentDN = $currentDN.Substring($currentDN.IndexOf(',') + 1)
|
||||||
|
}
|
||||||
|
$ouChain += $currentDN # root
|
||||||
|
|
||||||
|
if ($printDebug) {
|
||||||
|
Write-Host "OU chain (leaf → root):" -ForegroundColor Cyan
|
||||||
|
$ouChain | ForEach-Object { Write-Host $_ }
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 3. Gather GPO links for each OU in the chain
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
$gpoLinks = @{} # Hashtable: GPO_GUID => array of OU DNs
|
||||||
|
|
||||||
|
foreach ($ouDN in $ouChain) {
|
||||||
|
try {
|
||||||
|
$ouObj = Get-ADObject -Identity $ouDN -Properties gPLink -ErrorAction Stop
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Warning "Could not read OU '$ouDN': $_"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($ouObj.gPLink) {
|
||||||
|
foreach ($link in ($ouObj.gPLink -split ';')) {
|
||||||
|
$clean = $link.Trim(' ', '[', ']')
|
||||||
|
if ($clean -match '\{(?<guid>[0-9a-fA-F-]+)\}') {
|
||||||
|
$guid = $Matches.guid
|
||||||
|
if (-not $gpoLinks.ContainsKey($guid)) { $gpoLinks[$guid] = @() }
|
||||||
|
$gpoLinks[$guid] += $ouDN
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $gpoLinks.Count) {
|
||||||
|
Write-Host "No GPOs linked to any OU in the hierarchy." -ForegroundColor Yellow
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Found $($gpoLinks.Count) distinct GPO(s) linked in this OU tree." -ForegroundColor Green
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 4. Collect all restricted‑group entries into an array
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
$results = @() # Will hold objects: @{ADGroup=; LocalGroup=; GPO=}
|
||||||
|
|
||||||
|
$uniqueGpoGuids = $gpoLinks.Keys | Sort-Object -Unique
|
||||||
|
$ou_counter = 0
|
||||||
|
$totalItems = $uniqueGpoGuids.Count
|
||||||
|
$startTime = Get-Date
|
||||||
|
|
||||||
|
foreach ($gpoGuid in $uniqueGpoGuids) {
|
||||||
|
$ou_counter++
|
||||||
|
|
||||||
|
try { $gpo = Get-GPO -Guid $gpoGuid -ErrorAction Stop }
|
||||||
|
catch { Write-Warning "Unable to retrieve GPO $gpoGuid – skipping."; continue }
|
||||||
|
|
||||||
|
$percentComplete = [int]( ($ou_counter / $gpoLinks.Count) * 100 )
|
||||||
|
if ($ou_counter -gt 0) {
|
||||||
|
$elapsed = (Get-Date) - $startTime
|
||||||
|
$averagePerItem = $elapsed.TotalSeconds / $ou_counter
|
||||||
|
$remainingItems = $totalItems - $ou_counter
|
||||||
|
$secondsRemaining = [int]($averagePerItem * $remainingItems)
|
||||||
|
} else { $secondsRemaining = -1 }
|
||||||
|
|
||||||
|
Write-Progress -Activity "Processing GPOs" `
|
||||||
|
-Status $gpo.DisplayName `
|
||||||
|
-PercentComplete $percentComplete `
|
||||||
|
-SecondsRemaining $secondsRemaining `
|
||||||
|
-CurrentOperation "GPO $ou_counter of $totalItems"
|
||||||
|
|
||||||
|
# Grab XML report
|
||||||
|
try { $xmlString = Get-GPOReport -Guid $gpoGuid -ReportType Xml -ErrorAction Stop }
|
||||||
|
catch { Write-Warning "Unable to generate XML report for $($gpo.DisplayName)."; continue }
|
||||||
|
|
||||||
|
$xml = [xml]$xmlString
|
||||||
|
$ns = New-Object System.Xml.XmlNamespaceManager ($xml.NameTable)
|
||||||
|
$ns.AddNamespace('m', 'http://www.microsoft.com/GroupPolicy/Settings')
|
||||||
|
$ns.AddNamespace('q1', 'http://www.microsoft.com/GroupPolicy/Settings/Security')
|
||||||
|
$ns.AddNamespace('t', 'http://www.microsoft.com/GroupPolicy/Types')
|
||||||
|
$ns.AddNamespace('xsi','http://www.w3.org/2001/XMLSchema-instance')
|
||||||
|
|
||||||
|
$restrictedGroups = $xml.SelectNodes('//q1:RestrictedGroups', $ns)
|
||||||
|
|
||||||
|
foreach ($rg in $restrictedGroups) {
|
||||||
|
$adGroupName = ($rg.SelectSingleNode('q1:GroupName/t:Name', $ns)).InnerText
|
||||||
|
$localGroupName = ($rg.SelectSingleNode('q1:Memberof/t:Name', $ns)).InnerText
|
||||||
|
|
||||||
|
$results += [pscustomobject]@{
|
||||||
|
ADGroup = $adGroupName
|
||||||
|
LocalGroup = $localGroupName
|
||||||
|
GPO = $gpo.DisplayName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$elapsed = (Get-Date) - $startTime
|
||||||
|
$elapsedSeconds = [math]::Round($elapsed.TotalSeconds, 2)
|
||||||
|
Write-Host "`nFinished processing $totalItems GPO(s) after $elapsedSeconds seconds." -ForegroundColor Green
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 5. Print two separate lists
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
Write-Host "`n=== AD groups that are members of BUILTIN\Administrators ===" -ForegroundColor Yellow
|
||||||
|
$results | Where-Object {$_.LocalGroup -eq 'BUILTIN\Administrators'} |
|
||||||
|
Select-Object -ExpandProperty ADGroup | Sort-Object | Get-Unique
|
||||||
|
|
||||||
|
Write-Host "`n=== AD groups that are members of vnc_admin ===" -ForegroundColor Yellow
|
||||||
|
$results | Where-Object {$_.LocalGroup -eq 'vnc_admin'} |
|
||||||
|
Select-Object -ExpandProperty ADGroup | Sort-Object | Get-Unique
|
||||||
@@ -29,6 +29,28 @@ function getLinkedOUs(string $gpo): array|false
|
|||||||
//return implode("\n", $decoded);
|
//return implode("\n", $decoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# helper to reload OUs
|
||||||
|
if (isset($_GET['reload'])) {
|
||||||
|
// Call the Flask endpoint that reloads the YAML index
|
||||||
|
$reloadUrl = "http://172.17.0.1:5000/reload_index";
|
||||||
|
|
||||||
|
$ch = curl_init($reloadUrl);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_TIMEOUT, 10); // 10‑second timeout
|
||||||
|
$rawResponse = curl_exec($ch);
|
||||||
|
|
||||||
|
if ($rawResponse === false) {
|
||||||
|
error_log('cURL error [' . curl_errno($ch) . ']: ' . curl_error($ch));
|
||||||
|
} else {
|
||||||
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
if ($httpCode !== 200) {
|
||||||
|
error_log("API returned HTTP {$httpCode} for reload_index");
|
||||||
|
}
|
||||||
|
// Optionally inspect $rawResponse if you want to show a message
|
||||||
|
}
|
||||||
|
curl_close($ch);
|
||||||
|
}
|
||||||
|
|
||||||
$yaml_timestamp = json_decode(file_get_contents('http://172.17.0.1:5000/timestamp'), true);
|
$yaml_timestamp = json_decode(file_get_contents('http://172.17.0.1:5000/timestamp'), true);
|
||||||
$gpo_links = json_decode(file_get_contents('http://172.17.0.1:5000/gpo_links'), true);
|
$gpo_links = json_decode(file_get_contents('http://172.17.0.1:5000/gpo_links'), true);
|
||||||
$index_duration = json_decode(file_get_contents('http://172.17.0.1:5000/index_duration'), true);
|
$index_duration = json_decode(file_get_contents('http://172.17.0.1:5000/index_duration'), true);
|
||||||
@@ -180,6 +202,10 @@ else {
|
|||||||
<label for="group">Group Name:</label>
|
<label for="group">Group Name:</label>
|
||||||
<input type="text" id="group" name="group" required>
|
<input type="text" id="group" name="group" required>
|
||||||
<button type="submit">Query</button>
|
<button type="submit">Query</button>
|
||||||
|
</form>
|
||||||
|
<form action="index.php" method="GET" style="display:inline;">
|
||||||
|
<input type="hidden" name="reload" value="1">
|
||||||
|
<button type="submit">Reload Index</button>
|
||||||
</form><p>
|
</form><p>
|
||||||
Data indexed at '.$yaml_timestamp['timestamp'].' after '.$index_duration['duration'].' minutes<br>
|
Data indexed at '.$yaml_timestamp['timestamp'].' after '.$index_duration['duration'].' minutes<br>
|
||||||
Total Linked GPOs: '.$gpo_links['gpo_links'].'<p>
|
Total Linked GPOs: '.$gpo_links['gpo_links'].'<p>
|
||||||
|
|||||||
+12
-5
@@ -11,6 +11,13 @@
|
|||||||
group: "{{ service_user }}"
|
group: "{{ service_user }}"
|
||||||
mode: '0755'
|
mode: '0755'
|
||||||
|
|
||||||
|
|
||||||
|
- name: "API Service - {{ api_service_name }} - stop service"
|
||||||
|
systemd:
|
||||||
|
name: "{{ api_service_name }}.service"
|
||||||
|
state: stopped
|
||||||
|
ignore_errors: yes
|
||||||
|
|
||||||
- name: API Service - template app.py
|
- name: API Service - template app.py
|
||||||
template:
|
template:
|
||||||
src: app.py
|
src: app.py
|
||||||
@@ -46,11 +53,11 @@
|
|||||||
- name: API Service - api service handler
|
- name: API Service - api service handler
|
||||||
block:
|
block:
|
||||||
|
|
||||||
- name: "API Service - {{ api_service_name }} - stop service if running"
|
# - name: "API Service - {{ api_service_name }} - stop service if running"
|
||||||
ignore_errors: yes
|
# ignore_errors: yes
|
||||||
systemd:
|
# systemd:
|
||||||
name: "{{ api_service_name }}.service"
|
# name: "{{ api_service_name }}.service"
|
||||||
state: stopped
|
# state: stopped
|
||||||
|
|
||||||
- name: "API Service - template {{ api_service_name }}.service"
|
- name: "API Service - template {{ api_service_name }}.service"
|
||||||
vars:
|
vars:
|
||||||
|
|||||||
+42
-4
@@ -1,13 +1,27 @@
|
|||||||
import yaml
|
import yaml
|
||||||
import re
|
import re
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from flask import Flask, request, jsonify
|
from flask import Flask, request, jsonify, abort
|
||||||
|
import threading
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
### adding reload route
|
||||||
|
# Global cache and lock
|
||||||
|
data = None
|
||||||
|
_data_lock = threading.Lock()
|
||||||
|
|
||||||
|
def load_yaml():
|
||||||
|
"""Load the YAML file and return its contents."""
|
||||||
|
with open('{{ gpo_yaml_path }}', 'r', encoding='utf-8') as f:
|
||||||
|
return yaml.safe_load(f)
|
||||||
|
|
||||||
# Load GPO data
|
# Load GPO data
|
||||||
with open('{{ gpo_yaml_path }}', 'r') as file:
|
with _data_lock:
|
||||||
data = yaml.safe_load(file)
|
data = load_yaml()
|
||||||
|
|
||||||
|
# O.G. method
|
||||||
|
#with open('{{ gpo_yaml_path }}', 'r') as file:
|
||||||
|
# data = yaml.safe_load(file)
|
||||||
|
|
||||||
def strip_dn(dn: str, base_suffix: str = ",OU=Manufacturing,OU=Tesla Systems,DC=teslamotors,DC=com") -> str:
|
def strip_dn(dn: str, base_suffix: str = ",OU=Manufacturing,OU=Tesla Systems,DC=teslamotors,DC=com") -> str:
|
||||||
suffix = base_suffix.strip()
|
suffix = base_suffix.strip()
|
||||||
@@ -126,6 +140,30 @@ def linked_ous():
|
|||||||
|
|
||||||
return jsonify(linked)
|
return jsonify(linked)
|
||||||
|
|
||||||
|
# reload index route
|
||||||
|
@app.route('/reload_index', methods=['GET'])
|
||||||
|
def reload_index():
|
||||||
|
try:
|
||||||
|
new_data = load_yaml()
|
||||||
|
except Exception as exc:
|
||||||
|
# Log the exception if you have a logger; here we just abort
|
||||||
|
abort(500, description=f'Failed to load YAML: {exc}')
|
||||||
|
|
||||||
|
# Thread‑safe write to the global cache
|
||||||
|
with _data_lock:
|
||||||
|
global data
|
||||||
|
data = new_data
|
||||||
|
|
||||||
|
# Return the re‑loaded data (or some confirmation)
|
||||||
|
return jsonify({
|
||||||
|
'status': 'reloaded',
|
||||||
|
'gpo_links': data[0]['gpo_links']
|
||||||
|
})
|
||||||
|
|
||||||
|
# with open('{{ gpo_yaml_path }}', 'r') as file:
|
||||||
|
# data = yaml.safe_load(file)
|
||||||
|
# return jsonify({'status': 'reloaded', 'data': data})
|
||||||
|
|
||||||
# test route
|
# test route
|
||||||
@app.route('/test', methods=['GET'])
|
@app.route('/test', methods=['GET'])
|
||||||
def test():
|
def test():
|
||||||
@@ -137,7 +175,7 @@ def test():
|
|||||||
except TypeError:
|
except TypeError:
|
||||||
# Handle cases where data might be of a different type than expected
|
# Handle cases where data might be of a different type than expected
|
||||||
return jsonify({"error": "TypeError: Data is not a dictionary"}), 400
|
return jsonify({"error": "TypeError: Data is not a dictionary"}), 400
|
||||||
return jsonify({"result": "test", "OU_Root": root_ou})
|
return jsonify({"result": "test", "OU_Root": root_ou, "gpo_links": data[0]['gpo_links']})
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app.run(debug=False, host='{{ api_service_bind_ip }}', port={{ api_service_port }})
|
app.run(debug=False, host='{{ api_service_bind_ip }}', port={{ api_service_port }})
|
||||||
Reference in new issue
Block a user