Looping an array for servers and drive names - powershell

I'm trying to find old files on my servers and having a little trouble with the drive path for the Get-NeglectedFiles function. My serverpath keeps showing up as \server\ .
Function Get-NeglectedFiles
{
Param([string[]]$path,
[int]$numberDays)
$cutOffDate = (Get-Date).AddDays(-$numberDays)
Get-ChildItem -Path $path -Recurse |
Where-Object {$_.LastAccessTime -le $cutOffDate}
}
$Endresults = #()
$serverlist = get-content "C:\temp\serverlist.txt"
foreach($server in $serverlist) {
$results = Get-WmiObject -ComputerName $Server -Class Win32_Share | select name
$Endresults += New-Object psobject -Property #{
Servername = $server
Result = $results
}
}
foreach($drive in $server){
$drives = $results | Where-Object { $_ -ne $null}
$serverpath = "\\" + $server + "\" + $drives + "\"
}
{Get-NeglectedFiles -path $serverpath -numberDays 90 | select name, lastaccesstime
}

You're probably looking to do something like this (I've simplified it a bit but you can extend on it):
$serverlist = Get-Content 'C:\temp\serverlist.txt';
foreach ($server in $serverlist) {
$drives = Get-WmiObject -ComputerName $Server -Class Win32_Share;
foreach ($drive in $drives.Name) {
$serverpath = "\\$server\$drive\";
$serverpath;
Get-NeglectedFiles -path $serverpath -numberDays 90 | select Name, LastAccessTime;
};
};
Explanation:
Get list of server names from file serverlist.txt
For each server in that list:
Retrieve the list of share names on that server
For each share on that server generate a serverpath and run Get-NeglectedFiles
Side note:
You also should probably inspect what is being returned by:
Get-WmiObject -ComputerName $Server -Class Win32_Share
And make sure that all of the shares returned are ones you want to use. For example, when I run it, I get shares like IPC$, print$, ADMIN$, as well as the default drive shares, and all other custom shares that have been created on the server. You probably aren't going to be cleaning files out of those.
Another side note:
You might want to consider using the -File parameter inside of your Get-NeglectedFiles command so that you are only targeting files and not directories.

Related

Get Hostname and MAC address from all PCs in AD

I'm trying to get the hostname and the MAC address from all PCs in the Active Directory. I know that MAC addresses are not in the Activce Directory. That's why I already used a small script from someone else. The point is that I have to make a list of hostnames, which I can do, but then the other script runs into a problem because some computers are not online.
Can anyone help me get a list with only the pc's that are online?
This is the part that searches the list I create with hostnames.
$Computers = Import-CSV C:\Users\admin_stagiair\Desktop\Computers.txt
$result = #()
foreach ($c in $Computers){
$nic = Invoke-Command {
Get-WmiObject Win32_NetworkAdapterConfiguration -Filter 'ipenabled = "true"'
} -ComputerName $c.Name
$x = New-Object System.Object | select ComputerName, MAC
$x.Computername = $c.Name
$x.Mac = $Nic.MACAddress
$result += $x
}
$result | Export-Csv C:\Users\admin_stagiair\Desktop\Computers.csv -Delimiter ";" -NoTypeInformation
And this is the part that I tried to make search the list and filter out the online computers, which absolutely does not work and I can't figure out how to do it.
$Computers = Import-Csv C:\Users\admin_stagiair\Desktop\Computers.txt
foreach ($c in $Computers) {
$ping = Test-Connection -Quiet -Count 1
if ($ping) {
$c >> (Import-Csv -Delimiter "C:\Users\admin_stagiair\Desktop\online.txt")
} else {
"Offline"
}
}
Last bit, this is the part I use to create a list of all computers in the Active Directory.
Get-ADComputer -Filter {enabled -eq $true} -Properties * |
select Name > C:\Users\(user)\Desktop\Computers.txt
If you only want one property from Get-ADComputer don't fetch all
a computer could have more than one MAC, to avoid an array be returned join them.
$result += inefficiently rebuilds the array each time, use a PSCustomObject instead.
Try this (untested):
EDIT: first test connection, get MAC only when online
## Q:\Test\2018\09\18\SO_52381514.ps1
$Computers = (Get-ADComputer -Filter {enabled -eq $true} -Property Name).Name
$result = ForEach ($Computer in $Computers){
If (Test-Connection -Quiet -Count 1 -Computer $Computer){
[PSCustomPbject]#{
ComputerName = $Computer
MAC = (Invoke-Command {
(Get-WmiObject Win32_NetworkAdapterConfiguration -Filter 'ipenabled = "true"').MACAddress -Join ', '
} -ComputerName $Computer)
Online = $True
DateTime = [DateTime]::Now
}
} Else {
[PSCustomPbject]#{
ComputerName = $Computer
MAC = ''
Online = $False
DateTime = [DateTime]::Now
}
}
}
$result | Export-Csv C:\Users\admin_stagiair\Desktop\Computers.csv -Delimiter ";" -NoTypeInformation
What about trying something like this:
# Get all computers in list that are online
$Computers = Import-Csv C:\Users\admin_stagiair\Desktop\Computers.txt |
Select-Object -ExpandProperty Name |
Where-Object {Test-Connection -ComputerName $_ -Count 1 -Quiet}
# Grab the ComputerName and MACAddress
$result = Get-WmiObject -ComputerName $computers -Class Win32_NetworkAdapterConfiguration -Filter 'ipenabled = "true"' |
Select-Object -Property PSComputerName, MacAddress
$result | Export-Csv C:\Users\admin_stagiair\Desktop\Computers.csv -Delimiter ";" -NoTypeInformation

