Last active
November 4, 2018 01:14
-
-
Save gjyoung1974/10b846fb4d6d20d948aa70751872d7d2 to your computer and use it in GitHub Desktop.
manage local users for offline windows machines & set local security and group policies
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Gordon Young 2012 Microsoft© Corporation UserAdd.js A script to create local | |
* users, remove local users, and update passwords The actual maintenance work | |
* begins in the section: ***Start of script:*** v0.10092012 | |
*/ | |
// Collect some environment variables: | |
var objShell = new ActiveXObject("WScript.Shell"); | |
var objNetwork = WScript.CreateObject("Wscript.Network"); | |
var strComputer = objNetwork.ComputerName; | |
var os = objShell.RegRead("HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion\\ProductName"); | |
// get the active CA Common name (is the machine a HyperV Host or a CA Guest?) | |
// if this key is not present: | |
// 'HKLM\\SYSTEM\\CurrentControlSet\\Services\\CertSvc\\Configuration\\Active' | |
// Then we are a HV Host or some other machine, just use the local 'hostname' in | |
// this case.. | |
var sActiveConfig = strComputer; | |
try { | |
sActiveConfig = objShell.RegRead("HKLM\\SYSTEM\\CurrentControlSet\\Services\\CertSvc\\Configuration\\Active"); | |
} catch (err) { | |
; | |
// remove comment to view "Try" messages: | |
// WScript.Echo(sUser + ", " + err.message); | |
} | |
// TODO: copy to compressed file to virtual share? | |
// get the command line arguments: | |
if (WScript.Arguments.Count() >= 1) { | |
var args = WScript.Arguments; | |
} else { | |
WScript.Echo("Usage: cscript userAdd.js [backup,nobackup]"); | |
WScript.Echo("the \"backup\" flag enables event log backup, \"nobackup\" excludes event log backup"); | |
WScript.Quit(); | |
} | |
var sBackup = args.Item(0); | |
// Load the Window's XML parser: (user names and passwords are kept in | |
// usernames.xml). | |
// "usernames.xml" is the authoritative source for which accounts | |
// are intended to be present in the local accounts database | |
var xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); | |
xmlDoc.async = false; | |
// usernames.xml uses this schema: Add or remove additional <user> elements to | |
// manage users | |
// <?xml version="1.0" encoding="UTF-8"?> | |
// <local_users> | |
// <user> | |
// <username>_Admin</username> | |
// <password>SomePassword!</password> | |
// </user> | |
// </local_users> | |
// get the date/time from local system | |
var currentTime = new Date(); | |
WScript.echo("System time on target is: " + currentTime); | |
// clean up previous runs: | |
// remove the previous output directory if it exists: | |
var fs_obj = new ActiveXObject("Scripting.FileSystemObject"); | |
var strLogPath = fs_obj.getFolder("."); | |
var sFolderPat = strLogPath.path + '\\Logs'; | |
if (fs_obj.FolderExists(sFolderPat)) { | |
WScript.echo("An old log folder exists, deleting.."); | |
fs_obj.DeleteFolder(strLogPath.path + '\\Logs'); | |
} | |
// get the current working directory and create a subfolder called "Logs" | |
WScript.echo("Saving logs to: " + strLogPath.path + '\\Logs'); | |
fs_obj.CreateFolder(strLogPath.path + '\\Logs'); | |
// log any adds, removes, changes of local users and the Windows event logs | |
// which we are doing now: | |
var objLogFile = fs_obj.CreateTextFile(strLogPath.path + "\\Logs\\userAdd_Log.txt", 1); | |
objLogFile.WriteLine("Saving logs to: " + strLogPath.path + '\\LogsLogs\\userAdd_Log.txt'); | |
objLogFile.WriteLine("Starting maintenance at: " + currentTime); | |
objLogFile.WriteLine("Active CA Config name is: " + sActiveConfig); | |
// ****************************************************// | |
/* | |
* Start of maintenance: | |
*/ | |
// 1: Run the Add Users function to add an missing users, or simply update | |
// passwords. | |
f_set_policies(); // ensure local policies are set | |
xmlDoc.onreadystatechange = f_AddUsers; // once XML is loaded run f_AddUsers; | |
xmlDoc.load(".\\usernames.xml"); // Place "usernames.xml" in the same | |
// directory as this script. | |
// 2: now that we have added users, remove any user not in usernames.xml | |
f_RemoveUsers(); | |
// 3: Roll the event logs: | |
if (sBackup == "backup") { | |
// Create the "zip" of the logs: | |
// TODO: Collect a SHA1 Hash of the zipfile. | |
f_Roll_Event_Logs(); | |
f_CreateZip(); | |
} | |
// 4: Ensure x.509 serial number randomization | |
f_set_HighSerial(); | |
// 5: Done! | |
WScript.Echo("***Done!***"); | |
/* | |
* End of maintenance; | |
*/ | |
// ****************************************************// | |
/* | |
* Some utility functions below: | |
*/ | |
// A function test if an element is present in an Array. (Wouldn't it be nice if | |
// Javascript had this?) | |
// Arrays.asList(Array).contains(string); | |
function f_Is_In_Array(elem, array, i) { | |
var len; | |
var indexOf = Array.prototype.indexOf; | |
if (array) { | |
if (indexOf) { | |
return indexOf.call(array, elem, i); | |
} | |
len = array.length; | |
i = i ? i < 0 ? Math.max(0, len + i) : i : 0; | |
for (; i < len; i++) { | |
// Skip accessing in sparse arrays | |
if (i in array && array[i] === elem) { | |
return i; | |
} | |
} | |
} | |
return -1; | |
}; | |
// create a zip file of the log directory.. | |
function f_CreateZip() { | |
var shell = new ActiveXObject("Shell.Application"); | |
var fs_obj = new ActiveXObject("Scripting.FileSystemObject"); | |
var zipFilename = strLogPath.path + '\\' + sActiveConfig + '-LogFiles.zip'; | |
// create an empty zipfile :-) | |
var zipFile = fs_obj.CreateTextFile(zipFilename, 1, 0); | |
zipFile.Write('PK' + String.fromCharCode(5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)); | |
zipFile.Close(); | |
var source = strLogPath.path + '\\Logs'; | |
// Test if is null folder | |
if (source == null) { | |
// throw new TypeError(); | |
} | |
// copy all the source files into the zipfile | |
shell.NameSpace(zipFilename).CopyHere(source); | |
WScript.Sleep(2000); | |
// delete directory | |
fs_obj.DeleteFolder(strLogPath.path + '\\Logs'); | |
}; | |
// function: Add users which are present in username.xml and not on local | |
// machine, or if the user already exists, just update the password. | |
function f_AddUsers() { | |
// setup WScript Shell and environment variables | |
var colAccounts = GetObject('WinNT://' + strComputer + ',computer'); | |
colAccounts.filter = new Array("User"); | |
if (xmlDoc.readyState == 4) { | |
for ( var ii = 0; ii < xmlDoc.documentElement.childNodes.length; ii++) { | |
// try to create the user. if the user already exists, continue.. | |
try { | |
var sUser = xmlDoc.documentElement.childNodes[ii].firstChild.text; | |
// create the user account | |
var objUser = colAccounts.Create("user", | |
xmlDoc.documentElement.childNodes[ii].firstChild.text); | |
// set the password | |
objUser.SetPassword(xmlDoc.documentElement.childNodes[ii].lastChild.text); | |
// set local user account properties | |
var ADS_UF_DONT_EXPIRE_PASSWD = 0x10000; | |
var LOCAL_ACCOUNT_FLAGS = ADS_UF_DONT_EXPIRE_PASSWD; | |
objUser.Put("userFlags", LOCAL_ACCOUNT_FLAGS); | |
// reference "Enum" containing possible account flags: | |
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa772300(v=vs.85).aspx | |
// are we the local _Admin account? if so add to Administrator | |
// group | |
var sUserRegex = /IUSR_/g; | |
// var result=sUserRegex.test(sUser); | |
if (sUserRegex.test(sUser)) { | |
WScript.Echo("Returned value: " + result); | |
} | |
else { | |
if (sUser == "Admin") { | |
// Disable the builtin local Administrator account | |
objUser.Put("userFlags", 0x10002); | |
objUser.SetInfo(); | |
} else { | |
// "Power Users" Group | |
// commit changes to the user | |
objUser.SetInfo(); | |
// add the user to the Power Users group: | |
var Group = GetObject('WinNT://' + strComputer | |
+ '/Administrators,group'); | |
Group.Add(objUser.ADspath); | |
} | |
} | |
} catch (err) { | |
; | |
// uncomment to view "Try" messages: | |
// WScript.Echo(sUser + ", " + err.message); | |
} | |
} | |
} | |
} | |
// Function: remove local users which are not specified in usernames.xml | |
function f_RemoveUsers() { | |
// Setup WMI for running local queries | |
var username_list_xml = new Array(); | |
var password_list_xml = new Array(); | |
var colAccounts2 = GetObject('WinNT://' + strComputer + ',computer'); | |
colAccounts2.filter = new Array("User"); | |
var SWBemlocator = new ActiveXObject("WbemScripting.SWbemLocator"); | |
var objWMIService = SWBemlocator.ConnectServer(strComputer, "/root/CIMV2"); | |
// find local user accounts via "WMI Query" Language | |
var colItems = objWMIService | |
.ExecQuery("Select * from Win32_UserAccount Where LocalAccount = True"); | |
var e = new Enumerator(colItems); | |
// create an array from usernames.xml (the user name source file) | |
if (xmlDoc.readyState == 4) { | |
for ( var ii = 0; ii < xmlDoc.documentElement.childNodes.length; ii++) { | |
username_list_xml.push(xmlDoc.documentElement.childNodes[ii].firstChild.text); | |
password_list_xml.push(xmlDoc.documentElement.childNodes[ii].lastChild.text); | |
} | |
} | |
// write out a CSV file of the properties of the final state of local users | |
// on the machine: | |
var fs_obj = new ActiveXObject("Scripting.FileSystemObject"); | |
var a = fs_obj.CreateTextFile(".\\Logs\\" + strComputer + "_Local_User_Properties.csv", true); | |
// add the spreadsheet header row: | |
a.WriteLine("Name,Caption,AccountType,Description,Disabled,Domain,FullName,LocalAccount,Lockout,PasswordChangeable,PasswordExpires,PasswordRequired,SID,SIDType,Status"); | |
var q = 0; | |
for (; !e.atEnd(); e.moveNext()) { | |
var uSerName = e.item().Name; | |
var isInArray = f_Is_In_Array(uSerName, username_list_xml, 0); | |
// if the user is not in username.xml remove it. | |
if (isInArray == -1) { | |
WScript.Echo("Removing user: " + uSerName + " Not present in usernames.xml!"); | |
objLogFile.WriteLine("Removing user: " + uSerName + " Not present in usernames.xml!"); | |
colAccounts2.Delete("user", uSerName); | |
} else { | |
// If the user is intended to exist per "sernames.xml", just change | |
// the password: | |
if (isInArray > -1) { | |
// skip the PKITAMgrSVC | |
if (username_list_xml[q] != "PKITAMgrSvc") { | |
if (username_list_xml[q] == "Admin") { | |
// Disable the account | |
var objUserAdm = GetObject("WinNT://" + strComputer + "/" + username_list_xml[q] + ",user"); | |
objUserAdm.Put("userFlags", 0x10002); | |
objUserAdm.SetInfo(); | |
} | |
if (username_list_xml[q] == "Guest") { | |
// Disable the account | |
var objUserAdm = GetObject("WinNT://" + strComputer + "/" + username_list_xml[q] + ",user"); | |
objUserAdm.Put("userFlags", 0x10002); | |
objUserAdm.SetInfo(); | |
} | |
// set the password: | |
WScript.Echo("Setting password for: " | |
+ username_list_xml[q]); | |
objLogFile.WriteLine("Setting password for: " | |
+ username_list_xml[q]); | |
var objUser3 = GetObject("WinNT://" + strComputer + "/" | |
+ username_list_xml[q] + ",user"); | |
objUser3.SetPassword(password_list_xml[q]); | |
try { // Place all PKI operators in the local | |
// "Administrator's" group | |
var Group2 = GetObject('WinNT://' + strComputer | |
+ '/Administrators,group'); | |
Group2.Add(objUser3.ADspath); | |
objUser3.SetInfo(); | |
} catch (err) { | |
; // uncomment to view "Try" messages: | |
// WScript.Echo(sUser + ", " + err.message); | |
} | |
try { // Remove all PKI operators from the local "Power | |
// Users's" group | |
var Group3 = GetObject('WinNT://' + strComputer | |
+ '/Power Users,group'); | |
Group3.Remove(objUser3.ADspath); | |
objUser3.SetInfo(); | |
} catch (err) { | |
; // uncomment to view "Try" messages: | |
// WScript.Echo(sUser + ", " + err.message); | |
} | |
objUser3.SetInfo(); | |
q++; | |
} | |
} | |
} | |
} | |
// write a report of the final state of the users after running maintenance: | |
var colItems2 = objWMIService | |
.ExecQuery("Select * from Win32_UserAccount Where LocalAccount = True"); | |
var f = new Enumerator(colItems2); | |
for (; !f.atEnd(); f.moveNext()) { | |
// write the local user account info to the CSV file | |
a.WriteLine(f.item().Name + "," + f.item().Caption + "," | |
+ f.item().AccountType + "," + f.item().Description + "," | |
+ f.item().Disabled + "," + f.item().Domain + "," | |
+ f.item().FullName + "," + f.item().LocalAccount + "," | |
+ f.item().Lockout + "," + f.item().PasswordChangeable + "," | |
+ f.item().PasswordExpires + "," + f.item().PasswordRequired | |
+ "," + f.item().SID + "," + f.item().SIDType + "," | |
+ f.item().Status); | |
} | |
a.Close(); | |
} | |
function f_Roll_Event_Logs() { | |
// Grab "all" of the Windows event logs.. | |
var obj_Wmi = GetObject("winmgmts:{impersonationLevel=impersonate,(Backup)}!\\\\" | |
+ strComputer + "\\root\\CIMV2"); | |
var query_evts = obj_Wmi.ExecQuery("select * from Win32_NTEventLogFile"); | |
// This can be filtered with a "Where Clause" 'where LogFileName like 'App%' | |
// or LogFileName like 'Sys%'"); | |
var enum_Evt = new Enumerator(query_evts); | |
// save all of the Eventlogs into <Current Dir>\\Logs | |
for (enum_Evt.moveFirst(); !enum_Evt.atEnd(); enum_Evt.moveNext()) { | |
var strEvtName = enum_Evt.item().LogFileName; | |
// back it up from WMI to the disk: | |
enum_Evt.item().BackupEventLog( | |
strLogPath.path + '\\Logs\\' + strEvtName + ".evt"); | |
WScript.Echo("\nWriting event log: " + strEvtName); | |
objLogFile.WriteLine("\nWriting event log: " + strEvtName); | |
// Clear the event log: | |
enum_Evt.item().ClearEventLog(); | |
objLogFile.WriteLine("Clearing event log: " + strEvtName); | |
WScript.Echo("Clearing event log: " + strEvtName); | |
} | |
// stamp the ending time and close the log file | |
var endTime = new Date(); | |
objLogFile.WriteLine("Ending maintenance at: " + endTime); | |
objLogFile.WriteLine("***Done!***"); | |
objLogFile.Close(); | |
} | |
// ensure randomization of x.509 serial numbers is set: | |
function f_set_HighSerial() { | |
wShell = new ActiveXObject("WScript.Shell"); | |
wShell.run('certutil ñsetreg ca\HighSerial 0x33'); | |
} | |
/** | |
* To add change or delete any local machine policies add them to this section: | |
* refer to secedit command for INF file format info | |
* http://msdn.microsoft.com/en-us/subscriptions/cc742472(v=ws.10).aspx | |
*/ | |
// ensure local/offline policies are set, Audit, etc | |
function f_set_policies() { | |
//If is Windows 2k8 R2 Ent, Disable the local firewall | |
if (os = 'Windows Server 2008 R2 Enterprise') { | |
objShell.run('netsh advfirewall set allprofiles state off'); | |
} | |
var fso = new ActiveXObject("Scripting.FileSystemObject"); | |
var cWD = fso.getFolder("."); | |
var s = fso.CreateTextFile(cWD + '\\secedit.inf', 1); | |
s.writeline("; Begin -----------------------------"); | |
s.writeline("[Unicode]"); | |
s.writeline("Unicode=yes"); | |
s.writeline("[System Access]"); | |
s.writeline("PasswordComplexity = 0"); | |
s.writeline("[Event Audit]"); | |
// Set local audit policies | |
s.writeline("AuditSystemEvents = 3"); | |
s.writeline("AuditLogonEvents = 3"); | |
s.writeline("AuditObjectAccess = 3"); | |
s.writeline("AuditPrivilegeUse = 3"); | |
s.writeline("AuditProcessTracking = 3"); | |
s.writeline("AuditPolicyChange = 3"); | |
s.writeline("AuditAccountManage = 3"); | |
s.writeline("AuditAccountLogon = 3"); | |
s.writeline("[Version]"); | |
s.writeline("signature=\"$CHICAGO$\""); | |
s.writeline("Revision=1"); | |
s.writeline("[Profile Description]"); | |
s.writeline("Description=Set local audit policies"); | |
s.writeline("; End -----------------------------"); | |
s.Close(); | |
// execute secedit against the inf policy file: | |
wShell = new ActiveXObject("WScript.Shell"); | |
wShell.run('secedit.exe /configure /db secedit.sdb /cfg ' + cWD + '\\secedit.inf'); | |
WScript.Sleep(2000); | |
fso.DeleteFile(cWD + '\\secedit.inf'); | |
fso.DeleteFile(cWD + '\\secedit.sdb'); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment