ServerAdminCommon.psm1

#Common Powershell Functions
#Written by Luis Orta
#Contains a few tools here and there to help with time consuming tasks.
function Get-OpenFile{
    <#
    .Synopsis
       Checks file servers for open files by file name.
    .DESCRIPTION
       Outputs object based data to the pipeline. Object Fields are Locks, OpenMode, File, Hostname, and Accessedby. Computername can accept multiple inputs.
    .EXAMPLE
       Get-OpenFiles -ComputerName fileserver.contoso.com -FileName "Word.doc"
    .EXAMPLE
       Get-OpenFiles -ComputerName Fileserver1, Fileserver2, Fileserver3 -FileName "*reports*"
    .EXAMPLE
       Get-ADComputer fileserver1 | Select-Object -Property DNSHostName | Get-OpenFiles -FileName "*.docx"
    #>

        [CmdletBinding(HelpUri = 'https://luisrorta.com/2017/01/21/get-openfiles/')]
        param (
            # valid fileserver name here. Can accept multiple values
            [Parameter (Mandatory=$true,
                        ValueFromPipeline=$true,
                        ValueFromPipelineByPropertyName=$true,
                        Position = 0)]
            [Alias('Hostname','DNSHostName')]
            [string[]]$ComputerName,
    
            # Filename or part of filename. Single value only.
            [Parameter(Mandatory=$false,
                       Position = 1)]
            [string]$FileName
        )
        Process{
        foreach ($Computer in $Computername){
                    try{
                        $Files = $Files = openfiles.exe /query /s $ComputerName /fo csv /V | ConvertFrom-Csv -ErrorAction Stop
                            foreach ($File in $Files){
                                $File | Where-Object {$PSItem.'Open File (Path\executable)' -like $FileName}
                            }
                    }
                    catch{
                        Write-Warning "Error getting open files."
                    }
           }
        }
        }
    function Get-AddRemoveProgram{
    <#
    .Synopsis
       Looks for installed programs on a computer by a program name. Only part of the name is required to perform match.
       A quick warning, this cmdlet is slow.
    .DESCRIPTION
       Outputs object based data to the pipeline.Computername can accept multiple inputs.
    .EXAMPLE
       Get-AddRemovePrograms -ComputerName server.contoso.com -ProgramName "*Microsoft*"
    .EXAMPLE
       Get-AddRemovePrograms -ComputerName server, server2, server3 -ProgramName "*Microsoft*"
    .EXAMPLE
       Get-ADComputer server1 | Select-Object -Property DNSHostname | Get-AddRemovePrograms -ProgramName *Microsoft*
    #>

        [CmdletBinding(HelpUri = 'https://luisrorta.com/2017/01/26/get-addremoveprograms/')]
        Param(
            # valid server name here. Can accept multiple values
            [Parameter(Mandatory=$False,
                       ValueFromPipeline=$True,
                       ValueFromPipelineByPropertyName=$True,
                       HelpMessage="Enter a Valid Computer Name")]
            [Alias('Hostname','DNSHostName')]
            [string[]]$ComputerName = "localhost",
    
            [Parameter(Mandatory=$False,
                       HelpMessage="Enter a part of the program name Example:Office")]
            $ProgramName = "*"
            )
            PROCESS {
                    foreach ($computer in $ComputerName){
                try{
                $programs = Invoke-Command -ComputerName $computer{
                 $32bit = Get-ItemProperty HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*
                 $64bit = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*
                 return $32bit + $64bit} -ErrorAction Stop
    
        foreach ($program in $programs){
                    $program = Write-Output $program | Where-Object -Property Displayname -like $ProgramName
                        if ($program.DisplayName -ne $Null)
                            {
                            $properties = @{ComputerName = $computer
                                            ProgramName = $program.DisplayName
                                            Publisher = $program.Publisher
                                            Version = $program.DisplayVersion
                                            UninstallString = $program.UninstallString}
                            $obj = New-Object -TypeName PSObject -Property $properties
                            Write-Output $obj
                            }
        }    
                }
    
            catch{ Write-Warning "$Computer was not reachable."
            }
    
       }
    }
    }
    function Get-NetLocalGroup{
    <#
    .Synopsis
       Checks local groups on local or remote computers and displays the users in the group.
    .DESCRIPTION
       Command check which users are in the specified group on a local or remote computer.
       Parameters are not mandatory in this command. ComputerName by default is the localhost
       Group by default is Administrators. Accepts Pipeline Input.
    .EXAMPLE
       Get-NetLocalGroup
    Description
    ------------
    Returns local administrators of the current computer
    .EXAMPLE
        Get-NetLocalGroup -ComputerName Server1 -Group Administrators
    Description
    -----------
    Returns local administrators of the remote computer.
    #>

        [CmdletBinding(HelpUri = 'https://luisrorta.com/2017/02/19/get-netlocalgroup/')]
        Param(
            # ComputerName
            [Parameter(Mandatory=$false, 
                       ValueFromPipeline=$true,
                       ValueFromPipelineByPropertyName=$true,
                       Position=0)]
            [ValidateNotNull()]
            [ValidateNotNullOrEmpty()]
            [Alias('ComputerNames','Hostname','DNSHostName')] 
            [string[]]$Computername="LocalHost",
    
            # Local Computer Group
            [Parameter(Mandatory=$false,
                       Position=1)]
            [ValidateNotNull()]
            [ValidateNotNullOrEmpty()]
            [string]$Group = "Administrators"
        )
    
        Begin{
        }
        Process{
            foreach ($Computer in $Computername){
                    #Connect to remote computer and begin mining data
                    $ADGroup = Invoke-Command -ComputerName $Computer -ScriptBlock {
                    net localgroup $args[0]
                    Write-Output $ADGroup
                    } -ArgumentList $Group
                        for ($i=6; $i -lt $ADGroup.length-3; $i++){
                            #Return as hash table and turn into PSObjects.
                            $properties = @{ComputerName = $Computer
                                                    UserName = $ADGroup[$i]}
                            $obj = New-Object -TypeName PSObject -Property $properties
                            Write-Output $obj
                        }
                    
     
                    }
                                                            
            }
    }
    function Set-PrinterLocation{
    <#
    .Synopsis
       Sets Location information on remote printers or local hosts
    .DESCRIPTION
       Sets location property on remote print queues or local print queues. This command takes 3 parameters.
       Only the location is a mandatory parameter. Server will default to localhost and printer will default to all printers.
    .EXAMPLE
       The Following sets the location on remote print queus matching the sharename Print
       Set-PrinterLocation -Server Printserver1 -Location "Redmond, WA" -Printer *Print*
    .EXAMPLE
       The following sets the location on all local print queues to Redmond, WA
       Set-PrinterLocation -Server Printserver1 -Location "Redmond, WA"
    #>

        [CmdletBinding(HelpUri = 'https://luisrorta.com/2017/02/19/get-printerlocation-set-printerlocation/')]
        Param
        (
            # A Valid Print Server Name
            [Parameter(Mandatory=$True,
                       Position=0)]
            [ValidateNotNull()]
            [ValidateNotNullOrEmpty()]
            [string]$Server = "LocalHost",
    
            # Please Enter the location you want to set
            [Parameter(Mandatory=$true,
                       Position=1)]
            [ValidateNotNull()]
            [ValidateNotNullOrEmpty()]
            [string]$Location,
    
            # Filter for which Printers. Use wildcards if necessary
            [Parameter(Position=2)]
            [Alias("ShareName")]
            [string[]]$Printer = "*"
        )
    
        Begin{
        $PrintQueues = Get-WmiObject -Class Win32_Printer -ComputerName $Server | Where-Object {$_.ShareName -like $Printer}
        }
        Process{
                foreach ($PrintQueue in $PrintQueues){
                    $PrintQueue.Location = $Location
                    $PrintQueue.Put();
                }
        }
    }
    function Get-PrinterLocation{
    <#
    .Synopsis
       Gets Location information on remote printers or local hosts
    .DESCRIPTION
       Gets location property on remote print queues or local print queues
       Server will default to localhost and printer will default to all printers.
    .EXAMPLE
       The Following gets the location on remote print queus matching the sharename Print
       Get-PrinterLocation -Server Printserver1 -Printers *Print*
    .EXAMPLE
       The following gets the location on all local print queues
       Get-PrinterLocation
    #>

        [CmdletBinding(HelpUri = 'https://luisrorta.com/2017/02/19/get-printerlocation-set-printerlocation/')]
        Param
        (
            # Server
            [Parameter(Mandatory=$False,  
                       Position=0)]
            [ValidateNotNull()]
            [ValidateNotNullOrEmpty()]
            [string]$Server="LocalHost",
    
            # Printer ShareName
            [Parameter(Position=1)]
            [string]$Printers = "*"
        )
    
        Begin{
        $PrintQueues = Get-WmiObject -Class Win32_Printer -ComputerName $Server | Where-Object {$_.ShareName -like $Printers}
        }
        Process{
                foreach ($PrintQueue in $PrintQueues){
                                $properties = @{'Server'=$Server
                                                'ShareName'=$PrintQueue."ShareName"
                                                'Location'=$PrintQueue."Location"}
                                $obj = New-Object -TypeName PSObject -Property $properties
                                Write-Output $obj
                }
        }
    }
    function Get-ADSubnet{
    <#
    .Synopsis
       Finds Active directory sites that match the requested subnet.
    .DESCRIPTION
        Use this tool to find a corresponding active directory site for an IP address. Wildcards can be placed at any octet in this command.
    .EXAMPLE
       The Following gets the active directory site for the subnet 10.0.0.*.
       Get-ADSubnet -IPAddress 10.0.0.*
    .EXAMPLE
       The following gets the active directory sites for multiple subnets
       Get-ADSubnet -IPAddress 10.0.0.*,192.168.9.*
    #>

        [CmdletBinding(HelpUri = 'https://luisrorta.com/2017/03/11/get-adsubnet/')]
        Param
        (
            # Enter an IP Address Space. Ex: 10.0.0.*
            [Parameter(Mandatory=$True,
                       ValueFromPipeline=$True,
                       ValueFromPipelineByPropertyName=$True,
                       HelpMessage="Enter a Valid Computer Name",
                       Position=0)]
            #Need to consider what other cmdlets provide IP addresses in the pipeline.
            #[Alias('Name')]
            [ValidateNotNull()]
            [ValidateNotNullOrEmpty()]
    
            [string[]]$IPAddress
        )
        Begin{
        #This grabs the configuration database in the active directory schema in a text format we can feed into the get-adobject command.
        $Configuration = (Get-ADDomain | Select-Object SubordinateReferences).SubOrdinateReferences | Select-String -Pattern "Configuration"
        }
    
        Process{
            #First loop for every IP address entered as a parameter.
            foreach ($IP in $IPAddress){
             $Sites = (get-adobject -filter 'ObjectClass -eq "site"' -SearchBase $Configuration -Properties siteObjectBL) | Where-Object {$_.siteObjectBL -like ("*" + $IP)}#).siteObjectBL
               #Next we loop through the all of the possible return sites. This allows us to separate them in pipeline output for single objects.
                foreach ($Site in $Sites){
                #One more loop to go through all of the subnets that return in each site object. They are nested arrays, so this part separates each IP address to make clean pipeline output.
                                foreach ($SubnetCN in $Site.siteObjectBL){
                                #Cleanup the string and return only the IP and subnet
                                $Subnet = $SubnetCN.split("="",")[1]
                                #turn it into a a hash table and return as objects.
                                $properties = @{'Site'=$Site.Name
                                               'Subnet'=$Subnet
                                                }
                                $obj = New-Object -TypeName PSObject -Property $properties
                                Write-Output $obj
                                }
                }
            }
        }
    }
    
    function Get-LoggedOnUser {
    <#
    .Synopsis
       Retreives currently logged in domain users on remote computers
    .DESCRIPTION
       Retreives the list of currently logged in computers in the WMI object Win32_loggedonuser and outputs domain users logged in that are not the current user running the script.
    .EXAMPLE
       Get-LoggedOnUser -Computername computer1
    .EXAMPLE
       get-adcomputer -filter {name -like "*computer*"} | select -expandproperty name | get-loggedonUser
    .EXAMPLE
       Get-LoggedonUser -Computername computer1 -includelocal
      #>

        [CmdletBinding(HelpUri = 'https://luisrorta.com/2017/06/02/get-loggedonuser/')]
        [OutputType([String])]
        Param
        (
            # Param1 help description
            [Parameter(Mandatory=$false, 
                       ValueFromPipeline=$true,
                       ValueFromPipelineByPropertyName=$true, 
                       ValueFromRemainingArguments=$false, 
                       Position=0)]
            [ValidateNotNull()]
            [ValidateNotNullOrEmpty()]
            [Alias("name","cn","computer")] 
            [string[]]$Computername = "localhost",
    
            [Parameter(Mandatory=$false,
                       Position=1)]
            [Switch]$IncludeLocal
        )
    
        Process
        {
            try
            {
            foreach ($computer in $computername)
                {
                    #Enumerate the logged in users
                    $users = Get-CimInstance -ComputerName $computer -ClassName Win32_LoggedOnUser | Select-Object Antecedent -Unique 
                    #Check each output and filter out the current user and local service accounts.
                    if ($IncludeLocal) 
                    {
                        foreach($user in $users)
                        {
                            if ($user.Antecedent.name -ne $env:username) 
                            {
                                $obj = New-Object -TypeName PSCustomObject -Property @{'ComputerName' = $user.Antecedent.PSComputerName
                                    'Name' = $user.Antecedent.Name
                                    'Domain' = $user.Antecedent.Domain}
                                Write-Output $obj
                            }
                        }    
                    }
                    else {
                        foreach($user in $users)
                        {
                            if (($user.Antecedent.Domain -ne $user.Antecedent.PSComputerName) -and ($user.Antecedent.Name -ne $env:username) ) 
                            {
                                $obj = New-Object -TypeName PSCustomObject -Property @{'ComputerName' = $user.Antecedent.PSComputerName
                                    'Name' = $user.Antecedent.Name
                                    'Domain' = $user.Antecedent.Domain}
                                Write-Output $obj
                            }
                        }
                    }
                }
            }
            catch
            {
            Write-Error "Unable to connect to $computer to retreive user names"
            }
        }
    }
    
    function Get-ADPrinter 
    {
    <#
    .SYNOPSIS
        Finds printers that have been published in active directory.
    .DESCRIPTION
        Finds printers published in AD and returns information about the printer. Things like the server they are on, portname, UNC path, Driver, and Location.
    .EXAMPLE
        Get-ADPrinter -Printer TestPrinter01
    .EXAMPLE
        Get-ADPrinter -Printer TestPrinter01,TestPrinter02,TestPrinter03
    #>

        [CmdletBinding(HelpUri = 'https://luisrorta.com/2017/06/11/get-adprinter')]
        [OutputType([String])]
        Param (
            # Param1 help description
            [Parameter(Mandatory=$true,
                       Position=0,
                       ValueFromPipeline=$true,
                       ValueFromPipelineByPropertyName=$true)]
            [Alias("CN")] 
            [string[]]$Printer
        )
        
        process 
        {
            foreach ($Print in $Printer)
            {
                try 
                {
                        $DN = Get-ADObject -Filter {ObjectClass -eq "printQueue" -and PrinterName -like $Print} -Properties printerName,serverName,portName,uNCName,driverName,location
                        foreach ($D in $DN)
                        {
                            $properties = @{'Printer'=$D.printerName
                                            'Server'=$D.serverName
                                            'PortName'=$D.portName
                                            'UNC'=$D.uNCName
                                            'Driver'=$D.driverName
                                            'Location'=$D.location}
                            $obj = New-Object -TypeName PSObject -Property $properties
                            Write-Output $obj
                        }
                }
                
                catch 
                {
                        Write-Warning "No Valid Printer found"
                        $properties = @{'Printer'=$Print
                                        'Server'=$Null
                                        'PortName'=$Null
                                        'UNC'=$Null
                                        'Driver'=$Null
                                        'Location'=$Null}
                        $obj = New-Object -TypeName PSObject -Property $properties
                        Write-Output $obj
                }
            }    
        }
        
    }
    function Get-ADFolderACL {
    <#
    .SYNOPSIS
        Gets Active Directory Groups and Users of a file directory
    .EXAMPLE
        This example gets the the top level groups and users ACLs.
        Get-ADFolderACL -Path \\Test-Server\Folder Location
    .EXAMPLE
        This example will get all users and recurse through the groups to return the users in those groups.
        Get-ADFolderACL -Path \\Test-Server\Folder -Recurse
    #>

        [CmdletBinding()]
        Param (
            # Enter a valid local or UNC path
            [Parameter(Mandatory=$true,
                       Position=0,
                       ValueFromPipeline=$true,
                       ValueFromPipelineByPropertyName=$true)]
            [string[]]$Path,
            
            # Return all users of the groups
            [Parameter(Mandatory=$false,
                        Position=0)]
            [switch]$Recurse
        )
        
        process {
            foreach ($Pat in $Path)
            {
                Write-Verbose "Obtaining ACLS"
                $acls = Get-ACL -Path $Pat | ForEach-Object {$_.Access}
                if ($Recurse)
                {
                $Users = foreach ($acl in $acls)
                    {   
                        $Filter = $acl.identityreference.tostring().split("\",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
                        if ($Filter -ne $null)
                            {
                            Write-Verbose "Getting $Filter"
                            $User = Get-ADGroupMember -Identity $Filter -Recursive
                            $User = $User | Select-Object -Property Name,distinguishedName,ObjectClass
                            Write-Output $User
                            }
                    }
                    $Users = $Users | Select-Object -Property Name,distinguishedName,ObjectClass -Unique
                    Write-Output $Users
                } 
                else
                {
                        foreach ($acl in $acls)
                        {
                            $Filter = $acl.identityreference.tostring().split("\",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
                            if ($Filter -ne $null)
                                {
                                Write-Verbose "Getting $Filter"
                                $Users = Get-ADObject -Filter {SamAccountName -eq $Filter}
                                $Users = $Users | Select-Object -Property Name,distinguishedName,ObjectClass -Unique
                                Write-Output $Users
                                }
                        }
                }     
            }
        }
    }
    function Get-ADFolderACL {
    <#
    .SYNOPSIS
        Gets Active Directory Groups and Users of a file directory
    .EXAMPLE
        This example gets the the top level groups and users ACLs.
        Get-ADFolderACL -Path \\Test-Server\Folder Location
    .EXAMPLE
        This example will get all users and recurse through the groups to return the users in those groups.
        Get-ADFolderACL -Path \\Test-Server\Folder -Recurse
    #>

        [CmdletBinding(HelpUri = 'https://luisrorta.com/2017/07/25/get-adfolderacl/')]
        Param (
            # Enter a valid local or UNC path
            [Parameter(Mandatory=$true,
                       Position=0,
                       ValueFromPipeline=$true,
                       ValueFromPipelineByPropertyName=$true)]
            [string[]]$Path,
            
            # Return all users of the groups
            [Parameter(Mandatory=$false,
                        Position=0)]
            [switch]$Recurse
        )
        
        process {
            foreach ($Pat in $Path)
            {
                Write-Verbose "Obtaining ACLS"
                $acls = Get-ACL -Path $Pat | ForEach-Object {$_.Access}
                if ($Recurse)
                {
                $Users = foreach ($acl in $acls)
                    {   
                        $Filter = $acl.identityreference.tostring().split("\",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
                        if ($Filter -ne $null)
                            {
                            Write-Verbose "Getting $Filter"
                            $User = Get-ADGroupMember -Identity $Filter -Recursive
                            $User = $User | Select-Object -Property Name,distinguishedName,ObjectClass
                            Write-Output $User
                            }
                    }
                    $Users = $Users | Select-Object -Property Name,distinguishedName,ObjectClass -Unique
                    Write-Output $Users
                } 
                else
                {
                        foreach ($acl in $acls)
                        {
                            $Filter = $acl.identityreference.tostring().split("\",[System.StringSplitOptions]::RemoveEmptyEntries)[1]
                            if ($Filter -ne $null)
                                {
                                Write-Verbose "Getting $Filter"
                                $Users = Get-ADObject -Filter {SamAccountName -eq $Filter}
                                $Users = $Users | Select-Object -Property Name,distinguishedName,ObjectClass -Unique
                                Write-Output $Users
                                }
                        }
                }     
            }
        }
    }
    function Get-GlobalPrinter {
    <#
    .SYNOPSIS
        Gets Globally Installed printers on local or remote computers.
    .EXAMPLE
        This example gets all printers on local computer.
        Get-GlobalPrinter
     
    Printer UNC Server Computername
    ------- --- ------ ------------
    TestPrinter1 \\Serv1.test1.com\TestPrinter1 \\Serv1.test1.com TestPC01
    TestPrinter2 \\Serv2.test1.com\TestPrinter2 \\Serv2.test1.com TestPC01
    .EXAMPLE
        This example will get the printer TestPrinter2 from the remote computer Test-PC02
        Get-GlobalPrinter -Computername Test-PC01 -Printer TestPrinter2
    Printer UNC Server Computername
    ------- --- ------ ------------
    TestPrinter2 \\Serv2.test1.com\TestPrinter2 \\Serv2.test1.com TestPC01
    #>

        [CmdletBinding(HelpUri = 'https://luisrorta.com/2017/07/26/global-printer-bundle/')]
        Param (
            # Provide a valid computername
            [Parameter(Mandatory=$false,
                       Position=0,
                       ValueFromPipeline=$true,
                       ValueFromPipelineByPropertyName=$true)]
            [Alias("cn")] 
            [string[]]$Computername = 'localhost',
            # Provide a valid printer name
            [Parameter(Mandatory=$false,
                       Position=1)]
            [string[]]$Printer = '*')
        
        process 
        {
            foreach($Computer in $Computername)
            {
                Write-Verbose "Invoking Command to get printers on $Computer"
                $Printers = Invoke-Command -ComputerName $Computer -ScriptBlock{
                    $Printers = Get-ChildItem "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\Print\Connections"
                    foreach ($Printer in $Printers)
                    {
                        $properties = @{'Printer'=$Printer.GetValue("Printer").Split("\")[-1]
                                        'UNC'=$Printer.GetValue("Printer")
                                        'Server'=$Printer.GetValue("Server")
                                        'Computername'=$env:COMPUTERNAME}
                        $obj = New-Object -TypeName PSObject -Property $properties
                        Write-Output $obj | Where-Object Printer -like $args[0]
                    }
                } -ArgumentList $Printer
                foreach ($Print in $Printers)
                    {
                        Write-Output $Print | Select-Object Printer,UNC,Server,ComputerName 
                    } 
            }
        }
    }
    function Add-GlobalPrinter 
    {
    <#
    .SYNOPSIS
        Adds global printers on local or remote computers.
    .EXAMPLE
        This example Adds a global printer on the local computer.
        Add-GlobalPrinter -UNC \\Serv1.test1.com\TestPrinter1
    .EXAMPLE
        This example Adds multiple global printers on the local computer.
        Add-GlobalPrinter -UNC \\Serv1.test1.com\TestPrinter1,\\Serv2.test1.com\TestPrinter2
    .EXAMPLE
        This example Adds multiple global printers on a remote computer.
        Add-GlobalPrinter -Computername TestPC01 -UNC \\Serv1.test1.com\TestPrinter1,\\Serv2.test1.com\TestPrinter2
    #>

        [CmdletBinding(HelpUri = 'https://luisrorta.com/2017/07/26/global-printer-bundle/')]
        Param (
            # Enter a valid computer name
            [Parameter(Mandatory=$false,
                       Position=0,
                       ValueFromPipeline=$true,
                       ValueFromPipelineByPropertyName=$true)]
            [Alias("cn")] 
            [string[]]$ComputerName = 'localhost',
            # Enter a valid UNC path to a printer
            [Parameter(Mandatory=$false,
                       Position=1,
                       ValueFromPipeline=$true,
                       ValueFromPipelineByPropertyName=$true)]
           [string[]]$UNC)      
        
        process 
        {
            foreach($Computer in $Computername)
            {
                Write-Verbose "Invoking Command to Add printers on $Computer"
                Invoke-Command -ComputerName $Computer -ScriptBlock{
                    foreach($arg in $args)
                    {
                        rundll32 printui.dll PrintUIEntry /q /ga /n $arg
                    }
                        
                } -ArgumentList $UNC
            }
        }
    }
    function Remove-GlobalPrinter 
    {
    <#
    .SYNOPSIS
        Removes global printers on local or remote computers.
    .EXAMPLE
        This example removes a global printer on the local computer.
        Remove-GlobalPrinter -UNC \\Serv1.test1.com\TestPrinter1
    .EXAMPLE
        This example removes multiple global printers on the local computer.
        Remove-GlobalPrinter -UNC \\Serv1.test1.com\TestPrinter1,\\Serv2.test1.com\TestPrinter2
    .EXAMPLE
        This example removes multiple global printers on a remote computer.
        Remove-GlobalPrinter -Computername TestPC01 -UNC \\Serv1.test1.com\TestPrinter1,\\Serv2.test1.com\TestPrinter2
    #>

        [CmdletBinding(HelpUri = 'https://luisrorta.com/2017/07/26/global-printer-bundle/')]
        Param (
            # Param1 help description
            [Parameter(Mandatory=$false,
                       Position=0,
                       ValueFromPipeline=$true,
                       ValueFromPipelineByPropertyName=$true)]
            [Alias("cn")] 
            [string[]]$Computername = 'localhost',
            # Param1 help description
            [Parameter(Mandatory=$false,
                       Position=0,
                       ValueFromPipeline=$true,
                       ValueFromPipelineByPropertyName=$true)]
           [string[]]$UNC)      
        
        process 
        {
            foreach($Computer in $Computername)
            {
                Write-Verbose "Invoking Command to remove printers on $Computer"
                Invoke-Command -ComputerName $Computer -ScriptBlock{
                    foreach($arg in $args)
                    {
                        rundll32 printui.dll PrintUIEntry /q /gd /n $arg
                    }
                        
                } -ArgumentList $UNC
            }
        }
    }
    
    function Test-LocalCredential {
    <#
    .SYNOPSIS
        Tests local user account passwords against remote computers to verify credentials
    .EXAMPLE
        $Cred = Get-Credential
        Test-LocalCredential -Credential $Cred -ComputerName TestServ1
    .EXAMPLE
        Test-LocalCredential -Computername TestServ1, TestServ2, TestServ3
    #>

        [CmdletBinding(HelpUri = 'https://luisrorta.com/')]
        Param (
            # Enter valid credentials using get-credential
            [Parameter(Mandatory=$true,
                       Position=0)]
            [ValidateNotNull()]
            [ValidateNotNullOrEmpty()]
            [System.Management.Automation.PSCredential]$Credential = (Get-Credential),
            
            # Enter a computer name.
            [Parameter(Mandatory=$false,
                       Position=1)]
            [Alias("name","cn","computer","PSComputerName")] 
            [String[]]$ComputerName = $env:COMPUTERNAME
        )
        process 
        {
            foreach ($Computer in $ComputerName) 
            {
                Invoke-Command -ComputerName $Computer -ScriptBlock {
                    $Credential = $Using:Credential
                    Add-Type -AssemblyName System.DirectoryServices.AccountManagement
                    $DirectoryObject = New-Object System.DirectoryServices.AccountManagement.PrincipalContext('machine','localhost')
                    $Check = $DirectoryObject.ValidateCredentials($Credential.GetNetworkCredential().Username,$Credential.GetNetworkCredential().Password)
                    $Obj = New-Object -TypeName PSCustomObject -Property @{
                        UserName = $Credential.GetNetworkCredential().Username
                        CredentialCheck = $Check
                    }
                    Write-Output $Obj
                }
    
            }
        }
    }
    
    function Get-Netstat {
    <#
    .SYNOPSIS
        A powershell version of the command line utility netstat.
    .DESCRIPTION
        Uses invoke-command to run netstat remotely and perform string handling to create powershell objects.
    .EXAMPLE
        Get-Netstat -Computername TestServ1, TestServ2
    #>

        [CmdletBinding(HelpUri = 'https://luisrorta.com/')]
        Param (
            # Enter a valid computer Name
            [Parameter(Mandatory=$False,
                        Position=0)]
            [Alias("p1")] 
            [string[]]$Computername = "LocalHost",
            [Parameter(Mandatory=$false,
                        Position=1,
                        ParameterSetName='Listening')]
            [ValidateSet("LISTENING", "ESTABLISHED", "TIME_WAIT", "*")]
            $State = "*",
            [Parameter(Mandatory=$False,
                        Position=2)]
            [ValidateSet("InterNetwork", "InterNetworkV6", "*")]
            $AddressFamily = "*"
        )
        
        process 
        {
            foreach ($Computer in $Computername) 
            {
                Write-Verbose -Message "Connecting to $ComputerName to run Netstat. Will Return State $State and using AddressFamily $AddressFamily"
                Invoke-Command -ComputerName $Computer -ScriptBlock {
                    $Netstats = NETSTAT.EXE -ANO
                    for ($i = 4; $i -lt $Netstats.Count; $i++) 
                    {
                        $split = $Netstats[$i].split("",[System.StringSplitOptions]::RemoveEmptyEntries)
                        if ($split[0] -eq "TCP")
                        {
                            $obj = new-object -typename pscustomobject -Property @{Proto = $split[0]
                                                                               LocalAddress = [IPAddress]($split[1].Substring(0,$split[1].lastindexof(":")))
                                                                               LocalPort = [int]($split[1].split(":")[-1])
                                                                               RemoteAddress = [IPAddress]($split[2].Substring(0,$split[2].lastindexof(":")))
                                                                               RemotePort = [int]($split[2].split(":")[-1])
                                                                               State = $split[3]
                                                                               ProcessName = (Get-Process -Id $split[4]).Name
                                                                               ProcessID = [int]($split[4])}
                        }
                        if ($split[0] -eq "UDP"){
                            $obj = new-object -typename pscustomobject -Property @{Proto = $split[0]
                                                                               LocalAddress = [IPAddress]($split[1].Substring(0,$split[1].lastindexof(":")))
                                                                               LocalPort = [int]($split[1].split(":")[-1])
                                                                               RemoteAddress = [IPAddress]$IP = "0.0.0.0"
                                                                               RemotePort = 0
                                                                               State = "LISTENING"
                                                                               ProcessName = (Get-Process -Id $Split[3]).Name
                                                                               ProcessID = [int]($split[4])}
                        }
                        #$Filter = {$_.ProcessName -ne "System" -and $_.ProcessName -ne "svchost" -and $_.ProcessName -ne "RouterNT" -and $_.ProcessName -ne "wininit" -and $_.ProcessName -ne "lsass"}
                        Write-Output $obj | Where-Object -FilterScript {$_.State -like $Using:State -and $_.LocalAddress.AddressFamily -like $Using:AddressFamily }
                    }  
                }
            }
        }
    }
function Get-LastBootUpTime {
    <#
.SYNOPSIS
    Gets the last reboot time from specified computers
.DESCRIPTION
    Checks WMI for last boot up time for the target system.
.EXAMPLE
    Get-LastBootUpTime -ComputerName TestServ1
#>

    [CmdletBinding()]
    [Alias()]
    [OutputType([String])]
    Param (
        # Param1 help description
        [Parameter(Mandatory=$false,
                   Position=0,
                   ValueFromPipeline=$true,
                   ValueFromPipelineByPropertyName=$true,
                   ValueFromRemainingArguments=$false)]
        [string[]]$ComputerName = "localhost"
    )
    process 
    {
        foreach ($Computer in $ComputerName) 
        {
            $CimData = Get-CimInstance -ComputerName $Computer -ClassName Win32_OperatingSystem | Select-Object -Property LastBootUpTime
            $obj = New-Object -TypeName PSCustomObject -Property @{
                ComputerName = $Computer
                LastBootUpTime = $CimData.LastBootUpTime
            }
            Write-Output $obj
        }
    }
    
}