none
File Extensions,Size and Counts RRS feed

  • Question

  • Hi Guy's,

    I'm currently stuck with a script trying to iterate through a DFS file share and report on all the file extensions, their size and count. I have this part, but what i am trying to do is also only iterate through certain folders or should i say exclude certain folders. The folders that are being excluded are certain user folders. Ideally what i would like to do would be to query the AD OU and get the SAMID's and then any folders who's names match the SAMID from the OU exclude. So far i am not able to do this. I only seem to be able to exclude a single user. I have tried pulling the users into a text file and then getting the content from within but this does not seem to work any help greatly appreciated. Here is what i have so far:


    $directory = "\\URL\dfs\UserData\users01"

    #$users = ('*User1*,*User2*')

    #Get-Content E:\scripts\users.txt


    Get-ChildItem -Path $directory -Recurse |


    Where-Object { !$_.PSIsContainer -and $_.FullName -notmatch "user1" } |


    Group-Object Extension |


    Select-Object @{n="Extension";e={$_.Name -replace '^\.'}}, @{n="Size (MB)" ;e={[math]::Round((($_.Group | Measure-Object Length -Sum).Sum / 1MB), 2)}}, Count | Export-Csv -NoTypeInformation -path e:\scripts\FileExtCountexcludinguser1user2.csv

    if i try this it doesn't work:


    $directory = "\\URL\dfs\UserData\users01"

    #$users = ('*User*')

    Get-Content E:\scripts\users.txt

    Get-ChildItem -Path $directory -Recurse |

    Where-Object { !$_.PSIsContainer -and $_.FullName -notmatch "$users" } |

    Group-Object Extension |


    Select-Object @{n="Extension";e={$_.Name -replace '^\.'}}, @{n="Size (MB)" ;e={[math]::Round((($_.Group | Measure-Object Length -Sum).Sum / 1MB), 2)}}, Count | Export-Csv -NoTypeInformation -path e:\scripts\FileExtCountexcludingusers.csv

    Any help greatly appreciated. Ideally i am trying to be able to iterate through the list of users and exclude reporting on these users.

    Cheers

    Grizzly


    john adams

    Thursday, September 14, 2017 9:24 AM

Answers

  • Hi John,

    never you worry, AD queries in PowerShell do not require the PowerShell module:

    function Get-UserSamAccountName
    {
    	[CmdletBinding()]
    	Param (
    		[string]
    		$OUDN
    	)
    	
    	$objDomain = New-Object System.DirectoryServices.DirectoryEntry
    	$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
    	$objSearcher.SearchRoot = "LDAP://$OUDN"
    	$objSearcher.PageSize = 1000
    	$objSearcher.Filter = "(objectCategory=user)"
    	$objSearcher.SearchScope = "Subtree"
    	$null = $objSearcher.PropertiesToLoad.Add("samaccountname")
    	$Answer = @($objSearcher.FindAll())
    	
    	foreach ($item in $Answer) { $item.Properties["samaccountname"] }
    }

    Then you can run things like this:

    $users = Get-UserSamAccountName -OUDN "OU=..."
    
    # And later:
    
    Where-Object { !$_.PSIsContainer -and $_.FullName -notmatch "$($users -join "|")" } |

    Cheers,
    Fred


    There's no place like 127.0.0.1

    • Marked as answer by grizzly704 Thursday, September 14, 2017 10:16 AM
    Thursday, September 14, 2017 10:15 AM

