How to make sure the first character of a string is one slash ‘/’ with C#, use the TrimStart function

If you want to make sure the first character of a string is one slash, use the function:

public string PrependPath(string path, string firstCharacter)
{
    return firstCharacter + path.TrimStart(firstCharacter.ToCharArray());
}

 

To test the function use:

string firstCharacter = "/";
Console.WriteLine(PrependPath("TestPath", firstCharacter));
Console.WriteLine(PrependPath("/TestPath", firstCharacter));
Console.WriteLine(PrependPath("//TestPath", firstCharacter));
Console.WriteLine(PrependPath("///TestPath", firstCharacter));    

 

Result

/TestPath

/TestPath

/TestPath

/TestPath

How to determine if a local user is member of a local computer group before adding, with PowerShell and C#

To dermine if a local user is member of a local group, before adding the user to the local group in PowerShell I use a C# class. The C# class could be converted to pure PowerShell code, but I prefer the C# class with it’s using statements:

 

[PowerShell script]

"Set execution policy to [Unrestricted]"
Set-ExecutionPolicy Unrestricted

$ComputerName = "."
$ServiceAccountWebUserName = "\saWeb",

"Check if user is member of IIS_IUSRS"
$localGroup = New-Object Ada.Cdf.Deployment.LocalGroup
$localGroup.ComputerName = $ComputerName
$localGroup.Name = "IIS_IUSRS"
if(!$localGroup.ContainsUser($ServiceAccountWebUserName))
{
    "Add user to the IIS_IUSRS group"
    $localGroup.AddUser($ServiceAccountWebUserName)
}

