C# – Change Local Admin Password

email me

try
{
// change password
System.Diagnostics.Process.Start("net.exe", "user administrator " + "NewPasswordHere");
}
catch (Exception)
{
}

 

Notes

String 0ADSPath = “LDAP://onecity/CN=Users,
DC=onecity,DC=corp,DC=fabrikam,DC=com”;

DirectoryEntry oDirectoryEntry = new DirectoryEntry(oADSPath);
oDirectoryEntry.Username = UserName;
oDirectoryEntry.Password = SecureStringStoredPassword;

C# – Encrypt/Decrypt Password

email me

I wrote this in C# to encrypt and decrypt a password, which could be stored in a file, or perhaps the registry. It converts the password into a byte string, that byte string is converted using base64, and then is encrypted using AES. Take note, I have added permutations for the byte streams. These should be changed for every password you encrypt. One final thought, the overall security of this process depends on the protection of your C# code. So…compile this, and keep any source code in a secure location. This is not meant to be used as a secure app for distribution, just as a method to generate the encrypted password. If you’re trying to create a standalone app, look into AppendChar.

Screenshot

 

Code

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace SecurePassword
{
    class Encode_Decode
    {
        public static class Global
        {
            // set password
            public const string strPassword = "LetMeIn99$";

            // set permutations
            public const String strPermutation = "ouiveyxaqtd";
            public const Int32 bytePermutation1 = 0x19;
            public const Int32 bytePermutation2 = 0x59;
            public const Int32 bytePermutation3 = 0x17;
            public const Int32 bytePermutation4 = 0x41;         
        }


        // The console window
        public static void Main(String[] args)
        {           
            
            Console.Title = "Secure Password v2";
            Console.WriteLine("Output---");
            Console.WriteLine("");
            
            Console.WriteLine("Password:  " + Global.strPassword);

            string strEncrypted = (Encrypt(Global.strPassword));
            Console.WriteLine("Encrypted: " + strEncrypted);

            string strDecrypted = (Decrypt(strEncrypted));
            Console.WriteLine("Decrypted: " + strDecrypted);

            Console.ReadKey();
        }

 


        // encoding
        public static string Encrypt(string strData)
        {

            return Convert.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(strData)));
            // reference https://msdn.microsoft.com/en-us/library/ds4kkd55(v=vs.110).aspx

        }


        // decoding
        public static string Decrypt(string strData)
        {
            return Encoding.UTF8.GetString(Decrypt(Convert.FromBase64String(strData)));
            // reference https://msdn.microsoft.com/en-us/library/system.convert.frombase64string(v=vs.110).aspx

        }

        // encrypt
        public static byte[] Encrypt(byte[] strData)
        {
            PasswordDeriveBytes passbytes =
            new PasswordDeriveBytes(Global.strPermutation,
            new byte[] { Global.bytePermutation1,
                         Global.bytePermutation2,
                         Global.bytePermutation3,
                         Global.bytePermutation4
            });

            MemoryStream memstream = new MemoryStream();
            Aes aes = new AesManaged();
            aes.Key = passbytes.GetBytes(aes.KeySize/8);
            aes.IV = passbytes.GetBytes(aes.BlockSize/8);

            CryptoStream cryptostream = new CryptoStream(memstream,
            aes.CreateEncryptor(), CryptoStreamMode.Write);
            cryptostream.Write(strData, 0, strData.Length);
            cryptostream.Close();
            return memstream.ToArray();
        }

        // decrypt
        public static byte[] Decrypt(byte[] strData)
        {
            PasswordDeriveBytes passbytes =
            new PasswordDeriveBytes(Global.strPermutation,
            new byte[] { Global.bytePermutation1,
                         Global.bytePermutation2,
                         Global.bytePermutation3,
                         Global.bytePermutation4
            });

            MemoryStream memstream = new MemoryStream();
            Aes aes = new AesManaged();
            aes.Key = passbytes.GetBytes(aes.KeySize/8);
            aes.IV = passbytes.GetBytes(aes.BlockSize/8);

            CryptoStream cryptostream = new CryptoStream(memstream,
            aes.CreateDecryptor(), CryptoStreamMode.Write);
            cryptostream.Write(strData, 0, strData.Length);
            cryptostream.Close();
            return memstream.ToArray();
        }
        // reference
        // https://msdn.microsoft.com/en-us/library/system.security.cryptography(v=vs.110).aspx
        // https://msdn.microsoft.com/en-us/library/system.security.cryptography.cryptostream%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
        // https://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes(v=vs.110).aspx
        // https://msdn.microsoft.com/en-us/library/system.security.cryptography.aesmanaged%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396
    }
}

C# – Hashes and Comparisons

email me

This is hashing I’ve done in C#. I have included MD5, SHA1, SHA256, SHA384, and SHA512, the hashes, and then validation of a working versus non-working password.

Screenshot

The code…just copy/paste into Visual Studio Console Project and compile.

using System;
using System.Text;
using System.Security.Cryptography;

public class ReturnHash
{
    public static string ComputeHash(string plainText, string hashAlgorithm, byte[] saltBytes)
    {
        // Salt size
        saltBytes = new byte[8];

        // Convert Text to Array
        byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);

        // Create Array
        byte[] plainTextSaltBytes =
                new byte[plainTextBytes.Length + saltBytes.Length];

        // Copy Text into Array
        for (int i = 0; i < plainTextBytes.Length; i++)
            plainTextSaltBytes[i] = plainTextBytes[i];

        // Append Salt
        for (int i = 0; i < saltBytes.Length; i++)
            plainTextSaltBytes[plainTextBytes.Length + i] = saltBytes[i];

        // Algorithm
        HashAlgorithm hash;

        // Initialize Class
        switch (hashAlgorithm.ToUpper())
        {
            case "SHA1":
                hash = new SHA1Managed();
                break;

            case "SHA256":
                hash = new SHA256Managed();
                break;

            case "SHA384":
                hash = new SHA384Managed();
                break;

            case "SHA512":
                hash = new SHA512Managed();
                break;

            default:
                hash = new MD5CryptoServiceProvider();
                break;
        }

        byte[] hashBytes = hash.ComputeHash(plainTextSaltBytes);

        byte[] hashSaltBytes = new byte[hashBytes.Length +
                                            saltBytes.Length];

        // Hash to Array
        for (int i = 0; i < hashBytes.Length; i++)
            hashSaltBytes[i] = hashBytes[i];

        // Append Salt
        for (int i = 0; i < saltBytes.Length; i++)
            hashSaltBytes[hashBytes.Length + i] = saltBytes[i];

        // Convert result into a base64-encoded string.
        string hashValue = Convert.ToBase64String(hashSaltBytes);

        // Return Result
        return hashValue;
    }

    public static bool VerifyHash(string plainText,
                                  string hashAlgorithm,
                                  string hashValue)
    {
        // Base64-encoded hash
        byte[] hashWithSaltBytes = Convert.FromBase64String(hashValue);

        // Hash without Salt
        int hashSizeInBits, hashSizeInBytes;

        // Size of hash is based on the specified algorithm.
        switch (hashAlgorithm.ToUpper())
        {
            case "SHA1":
                hashSizeInBits = 160;
                break;

            case "SHA256":
                hashSizeInBits = 256;
                break;

            case "SHA384":
                hashSizeInBits = 384;
                break;

            case "SHA512":
                hashSizeInBits = 512;
                break;

            default: // MD5
                hashSizeInBits = 128;
                break;
        }

        // Convert to bytes.
        hashSizeInBytes = hashSizeInBits / 8;

        // Verify Hash Length
        if (hashWithSaltBytes.Length < hashSizeInBytes)
            return false;

        // Array to hold Salt
        byte[] saltBytes = new byte[hashWithSaltBytes.Length -
                                    hashSizeInBytes];

        // Salt to New Array
        for (int i = 0; i < saltBytes.Length; i++)
            saltBytes[i] = hashWithSaltBytes[hashSizeInBytes + i];

        string expectedHashString =
                    ComputeHash(plainText, hashAlgorithm, saltBytes);

        return (hashValue == expectedHashString);
    }
}

public class PasswordEncodingTest1
{
    [STAThread]
    static void Main(string[] args)
    {
        // Test Passwords
        string rightPassword = "TRUEPassword$";
        string wrongPassword = "FALSEPassword$";

        string passwordHashMD5 =
               ReturnHash.ComputeHash(rightPassword, "MD5", null);
        string passwordHashSha1 =
               ReturnHash.ComputeHash(rightPassword, "SHA1", null);
        string passwordHashSha256 =
               ReturnHash.ComputeHash(rightPassword, "SHA256", null);
        string passwordHashSha384 =
               ReturnHash.ComputeHash(rightPassword, "SHA384", null);
        string passwordHashSha512 =
               ReturnHash.ComputeHash(rightPassword, "SHA512", null);

        
        Console.WriteLine("Hash and Comparison--- \r\n");

        // md5
        Console.WriteLine("MD5 {0}", passwordHashMD5);
            Console.WriteLine("Right PW MD5     {0}",
            ReturnHash.VerifyHash(
            rightPassword, "MD5",
            passwordHashMD5).ToString());
            Console.WriteLine("Wrong PW MD5     {0}",
            ReturnHash.VerifyHash(
            wrongPassword, "MD5",
            passwordHashMD5).ToString());
            Console.WriteLine("");
            // reference https://en.wikipedia.org/wiki/MD5
            // https://msdn.microsoft.com/en-us/library/system.security.cryptography.md5(v=vs.110).aspx

        // sha1
        Console.WriteLine("SHA1 {0}", passwordHashSha1);
            Console.WriteLine("Right PW SHA1    {0}",
            ReturnHash.VerifyHash(
            rightPassword, "SHA1",
            passwordHashSha1).ToString());
            Console.WriteLine("Wrong PW SHA1    {0}",
            ReturnHash.VerifyHash(
            wrongPassword, "SHA1",
            passwordHashSha1).ToString());
            Console.WriteLine("");
            // reference https://en.wikipedia.org/wiki/SHA-1
            // https://msdn.microsoft.com/en-us/library/system.security.cryptography.sha1(v=vs.110).aspx

        // sha256
        Console.WriteLine("SHA256 {0}", passwordHashSha256);
            Console.WriteLine("Right PW SHA256  {0}",
            ReturnHash.VerifyHash(
            rightPassword, "SHA256",
            passwordHashSha256).ToString());
            Console.WriteLine("Wrong PW SHA256  {0}",
            ReturnHash.VerifyHash(
            wrongPassword, "SHA256",
            passwordHashSha256).ToString());
            Console.WriteLine("");
            // reference https://en.wikipedia.org/wiki/SHA-2
            // https://msdn.microsoft.com/en-us/library/system.security.cryptography.sha256(v=vs.110).aspx

        // sha384
        Console.WriteLine("SHA384 {0}", passwordHashSha384);
            Console.WriteLine("Right PW SHA384  {0}",
            ReturnHash.VerifyHash(
            rightPassword, "SHA384",
            passwordHashSha384).ToString());
            Console.WriteLine("Wrong PW SHA384  {0}",
            ReturnHash.VerifyHash(
            wrongPassword, "SHA384",
            passwordHashSha384).ToString());
            Console.WriteLine("");
            // reference https://msdn.microsoft.com/en-us/library/system.security.cryptography.sha384(v=vs.110).aspx

        // sha512
        Console.WriteLine("SHA512 {0}", passwordHashSha512);
            Console.WriteLine("Right PW SHA512  {0}",
            ReturnHash.VerifyHash(
            rightPassword, "SHA512",
            passwordHashSha512).ToString());
            Console.WriteLine("Wrong PW SHA512  {0}",
            ReturnHash.VerifyHash(
            wrongPassword, "SHA512",
            passwordHashSha512).ToString());
            // reference https://msdn.microsoft.com/en-us/library/system.security.cryptography.sha512(v=vs.110).aspx

        Console.WriteLine("");
        Console.WriteLine("");


        Console.ReadKey();
    }
}

Encoding and Decoding a Stored Password: v1

email me

I created this to store passwords in a more secure manner. It could be used to encode a password, and also to retrieve and decode a password. I do permutations based upon a specified number to perform the shift in ASCII (polyalphabetic substitution cipher), but…I’d also like to add a more advanced method, which would increase the complexity of encoding using multiple formulas based upon positioning. As this will be considered v1, I’ll post v2 when complete.

Screenshots

Option Explicit

Dim ASCII_Code1, returnASCII, returnCharacterPermutation, concatenateASCIIPermutation
Dim addPermutation, concatenateASCII, concatenatePermutation, password, iStep, Permutation1
Dim reversePermutation, returnCharacterPermutation2, concatenateASCIIPermutation2, returnLength
Dim concatenateASCII2, ASCII_Registry, Permutation2, concatenatePermutation2, PasswordLength, PermutationConstant


''''''''''''''''''''''''''''''''''''''''''''''''''''''
'PASSWORD TO ENCODE
password = "ABC-XYZ-abc-xyz-0987654321-""-!@#$%^&*(){}|\|/?<>,._-+=`~"
'password = "ABCDEFGHIJKLMNOPQRSTUVWXYZ-abcdefghijklmnopqrstuvwxyz-0987654321-""-!@#$%^&*(){}|\|/?<>,._-+=`~"

'PERMUTATION CAN BE CHANGED 1-55
Permutation1 = 3

''''''''''''''''''''''''''''''''''''''''''''''''''''''




'SESSION
PasswordLength = 0
iStep = 0
concatenateASCII = ""
concatenatePermutation = ""
ASCII_Code1 = ""
ASCII_Registry = ""

PermutationConstant = Permutation1


'ENCODES PASSWORD

FOR iStep = 1 TO LEN(password)
   ASCII_Code1 = Asc(MID(password,iStep,1))
   'num conversion
   ASCII_Code1 = CINT(ASCII_Code1)
   
   concatenateASCII = ASCII_Code1 & " " & concatenateASCII     
   
   'ADD PERMUTATION
   addPermutation = ASCII_Code1 + Permutation1
   returnCharacterPermutation = chr(addPermutation)
   concatenateASCIIPermutation = returnCharacterPermutation + concatenateASCIIPermutation   
   concatenatePermutation = addPermutation & " " & concatenatePermutation
    
   Permutation1 = Permutation1 + 1
NEXT




'DECODES PASSWORD - WILL COME FROM REGISTRY
'concatenateASCIIPermutation = regvalue, eventually

'return length of password
PasswordLength = LEN(concatenateASCIIPermutation)
'the permutation numeric is created subtracting 1 from original number 
'and adding the total length of the encoded password
Permutation2 = (PermutationConstant - 1) + PasswordLength

iStep = 0
FOR iStep = 1 TO LEN(concatenateASCIIPermutation)
   ASCII_Registry = Asc(MID(concatenateASCIIPermutation,iStep,1))
   'num conversion
   ASCII_Registry = CINT(ASCII_Registry)
   
   concatenateASCII2 = ASCII_Registry & " " & concatenateASCII2
   
   'REVERSE PERMUTATION   
   reversePermutation = ASCII_Registry - Permutation2 
   returnCharacterPermutation2 = chr(reversePermutation)
   concatenateASCIIPermutation2 = returnCharacterPermutation2 + concatenateASCIIPermutation2   
   concatenatePermutation2 = reversePermutation & " " & concatenatePermutation2   
   
   Permutation2 = Permutation2 - 1
NEXT
   

   
'OUTPUT
MSGBOX "[Password]" & chr(10) & password & chr(10) & chr(10) & _
	   "[ASCII Code]" & chr(10) & concatenateASCII & chr(10) & chr(10) & _
	   "[Add Permutation]" & chr(10) & concatenatePermutation & chr(10) & chr(10) & _	   
	   "[Encoded Password]" & chr(10) & concatenateASCIIPermutation & chr(10) & chr(10) & _
	   "[Registry ASCII Code]" & chr(10) & concatenateASCII2 & chr(10) & chr(10) & _
	   "[Reverse Permutation]" & chr(10) & concatenatePermutation2,64,"Decode"
	   
MSGBOX "[Password]" & chr(10) & password & chr(10) & chr(10) & _
       "[Encoded Password]" & chr(10) & concatenateASCIIPermutation & chr(10) & chr(10) & _
	   "[Decoded Password]" & chr(10) & concatenateASCIIPermutation2,64,"Decode"
	   


ASCII_Code1 = ""
returnASCII = ""
returnCharacterPermutation = ""
concatenateASCIIPermutation = ""
addPermutation = ""
concatenateASCII = ""
concatenatePermutation = ""
password = ""
iStep = ""
Permutation1 = ""
reversePermutation = ""
returnCharacterPermutation2 = ""
concatenateASCIIPermutation2 = ""
returnLength = ""
concatenateASCII2 = ""
ASCII_Registry = ""
Permutation2 = ""
concatenatePermutation2 = ""
PasswordLength = ""
PermutationConstant = ""
   
WScript.Quit(0)

 

Notes

In v2 (to provide better security for passwords, specifically), what I’m going to do is create unique formulas for each position. It may be written in VB or C#.

So, a potential password, let’s say LetMeIn99$, could look like this once encoded: AAAAAAAAAA.

The idea is that there is zero correlation between one position, to the next, as seen from the onlooker.

 

From Bruce Schneier’s Applied Cryptography, 2nd edition…

Substitution Ciphers

A substitution cipher is one in which each character in the plaintext is substituted for another character in the ciphertext. The receiver inverts the substitution on the ciphertext to recover the plaintext. In classical cryptography, there are four types of substitution ciphers:

— A simple substitution cipher, or monoalphabetic cipher, is one in which each character of the plaintext is replaced with a corresponding character of ciphertext. The cryptograms in newspapers are simple substitution ciphers.

— A homophonic substitution cipher is like a simple substitution cryptosystem, except a single character of plaintext can map to one of several characters of ciphertext. For example, “A” could correspond to either 5, 13, 25, or 56, “B” could correspond to either 7, 19, 31, or 42, and so on.

— A polygram substitution cipher is one in which blocks of characters are encrypted in groups. For example, “ABA” could correspond to “RTQ,” “ABB” could correspond to “SLL,” and so on.

— A polyalphabetic substitution cipher is made up of multiple simple substitution ciphers. For example, there might be five different simple substitution ciphers used; the particular one used changes with the position of each character of the plaintext.

The famous Caesar Cipher, in which each plaintext character is replaced by the character three to the right modulo 26 (“A” is replaced by “D,” “B” is replaced by “E,”…, “W” is replaced by “Z,” “X” is replaced by “A,” “Y” is
replaced by “B,” and “Z” is replaced by “C”) is a simple substitution cipher. It’s actually even simpler, because the ciphertext alphabet is a rotation of the plaintext alphabet and not an arbitrary permutation.

ROT13 is a simple encryption program commonly found on UNIX systems; it is also a simple substitution cipher. In this cipher, “A” is replaced by “N,” “B” is replaced by “O,” and so on. Every letter is rotated 13 places. Encrypting a file twice with ROT13 restores the original file.

P = ROT13 (ROT13 (P))

ROT13 is not intended for security; it is often used in Usenet posts to hide potentially offensive text, to avoid giving away the solution to a puzzle, and so forth.

Simple substitution ciphers can be easily broken because the cipher does not hide the underlying frequencies of the different letters of the plaintext. All it takes is about 25 English characters before a good cryptanalyst can reconstruct the plaintext [1434]. An algorithm for solving these sorts of ciphers can be found in [578,587,1600,78,1475,1236,880]. A good computer algorithm is [703].

Homophonic substitution ciphers were used as early as 1401 by the Duchy of Mantua [794]. They are much more complicated to break than simple substitution ciphers, but still do not obscure all of the statistical properties of the plaintext language. With a known-plaintext attack, the ciphers are trivial to break. A ciphertext-only attack is harder, but only takes a few seconds on a computer. Details are in [1261].

Polygram substitution ciphers are ciphers in which groups of letters are encrypted together. The Playfair cipher, invented in 1854, was used by the British during World War I [794]. It encrypts pairs of letters together. Its cryptanalysis is discussed in [587,1475,880]. The Hill cipher is another example of a polygram substitution cipher [732]. Sometimes you see Huffman coding used as a cipher; this is an insecure polygram substitution cipher.

Polyalphabetic substitution ciphers were invented by Leon Battista in 1568 [794]. They were used by the Union army during the American Civil War. Despite the fact that they can be broken easily [819,577,587,794] (especially with the help of computers), many commercial computer security products use ciphers of this form [1387,1390,1502]. (Details on how to break this encryption scheme, as used in WordPerfect, can be found in [135,139].) The Vigenère cipher, first published in 1586, and the Beaufort cipher are also examples of polyalphabetic substitution ciphers.

Polyalphabetic substitution ciphers have multiple one-letter keys, each of which is used to encrypt one letter of the plaintext. The first key encrypts the first letter of the plaintext, the second key encrypts the second letter of the plaintext, and so on. After all the keys are used, the keys are recycled. If there were 20 one-letter keys, then every twentieth letter would be encrypted with the same key. This is called the period of the cipher. In classical cryptography, ciphers with longer periods were significantly harder to break than ciphers with short periods. There are computer techniques that can easily break substitution ciphers with very long periods.

A running-key cipher—sometimes called a book cipher—in which one text is used to encrypt another text, is another example of this sort of cipher. Even though this cipher has a period the length of the text, it can also be broken easily [576,794].

Buy book here: Amazon.com — Applied Cryptography

Script Engine Example in Batch – v1

email me

I wrote this to install a ‘prepared’ package to a single computer, or to a list of computers. It’s low tech to get around the numerous problems associated with remote installation using PowerShell, VBScript, or desktop management applications.

I do plan on adding a form to collect the necessary variables, and a GUI layout to add a little pizzazz to the overall look and functionality. I’ll post that when complete.

* be careful with this, especially if deploying packages to multiple computers

@echo off
Title Script Engine v1 – by Eddie Jackson
color 0a

::PACKAGE PATH, WITHOUT C:\ – will come from form
Set AppPath=Progra~2\CompanyFolder

::APP NAME – will come from form
set AppName=FooApp

::PACKAGE/SETUP FILE NAME – will come from form
Set FileName=FooSetup.exe

::USE, IF ONLY NEEDED FOR ONE COMPUTER
::otherwise, set OnePC=NO – will come from form
Set OnePC=NO

::——————————————————-

Set CurDir=C:\_ENGINE
Set PCList=computers.txt
setLocal EnableDelayedExpansion
set FilePath=c:\%AppPath%\%FileName%
If Not Exist%CurDir%\logs” (
md %CurDir%\logs
)

If Not Exist%CurDir%\paexec.exe” (
cls
echo Missing PAExec.exe file.
echo.
pause
exit
)

::GENERATES REMOTE SEQUENCE FILE
echo @echo off>remoteCommand.cmd
echo title %AppName%>>remoteCommand.cmd
echo cls>>remoteCommand.cmd
echo Echo Installing %AppName%…please wait>>remoteCommand.cmd
echo%FilePath%”>>remoteCommand.cmd

::CREATES DATE AND TIME TIMESTAMP
::sets a static timestamp
for /F “tokens=2-4 delims=/- ” %%p in (‘date/T’) do set mdate=%%r%%p%%q
for /F “tokens=1-2 delims=:- ” %%p in (‘time/T’) do set mtime=%%p%%q
Set ReportN=%mdate%_%mtime%.txt
if %OnePC% NEQ NO goto SINGLE

echo Multiple PC mode
echo.

for /f “tokens=* delims=%%a in (%PCList%) do (

::sets dynamic timestamp
FOR /F “TOKENS=*” %%B IN (‘DATE/T’) DO SET NowD=%%B
FOR /F “TOKENS=*” %%A IN (‘TIME/T’) DO SET NowT=%%A

cls
echo Accessing: %%a
c:\windows\System32\ping.exe %%a | find “Reply” > nul
if errorlevel 1 (echo !NowD! !NowT!, %%a, OFFLINE >> “logs\%ReportN%” & echo %%a OFFLINE & echo.
) else (
echo.
echo Copying files to %%aif not exist “\\%%a\c$\%AppPath%md “\\%%a\c$\%AppPath%”

copy /y /v “%CurDir%\remoteCommand.cmd” “\\%%a\c$\%AppPath%”
copy /y /v “%CurDir%\%FileName%” “\\%%a\c$\%AppPath%”
c:\windows\System32\ping.exe -n 4 127.0.0.1>nul
cls
echo Launching %AppName% on %%a…
“%CurDir%\PAExec.exe” \\%%a -s -i -w “c:\%AppPath%” “c:\%AppPath%\remoteCommand.cmd”
echo !NowD! !NowT!, %%a, ONLINE >> “logs\%ReportN%” & echo %%a ONLINE & echo.
)
)
goto END

:SINGLE
FOR /F “TOKENS=*” %%B IN (‘DATE/T’) DO SET NowD=%%B
FOR /F “TOKENS=*” %%A IN (‘TIME/T’) DO SET NowT=%%A

set ReportN=_SingleReport.txt
cls
echo Single PC mode
echo.
echo Accessing: %OnePC%
c:\windows\System32\ping.exe %OnePC% | find “Reply” > nul
if errorlevel 1 (echo %NowD% %NowT%, %OnePC%, OFFLINE >> “logs\%ReportN%” & echo %OnePC% OFFLINE & echo.
) else (
echo.
echo Copying files to %OnePC%if not exist “\\%OnePC%\c$\%AppPath%md “\\%OnePC%\c$\%AppPath%”

copy /y /v “%CurDir%\remoteCommand.cmd” “\\%OnePC%\c$\%AppPath%”
copy /y /v “%CurDir%\%FileName%” “\\%OnePC%\c$\%AppPath%”
c:\windows\System32\ping.exe -n 4 127.0.0.1>nul
echo Launching %AppName% on %OnePC%%CurDir%\PAExec.exe \\%OnePC% -s -i -w c:\%AppPath% c:\%AppPath%\remoteCommand.cmd
echo %NowD% %NowT%, %OnePC%, ONLINE >> “logs\%ReportN%” & echo %OnePC% ONLINE & echo.
)
goto END
:END
@echo on
pause
exit /b 0

 

 

Notes

FOR /F “tokens=*” %%A IN (C:\script\servers.txt) DO WMIC /Node:%%A Process call create “cmd.exe /c gpupdate”

$List = ‘server1′,’server2′,’server3′,’server4’
foreach ($server in $List) {psexec \\$server gpupdate}

Update Line from LANDesk Actions.ini File

email me

This will uppdate the line containing SDCLIENTRECONNECT from the LANDesk actions.ini file. It adds missing double quotes.

From

SDCLIENTRECONNECT=C:\Program Files (x86)\LANDesk\LDClient\sdclient.exe /policyfile=”” /reconnect

To

SDCLIENTRECONNECT=C:\Program Files (x86)\LANDesk\LDClient\sdclient.exe /policyfile=”” /reconnect

Option Explicit 

Dim txtFile, oFSO, oFile, txtLine, modifiedFile, modLineContaining, replacementLine

Const ForReading = 1
Const ForWriting = 2

txtFile = "C:\Program Files (x86)\LANDesk\Shared Files\cbaroot\actions.ini"
modLineContaining = "SDCLIENTRECONNECT"

replacementLine = "SDCLIENTRECONNECT=""C:\Program Files (x86)\LANDesk\LDClient\sdclient.exe"" /policyfile="""" /reconnect"

Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFile = oFSO.OpenTextFile(txtFile, ForReading)

Do Until oFile.AtEndOfStream
    txtLine = oFile.ReadLine
    If InStr(txtLine, modLineContaining) = 0 Then
        modifiedFile = modifiedFile & txtLine & vbCrLf
	else
		modifiedFile = modifiedFile & replacementLine & vbCrLf
    End If
Loop

oFile.Close

Set oFile = oFSO.OpenTextFile(txtFile, ForWriting)

oFile.Write modifiedFile
oFile.Close

'session clean up
txtFile = ""
txtLine = ""
txtLine = ""
modifiedFile = ""
modLineContaining = "" 
replacementLine = ""
set oFSO = Nothing
set oFile = Nothing

WScript.Quit(0)

 
Also see Remove Line from LANDesk Actions.ini File

 
Notes

Client Issue

When a LANDesk package is pushed and there is a pending update, the SDCLIENTRECONNECT line is generated.

Once this line exists, no other packages can be pushed until one of two things happen:

#1 The machine is rebooted. Once rebooted, the SDCLIENTRECONNECT line is cleared and packages can be pushed.

#2 Add quotes around Program Files (x86), just like the other lines. When quotes are added, packages can be pushed.

 
Technical Problem

Missing quotes on the SDCLIENTRECONNECT line if forcing unnecessary reboots.

 
Solution

Add quotes to generated line.

Screenshot

Remove Line from LANDesk Actions.ini File

email me

This will remove the line containing SDCLIENTRECONNECT from the LANDesk actions.ini file

Option Explicit 

Dim txtFile, oFSO, oFile, txtLine, modifiedFile, delLine

Const ForReading = 1
Const ForWriting = 2

txtFile = "C:\Program Files (x86)\LANDesk\Shared Files\cbaroot\actions.ini"
delLine = "SDCLIENTRECONNECT"

Set oFSO = CreateObject("Scripting.FileSystemObject")
Set oFile = oFSO.OpenTextFile(txtFile, ForReading)

Do Until oFile.AtEndOfStream
    txtLine = oFile.ReadLine
    If InStr(txtLine, delLine) = 0 Then
        modifiedFile = modifiedFile & txtLine & vbCrLf
    End If
Loop


oFile.Close

Set oFile = oFSO.OpenTextFile(txtFile, ForWriting)

oFile.Write modifiedFile
oFile.Close

'session clean up
txtFile = ""
delLine = ""
txtLine = ""
modifiedFile = ""
set oFSO = Nothing
set oFile = Nothing

WScript.Quit(0)

Also see Update Line from LANDesk Actions.ini File File

 
Notes

Client Issue

When a LANDesk package is pushed and there is a pending update, the SDCLIENTRECONNECT line is generated.

Once this line exists, no other packages can be pushed until one of two things happen:

#1 The machine is rebooted. Once rebooted, the SDCLIENTRECONNECT line is cleared and packages can be pushed.

#2 Add quotes around Program Files (x86), just like the other lines. When quotes are added, packages can be pushed.

 
Technical Problem

Missing quotes on the SDCLIENTRECONNECT line if forcing unnecessary reboots.

 
Solution

Add quotes to generated line.

Screenshot

Add/Remove User from Local Admin, based upon AD Group; User Aware

email me

This script will add or remove a domain user from the local admin group based upon their membership to an Active Directory group. You basically just drop the user into the specified group, and the script does the rest. If the user is removed from the AD group, they will be removed from the local admin group.

I tested this in the System Account, so it can be deployed using desktop management software (preferably as a regular policy), or even as part of a login script.

Things to note:

  • Was tested in W7 and W10.
  • Was tested in the system account security context.
  • Tested locally and in desktop management software.
  • If user is not logged in, the script will exit.
  • To acquire the current user from the system account, I query the explorer.exe process and return the user.
  • It does work with nested AD groups.
  • Compile this script, if used in production.

 

'I've left some simple logging in place, 
'which I used for testing in the SYSTEM ACCOUNT
'you can remove it if not needed

Option Explicit
'on error resume next

Dim objRootDSE, objDBConnection, objRecord, objList, strDomain, strADGroup, strDB
Dim StrGroupDN, strSpace, strCurrentUser, MembershipStatus, strComputer, Return
Dim strNameOfUser, ReturnedUserName, objShell, strUserDomain, colProcesses, objProcess
Set objShell = CreateObject("Wscript.Shell")


'IS USER LOGGED IN?
UserLoggedInStatus()


'AD GROUP - AD GROUP TO QUERY - GROUPS CAN BE NESTED
'SYSTEM ACCOUNT: NO ISSUES
strADGroup = "AD_Group_Name_Here"


'COMPUTERNAME
'SYSTEM ACCOUNT: NO ISSUES
'set this computer name
strComputer = objShell.ExpandEnvironmentStrings("%COMPUTERNAME%")
strComputer = trim(strComputer)
strComputer = lcase(strComputer)


'DOMAIN OR WORKGROUP
'SYSTEM ACCOUNT: NO ISSUES
strUserDomain = objShell.ExpandEnvironmentStrings("%USERDOMAIN%")
strUserDomain = trim(strUserDomain)
strUserDomain = lcase(strUserDomain)

'used for logging - can be removed if not needed
objShell.run "cmd / md c:\setup",0,true

'WORKGROUP STATUS
'IF WORKGROUP IS FOUND, EXIT
'return in user context
if strUserDomain = strComputer then
	objShell.run "cmd /c echo user context workgroup>C:\setup\_ComputerType.txt",0,false
	Leave()
'return in SYSTEM ACCOUNT
elseif strUserDomain = "workgroup" then	
	objShell.run "cmd /c echo system account workgroup>C:\setup\_ComputerType.txt",0,false
	Leave()
elseif strUserDomain = "OtherWorkgroupName" then
	objShell.run "cmd /c echo system account workgroup>C:\setup\_ComputerType.txt",0,false
	Leave()
else
	objShell.run "cmd /c echo " & strUserDomain & ">C:\setup\_ComputerType.txt",0,false
end if



'DOMAIN
'SYSTEM ACCOUNT: NO ISSUES
Set objRootDSE = GetObject("LDAP://RootDSE")
strDomain = Trim(objRootDSE.Get("DefaultNamingContext"))



'USERNAME
'SYSTEM ACCOUNT: NO ISSUES
'Returns username from explorer.exe process
GetUserName()
strCurrentUser = ReturnedUserName
strCurrentUser = trim(strCurrentUser)
strCurrentUser = lcase(strCurrentUser)
objShell.run "cmd /c echo " & strCurrentUser & ">C:\setup\_CurrentUser.txt",0,false
'some simple validation
if strCurrentUser = strComputer & "$" then Leave()
if strCurrentUser = "" then Leave()
if strCurrentUser = "system" then Leave()

'Group membership is false(disabled) by default
MembershipStatus = "False"


'SET AD DB 
strDB = "Select ADsPath From 'LDAP://" & strDomain & "' Where ObjectCategory = 'Group' AND Name = '" & strADGroup & "'"

Set objDBConnection = CreateObject("ADODB.Connection")
objDBConnection.Provider = "ADsDSOObject":	objDBConnection.Open "Active Directory Provider"
Set objRecord = CreateObject("ADODB.Recordset")

'CONNECT TO AD
objRecord.Open strDB, objDBConnection

'TEST CONNECTION
'Can't find group - exiting now
if objRecord.EOF = True then
	Leave()
end if

'SCAN THROUGH TO END OF GROUPS
'if at end, leave
If objRecord.EOF Then
	'Group not found
	on error resume next

'If not at end, access next group
Elseif Not objRecord.EOF Then	
	on error resume next	
	objRecord.MoveLast:	objRecord.MoveFirst
	While Not objRecord.EOF		
		StrGroupDN = Trim(objRecord.Fields("ADsPath").Value)
		Set objList = CreateObject("Scripting.Dictionary")
		strSpace = " "
		NestedADMembers StrGroupDN, strSpace, objList, strCurrentUser
		Set objList = Nothing		
		objRecord.MoveNext
	Wend
End If


'ONCE COMPLETE, CHECK MEMBERSHIP STATUS
If MembershipStatus = "True" then 
	AccessGranted()
Else
	RemoveAccess()
end if

'Exit
Leave()




'MY SUBs AND FUNCTIONS

'Grant admin access
Sub AccessGranted()
	on error resume next
	'SYSTEM ACCOUNT: NO ISSUES
	objShell.run "cmd /c echo GRANT ACCESS>C:\setup\_AccessStatus.txt",0,false	
	objShell.run "net localgroup administrators " & strUserDomain & "\" & strCurrentUser & " /add",0,false
	WScript.Sleep 2000	
end sub


'Remove admin access
Sub RemoveAccess()
	on error resume next
	'SYSTEM ACCOUNT: NO ISSUES
	objShell.run "cmd /c echo REMOVE ACCESS>C:\setup\_AccessStatus.txt",0,false	
	objShell.run "net localgroup administrators " & strUserDomain & "\" & strCurrentUser & " /delete",0,false
	WScript.Sleep 2000
end sub


'Exit
Sub Leave()
	on error resume next	
	objRecord.Close:	Set objRecord = Nothing
	objDBConnection.Close:	Set objDBConnection = Nothing
	Set objRootDSE = Nothing
	WScript.Sleep 5000
	WScript.Quit
end sub


'Cycles through main Group
Sub MainADGroup(ADPath, strSpace, objList, strCurrentUser)
	on error resume next
	Dim objADGroup, objADMember, strADMember
	Set objADGroup = GetObject(ADPath)	
	For Each objADMember In objADGroup.Members		
		strADMember = trim(objADMember.sAMAccountName)
		strADMember = lcase(strADMember)
		
		'SYSTEM ACCOUNT: NO ISSUES
		objShell.run "cmd /c echo " & strADMember & ">>C:\setup\_GroupMembers.txt",0,false
		
		if strADMember = strCurrentUser then
			MembershipStatus = "True"
		end if
	Next	
End Sub


'Cycle through Nested Groups
Function NestedADMembers (ADPath, strSpace, objList, strCurrentUser)
	on error resume next
	Dim objGroup, objMember, strGroupMember
	Set objGroup = GetObject(ADPath)	
	Set objShell = CreateObject("Wscript.Shell")
	
	For Each objMember In objGroup.Members		
		strGroupMember = trim(objMember.sAMAccountName)
		strGroupMember = lcase(strGroupMember)
		
		'SYSTEM ACCOUNT: NO ISSUES
		objShell.run "cmd /c echo " & strGroupMember & ">>C:\setup\_GroupMembers.txt",0,false
		
		if strGroupMember = strCurrentUser then
			MembershipStatus = "True"			
		end if
		
		If Strcomp(Trim(objMember.Class), "Group", vbTextCompare) = 0 Then
			If objList.Exists(objMember.ADsPath) Then	
				'do nothing
			Else
				objList.Add objMember.ADsPath, 1
				MainADGroup objMember.ADsPath, strSpace & " ", objList, strCurrentUser				
			End If
		End If
	Next
End Function


'Returns Username from explorer.exe
Function GetUserName()
	on error resume next
	strComputer = "." 
	Set objShell = CreateObject("Wscript.Shell")
	Set colProcesses = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2").ExecQuery("Select * from Win32_Process Where Name = 'explorer.exe'")
	For Each objProcess in colProcesses 
		Return = objProcess.GetOwner(strNameOfUser)
		'SYSTEM ACCOUNT: NO ISSUES
		objShell.run "cmd /c echo " & strNameOfUser & ">C:\setup\_ExplorerProcessUser.txt",0,false
		If Return <> 0 Then 
			'no owner
		Else 		
			ReturnedUserName = strNameOfUser
		End If 
	Next 
End function


'Returns if user is logged on
Function UserLoggedInStatus()
	on error resume next
	Dim LoggedInStatus, objWMIService, colItems, strComputer, objshell, objItem
	Set objShell = CreateObject("Wscript.Shell")

	strComputer = "."

	Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
	Set colItems = objWMIService.ExecQuery("Select * from Win32_ComputerSystem",,48)

	For Each objItem in colItems
		LoggedInStatus = trim(objItem.UserName) & ""
	next

	if LoggedInStatus = "" then 
		'SYSTEM ACCOUNT: NO ISSUES
		UserLoggedInStatus = FALSE
		objShell.run "cmd /c echo " & UserLoggedInStatus & ">C:\setup\_LogonStatus.txt",0,false
		WScript.Quit(0)
	end if


	if LoggedInStatus <> "" then 
		'SYSTEM ACCOUNT: NO ISSUES
		UserLoggedInStatus = TRUE
		objShell.run "cmd /c echo " & UserLoggedInStatus & ">C:\setup\_LogonStatus.txt",0,false
	end if

	Set objItem = Nothing
	Set colItems = Nothing
	Set objWMIService = Nothing
End function

Generate Report of Last Boot Time from Computers.txt

email me

This can be used to return Last Boot Time on remote computers. Just add computer names into computers.txt, and run script. A report will be generated, as well as each computer’s info displayed in a pop up. You can disable the pop up if not needed.

VBScript

Option Explicit

Dim objFSO, txtComputers, txtReport, RemoteComputer, objWMI, varBootup
Dim varLastBootTime, varSystemUptime, colOS, objOS, txtCName, txtRName

'computers text file
txtCName = "_Computers.txt"

'report text file
txtRName = "_Boot_Report.txt"


Const ForReading = 1 
Set objFSO = CreateObject("Scripting.FileSystemObject")  
    
Set txtComputers = objFSO.OpenTextFile(txtCName, ForReading) 
Set txtReport = objFSO.CreateTextFile(txtRName) 
Do Until txtComputers.AtEndOfStream  
    RemoteComputer = txtComputers.Readline 
    Set objWMI = GetObject ("winmgmts:\\" & RemoteComputer & "\root\cimv2") 
    Set colOS = objWMI.ExecQuery ("Select * from Win32_OperatingSystem") 
    For Each objOS in colOS 
        varBootup = objOS.LastBootUpTime
        varLastBootTime = DateTranslation(varBootup)     
        'Writes to report
        txtReport.WriteLine "Remote Computer: " & RemoteComputer 
        txtReport.WriteLine "Last Reboot: " & varLastBootTime 
        varSystemUptime = DateDiff("h", varLastBootTime, Now)    
        txtReport.WriteLine "System online since " & varSystemUptime & " hours" & vbCrLf
        'Display pop up
        msgbox RemoteComputer & ": " & varLastBootTime,,"Last Boot"
    Next 

Loop 

'close write to text file
txtComputers.Close 

'done
MsgBox "Done! Report name  " & chr(34) & txtRName & chr(34) & "  was created.",64,"Report"
WScript.Quit(0)

'clear session
set objFSO = Nothing
set txtComputers = Nothing
set txtReport = Nothing
set objWMI = Nothing
set colOS = Nothing
varBootup = ""
RemoteComputer = ""
varLastBootTime = ""
varSystemUptime = "" 
objOS = ""

'translates boot time
Function DateTranslation(varBootup) 
    DateTranslation = CDate(Mid(varBootup, 5, 2) & "/" & Mid(varBootup, 7, 2) & "/" & Left(varBootup, 4) _ 
         & " " & Mid (varBootup, 9, 2) & ":" & Mid(varBootup, 11, 2) & ":" & Mid(varBootup, 13, 2)) 
End Function

 

PowerShell

Clear-Host
 
$txtComputers = "C:\Report\_Computers.txt"
$txtReport = "C:\Report\_Boot_Report.txt"
 
 
#Returns computer names
$computers = Get-Content $txtComputers
 
#Cycles through
foreach ($RemoteComputer in $computers)
    {     
    #Is computer online
    If (Test-Connection -ComputerName $RemoteComputer -ErrorAction SilentlyContinue -Quiet -Count 2) {
        #Returns boot time
        Get-WmiObject Win32_OperatingSystem -ComputerName $RemoteComputer).LastBootUpTime  
        #Convert the last boot time to a reader friendly date time format 
        $time = [System.Management.ManagementDateTimeConverter]::ToDateTime($objWMI)
        #output to console
        $RemoteComputer + "`n" + $time + "`n"
        #output to file
        "$RemoteComputer" + " " + "$time" | Out-file -FilePath $txtReport -Append
    }
    }

	Start-Sleep 5

# Clear session
$txtComputers = ""
$txtReport = ""
$computers = ""
$computer = ""
$objWMI = ""

 

Batch

cls
@Echo off
Title Last Reboot
color 0a
Setlocal EnableDelayedExpansion

:: Report name
Set report=Boot_Report.txt

:: Enter name of computer text file
Set PCList=Computers.txt

cls
Echo Searching for boot times...

:: PROGRAM ROUTINE
:CYCLE
for /f "tokens=* delims= "%%a in (%PCList%) do (
ping %%a | find "Reply" > nul
if errorlevel 1 (
echo %%a>> "_offline.txt"
echo.
%windir%\system32\ping.exe -n 2 127.0.0.1>nul
) else (
echo.
for /f "tokens=1,2,3,4" %%g in ('dir /a:h \\%%a\c$\pagefile.sys ^|find "pagefile.sys"') do (
set bTime=%%a %%g %%h %%i
echo !bTime!
echo !bTime!>>%report%
echo.>>%report%
echo.
)
)
)
pause
endlocal
exit /b 0

:: NOTES
:: you could also script network statistics workstation
:: command: net statistics workstation
:: output: Statistics since 2/21/2017 7:49:38 PM
::
:: or use WMIC for /f %%A in ('WMIC OS GET LASTBOOTUPTIME ^| find "."') DO set DTS=%%A

Verify Credentials of AD User

email me

These scripts would be a great addition to any form, where you’re passing AD credentials. These allow you to verify the credentials are correct before continuing.

VBScript

'These values will come from a form
strDomain = "YourDomain"
strUsername = "YourUserName"
strUserPassword = InputBox("","Password")

'Returns True or False
msgbox CheckAccess(strDomain,strUsername,strUserPassword)

Function CheckAccess(strDomain, strUsername, strUserPassword)

	Dim objAD, objADUser, strADPath

	Const ADS_SECURE_AUTHENTICATION = &h0001
	Const ADS_CHASE_REFERRALS_ALWAYS = &H60


	strADPath = "LDAP://" & strDomain & "/OU=Users,DC=" & strDomain

	On Error Resume Next
	set objAD = GetObject("LDAP:")
	set objADUser = objAD.OpenDSObject (strADPath, strUsername, strUserPassword, ADS_SECURE_AUTHENTICATION OR ADS_CHASE_REFERRALS_ALWAYS)
	if Err.Number <> 0 then    
		CheckAccess = False
	else
		CheckAccess = True
	end if
	
	Err.Clear
	On Error Goto 0

	set objAD = Nothing
	set objADUser = Nothing
	set strADPath = Nothing
	strUserPassword = ""
	strUsername = ""
	strDomain = ""        

End Function

PowerShell

$strUserName = "YourUsername"
$strUserPassword = "UserPassword"

CheckAccess $strUserName $strUserPassword 


Function CheckAccess {
   
    if (!($strUserName) -or !($strUserPassword)) {
        Write-Warning 'Please verify your username and password!'
    } else {
        Add-Type -AssemblyName System.DirectoryServices.AccountManagement
        $objAD = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('domain')
        $objAD.ValidateCredentials($strUserName, $strUserPassword)
    }
}

LANDesk Creating Numerous C:\Users Profiles

email me

Here is a weird one for you. We’re upgrading to the latest version of LANDesk, and for some reason, numerous LD profile folders are being generated in C:\users (I’m thinking is has to do with the ALS service/LANDesk account). The folders either start with ALS_SVC or TEMP.%computername%.

Two things you’ll need to do

#1 Remove LD profiles from C:\users

#2 Remove LD profiles from the registry path:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList

* see updates below

* see Steps to Disable Agentless Scanning, to completely disable it

Screenshot

 

Solution

Initially, I wrote scripts in PowerShell and VB that looked at folder size and removed 0 byte sizes, the problem was…while they worked in other folders just fine, they could not read all of the C:\users folders—-no access (certain vb and ps objects just could not access the folders). So, I went low tech with this one. And, guess what? It worked. I wrote this batch and compiled it into an EXE. Once launched, it found the 0 byte size folders and removed them.

@echo off
title LANDesk Cleanup
setlocal enabledelayedexpansion

set DName=
set FSize=0

C:
cd\users

:: return objects in directory
for /f “tokens=*” %%A in (‘dir c:\users /a /b’) do (
set DName=c:\users\%%A
echo Checking !DName!…

:: scan through all folders
:: set total size of folder to cumulative file sizes
for /f “tokens=3-4” %%B in (‘dir “!DName!” /s ^| find /i “file(s)”‘) do (
set FSize=%%B
)
:: if no files were returned, this directory has a size 0
:: if size 0, then remove folder and all subfolders
:: log the action
if !FSize! EQU 0 rd /q /s “!DName!” && echo %Date% !DName! !FSize!>>C:\users\removal_log.txt
echo.
)
endlocal

 

* Update 02/20/2017

So, come to find out, NOT all the LANDesk profile folders were 0 byte. So, I ended up writing a new script to just delete ALS_SVC* and TEMP.* folders

@echo off
title LANDesk Cleanup
setlocal enabledelayedexpansion

c:
cd\users
cls

set DName=
set FSize=
set CName=%computername%
set str1=ALS_SVC
set str2=TEMP.
set found=false

:: GET FOLDER
For /d %%A in (*) Do (
set found=false
set DName=c:\users\%%A

:: UPDATE TIMESTAMP
for /F “TOKENS=*” %%T in (‘Time /t’) do set NowT=%%T

:: FIND FOLDER SIZE
for /f “tokens=3-4” %%S in (‘dir “!DName!” /s ^| find /i “file(s)”‘) do set FSize=%%S

:: DELETE ALS_SVC*
echo %%A | find /i “%str1%”>nul && (

echo DELETE: “!DName!”
rd /q /s “!DName!” && echo DELETED %Date% !NowT! “!DName!” !FSize!>>c:\users\removal_log.txt
set found=true
)

:: DELETE TEMP.*
echo %%A | find /i “%str2%”>nul && (

echo DELETE: “!DName!”
rd /q /s “!DName!” && echo DELETED %Date% !NowT! “!DName!” !FSize!>>c:\users\removal_log.txt
set found=true
)

IF NOT ALS OR TEMP, LOG
if !found! EQU false echo NOT_DELETED %Date% !NowT! “!DName!” !FSize!>>c:\users\removal_log.txt

)

set DName=
set FSize
set CName=
set str1=
set str2=

endlocal
exit /b 0

 

* Update 02/21/2017

Yet another update. It was requested that the amount of folders being deleted, a timestamp of the last run date of the deployment package, and total times the package has run, all be tracked. So, I modified the script above to add counters for the package and for the folders being deleted. These variables are added to the registry, which are then scanned via our desktop management software.

The reg keys that are added

In LANDesk, these are published to clients

The final result, an updated inventory record with tracking info

The modified script – this was compiled

@echo off
title LANDesk Cleanup
setlocal enabledelayedexpansion

c:
cd\users
cls

:: folders to look for
set str1=ALS_SVC
set str2=TEMP.

:
: directory name
set DName=

:: directory size
set FSize=

:: bool: if folder matches logic
set found=false

set fCount=
set rCount1=0
set rCount2=0

:: reg key
set AppName=ProfileCleanup
set RegKey1=”HKLM\SOFTWARE\LANDesk\Tracking\%AppName%” /v “Counter”
set RegKey2=”HKLM\SOFTWARE\LANDesk\Tracking\%AppName%” /v “Last_Run”
set RegKey3=”HKLM\SOFTWARE\LANDesk\Tracking\%AppName%” /v “Folders_Deleted”

:: REMOVE ALL ALS_SVC* FOLDERS
For /d %%A in (*) Do (
set found=false
set DName=c:\users\%%A
for /F “TOKENS=*” %%T in (‘Time /t’) do set NowT=%%T

for /f “tokens=3-4” %%S in (‘dir “!DName!” /s ^| find /i “file(s)”‘) do set FSize=%%S

echo %%A | find /i “%str1%”>nul && (
echo DELETE: “!DName!”
rd /q /s “!DName!” && echo DELETED %Date% !NowT! “!DName!” !FSize!>>c:\users\removal_log.txt
set found=true
set /a fCount+=1
)

echo %%A | find /i “%str2%”>nul && (
echo DELETE: “!DName!”
rd /q /s “!DName!” && echo DELETED %Date% !NowT! “!DName!” !FSize!>>c:\users\removal_log.txt
set found=true
set /a fCount+=1
)

if !found! EQU false echo NOT_DELETED %Date% !NowT! “!DName!” !FSize!>>c:\users\removal_log.txt
)
:: if counter key doesn’t exist, create it
%windir%\sysnative\REG QUERY %RegKey1%
if %ERRORLEVEL% EQU 0 goto :Main
if %ERRORLEVEL% EQU 1 goto :Add
ping -n 4 127.0.0.1>nul

:Add
cls
echo Key not found. Adding Key…
%windir%\sysnative\REG ADD %RegKey1% /d 0 /reg:64 /f
%windir%\sysnative\REG ADD %RegKey3% /d 0 /reg:64 /f
ping -n 4 127.0.0.1>nul

:: retrieve current count and add 1
:: this is how many times the script has run
:Main
cls
echo Retrieving Reg Count…
FOR /f “tokens=1,2,3” %%a In (‘%windir%\sysnative\REG QUERY %RegKey1% /reg:64’) do (
set rCount1=%%c
set /a rCount1+=1 && %windir%\sysnative\REG ADD %RegKey1% /d !rCount1! /reg:64 /f
ping -n 4 127.0.0.1>nul
)

:: this is how many folders have been deleted
cls
echo Retrieving Folder Count…
FOR /f “tokens=1,2,3” %%a In (‘%windir%\sysnative\REG QUERY %RegKey3% /reg:64’) do (
set rCount2=%%c
set /a “rCount2=!rCount2!+!fCount!” && %windir%\sysnative\REG ADD %RegKey3% /d !rCount2! /reg:64 /f
)
:: add timestamp to registry
%windir%\sysnative\REG ADD %RegKey2% /d “%date% %time%” /reg:64 /f

:: clear session
set RegKey1=
set RegKey2=
set RegKey3=
set fCount=
set rCount1=
set rCount2=
set AppName=

set DName=
set FSize
set str1=
set str2=
set NowT=

:: clean up registry keys
cls
echo Removing LANDesk ALS profiles…
for /f “tokens=1,2 delims==” %%s IN (‘WMIC path win32_useraccount where name^=’administrator’ get sid /value ^| find /i “SID”‘) do set SID=%%t
set RootSID=%SID:~0,-5%
set RegPath=HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList

set /a counter=3
:loop1
if %counter% lss 10 (
reg delete “%regPath%\%RootSID%-100%counter%” /f /reg:64
set /a counter+=1
goto :loop1
)

set /a counter=10
:loop2
if %counter% lss 100 (
reg delete “%regPath%\%RootSID%-10%counter%” /f /reg:64
set /a counter+=1
goto :loop2
)

set /a counter=100
:loop3
if %counter% lss 999 (
reg delete “%regPath%\%RootSID%-1%counter%” /f /reg:64
set /a counter+=1
goto :loop3
)

endlocal
exit /b 0

* Note how %windir%\sysnative was used. You’ll use %windir%\system32 if using a 64bit compiler or running script not compiled

 

Notes

As for what’s causing the issue, beyond the ALS account, LANDesk is still trying to figure it out.

Some C# code I was working on to do the task above.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

class LANDeskProfileCleanup
{
    public static void Main(string[] args)
    {
        try
        {
            string dirPath = @"c:\users\";

            List<string> dirs List<string>(Directory.EnumerateDirectories(dirPath));

            foreach (var dir in dirs)
            {
                long length = Directory.GetFiles(dir, "*", SearchOption.AllDirectories).Sum(t =&amp;amp;gt; (new FileInfo(t).Length));
                Console.WriteLine("{0}", dir.Substring(dir.LastIndexOf("\\") + 1));
            }
            //Console.WriteLine("{0} Folders found.", dirs.Count);
        }
        catch (UnauthorizedAccessException UAEx)
        {
            Console.WriteLine(UAEx.Message);
        }
        catch (PathTooLongException PathEx)
        {
            Console.WriteLine(PathEx.Message);
        }
        Console.ReadLine();
    }


    public static long DirSize(DirectoryInfo dir)
    {
        return dir.GetFiles().Sum(fi => fi.Length) +
               dir.GetDirectories().Sum(di => DirSize(di));
    }
}