perf: windows ad gather account

This commit is contained in:
feng 2025-04-10 15:31:39 +08:00 committed by ZhaoJiSen
parent d6b1a577fc
commit 1254d28463
8 changed files with 124 additions and 75 deletions

View File

@ -13,6 +13,7 @@ def parse_date(date_str, default=None):
formats = [
'%Y/%m/%d %H:%M:%S',
'%Y-%m-%dT%H:%M:%S',
'%Y-%m-%d %H:%M:%S',
'%d-%m-%Y %H:%M:%S',
'%Y/%m/%d',
'%d-%m-%Y',
@ -26,7 +27,6 @@ def parse_date(date_str, default=None):
return default
# TODO 后期会挪到 playbook 中
class GatherAccountsFilter:
def __init__(self, tp):
self.tp = tp
@ -208,14 +208,35 @@ class GatherAccountsFilter:
key, value = parts
user_info[key.strip()] = value.strip()
detail = {'groups': user_info.get('Global Group memberships', ''), }
user = {
'username': user_info.get('User name', ''),
'date_password_change': parse_date(user_info.get('Password last set', '')),
'date_password_expired': parse_date(user_info.get('Password expires', '')),
'date_last_login': parse_date(user_info.get('Last logon', '')),
username = user_info.get('User name')
if not username:
continue
result[username] = {
'username': username,
'date_password_change': parse_date(user_info.get('Password last set')),
'date_password_expired': parse_date(user_info.get('Password expires')),
'date_last_login': parse_date(user_info.get('Last logon')),
'groups': detail,
}
return result
@staticmethod
def windows_ad_filter(info):
result = {}
for user_info in info['user_details']:
detail = {'groups': user_info.get('GlobalGroupMemberships', ''), }
username = user_info.get('SamAccountName')
if not username:
continue
result[username] = {
'username': username,
'date_password_change': parse_date(user_info.get('PasswordLastSet')),
'date_password_expired': parse_date(user_info.get('PasswordExpires')),
'date_last_login': parse_date(user_info.get('LastLogonDate')),
'groups': detail,
}
result[user['username']] = user
return result
@staticmethod

View File

@ -4,10 +4,10 @@ version: 1
method: gather_accounts
category:
- host
- ds
type:
- windows
- windows_ad
i18n:
Windows account gather:

View File

@ -0,0 +1,74 @@
- hosts: demo
gather_facts: no
tasks:
- name: Import ActiveDirectory module
win_shell: Import-Module ActiveDirectory
args:
warn: false
- name: Get the SamAccountName list of all AD users
win_shell: |
Import-Module ActiveDirectory
Get-ADUser -Filter * | Select-Object -ExpandProperty SamAccountName
register: ad_user_list
- name: Set the all_users variable
set_fact:
all_users: "{{ ad_user_list.stdout_lines }}"
- name: Get detailed information for each user
win_shell: |
Import-Module ActiveDirectory
$user = Get-ADUser -Identity {{ item }} -Properties Name, SamAccountName, Enabled, LastLogonDate, PasswordLastSet, msDS-UserPasswordExpiryTimeComputed, MemberOf
$globalGroups = @()
if ($user.MemberOf) {
$globalGroups = $user.MemberOf | ForEach-Object {
try {
$group = Get-ADGroup $_ -ErrorAction Stop
if ($group.GroupScope -eq 'Global') { $group.Name }
} catch {
}
}
}
$passwordExpiry = $null
$expiryRaw = $user.'msDS-UserPasswordExpiryTimeComputed'
if ($expiryRaw) {
try {
$passwordExpiry = [datetime]::FromFileTime($expiryRaw)
} catch {
$passwordExpiry = $null
}
}
$output = [PSCustomObject]@{
Name = $user.Name
SamAccountName = $user.SamAccountName
Enabled = $user.Enabled
LastLogonDate = if ($user.LastLogonDate) { $user.LastLogonDate.ToString("yyyy-MM-dd HH:mm:ss") } else { $null }
PasswordLastSet = if ($user.PasswordLastSet) { $user.PasswordLastSet.ToString("yyyy-MM-dd HH:mm:ss") } else { $null }
PasswordExpires = if ($passwordExpiry) { $passwordExpiry.ToString("yyyy-MM-dd HH:mm:ss") } else { $null }
GlobalGroupMemberships = $globalGroups
}
$output | ConvertTo-Json -Depth 3
loop: "{{ all_users }}"
register: ad_user_details
ignore_errors: yes
- set_fact:
info:
user_details: >-
{{
ad_user_details.results
| selectattr('rc', 'equalto', 0)
| map(attribute='stdout')
| select('truthy')
| map('from_json')
}}
- debug:
var: info

View File

@ -0,0 +1,15 @@
id: gather_accounts_windows_ad
name: "{{ 'Windows account gather' | trans }}"
version: 1
method: gather_accounts
category:
- ds
type:
- windows_ad
i18n:
Windows account gather:
zh: 使用命令 Get-ADUser 收集 Windows 账号
ja: コマンド Get-ADUser を使用して Windows アカウントを収集する
en: Using command Get-ADUser to gather accounts

View File

@ -1,6 +1,6 @@
import time
from collections import defaultdict
import time
from django.utils import timezone
from accounts.const import AutomationTypes
@ -222,6 +222,7 @@ class GatherAccountsManager(AccountBasePlaybookManager):
def _collect_asset_account_info(self, asset, info):
result = self._filter_success_result(asset.type, info)
accounts = []
for username, info in result.items():
self.asset_usernames_mapper[str(asset.id)].add(username)

View File

@ -1,64 +0,0 @@
# Generated by Django 4.1.13 on 2025-03-31 02:49
import json
import django
from django.db import migrations, models
from assets.const.types import AllTypes
class Migration(migrations.Migration):
dependencies = [
("assets", "0015_automationexecution_type"),
]
operations = [
migrations.RunPython(add_ad_host_type),
migrations.CreateModel(
name="DS",
fields=[
(
"asset_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="assets.asset",
),
),
(
"domain_name",
models.CharField(
blank=True,
default="",
max_length=128,
verbose_name="Domain name",
),
),
],
options={
"verbose_name": "Active Directory",
},
bases=("assets.asset",),
),
migrations.AddField(
model_name="platform",
name="ds",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="ad_platforms",
to="assets.ds",
verbose_name="Active Directory",
),
),
migrations.AddField(
model_name="platform",
name="ds_enabled",
field=models.BooleanField(default=False, verbose_name="DS enabled"),
),
]

View File

@ -48,7 +48,7 @@ def add_ds_platforms(apps, schema_editor):
},
"gather_accounts_enabled": true,
"gather_accounts_method": "gather_accounts_windows",
"gather_accounts_method": "gather_accounts_windows_ad",
"gather_accounts_params": {
},

View File

@ -2,6 +2,7 @@
import json
import os
import re
import sys
from collections import defaultdict
import sys
@ -194,6 +195,7 @@ class JMSInventory:
secret_info = {k: v for k, v in asset.secret_info.items() if v}
host = {
'name': name,
'local_python_interpreter': sys.executable,
'jms_asset': {
'id': str(asset.id), 'name': asset.name, 'address': asset.address,
'type': tp, 'category': category,