Usefull powershell scripts

function to sum durations found in text and report total minutes

function sum_durations_from_text($text) {
   <# 
   read multiline text containing time durations like 01:37:54 or 37:54
   from each line keep only the LAST such duration
   return the sum of all durations in minutes

    EXAMPLE
   -------
   sum_durations_from_text """
   Fixed Calls
       5:20:23 PM    dpapa  (211)    ndemou     (220)           220 211            06:08
   Teams Calls
      Dimitris Papageorgiou  03-May-22 14:06  00:41 Outgoing    
      Dimitris Papageorgiou  03-May-22 11:35  03:41 Tuesday Incoming    
   Mobile calls
      see 2 screenshots  1:24:00
   """
   ...
   94
   #>
   $mins = (
   $text -split "`n"  | %{
      $_ = $_.TrimEnd()
      write-host -NoNewline -foregroundcolor white  "$_"
      $_
   } |   %{ 
      # split line on spaces and keep only "words" that match digit:digit
      # and from these "words" keep only the last one
      $fields = ($_ -split ' ' | ?{ $_ -match '[0-9]:[0-9]' }) 
      $time = $null
      if ($fields -is [array]) {$time = $fields[-1]}
      if ($fields -is [string]) {$time = $fields}
      if ($time) {
         write-host -NoNewline -foregroundcolor yellow " --> "
         write-host            -foregroundcolor cyan "$time"
      } else {
         write-host ""
      }
      $time 
   } | %{ 
      $time =  ($_ -split ":")
      if ($time.Length -eq 3) { # hh:mm:ss
         $mins =  ($time[0] -as [int])*60
         $mins += ($time[1] -as [int])
         $mins += ($time[2] -as [int])/60
      } else {                  # mm:ss
         $mins =  ($time[0] -as [int])
         $mins += ($time[1] -as [int])/60
      }
      $mins
   } | Measure-Object -sum
   ).sum
   $mins -as [int]
}

Change fields in active directory

If you need to change for example the job title of the users
Import-Module ActiveDirectory
$data = import-csv -path C:\Users\Administrator\Desktop\scripts\vathmides.csv
foreach ($user in $data)
{
$mailonly=$user.mail
$aduser= Get-ADUser -Filter {EmailAddress -eq $mailonly}
if ($aduser.Length) {
echo "ERROR: $($aduser.length) results for $mailonly"
} else {
if (-not $aduser.Name) {
echo "ERROR: no user with email $mailonly"
} else {
echo "$($aduser.Name) $mailonly new title will be $($user.title)"
Get-ADUser -Filter {EmailAddress -eq $mailonly} | Set-ADUser -Replace @{title = $user.title
}}}}

how the csv should look
"mail","title"
"thanos.aggeloudis@mazars.gr","Assistant"
"vasilis.agortsas@mazars.gr","Senior 3"
"ioannis.alexandridis@mazars.gr","Manager "

Recursively take ownership of a directory safely

To recursively take ownership of a directory most sites sugest to run takeown with options /r /D y. But if the administrator that runs this command does not have list/read access in any subfolder this command will DELETE EXISTING PERMISSIONS! (see this serverfault answer).

The solution is this: https://serverfault.com/a/1123477/67528

Listen to TCP port to test firewall


function Listen-Port ($port=80){
<#
.DESCRIPTION
Temporarily listen on a given port for connections dumps connections to the screen - useful for troubleshooting
firewall rules.

.PARAMETER Port
The TCP port that the listener should attach to

.EXAMPLE
PS C:\> listen-port 443
Listening on port 443, press CTRL+C to cancel

DateTime                                      AddressFamily Address                                                Port
--------                                      ------------- -------                                                ----
3/1/2016 4:36:43 AM                            InterNetwork 192.168.20.179                                        62286
Listener Closed Safely

.INFO
Created by Shane Wright. Neossian@gmail.com

#>
    $endpoint = new-object System.Net.IPEndPoint ([system.net.ipaddress]::any, $port)    
    $listener = new-object System.Net.Sockets.TcpListener $endpoint
    $listener.server.ReceiveTimeout = 3000
    $listener.start()    
    try {
    Write-Host "Listening on port $port, press CTRL+C to cancel"
    While ($true){
        if (!$listener.Pending())
        {
            Start-Sleep -Seconds 1; 
            continue; 
        }
        $client = $listener.AcceptTcpClient()
        $client.client.RemoteEndPoint | Add-Member -NotePropertyName DateTime -NotePropertyValue (get-date) -PassThru
        $client.close()
        }
    }
    catch {
        Write-Error $_          
    }
    finally{
            $listener.stop()
            Write-host "Listener Closed Safely"
    }

}

List of latest logins/logouts

function get-logonhistory{
Param (
 [string]$Computer = (Read-Host Remote computer name),
 [int]$Days = 10
 )
 cls
 $Result = @()
 Write-Host "Gathering Event Logs, this can take awhile..."
 $ELogs = Get-EventLog System -Source Microsoft-Windows-WinLogon -After (Get-Date).AddDays(-$Days) -ComputerName $Computer
 If ($ELogs)
 { Write-Host "Processing..."
 ForEach ($Log in $ELogs)
 { If ($Log.InstanceId -eq 7001)
   { $ET = "Logon"
   }
   ElseIf ($Log.InstanceId -eq 7002)
   { $ET = "Logoff"
   }
   Else
   { Continue
   }
   $Result += New-Object PSObject -Property @{
    Time = $Log.TimeWritten
    'Event Type' = $ET
    User = (New-Object System.Security.Principal.SecurityIdentifier $Log.ReplacementStrings[1]).Translate([System.Security.Principal.NTAccount])
   }
 }
 $Result | Select Time,"Event Type",User | Sort Time -Descending | Out-GridView
 Write-Host "Done."
 }
 Else
 { Write-Host "Problem with $Computer."
 Write-Host "If you see a 'Network Path not found' error, try starting the Remote Registry service on that computer."
 Write-Host "Or there are no logon/logoff events (XP requires auditing be turned on)"
 }
}

# USAGE:

get-logonhistory -Computer ComputerName -Days 30

List all processes and their users

$ProcessList = Get-CimInstance -Query "SELECT * from Win32_Process"
foreach ($proc in ($ProcessList)) { 
   $ProcessInfo=Invoke-CimMethod -InputObject $proc -MethodName GetOwner
   $proc_id = $proc.processid
   $proc_name = $proc.Name
   $proc_user = $ProcessInfo.User
   if ($proc_user -eq "dhcpdns") {echo "$proc_id, $proc_name, $proc_user"} else {echo "."}
   #echo "$proc_id, $proc_name, $proc_user"
   }

Check the disk usage of a directory

Example usage: Get-DirSize -path "c:\Users" | Sort-Object -Property size

