BAMCIS.UserAccounts.psm1

Function Get-UserProfiles {
    <#
        .SYNOPSIS
            Gets all of the user profiles on the system.
 
        .DESCRIPTION
            The Get-UserProfiles cmdlet uses the Win32_UserProfile WMI class to get user profile paths. It ignores special profiles like the local system.
 
        .EXAMPLE
            Get-UserProfiles
 
            Gets all of the user profiles on the system as an array of path strings.
 
        .INPUTS
            None
 
        .OUTPUTS
            System.String[]
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 4/25/2016
    #>

    [CmdletBinding()]
    [OutputType([System.String[]])]
    Param(
    )

    Begin {}

    Process {
        Write-Output -InputObject (Get-CimInstance -Class Win32_UserProfile | Where-Object {$_.Special -eq $false} | Select-Object -ExpandProperty LocalPath)
    }

    End {        
    }
}

Function Remove-UserProfile {
    <#
        .SYNOPSIS
            Removes the specified user profile.
 
        .DESCRIPTION
            Removes the specified user profile via WMI.
 
        .PARAMETER Name
            The name of the user profile to delete.
     
        .EXAMPLE
            Remove-UserProfile -Name john.smith -Force
 
        .INPUTS
            System.String
 
        .OUTPUTS
            None
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 8/22/2018
    #>

    [CmdletBinding(SupportsShouldProcess = $true)]
    Param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [System.String]$Name,

        [Parameter()]
        [Switch]$Force
    )

    Begin{}

    Process {
        #[PSTypeName("Microsoft.Management.Infrastructure.CimInstance#root/cimv2/Win32_UserProfile")]
        [Microsoft.Management.Infrastructure.CimInstance[]]$Profiles = Get-CimInstance -ClassName Win32_UserProfile | Where-Object {$_.LocalPath -like "*$Name"}
        
        if ($Profiles -eq $null -or $Profiles.Length -eq 0)
        {
            throw New-Object -TypeName System.ArgumentException("No profiles matched the input $Name")
        }
        elseif ($Profiles.Length -gt 1) 
        {
            throw New-Object -TypeName System.ArgumentException("More than 1 profile matches the input $Name")
        }
        else
        {
            $ConfirmMessage = "You are about to delete profile $Name."
            $WhatIfDescription = "Deleted profile $Name"
            $ConfirmCaption = "Delete Profile"

            if ($Force -or $PSCmdlet.ShouldProcess($WhatIfDescription, $ConfirmMessage, $ConfirmCaption))
            {
                Write-Verbose -Message "Removing profile $Name : $($Profiles[0].LocalPath)"
                $Profiles[0] | Remove-CimInstance
            }
        }       
    }

    End {

    }
}