Trying to add PSCustomObjects to an ArrayList on a remote machine

I have some experience with PowerShell, and usually Google or searching forums like these yields the answers when I have questions - but not this time.
I'm trying to collect the number of .log files in a directory on a remote server, then I'd like to store the location (drive letter and folder path) and the count in an array list for later. So far everything is working as I'd expect, but I'm running into trouble adding my PSCustomObjects to the array list. I'm not sure if it's because I'm executing on a remote server or if something else is causing the problem. Here is my code:
$server = Read-Host -Prompt 'Please enter the server name'
[System.Collections.ArrayList]$returnObj = #()
Invoke-Command -ComputerName $server {
$drives = Get-PSDrive -PSProvider FileSystem |
Where-Object {$_.Description -like "ExVol*"} |
Select-Object Root
foreach ($d in $drives) {
Set-Location -Path $d.Root
$folders = Get-ChildItem -Path $d.Root |
Where-Object {$_.Name -like "*.log"} |
Select-Object Name
foreach ($f in $folders) {
$count = (Get-ChildItem -Path $f.Name).Count
$obj = [PSCustomObject]#{
LogFolder = $d.Root.Trim() + $f.Name
LogFileCount = $count
}
Write-Host $obj
$returnObj.Add($obj | Select-Object DatabaseFolder,LogFileCount)
}
}
}
$returnObj
In this format I get a syntax error on the line
$returnObj.Add($obj | Select-Object DatabaseFolder,LogFileCount)
If I change the above line to $returnObj.Add($obj) I avoid the syntax error, but instead I get an error saying I cannot call a method on a null valued expression.
I've tried creating the ArrayList inside the Invoke-Command and I've tried using New-Object instead of PSCustomObject to no avail.
I think your mixing stuff a bit up, this will do:
$returnObj = Invoke-Command -ComputerName $server {
$drives = Get-PSDrive -PSProvider FileSystem |
Where-Object {$_.Description -like "ExVol*"} |
Select-Object Root
foreach ($d in $drives) {
Set-Location -Path $d.Root
$folders = Get-ChildItem -Path $d.Root |
Where-Object {$_.Name -like "*.log"} |
Select-Object Name
foreach ($f in $folders) {
$count = (Get-ChildItem -Path $f.Name).Count
[PSCustomObject]#{
LogFolder = $d.Root.Trim() + $f.Name
LogFileCount = $count
}
}
}
}
$returnObj
The problem is this line:
[System.Collections.ArrayList]$returnObj = #()
is declared outside of the Invoke-Command -ScriptBlock. This means it's not available within the session on the remote machine, and as such can not be used there.
On a side note, you cannot fill an array like you fill a Hashtable with data.
Arrays are filled like $MyArray = #(); $MyArray += 'MyValue'
Hashtables like $MyHash=#{}; $MyHash.SomeKey = 'SomeValue' or as you indicated $MyHash.Add('SomeKey', 'SomeValue')
ArrayLists are filled like [System.Collections.ArrayList]$MyArrayList = #(); $MyArrayList.Add('SomeValue')
I hope this makes it a bit more clear. The return values can always be catched before the Invoke-Command or even before a simple foreach (). For example $result = 0..3 | ForEach-Object {$_} is perfectly valid too.
You need to actually return your object from the remote system to your local system since you cannot use your $returnObj within the remote session.
As an example:
$returnValue = Invoke-Command -ComputerName $server {
$obj = [PSCustomObject]#{
LogFolder = $d.Root.Trim() + $f.Name
LogFileCount = $count
}
#return the object via the pipline
$obj
}
$returnObj.Add($returnValue | Select-Object DatabaseFolder,LogFileCount)
The above example is lacking of proper error handling, therefore you would get an error if the remote system is not reachable but it's a start.