All replies

  • Hi John,

    you need to use the regex "or" logic functionality, in order to make this work:

    Where-Object { !$_.PSIsContainer -and $_.FullName -notmatch "user1|user2" } |

    Yes, the pipe symbol is the regex "or".

    Doing it in bulk:

    $users = Get-ADUser -Filter "*" -SearchBase "OU=..."
    Where-Object { !$_.PSIsContainer -and $_.FullName -notmatch "$($users.samaccountname -join "|")" } |

    Cheers,
    Fred


    There's no place like 127.0.0.1

    Thursday, September 14, 2017 9:48 AM
  • Hi Fred,

    That's great. Only issue we have is that we are using 2k8 only and don't have get-aduser available without having to spend months trying to get it approved to download the module. So i'm using:

    dsquery user "OU..." | dsget user -samid

    not sure if this will work?

    Cheers

    Grizzly


    john adams

    Thursday, September 14, 2017 9:53 AM
  • Hi John,

    never you worry, AD queries in PowerShell do not require the PowerShell module:

    function Get-UserSamAccountName
    {
    	[CmdletBinding()]
    	Param (
    		[string]
    		$OUDN
    	)
    	
    	$objDomain = New-Object System.DirectoryServices.DirectoryEntry
    	$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
    	$objSearcher.SearchRoot = "LDAP://$OUDN"
    	$objSearcher.PageSize = 1000
    	$objSearcher.Filter = "(objectCategory=user)"
    	$objSearcher.SearchScope = "Subtree"
    	$null = $objSearcher.PropertiesToLoad.Add("samaccountname")
    	$Answer = @($objSearcher.FindAll())
    	
    	foreach ($item in $Answer) { $item.Properties["samaccountname"] }
    }

    Then you can run things like this:

    $users = Get-UserSamAccountName -OUDN "OU=..."
    
    # And later:
    
    Where-Object { !$_.PSIsContainer -and $_.FullName -notmatch "$($users -join "|")" } |

    Cheers,
    Fred


    There's no place like 127.0.0.1

    • Marked as answer by grizzly704 Thursday, September 14, 2017 10:16 AM
    Thursday, September 14, 2017 10:15 AM
  • Also note:

    • You can install server administration tools on client OSes (They are called RSAT: Remote Server Administration Tools). With those you can manage servers from your desktop, which may be an easier sell. They can be downloaded from Microsoft.
    • To remotely manage 2008 DCs with the AD Module (which is nice to have, since most examples use it), you can install the Gateway service on the Server (which is probably where that approval process will kick in. Note: I'd recommend going through the process for that, if you can't get Management to cough up the cash for a 2016 Migration.

    Cheers,
    Fred


    There's no place like 127.0.0.1

    Thursday, September 14, 2017 10:20 AM
  • Cheers Fred, will give that a go, looks like it will do the trick :)

    john adams

    Thursday, September 14, 2017 10:23 AM
  • HI Fred,

    Another one for you, 

    If i wanted to iterate through several folders i.e. We have 6 user folders named users1 through to 6. These contain the individual user folders and some users may have the same folder twice or even 4,5,6 or more times. How would i get my script to go through each folder?

    I currently do this but for a different method much simpler of just getting file ext and counts but customer has since added:

    function ProcessFolder ($folderName)

    {

        Get-ChildItem \\URL\dfs\UserData\$folderName -Recurse -file | Group Extension -NoElement | sort Size, Count -desc | select Size, count, name | Export-Csv -NoTypeInformation -path e:\scripts\FileExtCount$folderName.csv

    }

    $UserFolders = (1,2,3,4,5,6)

    Foreach ($num in $UserFolders)

    {

        Write-Host Processing folder: Users0$num

        ProcessFolder "Users0$num"

    So what i would like to do now is iterate through these folders check ad then the username is checked against the folders and lists all the ext types,sizes,counts? But not sure how i can achieve that with what i have done already and now with what you given? any help 

    Cheers

    Grizzly


    john adams

    Thursday, September 14, 2017 12:11 PM
  • Hi John,

    can you give a short example that represents input & Output of what you are trying to do, that illuminates the issue you are having?

    Cheers,
    Fred


    There's no place like 127.0.0.1

    Thursday, September 14, 2017 1:42 PM
  • Hi Fred,

    Ignore my last, i was being a schoolboy. I just need to go through at a higher level. i now have the results i need.

    Thanks Fred for your help :)

    Grizzly


    john adams

    Thursday, September 14, 2017 2:07 PM
  • Hi All,

    After help from Fred i have manged to get the results i required. However, i need to exclude folders with a certain name. So i have attempted to do this but for some reason it's still iterating through them or at least trying to. Any help greatly appreciated:

    # File Survey Script for the DFS Share

    #Get the current date

    $checkdate = Get-Date

    #Edit the date to give DTS

    $checkdate = $checkdate.ToString("HH-mm_dd-MMM-yyy")

    # create the Function for retrieving users from an OU

    function Get-UserSamAccountName

    {

                   [CmdletBinding()]

                   Param ([string]$OUDN)

                  

                   $objDomain = New-Object System.DirectoryServices.DirectoryEntry

                   $objSearcher = New-Object System.DirectoryServices.DirectorySearcher

                   $objSearcher.SearchRoot = "LDAP://$OUDN"

                   $objSearcher.PageSize = 1000

                   $objSearcher.Filter = "(objectCategory=user)"

                   $objSearcher.SearchScope = "Subtree"

                   $null = $objSearcher.PropertiesToLoad.Add("samaccountname")

                   $Answer = @($objSearcher.FindAll())

                  

                   foreach ($item in $Answer) { $item.Properties["samaccountname"] }

    }

    #Change the OU for whichever Unit Unit1,Unit2 or Unit3

    $users = Get-UserSamAccountName -OUDN "OU=unit1,OU= Users,OU=User Accounts,DC=something,DC=net"


    #==============================================================================#

    #Unit being run

    $unit = "unit1"

    # Directory input required to be searched

    $directory = "Users06"

                 #"Users02",

                 #"Users03",

                 #"Users04",

                 #"Users05",

                 #"Users06"

    $outputfilename = "$unit" + "-" + $checkdate

    $exclusions = @("*\profile.V2*")

    function Get-DFSSharesInventory()

    {

        foreach ($share in $directory)

        {

    #Get all items

    Get-ChildItem -Path "\\url\dfs\UserData\$share" -Exclude $exclusions -Recurse |

    %{

    $allowed = $true

    foreach ($exclude in $exclusions)

    {

    if ((Split-Path $_.FullName -Leaf) -ilike $exclude) {

    $allowed = $false

    break

    }

    }

    if ($allowed){

    #Get only files

    Where-Object { !$_.PSIsContainer -and $_.FullName -match "$($users -join "|")" } |

    #Group by extension

    Group-Object Extension |

    #Get data

    Select-Object @{n="Extension";e={$_.Name -replace '^\.'}}, @{n="Size (MB)" ;e={[math]::Round((($_.Group | Measure-Object Length -Sum).Sum / 1MB), 3)}}, Count | Export-Csv -NoTypeInformation -LiteralPath "e:\scripts\$outputfilename.csv"

        }

    }

    }

    }

    Get-DFSSharesInventory


    john adams

    Friday, September 22, 2017 8:51 AM
  • Hi John,

    this is mostly because the dir command will pass all child items of folders that are successfully excluded by the pattern (which is broken to begin with). I've pared down and reformatted that script a bit, so here's a version that may work for you. May not either, haven't really tested it, just applied some experience in code layout:

    <#
    	.SYNOPSIS
    		Generates a report on Userhome usage
    	
    	.DESCRIPTION
    		Generates a report on Userhome usage
    	
    	.PARAMETER OrganizationalUnitDN
    		The OU in which the users recide whose userhomes should be evaluated
    	
    	.PARAMETER DirectoryBase
    		The base directory on the dfs share to search
    		This script will search $BaseShare\$DirectoryBase
    
    	.PARAMETER Exclusions
    		No object whose full name matches this will be considered
    
    	.PARAMETER BaseShare
    		The basic share to search.
    		This script will search $BaseShare\$DirectoryBase
    #>
    [CmdletBinding()]
    Param (
    	[string]
    	$OrganizationalUnitDN = "OU=unit1,OU= Users,OU=User Accounts,DC=something,DC=net",
    	
    	[string]
    	$DirectoryBase = "Users06",
    	
    	[string]
    	$Exclusions = "profile\.V2",
    	
    	[string]
    	$BaseShare = "\\url\dfs\UserData"
    )
    
    
    
    #region Utility Functions
    function Get-UserSamAccountName
    {
    	[CmdletBinding()]
    	Param (
    		[string]
    		$OUDN
    	)
    	
    	$objDomain = New-Object System.DirectoryServices.DirectoryEntry
    	$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
    	$objSearcher.SearchRoot = "LDAP://$OUDN"
    	$objSearcher.PageSize = 1000
    	$objSearcher.Filter = "(objectCategory=user)"
    	$objSearcher.SearchScope = "Subtree"
    	$null = $objSearcher.PropertiesToLoad.Add("samaccountname")
    	$Answer = @($objSearcher.FindAll())
    	
    	foreach ($item in $Answer) { $item.Properties["samaccountname"] }
    }
    
    function Get-DFSSharesInventory
    {
    	[CmdletBinding()]
    	Param (
    		$Directory,
    		
    		$OutputFileName,
    		
    		$Users,
    		
    		$Exclusions,
    		
    		$BaseShare
    	)
    	
    	$select_name = @{ n = "Extension"; e = { $_.Name -replace '^\.' } }
    	$select_size = @{ n = "Size (MB)"; e = { [math]::Round((($_.Group | Measure-Object Length -Sum).Sum / 1MB), 3) } }
    	
    	foreach ($share in $Directory)
    	{
    		#Get all items
    		Get-ChildItem -Path "$BaseShare\$share" -Recurse |
    			Where-Object FullName -NotMatch $Exclusions |
    			#Get only files
    			Where-Object { !$_.PSIsContainer -and $_.FullName -match "$($Users -join "|")" } |
    			#Group by extension
    			Group-Object Extension |
    			#Get data
    			Select-Object $select_name, $select_size, Count | Export-Csv -NoTypeInformation -LiteralPath "e:\scripts\$outputfilename.csv"
    	}
    }
    #endregion Utility Functions
    
    $checkdate = Get-Date -Format "HH-mm_dd-MMM-yyy"
    
    $users = Get-UserSamAccountName -OUDN $OrganizationalUnitDN
    $unit = $OrganizationalUnitDN.Split(",")[0].Split("=")[1]
    
    $outputfilename = "$($unit)-$($checkdate)"
    
    
    Get-DFSSharesInventory -Directory $DirectoryBase -OutputFileName $outputfilename -Users $users -Exclusions $exclusions -BaseShare $BaseShare

    Note that now the script accepts parameters :)

    Cheers,
    Fred


    There's no place like 127.0.0.1

    Friday, September 22, 2017 9:33 AM
  • Thanks Fred, i shall give this a go. Does it show that i'm bumbling my way through this ;)

    Much appreciated fella

    Grizzly :)


    john adams

    Friday, September 22, 2017 10:03 AM
  • Hi John,

    don't you worry, we all started at some pointed. Usually patched together things we found on google, tried it, searched again, tried again, asked questions, not really knowing what we needed to ask for, got answers that might have been pointed in the right direction or not. Tried again and again until we either succeeded or gave up.

    The beginning is always rough, for everybody.

    Building up the foundations based on planned courses can prevent a lot of that, but then, most often a task just "falls on your head" and you don't exactly get freed up for a month worth of coursework to avoid many of the early issues.

    The one advise I have to give is to segment your code properly:

    • Create regions like I did for the helper functions (most editors can collapse them, so you can hide the pieces you don't want to look at right now)
    • All the information a function needs should be given in the param block. Don't just "grab" a variable from somewhere in the script, give it to the function through a parameter. Helps a lot when troubleshooting stuff.

    Otherwise your script will likely turn into a patchwork script where you loose control over it and "just pray that it works". That's bad for the script health, but even worse for your learning effect (because it becomes seriously hard to pick things up from it again - it just becomes a sealed document you pray to never have to touch again).

    Cheers,
    Fred


    There's no place like 127.0.0.1

    Friday, September 22, 2017 10:20 AM
  • Hi Fred,

    I'm trying to select 2 different OU's by adding a second in the variable and doing a foreach loop. However, it's not working it's only seeinig the 1st ou, any ideas?

    Cheers

    Grizzly


    john adams

    Tuesday, September 26, 2017 3:28 PM
  • Hi,

    update the parameter type of $OrganizationalUnit from [string] to [string[]].

    Then loop through it.

    Cheers,
    Fred


    There's no place like 127.0.0.1

    Tuesday, September 26, 2017 3:54 PM