function Get-DirSize {
<#
.Synopsis
  Gets a list of directories and sizes.
.Description
  This function recursively walks the directory tree and returns the size of
  each directory found.
.Parameter path
  The path of the root folder to start scanning.
.Example
  (Get-DirSize $env:userprofile | sort Size)[-2]
  Get the largest folder under the user profile.
.Example
  Get-DirSize -path "c:data" | Sort-Object -Property size 
  Displays folders and sub folders from c:data in descending size order
.Outputs
  [PSObject]
.Notes
 NAME:  Get-DirSize
 AUTHOR: ToJo2000
 LASTEDIT: 8/12/2009
 KEYWORDS: 2009 Summer Scripting Games, Beginner Event 8, Get-ChildItem, Files and Folders
.Link
 Http://www.ScriptingGuys.com
#Requires -Version 2.0
#>
  param([Parameter(Mandatory = $true, ValueFromPipeline = $true)][string]$path)
  BEGIN {}
 
  PROCESS{
    $size = 0
    $folders = @()
 
    foreach ($file in (Get-ChildItem $path -Force -ea SilentlyContinue)) {
      if ($file.PSIsContainer) {
        $subfolders = @(Get-DirSize $file.FullName)
        $size += $subfolders[-1].Size
        $folders += $subfolders
      } else {
        $size += $file.Length
      }
    }
 
    $object = New-Object -TypeName PSObject
    $object | Add-Member -MemberType NoteProperty -Name Folder `
                         -Value (Get-Item $path).FullName
    $object | Add-Member -MemberType NoteProperty -Name Size -Value $size
    $folders += $object
    Write-Output $folders
  }
 
  END {}
} # end function Get-DirSize 

Regarding open files on a server

Get a list of files open on the server

From https://gallery.technet.microsoft.com/scriptcenter/ef8de213-45b6-4751-8c77-01d1b6623e16 (A collection of functions that are helpful with managing local computers. These functions perform at their best when they are used in conjunction with my ActiveDirectory library. For example you could add a local account to all the computers in a particular OU, or get a list of running services on your Domain Controllers.)

Also read this thread https://social.technet.microsoft.com/Forums/windowsserver/en-US/67e94a6a-47e8-420a-b28a-759d876fb15e/how-to-list-open-files-using-powershell?forum=winserverpowershell

And there is the DOS command: openfiles /query /v | findstr /i /c:"some part of a folder name"
Function Get-OpenFiles 
{ 
    <# 
        .SYNOPSIS 
            Get a list of files open on the server 
        .DESCRIPTION 
            This function returns a list of files open on a given server. The output is 
            similar to that of the Manage Open Files from the Share and Storage Management 
            console. 
        .PARAMETER ComputerName 
            The NetBIOS or FQDN of the computer 
        .EXAMPLE 
            Get-OpenFiles -ComputerName fs 
 
            User          Path                              LockCount 
            ----          ----                              --------- 
            User1         F:\Users\User1\Documents\Data\...         0 
            User2         P:\Public                                 0 
 
            Description 
            ----------- 
            This example shows the basic usage of this command. 
        .NOTES 
            FunctionName : Get-OpenFiles 
            Created by   : Jeff Patton 
            Date Coded   : 09/26/2011 13:01:38 
        .LINK 
            https://code.google.com/p/mod-posh/wiki/ComputerManagement#Get-OpenFiles 
    #> 
    [CmdletBinding()] 
    Param 
        ( 
        $ComputerName = (hostname) 
        ) 
    Begin 
    { 
        $OpenFiles = @() 
        $Server = [adsi]"WinNT://$($ComputerName)/LanmanServer" 
        $Resources = $Server.PSBase.Invoke("Resources") 
        } 
    Process 
    { 
        foreach ($Resource in $Resources) 
        { 
            Try 
            { 
                $UserResource = New-Object -TypeName PSobject -Property @{ 
                    User = $Resource.GetType().InvokeMember("User","GetProperty",$null,$Resource,$null) 
                    Path = $Resource.GetType().InvokeMember("Path","GetProperty",$null,$Resource,$null) 
                    LockCount = $Resource.GetType().InvokeMember("LockCount","GetProperty",$null,$Resource,$null) 
                    } 
                } 
            Catch 
            { 
                } 
            $OpenFiles += $UserResource 
            } 
        } 
    End 
    { 
        Return $OpenFiles 
        } 
    } 

Create a list of which users open what files

You can schedule this to run daily on a file server in order to collect to c:\openfiles.csv a list of which user opens what files ( username ; c:\path\to\file)
# run a few times every several seconds and collect a list
# of who user has what file open in a csv file
# only unique entries are found in the final csv file
# Nick Demou, 2019 function afewtimes { Write-Host "Capturing open files..." For ($i=1; $i -le 20; $i++) { # 20 times * ~3mins each => ~1hour $cnt = 0 $last = "" net files |select-string "[:]" | %{ $_ -replace ' .*', '' } | select-string "[0-9]" | ForEach-Object { $out = (net files "$_" 2> $null | select-string "^User name|^Path|^$") $out = %{ $out -replace '^User name *', '' } $out = %{ $out -replace '^Path *', ' ; ' } $cnt += 1 if ($out -ne "") { $last=$out write-output "$out" } } Write-Host "$cnt open files, last: $last" sleep 180 # 3mins } } $hours=11 For ($i=1; $i -le $hours; $i++) { afewtimes | Select-Object -Unique > c:\openfiles.tmp cat c:\openfiles.tmp >> c:\openfiles.txt }
Topic revision: r16 - 22 Feb 2023, NickDemou
Copyright © enLogic