Powershell - get content from user profiles

new to Powershell and scripting, so here it goes. I'm trying to go through a list of pc's to pull out any Java exception sites (eventually I will query AD for all my workstations). The exception.sites file is located in each user profile \AppData\LocalLow\Sun\Java\Deployment\security folder. I'm not sure why I'm only pulling the sites from my workstation though.
$comps = Get-Content \\server1\users\james\test\comps.txt
$addPath = "\AppData\LocalLow\Sun\Java\Deployment\security"
$userprofiles = Get-WmiObject win32_userprofile -filter "LocalPath Like '%\\Users\\%'" | Select-Object -ExpandProperty Localpath | foreach {$_ + $addpath}
foreach ($pc in $comps)
{foreach ($profile in $userprofiles)
{if ((test-path "$profile\exception.sites") -ne $false)
{get-content -path "$profile\exception.sites" | Out-File \\server2\packages\java\siteexceptions\SiteExceptions.txt -Append
}
}
}
you need to make a couple of adjustments to the code.
Get-WmiObject has a -computername parameter which will make it run on a remote computer. Also you will need to move the WMi query inside the computer loop so that the query is run once on each computer that you are iterating.
Note: Since the destination points to a location within another user's profile, you will need administrator privileges to be able to traverse the protected path.
foreach ($pc in $comps)
{
$userprofiles = Get-WmiObject win32_userprofile -filter "LocalPath Like '%\\Users\\%'" -ComputerName $pc
foreach ($profile in $userprofiles)
{
$drive = (Split-Path -Path $profile.localpath -Qualifier) -replace ':','$'
$Remotepath = "\\$pc\$drive" + (Split-Path -Path $profile.localpath -NoQualifier) + $addPath + '\exception.sites'
if (test-path $Remotepath)
{
get-content -path $Remotepath | Out-File \\server2\packages\java\siteexceptions\SiteExceptions.txt -Append
}
}
}

Powershell Script to check drive letter for shared drives, then check those drives to see if Volume Shadow Copy is enabled