[C# Class]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;
using System.Collections;

namespace Ada.Cdf.Deployment
{
    public class LocalGroup
    {
        /// <summary>
        /// Name of the computer use "." for executing computer
        /// </summary>
        string _computerName = Environment.MachineName;
        public string ComputerName 
        {
            get
            {
                if (!string.IsNullOrEmpty(_computerName) && _computerName.Equals("."))
                {
                    _computerName = Environment.MachineName;
                }
                return _computerName;
            }
            set
            {
                _computerName = value;
            }
        }
        public string Name { get; set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="userName"></param>
        /// <returns></returns>
        public bool ContainsUser(string userName)
        {

            // Validate properties
            if (string.IsNullOrEmpty(this.ComputerName)) { throw new ArgumentNullException("this.ComputerName"); }
            if (string.IsNullOrEmpty(this.Name)) { throw new ArgumentNullException("this.Name"); }

            bool result = false;
            using (DirectoryEntry groupEntry = new DirectoryEntry(string.Format("WinNT://{0}/{1},group", this.ComputerName, this.Name)))
            {
                // Get group members
                IEnumerable groupMembers = (IEnumerable)groupEntry.Invoke("Members");

                // Loop members
                foreach (object member in groupMembers)
                {
                    using (DirectoryEntry memberEntry = new DirectoryEntry(member))
                    {
                        if (memberEntry.Name.ToLower().Equals(userName.ToLower()))
                        {
                            result = true;
                        }
                    }
                }
            }

            return result;
        }
        /// <summary>
        /// Add a local user to a local group
        /// </summary>
        /// <param name="userName"></param>
        public void AddUser(string userName)
        {

            // Validate properties
            if (string.IsNullOrEmpty(this.ComputerName)) { throw new ArgumentNullException("this.ComputerName"); }
            if (string.IsNullOrEmpty(this.Name)) { throw new ArgumentNullException("this.Name"); }

            using (DirectoryEntry groupEntry = new DirectoryEntry(string.Format("WinNT://{0}/{1},group", this.ComputerName, this.Name)))
            {
              // Add user to group
              groupEntry.Invoke("Add", new object[] { string.Format("WinNT://{0}/{1}", this.ComputerName, userName) });
            }

        }
    }
}

# How to list members of the local computer group [IIS_IUSRS] with PowerShell

If you want to list all members of the local computer group IIS_IUSRS with PowerShell, use this script:

 

[Script]

"Set execution policy to [Unrestricted]"
Set-ExecutionPolicy Unrestricted

$iisIUSRS = [ADSI]"WinNT://L014/IIS_IUSRS,group"

"Loop members"
foreach ($member in $iisIUSRS.Invoke("Members", $null))
{
    # Cast object to type DirectoryEntry
    $de = New-Object System.DirectoryServices.DirectoryEntry($member)
    # Determine username
    $userName = $de.Name
    # Show the username
    "Member is $userName"
}

 

[Result]

PS C:\Temp> C:\Temp\Test.ps1
Set execution policy to [Unrestricted]
Loop members
Member is SQLServerReportServerUser$L001$MSRS10.MSSQLSERVER
Member is Administrator
Member is saAsaWeb

How to call a PowerShell script with named parameters from a PowerShell (*.ps1) script

If you want to call a PowerShell script (Test2.ps1) with named parameters from a PowerShell script (Test1.ps1), you can’t use the & operator, like & “C:\Temp\Test2.ps1” –Computer L001 –User User1, because the & command is an alias for the Get-Command cmdlet. The Get-Command cmdlet executes precisely one command, see : http://powershell.com/cs/blogs/ebook/archive/2009/03/30/chapter-12-command-discovery-and-scriptblocks.aspx#the-call-operator-quotampquot, but you can use script blocks to execute more then one command or the Invoke-Expression cmdlet. I used the Invoke-Expression cmdlet to call a PowerShell script with named parameters from an other PowerShell script.

[Test1.ps1]

$command = “C:\Temp\Test2.ps1” –Computer L014 –User User1

Invoke-Expression $command

 

 

[Test2.ps1]

param
(
    [string]$Computer = "MyDefaultComputer",
    [string]$User = "MyDefaultUser"

)

$Computer

$User

 

 

[Result]

L014

User1

To combine, format, join or concat strings in PowerShell

You can concat strings in PowerShell by using the + operator, but like in C#, I think it is better to use a formatting approach by using the –f string operator:

 

[-f operator]

PS C:\Users\rLisdonk> "My computer {0} has {1} MB of memory." -f "L001", "4096"
My computer L001 has 4096 MB of memory.

 

[Double quoted string with variables]

You could also use variables in the double quoted string, like:

$myComputer = “L001”

$memory = “4096”

PS C:\Users\rLisdonk> "My computer $myComputer has $memory MB of memory."

 

[+= operator]

You could also use += operator to concat strings

PS C:\Users\rLisdonk> $test = "This is a "
PS C:\Users\rLisdonk> $test += "test string"
PS C:\Users\rLisdonk> $test
This is a test string

 

[Escape characters]

see http://www.techotopia.com/index.php/Windows_PowerShell_1.0_String_Quoting_and_Escape_Sequences

How to grant “Log on as a service” rights to an user account, using PowerShell

If you want to grant “Log on as a service” rights to a user account, using PowerShell you can use the secedit.exe tool, using a *.inf security template file.

 

[Script]

secedit /import /db secedit.sdb /cfg "C:\Temp\MySecurityTemplate.inf" /silent >nul

gpupdate /force

 

[MySecurityTemplate.inf]

[Unicode]
Unicode=yes
[Version]
signature="$CHICAGO$"
Revision=1
[Registry Values]
[Profile Description]
Description=Set log on as a service etc
[Privilege Rights]
SeServiceLogonRight = *S-1-5-21-3051157669-1303869509-1073059025-1019

 

You can generate the “C:\Temp\MySecurityTemplate.inf” by following the steps on http://msdn.microsoft.com/en-us/library/ms933182(WinEmbedded.5).aspx

 

[But in some cases, this was not what I wanted]

An other solution, is to P/Invoke the LSA functions in the advapi32.dl with C#.

Willy created a LSA wrapper for C#: http://www.developmentnow.com/g/36_2005_3_0_0_223925/LSA-functions.htm

I used this wrapper class to grant “Log on as a service” rights to an user account, using PowerShell

After building the assembly containing the LSA wrapper class, you can grant access with this PowerShell script:

 

[Test.ps1]

# [General]
"Set execution policy to [Unrestricted]"
Set-ExecutionPolicy Unrestricted

"Load assemblies"
$assemblyLsa = "C:\Temp\MyLsaWrapper.dll"
[System.Reflection.Assembly]::LoadFrom($assemblyLsa)

“Grant Log on as a service rights to the account MyComputer\User1”

[MyLsaWrapper.LsaWrapperCaller]::("MyComputer\User1", "SeServiceLogonRight")

 

 

[LsaWrapper in C#]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace MyLsaWrapper
{
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Management;
    using System.Runtime.CompilerServices;
    using System.ComponentModel;

    using LSA_HANDLE = IntPtr;

    [StructLayout(LayoutKind.Sequential)]
    struct LSA_OBJECT_ATTRIBUTES
    {
        internal int Length;
        internal IntPtr RootDirectory;
        internal IntPtr ObjectName;
        internal int Attributes;
        internal IntPtr SecurityDescriptor;
        internal IntPtr SecurityQualityOfService;
    }
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    struct LSA_UNICODE_STRING
    {
        internal ushort Length;
        internal ushort MaximumLength;
        [MarshalAs(UnmanagedType.LPWStr)]
        internal string Buffer;
    }
    sealed class Win32Sec
    {
        [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true),
        SuppressUnmanagedCodeSecurityAttribute]
        internal static extern uint LsaOpenPolicy(
        LSA_UNICODE_STRING[] SystemName,
        ref LSA_OBJECT_ATTRIBUTES ObjectAttributes,
        int AccessMask,
        out IntPtr PolicyHandle
        );

        [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true),
        SuppressUnmanagedCodeSecurityAttribute]
        internal static extern uint LsaAddAccountRights(
        LSA_HANDLE PolicyHandle,
        IntPtr pSID,
        LSA_UNICODE_STRING[] UserRights,
        int CountOfRights
        );

        [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true),
        SuppressUnmanagedCodeSecurityAttribute]
        internal static extern int LsaLookupNames2(
        LSA_HANDLE PolicyHandle,
        uint Flags,
        uint Count,
        LSA_UNICODE_STRING[] Names,
        ref IntPtr ReferencedDomains,
        ref IntPtr Sids
        );

        [DllImport("advapi32")]
        internal static extern int LsaNtStatusToWinError(int NTSTATUS);

        [DllImport("advapi32")]
        internal static extern int LsaClose(IntPtr PolicyHandle);

        [DllImport("advapi32")]
        internal static extern int LsaFreeMemory(IntPtr Buffer);

    }
    /// <summary>
    /// This class is used to grant "Log on as a service", "Log on as a batchjob", "Log on localy" etc.
    /// to a user.
    /// </summary>
    public sealed class LsaWrapper : IDisposable
    {
        [StructLayout(LayoutKind.Sequential)]
        struct LSA_TRUST_INFORMATION
        {
            internal LSA_UNICODE_STRING Name;
            internal IntPtr Sid;
        }
        [StructLayout(LayoutKind.Sequential)]
        struct LSA_TRANSLATED_SID2
        {
            internal SidNameUse Use;
            internal IntPtr Sid;
            internal int DomainIndex;
            uint Flags;
        }

        [StructLayout(LayoutKind.Sequential)]
        struct LSA_REFERENCED_DOMAIN_LIST
        {
            internal uint Entries;
            internal LSA_TRUST_INFORMATION Domains;
        }

        enum SidNameUse : int
        {
            User = 1,
            Group = 2,
            Domain = 3,
            Alias = 4,
            KnownGroup = 5,
            DeletedAccount = 6,
            Invalid = 7,
            Unknown = 8,
            Computer = 9
        }

        enum Access : int
        {
            POLICY_READ = 0x20006,
            POLICY_ALL_ACCESS = 0x00F0FFF,
            POLICY_EXECUTE = 0X20801,
            POLICY_WRITE = 0X207F8
        }
        const uint STATUS_ACCESS_DENIED = 0xc0000022;
        const uint STATUS_INSUFFICIENT_RESOURCES = 0xc000009a;
        const uint STATUS_NO_MEMORY = 0xc0000017;

        IntPtr lsaHandle;

        public LsaWrapper()
            : this(null)
        { }
        // // local system if systemName is null
        public LsaWrapper(string systemName)
        {
            LSA_OBJECT_ATTRIBUTES lsaAttr;
            lsaAttr.RootDirectory = IntPtr.Zero;
            lsaAttr.ObjectName = IntPtr.Zero;
            lsaAttr.Attributes = 0;
            lsaAttr.SecurityDescriptor = IntPtr.Zero;
            lsaAttr.SecurityQualityOfService = IntPtr.Zero;
            lsaAttr.Length = Marshal.SizeOf(typeof(LSA_OBJECT_ATTRIBUTES));
            lsaHandle = IntPtr.Zero;
            LSA_UNICODE_STRING[] system = null;
            if (systemName != null)
            {
                system = new LSA_UNICODE_STRING[1];
                system[0] = InitLsaString(systemName);
            }

            uint ret = Win32Sec.LsaOpenPolicy(system, ref lsaAttr,
            (int)Access.POLICY_ALL_ACCESS, out lsaHandle);
            if (ret == 0)
                return;
            if (ret == STATUS_ACCESS_DENIED)
            {
                throw new UnauthorizedAccessException();
            }
            if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY))
            {
                throw new OutOfMemoryException();
            }
            throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret));
        }

        public void AddPrivileges(string account, string privilege)
        {
            IntPtr pSid = GetSIDInformation(account);
            LSA_UNICODE_STRING[] privileges = new LSA_UNICODE_STRING[1];
            privileges[0] = InitLsaString(privilege);
            uint ret = Win32Sec.LsaAddAccountRights(lsaHandle, pSid, privileges, 1);
            if (ret == 0)
                return;
            if (ret == STATUS_ACCESS_DENIED)
            {
                throw new UnauthorizedAccessException();
            }
            if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY))
            {
                throw new OutOfMemoryException();
            }
            throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret));
        }


        public void Dispose()
        {
            if (lsaHandle != IntPtr.Zero)
            {
                Win32Sec.LsaClose(lsaHandle);
                lsaHandle = IntPtr.Zero;
            }
            GC.SuppressFinalize(this);
        }
        ~LsaWrapper()
        {
            Dispose();
        }
        // helper functions

        IntPtr GetSIDInformation(string account)
        {
            LSA_UNICODE_STRING[] names = new LSA_UNICODE_STRING[1];
            LSA_TRANSLATED_SID2 lts;
            IntPtr tsids = IntPtr.Zero;
            IntPtr tdom = IntPtr.Zero;
            names[0] = InitLsaString(account);
            lts.Sid = IntPtr.Zero;
            Console.WriteLine("String account: {0}", names[0].Length);
            int ret = Win32Sec.LsaLookupNames2(lsaHandle, 0, 1, names, ref tdom, ref tsids);
            if (ret != 0)
                throw new Win32Exception(Win32Sec.LsaNtStatusToWinError(ret));
            lts = (LSA_TRANSLATED_SID2)Marshal.PtrToStructure(tsids,
            typeof(LSA_TRANSLATED_SID2));
            Win32Sec.LsaFreeMemory(tsids);
            Win32Sec.LsaFreeMemory(tdom);
            return lts.Sid;
        }

        static LSA_UNICODE_STRING InitLsaString(string s)
        {
            // Unicode strings max. 32KB
            if (s.Length > 0x7ffe)
                throw new ArgumentException("String too long");
            LSA_UNICODE_STRING lus = new LSA_UNICODE_STRING();
            lus.Buffer = s;
            lus.Length = (ushort)(s.Length * sizeof(char));
            lus.MaximumLength = (ushort)(lus.Length + sizeof(char));
            return lus;
        }
    }
    public class LsaWrapperCaller
    {
        public static void AddPrivileges(string account, string privilege)
        {
            using (LsaWrapper lsaWrapper = new LsaWrapper())
            {
                lsaWrapper.AddPrivileges(account, privilege);
            }
        }
    }
}