Function Get-AccountSid {
    <#
        .SYNOPSIS
            Gets the SID of a given username.
 
        .DESCRIPTION
            The cmdlet gets the SID of a username, which could a service account, local account, or domain account. The cmdlet returns null if the username could not be translated.
 
        .PARAMETER UserName
            The name of the user or service account to get the SID of.
 
        .PARAMETER ComputerName
            If the account is local to another machine, such as an NT SERVICE account or a true local account, specify the computer name the account is on.
 
        .PARAMETER Credential
            The credentials used to connect to the remote machine.
             
        .INPUTS
            None
 
        .OUTPUTS
            System.Security.Principal.SecurityIdentifier
 
        .EXAMPLE
            Get-AccountSid -UserName "Administrator"
 
            Gets the SID for the Administrator account.
 
        .EXAMPLE
            Get-AccountSid -UserName "NT AUTHORITY\Authenticated Users"
 
            Gets the SID for the Authenticated Users group.
 
        .EXAMPLE
            Get-AccountSid -UserName "NT AUTHORITY\System"
 
            Gets the SID for the SYSTEM account. The user name could also just be "System".
 
        .EXAMPLE
            Get-AccountSid -UserName "NT SERVICE\MSSQLSERVER" -ComputerName SqlServer
 
            Gets the SID for the virtual MSSQLSERVER service principal.
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/23/2017
    #>

    [CmdletBinding()]
    [OutputType([System.Security.Principal.SecurityIdentifier])]
    Param(
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]$UserName,

        [Parameter(Position = 1)]
        [ValidateNotNull()]
        [System.String]$ComputerName = [System.String]::Empty,

        [Parameter()] 
        [ValidateNotNull()]
        [System.Management.Automation.Credential()]
        [System.Management.Automation.PSCredential]$Credential = [System.Management.Automation.PSCredential]::Empty  
    )

    Begin {    
    }

    Process{
        Write-Log -Message "Getting SID for $UserName." -Level VERBOSE

        [System.String]$Domain = [System.String]::Empty
        [System.String]$Name = [System.String]::Empty

        if ($UserName.IndexOf("\") -ne -1) 
        {
            [System.String[]]$Parts = $UserName.Split("\")
            $Domain = $Parts[0]

            #If the UserName is something like .\john.doe, change the computer name
            if ($Domain -iin $script:LocalNames)
            {
                #Use an empty string for the domain name on the local computer
                $Domain = [System.String]::Empty
            }

            $Name = $Parts[1]            
        }
        elseif ($UserName.IndexOf("@") -ne -1) 
        {
            [System.String[]]$Parts = $UserName.Split("@")
            $Domain = $Parts[1]
            $Name = $Parts[0]
        }
        else 
        {
            try 
            {
                $Domain = Get-ADDomain -Current LocalComputer -ErrorAction Stop | Select-Object -ExpandProperty Name
            }
            catch [Exception] 
            {
                #Use an empty string for the domain name on the local computer
                $Domain = [System.String]::Empty
            }

            $Name = $UserName
        }

        if ([System.String]::IsNullOrEmpty($ComputerName) -or $ComputerName -iin $script:LocalNames) 
        {
            try 
            {
                $User = New-Object -TypeName System.Security.Principal.NTAccount($Domain, $Name)
                $UserSid = $User.Translate([System.Security.Principal.SecurityIdentifier])
            }
            catch [Exception]
            {
                Write-Log -Message "Exception translating $Domain\$Name." -ErrorRecord $_ -Level VERBOSEERROR
                $UserSid = $null
            }
        }
        else 
        {
            $Session = New-PSSession -ComputerName $ComputerName -Credential $Credential
                
            $UserSid = Invoke-Command -Session $Session -ScriptBlock { 
                try
                {
                    $User = New-Object -TypeName System.Security.Principal.NTAccount($args[0], $args[1])
                    Write-Output -InputObject $User.Translate([System.Security.Principal.SecurityIdentifier])
                }
                catch [Exception]
                {
                    Write-Log -Message "Exception translating $($args[0])\$($args[1])" -ErrorRecord $_ -Level VERBOSEERROR
                    Write-Output -InputObject $null

                }
            } -ArgumentList @($Domain, $Name)

            Remove-PSSession -Session $Session
        }
        
        Write-Output -InputObject $UserSid
    }

    End {        
    }
}

Function Get-AccountTranslatedNTName {
    <#
        .SYNOPSIS
            Gets the full NT Account name of a given username.
 
        .DESCRIPTION
            The cmdlet gets the SID of a username, which could a service account, local account, or domain account and then translates that to an NTAccount. The cmdlet returns null if the username
            could not be translated.
 
        .PARAMETER UserName
            The name of the user or service account to get the SID of.
 
        .PARAMETER ComputerName
            If the account is local to another machine, such as an NT SERVICE account or a true local account, specify the computer name the account is on.
 
        .PARAMETER Credential
            The credentials used to connect to the remote machine.
             
        .INPUTS
            None
 
        .OUTPUTS
            System.String
 
        .EXAMPLE
            Get-AccountTranslatedNTName -UserName "Administrator"
 
            Gets the NT account name for the Administrator account, which is BUILTIN\Administrator.
 
        .EXAMPLE
            Get-AccountTranslatedNTName -UserName "Authenticated Users"
 
            Gets the NT account name for the Authenticated Users group, which is NT AUTHORITY\Authenticated Users.
 
        .EXAMPLE
            Get-AccountTranslatedNTName -UserName "System"
 
            Gets the NT account name for the SYSTEM account, which is NT AUTHORITY\System
 
        .EXAMPLE
            Get-AccountTranslatedNTName -UserName "MSSQLSERVER" -ComputerName SqlServer
 
            Gets the NT account name for the virtual MSSQLSERVER service principal, which is NT SERVICE\MSSQLSERVER.
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/23/2017
    #>

    [CmdletBinding()]
    [OutputType([System.String])]
    Param(
        [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true)]
        [ValidateNotNullOrEmpty()]
        [System.String]$UserName,

        [Parameter(Position=1)]
        [ValidateNotNull()]
        [System.String]$ComputerName = [System.String]::Empty,

        [Parameter()] 
        [ValidateNotNull()]
        [System.Management.Automation.Credential()]
        [System.Management.Automation.PSCredential]$Credential = [System.Management.Automation.PSCredential]::Empty  
    )

    Begin {    
    }

    Process{
        Write-Log -Message "Getting NT Account for $UserName." -Level VERBOSE

        [System.Security.Principal.SecurityIdentifier]$UserSid = Get-AccountSid -UserName $UserName.Trim() -ComputerName $ComputerName -Credential $Credential

        [System.String]$NTName = [System.String]::Empty

        if ($UserSid -ne $null)
        {
            if ([System.String]::IsNullOrEmpty($ComputerName) -or $ComputerName -iin $script:LocalNames) 
            {
                try
                {
                    [System.Security.Principal.NTAccount]$NTAccount = $UserSid.Translate([System.Security.Principal.NTAccount])
                    $NTName = $NTAccount.Value.Trim()
                }
                catch [Exception]
                {
                    Write-Log -Message "Exception translating SID $($UserSid.Value) for $UserName to NTAccount." -ErrorRecord $_ -Level VERBOSEERROR
                    $NTName = $null
                }
            }
            else 
            {
                $Session = New-PSSession -ComputerName $ComputerName -Credential $Credential
                
                $NTName = Invoke-Command -Session $Session -ScriptBlock { 
                    try
                    {
                        [System.Security.Principal.NTAccount]$NTAccount = ([System.Security.Principal.SecurityIdentifier]$args[0]).Translate([System.Security.Principal.NTAccount])
                        Write-Output -InputObject $NTAccount.Value.Trim()
                    }
                    catch [Exception]
                    {
                        Write-Log -Message "Exception translating SID $($args[0].Value) to NTAccount." -ErrorRecord $_ -Level VERBOSEERROR
                        Write-Output -InputObject $null
                    }
                } -ArgumentList @($UserSid)

                Remove-PSSession -Session $Session
            }
        }
        else
        {
            $NTName = $null
        }

        Write-Output -InputObject $NTName
    }

    End {        
    }
}

Function Get-LocalGroupMembers {
    <#
        .SYNOPSIS
            Gets the members of a local group
 
        .DESCRIPTION
            This cmdlet gets the members of a local group on the local or a remote system. The values are returned as DirectoryEntry values in the format WinNT://Domain/Name.
 
        .PARAMETER LocalGroup
            The local group on the computer to enumerate.
 
        .PARAMETER ComputerName
            The name of the computer to query. This defaults to the local computer.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            System.String[]
 
        .EXAMPLE
            Get-LocalGroupMembers -LocalGroup Administrators
 
            Gets the membership of the local administrators group on the local machine.
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 8/25/2016
    #>
  
    [CmdletBinding()]
    [OutputType([System.String[]])]
    Param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [ValidateNotNullOrEmpty()]
        [System.String]$LocalGroup,        

        [Parameter(Position = 1)]
        [ValidateNotNullOrEmpty()]
        [System.String]$ComputerName = $env:COMPUTERNAME
    )

    Begin {
    }

    Process {
        $Group = [ADSI]"WinNT://$ComputerName/$LocalGroup,group"    
                                    
        $Members = $Group.Invoke("Members", $null) | Select-Object @{Name = "Name"; Expression = {$_[0].GetType().InvokeMember("ADSPath", "GetProperty", $null, $_, $null)}} | Select-Object -ExpandProperty Name                

        Write-Output -InputObject $Members
    }

    End {        
    }
}