First Post here. First off, I really have little experience with scripting, so thank you for your kindness.
I have a script I found that can somewhat get the drive letters that have shares on it
Push-Location
Set-Location 'HKLM:\SYSTEM\CurrentControlSet\services\LanmanServer\Shares'
Get-Item . |
Select-Object -ExpandProperty property |
ForEach-Object {
New-Object psobject -Property #{"property"=$_;"Value" = (Get-ItemProperty -Path . -Name $_).$_}
} |
Format-Table property, value -AutoSize
Pop-Location
The "value" results are long:
{CSCFlags=0, MaxUses=4294967295, Path=C:\location, Permissions=x...}
Ideally, all I need is the drive letter. Then I would like to use the drive letter found to see if Volume Shadow Copy is enabled on said drive.
The following will create an array of all drive letters on a target server and then check each using wmi to see if volume shadow storage is present.
# Specify host to check
$serverName = "localhost"
# Create an empty array to hold our drive letters
$driveLetterArray = #()
# Use wmi to get all share paths
(get-wmiobject win32_share -computername $serverName).path | foreach {
# Get just the drive letter of the share path
$driveLetter = $_.split(":")[0]
# Send drive letter to array if it doesn't already exist in that array
if (($driveLetter) -and ($driveLetterArray -notcontains $driveLetter)) {
$driveLetterArray += $driveLetter
}
}
# Check if that drive letter has shadowstorage
foreach ($letter in $driveLetterArray) {
# Clean up the letter variable so it will be able to match results from gwmi win32_volume
$letter = $letter + ":\"
$deviceID = (gwmi win32_volume -computername $serverName | Where-Object {$_.Name -eq $letter}).deviceID
# Clean up the deviceID variable so it will be able to match results from gwmi win32_shadowstorage
$deviceID = $deviceID.TrimStart("\\?\")
$deviceID = "Win32_Volume.DeviceID=`"\\\\?\\" + $deviceID + "\`""
$shadowQuery = gwmi win32_shadowstorage -computername $serverName | Where-Object {$_.Volume -eq $deviceID}
# Report findings to the user
if ($shadowQuery) {
"Volume shadow enabled on drive $letter"
} else {
"Volume shadow NOT enabled on drive $letter"
}
}
This is pretty horrible, and I'm sure there's a better way to address your problem, but the below effectively looks at your public shares, finds the drive letter, looks up the VolumeID for that drive letter and then checks to see if the VolumeID is present in the shadow copy config:
$paths = $(gwmi win32_share | ? {$_.name -notmatch '.*\$$' } | select -expandproperty path) -replace '([A-Z]:).*', '$1'
foreach($p in $($paths | sort-object -Unique)) {
$deviceId = gwmi win32_volume | ? { $_.driveletter -eq $p } | select -expandproperty deviceid
if( $(gwmi win32_shadowcopy | ? {$_.volumename -eq $deviceId }) ) {
Write-Host "$($p) Volume Shadow Copy enabled"
}
else {
Write-Host "$($p) No Volume Shadow Copy"
}
}

Get total size of all Shared Folders (Except Admin Shares) from a list of Servers?

I'm looking to calculate the total size of all shared folders (except admin shares) on a number of different servers (consolidating all accessed files to a NAS box for easier backup / restore) but am having a bit of trouble finding a solution.
I'm certain this could be done in powershell but I just can't find the right information to get me going, I can currently spit out a list of all shares on the servers but am not sure where to go from here:
$servers =#(
"server1",
"server2")
foreach($server in $servers)
{
get-WmiObject Win32_Share -computerName $server -filter "Type = 0"
}
I would try to use Get-ChildItem to list the files and Measure-Object to count the sizes
$servers = #("server1", "server2")
$sizes = #()
foreach($server in $servers) {
write-host "Server: $server"
$serverSizes = #(gwmi -class Win32_Share -ComputerName $server -filter "Type = 0" |
% {
write-host " share: $($_.Name)"
$s = gci \\$server\$($_.Name) -recurse -force | Measure-Object -Property length -Sum
New-Object PSObject -property #{Name=$_.Name; Server=$server; TotalSize=$s.Sum }
})
if ($serverSizes) {
$totalServerSize = $serverSizes | Measure-Object -Property TotalSize -Sum
$serverSizes += New-Object PSObject -property #{Name="__Total__"; Server=$server; TotalSize=$totalServerSize.Sum }
$sizes += $serverSizes
}
}
Then you can e.g. select the total sizes like this:
$sizes |
? { $_.Name -eq '__Total__' } |
Select-Object Server,#{L='Size in MB'; E={$_.TotalSize/1mb}},#{L='Size in GB'; E={$_.TotalSize/1gb}}