Create local user account with [User must change password at next logon] and [Password never expires] set in PowerShell

If you want to create service accounts with PowerShell and the result must be like:

image

 

Use this PowerShell script:

 

# To run this script use: & "C:\Users\rLisdonk\Desktop\ToServer\Test.ps1"

$computerName = "MyComputerName”
$serviceAccountWebName = "saAsaWeb"
$serviceAccountWebPassword = "MyPassword123"

 

"Get computer info"
$computer = [ADSI]("WinNT://" + $computerName + ",computer")

 

"Determine if user [saAsaWeb] exists"
$serviceAccount = [ADSI]("WinNT://" + $computerName + "/$serviceAccountWebName" + ",user")
if(!$serviceAccount.Name)
{
    "Create user [saAsaWeb]"
    $user = $computer.Create("user", $serviceAccountWebName)

    "Set password"
    $user.SetPassword($serviceAccountWebPassword)
    $user.SetInfo()

    "Disable [User must change password at next logon]"
    $user.PasswordExpired = 0
    $user.SetInfo()

    "Enable [Password never expires]"
    $wmiuser = Get-WmiObject -class "Win32_UserAccount" -filter "name=’$serviceAccountWebName’"
    $wmiuser.PasswordExpires = $false
    $wmiuser.Put()
}

How to determine the program files folder with PowerShell, by using enums and nested types

If you want to use the systems “%PROGRAMFILES%” folder in PowerShell, use:

 

$rootInstallationFolder = [System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::ProgramFiles)
$rootInstallationFolder

Result

C:\Program Files (x86)

Note

Because SpecialFolders is a nested type in System.Environment, you must use the “+” sign and not a “.”, else you get the error:

Unable to find type [System.Environment.SpecialFolder]: make sure that the assembly containing this type is loaded.