Skip to content

Instantly share code, notes, and snippets.

@gjyoung1974
Last active November 4, 2018 01:14
Show Gist options
  • Save gjyoung1974/10b846fb4d6d20d948aa70751872d7d2 to your computer and use it in GitHub Desktop.
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
/**
* 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