Function Add-DomainMemberToLocalGroup {
    <#
        .SYNOPSIS
            Adds a domain user or group to a local group.
 
        .DESCRIPTION
            This cmdlet adds a domain user or group to a local group on a specified computer. The cmdlet returns true if the member is added or is already a member of the group.
 
            The cmdlet uses the current computer domain to identify the domain member.
 
        .PARAMETER LocalGroup
            The local group on the computer that will have a member added.
 
        .PARAMETER Member
            The domain user or group to add.
 
        .PARAMETER MemberType
            The type of the domain member, User or Group. This defaults to User.
 
        .PARAMETER ComputerName
            The name of the computer on which to add the local group member. This defaults to the local computer.
 
        .INPUTS
            None
 
        .OUTPUTS
            System.Boolean
 
        .EXAMPLE
            Add-DomainMemberToLocalGroup -LocalGroup Administrators -Member "Exchange Trusted Subsystem" -MemberType Group
 
            Adds the domain group to the local administrators group on the local machine.
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 8/25/2016
    #>
  
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    Param(
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [System.String]$LocalGroup,

        [Parameter(Mandatory = $true, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [System.String]$Member,

        [Parameter(Position = 2)]
        [ValidateSet("Group", "User")]
        [System.String]$MemberType = "User",

        [Parameter(Position = 3)]
        [ValidateNotNullOrEmpty()]
        [System.String]$ComputerName = $env:COMPUTERNAME
    )

    Begin {
    }

    Process {
        $Success = $false

        $Domain = Get-ComputerDomain
        $Domain = $Domain.Substring(0, $Domain.IndexOf("."))

        $Group = [ADSI]"WinNT://$ComputerName/$LocalGroup,group"    
        
        if ($Group.Path -ne $null)    
        {                    
            $Members = $Group.Invoke("Members", $null) | Select-Object @{Name = "Name"; Expression = {$_[0].GetType().InvokeMember("ADSPath", "GetProperty", $null, $_, $null)}} | Select-Object -ExpandProperty Name        
            $NewMember = [ADSI]"WinNT://$Domain/$Member,$MemberType"
                            
            $Path = $NewMember.Path.Remove($NewMember.Path.LastIndexOf(","))
            
            if ($Members -inotcontains $Path)
            {
                try {
                    $Group.Add($NewMember.Path)
                    Write-Log -Message "Successfully added $Member to $($Group.Name)" -Level VERBOSE
                    $Success = $true
                }
                catch [Exception] {
                    Write-Log -ErrorRecord $_ -Level ERROR
                }
            }
            else
            {
                Write-Log -Message "$($NewMember.Name) already a member of $($Group.Name)." -Level VERBOSE
                $Success = $true
            }
        }
        else
        {
            Write-Log -Message "$LocalGroup local group could not be found." -Level VERBOSE
        }

        Write-Output -InputObject $Success
    }

    End {
        
    }
}

Function Set-LocalAdminPassword {
    <#
        .SYNOPSIS
            Sets the local administrator password.
 
        .DESCRIPTION
            Sets the local administrator password and optionally enables the account if it is disabled.
 
            If the password is not specified, the user will be prompted to enter the password when the cmdlet is run. The admin account is
            identified by matching its SID to *-500, which should be unique for the local machine.
 
        .PARAMETER Password
            The new password for the local administrator account.
 
        .PARAMETER EnableAccount
            Specify to enable the local administrator account if it is disabled.
 
        .INPUTS
            System.Security.SecureString
         
        .OUTPUTS
            None
 
        .EXAMPLE
            Set-LocalAdminPassword -EnableAccount
 
            The cmdlet will prompt the user to enter the new password.
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 10/23/2017
    #>

    [CmdletBinding()]
    [OutputType()]
    Param (
        [Parameter(Position=0 , ValueFromPipeline=$true)]
        [ValidateNotNull()]
        [System.Security.SecureString]$Password,

        [Parameter()]
        [Switch]$EnableAccount
    )
    Begin {       
    }
    
    Process {
        $HostName = $env:COMPUTERNAME 
        $Computer = [ADSI]"WinNT://$HostName,Computer" 

        while ($Password -eq $null) 
        {
            $Password = Read-Host -AsSecureString -Prompt "Enter the new administrator password"
        }

        $Name = Get-LocalUser| Where-Object {$_.SID.Value -match "S-1-5-21-.*-500"} | Select-Object -ExpandProperty Name -First 1

        Write-Log -Message "The local admin account is $Name" -Level VERBOSE
        $User = [ADSI]"WinNT://$HostName/$Name,User"
        $PlainTextPass = Convert-SecureStringToString -SecureString $Password
                
        Write-Log -Message "Setting password." -Level VERBOSE
        $User.SetPassword($PlainTextPass)
                
        if ($EnableAccount) 
        {
            #The 0x0002 flag specifies that the account is disabled
            #The binary AND operator will test the value to see if the bit is set, if it is, the account is disabled.
            #Doing a binary OR would add the value to the flags, since it would not be present, the OR would add it
            if ($User.UserFlags.Value -band "0x0002") 
            {
                Write-Log -Message "The account is current disabled with user flags $($User.UserFlags.Value)" -Level VERBOSE
                #The binary XOR will remove the flag, which enables the account, the XOR means that if both values have the bit set, the result does not
                #If only 1 value has the bit set, then it will remain set, so we need to ensure that the bit is actually set with the -band above for the XOR to actually
                #remove the disabled value
                $User.UserFlags = $User.UserFlags -bxor "0x0002"
                $User.SetInfo()
            }
        }
    }
    
    End {        
    }       
}

Function Test-IsLocalAdmin {
    <#
        .SYNOPSIS
            Tests is the current user has local administrator privileges.
 
        .DESCRIPTION
            The Test-IsLocalAdmin cmdlet tests the user's current Windows Identity for inclusion in the BUILTIN\Administrators role.
 
        .INPUTS
            None
 
        .OUTPUTS
            System.Boolean
 
        .EXAMPLE
            Test-IsLocalAdmin
 
            This command returns true if the current is running the session with local admin credentials and false if not.
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/27/2016
 
        .FUNCTIONALITY
            The intended use of this cmdlet is to test for administrative credentials before running other commands that require them.
    #>

    [CmdletBinding()]
    [OutputType([System.Boolean])]
    Param()

    Begin {}

    Process {
        Write-Output -InputObject ([System.Security.Principal.WindowsPrincipal][System.Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)
    }

    End {}
 }