add gpo counter variable to powershell script for visbility

This commit is contained in:
2026-02-17 10:28:52 -08:00
parent 5327f865b9
commit 364da0159b
5 changed files with 130 additions and 47 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ project_folder: "/opt/cosmos/gpo_site"
api_service_name: "gpo_api"
api_service_folder: "{{ project_folder }}/api"
api_service_port: "5000"
api_service_bind_ip: "0.0.0.0"
api_service_bind_ip: "172.17.0.1"
dashboard_web_root: "{{ project_folder }}/dashboard"
container_name: "GPO-Dashboard"
+54 -27
View File
@@ -29,8 +29,11 @@ function getLinkedOUs(string $gpo): array|false
//return implode("\n", $decoded);
}
?>
$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);
$index_duration = json_decode(file_get_contents('http://172.17.0.1:5000/index_duration'), true);
?>
<!DOCTYPE html>
<html lang="en">
@@ -42,9 +45,13 @@ function getLinkedOUs(string $gpo): array|false
<body>
<div class="container">
<h2>Query GPOs by Group</h2>
This tool allows you to query Group Policy Objects in Active Directory by Restricted Group.<br>
This tool is intended to help determine what OU to use for a specific group.<br>
Users often request systems with a certain security group assigned.<br>
Since AD has no way to look this up, this makes it challenging to match groups to OUs.<p>
This site has all restricted groups indexed by GPO and OU.<br>
Search is case insensitive and supports partial match. <br>
Once you have the GPO name, you can locate this in the GPO management snap-in and find what OUs it links to.<br><p>
The OUs liked to the GPOs will show up as a hover tooltip on the GPO name.<br>
Clicking on the GPO name will list all linked OUs.<br><p>
</div>
<div class="container">
@@ -56,8 +63,9 @@ if (isset($_GET['group'])) {
<form action="index.php" method="GET">
<label for="group">Group Name:</label>
<input type="text" id="group" name="group" value="'.htmlspecialchars($_GET['group']).'" required>
<button type="submit">Query</button>
<button type="submit">Query</button><br>
</form><p>
Return <a href=/>Home</a><br>
';
$group = urlencode($_GET['group']);
$url = "http://172.17.0.1:5000/gpo?group=$group";
@@ -107,31 +115,43 @@ if (isset($_GET['group'])) {
}
ksort($gpoMap, SORT_STRING | SORT_FLAG_CASE);
if($numResults > 0){
echo 'Total results: '.$numResults.'<br>
echo '
Total results: '.$numResults.'<br>
Data indexed at '.$yaml_timestamp['timestamp'].' after '.$index_duration['duration'].' minutes<br>
Total Linked GPOs: '.$gpo_links['gpo_links'].'<p>
<table border="1" cellpadding="4" cellspacing="0">
<tr>
<th>
GPO
</th>
<th>
Administrators
</th>
<th>
vnc_admin
</th>
</tr>
';
echo '<table border="1" cellpadding="4" cellspacing="0">
';
echo '<tr><th>GPO</th><th>Administrators</th><th>vnc_admin</th></tr>
';
foreach ($gpoMap as $gpoName => $cols) {
// Remove duplicates and build commaseparated lists
$adminGroups = implode('<br>', array_unique($cols['administrators']));
$vncGroups = implode('<br>', array_unique($cols['vnc_admin']));
$linkedOUs = implode("\n", getLinkedOUs($gpoName));
echo ' <tr>
';
echo " <td>
echo "
<tr>
<td>
<div title='$linkedOUs'>
<a href=/?gpo='$gpoName'>{$gpoName}</a>
</div></td>
<a href=/?gpo=$gpoName>{$gpoName}</a>
</div>
</td>
<td>
{$adminGroups}
</td>
<td>
{$vncGroups}
</td>
</tr>
";
echo " <td>{$adminGroups}</td>
";
echo " <td>{$vncGroups}</td>
";
echo ' </tr>
';
}
echo '</table>
';
@@ -140,17 +160,20 @@ if (isset($_GET['group'])) {
echo 'No results found for query <b>'.$_GET['group'].'</b>.<p>';
}
}
}
// if you click on a GPO name, print all OUs
else if (isset($_GET['gpo'])) {
echo 'OUs linked to GPO <b>'.$_GET['gpo'].':</b><p>';
echo 'OUs linked to GPO <b>'.$_GET['gpo'].':</b><p><ul>';
$LinkedOUs = getLinkedOUs($_GET['gpo']);
echo implode("<br>", $LinkedOUs);
echo '<br>Return <a href=/>Home</a>';
foreach ($LinkedOUs as $OU){
echo "<li>".$OU."</li>";
}
#echo implode("<li>", $LinkedOUs);
echo '</ul><p>Return <a href=/>Home</a>';
}
else {
echo '
<form action="index.php" method="GET">
@@ -158,10 +181,14 @@ else {
<input type="text" id="group" name="group" required>
<button type="submit">Query</button>
</form><p>
Data indexed at '.$yaml_timestamp['timestamp'].' after '.$index_duration['duration'].' minutes<br>
Total Linked GPOs: '.$gpo_links['gpo_links'].'<p>
No group specified.</p>
';
echo "<p>No group specified.</p>";
}
echo "</div>";
echo "</div>
";
//echo "<p>Source code can be found <a target='_blank' rel='noopener noreferrer' href='https://gitea.matt-cloud.com/matt/gpo_lookup'>here</a>.<p>";
?>
-5
View File
@@ -67,11 +67,6 @@ h1, h2, h3, h4 {
color: #bdc3c7; /* Dimmer text color */
}
ul {
list-style-type: none;
padding: 0;
}
li {
margin-bottom: 10px;
color: #bdc3c7; /* Dimmer text color */
+16 -7
View File
@@ -18,6 +18,8 @@ if (Test-Path $OutputFile) {
Write-Output "Enumerating OUs under '$TargetOU' ..."
Add-Content -Path $OutputFile -Value "---"
Add-Content -Path $OutputFile -Value "- root_ou: $TargetOU"
$Start_Date = (Get-Date).ToString('MM/dd/yyyy hh:mm:ss tt')
Add-Content -Path $OutputFile -Value " init_timestamp: $Start_Date"
$ouObjects = Get-ADObject `
-Filter 'ObjectClass -eq "organizationalUnit"' `
-SearchBase $TargetOU `
@@ -69,7 +71,10 @@ Add-Content -Path $OutputFile -Value " gpo_links: $($gpoLinks.Count)"
Add-Content -Path $OutputFile -Value " configured_gpos:"
$ou_counter = 0
foreach ($gpoGuid in $gpoLinks.Keys) {
$ou_counter++
try {
$gpo = Get-GPO -Guid $gpoGuid -ErrorAction Stop
}
@@ -77,18 +82,19 @@ foreach ($gpoGuid in $gpoLinks.Keys) {
Write-Warning "Unable to retrieve GPO $gpoGuid skipping."
continue
}
Write-Host "GPO: $($gpo.DisplayName)"
Write-Host "GPO $ou_counter of $($gpoLinks.Count): $($gpo.DisplayName)"
Add-Content -Path $OutputFile -Value " $($gpo.DisplayName):"
# ok, i need to start generating a variable with all links in it
# -------- Linked OUs ----------
$linkedOUs = $gpoLinks[$gpoGuid]
Write-Host "Linked OUs:"
Add-Content -Path $OutputFile -Value " Linked_OUs:"
$linkedOUs = $gpoLinks[$gpoGuid] # <-- the list we built earlier
Add-Content -Path $OutputFile -Value " - Linked_OUs:"
foreach ($ouDn in $linkedOUs) {
Write-Host "$ouDn"
Add-Content -Path $OutputFile -Value " - $ouDn"
}
# Pull the XML report
try {
$xmlString = Get-GPOReport -Guid $gpoGuid -ReportType Xml -ErrorAction Stop
@@ -97,8 +103,9 @@ foreach ($gpoGuid in $gpoLinks.Keys) {
Write-Warning "Unable to generate XML report for $($gpo.DisplayName)."
continue
}
$xml = [xml]$xmlString
# process the XML data
$ns = New-Object System.Xml.XmlNamespaceManager ($xml.NameTable)
$ns.AddNamespace('m', 'http://www.microsoft.com/GroupPolicy/Settings') # default namespace
$ns.AddNamespace('q1', 'http://www.microsoft.com/GroupPolicy/Settings/Security')
@@ -113,10 +120,12 @@ foreach ($gpoGuid in $gpoLinks.Keys) {
$adGroupName = ($rg.SelectSingleNode('q1:GroupName/t:Name', $ns)).InnerText
$localGroupName = ($rg.SelectSingleNode('q1:Memberof/t:Name', $ns)).InnerText
$plainAD_Groupname = ($adGroupName -split '\\')[-1]
Write-Output "AD Group: $adGroupName - Local Group: $localGroupName"
#Write-Output "AD Group: $adGroupName - Local Group: $localGroupName"
Add-Content -Path $OutputFile -Value " - ad_group: $plainAD_Groupname"
Add-Content -Path $OutputFile -Value " local_group: $localGroupName"
}
Add-Content -Path $OutputFile -Value ""
}
$Current_Date = (Get-Date).ToString('MM/dd/yyyy hh:mm:ss tt')
Add-Content -Path $OutputFile -Value " timestamp: $Current_Date"
Add-Content -Path $OutputFile -Value "..."
+58 -6
View File
@@ -1,5 +1,6 @@
import yaml
import re
from datetime import datetime
from flask import Flask, request, jsonify
app = Flask(__name__)
@@ -8,6 +9,23 @@ app = Flask(__name__)
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:
suffix = base_suffix.strip()
dn_clean = dn.strip()
lowered_dn = dn_clean.lower()
lowered_suffix = suffix.lower()
idx = lowered_dn.rfind(lowered_suffix)
if idx != -1:
dn_clean = dn_clean[:idx].rstrip(',')
# Pull all OU=… values (leaf → root order)
ou_values: List[str] = re.findall(r'OU=([^,]+)', dn_clean, flags=re.IGNORECASE)
if not ou_values:
return ''
ou_values = [v.strip() for v in reversed(ou_values)]
return '\\'.join(ou_values)
@app.route('/gpo', methods=['GET'])
def get_groups():
group_name = request.args.get('group')
@@ -37,6 +55,33 @@ def get_groups():
})
return jsonify(gpos)
@app.route("/gpo_links", methods=["GET"])
def gpo_links():
try:
gpo_link_count = data[0]['gpo_links']
except:
gpo_link_count = "whoops"
return jsonify({"gpo_links": gpo_link_count})
@app.route("/timestamp", methods=["GET"])
def timestamp():
try:
yaml_timestamp = data[0]['timestamp']
except:
yaml_timestamp = "whoops"
return jsonify({"timestamp": yaml_timestamp})
@app.route("/index_duration", methods=["GET"])
def index_duration():
fmt="%m/%d/%Y %I:%M:%S %p"
try:
start_timestamp = data[0]['init_timestamp']
end_timestamp = data[0]['timestamp']
duration = (datetime.strptime(end_timestamp, fmt) - datetime.strptime(start_timestamp, fmt)).total_seconds() / 60
return jsonify({"duration": duration, "start_timestamp": start_timestamp, "end_timestamp": end_timestamp})
except:
return jsonify({'duration': "whoops"})
@app.route("/linked_ous", methods=["GET"])
def linked_ous():
gpo_name = request.args.get("gpo")
@@ -55,21 +100,28 @@ def linked_ous():
if gpo_entry is None:
continue
def _add_ous(ous):
if not isinstance(ous, list):
return
for dn in ous:
if isinstance(dn, str):
# 1. strip out OU/ DC components
# 2. keep the *leaf* and its *parent* (e.g. NA\SJC18)
stripped = strip_dn(dn)
if stripped:
linked.append(stripped)
# Case 1: direct dict → look for Linked_OUs key
if isinstance(gpo_entry, dict):
if "Linked_OUs" in gpo_entry:
ous = gpo_entry["Linked_OUs"]
if isinstance(ous, list):
linked.extend(ous)
_add_ous(gpo_entry["Linked_OUs"])
continue
# Case 2: list of dicts → find the dict that has the Linked_OUs key
if isinstance(gpo_entry, list):
for sub in gpo_entry:
if isinstance(sub, dict) and "Linked_OUs" in sub:
ous = sub["Linked_OUs"]
if isinstance(ous, list):
linked.extend(ous)
_add_ous(sub["Linked_OUs"])
return jsonify(linked)