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
}
}
}
Related
I am trying to come up with a PowerShell script that will give me an accurate count of the number of user profiles that are on a pc. Excluding any pc that has less that 5 accounts. I don't use PowerShell that often so I don't know the correct syntax for it.
$excludedAccounts = #('admin', "Administrator", "Domain Admins", "LocalAdmin")
$userProfiles = Get-ChildItem -Path "C:\Users" -Directory | Select-Object -ExpandProperty Name
$count = 5
($profile in $userProfiles) {
(-not ($excludedAccounts -contains $profile)) {
$count++
}
}
Write-Host "Number of user profiles (excluding domain and local admin accounts): $count"
This works pretty well, but it isn't what I need.
In your code you forgot a foreach and an if, but actually you can do this with a simple Where-Object clause in which you do a regex -notmatch on string admin like this:
# get all user profile folder names except for those that have the word 'admin' in it
$userProfiles = #(Get-ChildItem -Path "C:\Users" -Directory | Where-Object { $_.Name -notmatch 'admin' }).Name
Write-Host "Number of user profiles (excluding domain and local admin accounts): $($userProfiles.Count)"
If you need to do this on multiple computers, use Invoke-Command like so:
$cred = Get-Credential -Message 'Please enter your admin credentials here'
$computers = 'pc01', 'pc02', 'pc03' # the list of computers to probe
$result = Invoke-Command -ComputerName $computers -Credential $cred -ScriptBlock {
# get all user profile folder names except for those that have the word 'admin' in it
$userProfiles = #(Get-ChildItem -Path "C:\Users" -Directory | Where-Object { $_.Name -notmatch 'admin' }).Name
# now output an object to collect in the $result variable
[PsCustomObject]#{
'ComputerName' = $env:COMPUTERNAME
'Non-Admin Profiles' = $userProfiles.Count
}
}
Then if you want to see what computers have 5 or more non admin profiles for instance you do
$result | Where-Object { $_.'Non-Admin Profiles' -ge 5 }
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.
I'm trying to Get the Name, Manufacturer, and model of computers so i can distinguish what computers are out of warranty in AD.
I'm trying to do this by getting the computer names and putting there info into the corresponding .csv file but this fails and puts 1 ou to multiple .csv files and then moves to the second ou and does the same thing?
$myMultiArray = #(("OU=Domain Controllers,DC=FABRIKAM,DC=COM"),
("OU=Computers,DC=FABRIKAM,DC=COM"))
$myFileArray = #(("D:\VS-Code\Powershell\AD_Computer_Management\OUs\Domain
Controllers.csv"),("D:\VS-
Code\Powershell\AD_Computer_Management\OUs\Computers.csv"))
foreach ($MultiOU in $myMultiArray) {
Get-ADComputer -Filter * -SearchBase $MultiOU -SearchScope 2 | Select-object Name | Out-File -FilePath "D:\VS-Code\Powershell\AD_Computer_Management\OUs\garbage.csv"
For ($i = 0; $i – $myFileArray.Length - 1; $i++) {
Write-Host $myMultiArray[$i]
[string[]]$cnArray = Get-Content -Path 'D:\VS-Code\Powershell\AD_Computer_Management\OUs\garbage.csv'
Write-Host $OU
if ($i -eq $i) {
foreach($CN in $cnArray){
Get-WmiObject -Class:Win32_ComputerSystem -ComputerName $OU | Format-List -Property Name, Manufacturer, Model | Out-File -FilePath $myFileArray[$1]
}
}
}
}
I've tried multiple variations of different loops and if statements.
I think there are two things:
Out-File -FilePath $myFileArray[$1]
Should be:
Out-File -FilePath $myFileArray[$i]
And also you might need to append:
Out-File -FilePath $myFileArray[$i] -Append
There are a couple of things wrong in your code, like $i – $myFileArray.Length, which should be $i –lt $myFileArray.Length.
Then there is Out-File -FilePath $myFileArray[$1] as Bernard Moeskops already mentioned.
Also your code seems to want to create both the Domain Controllers.csv aswell as the Computers.csv files regardless of the OU you are currently in.
Lastly, you are using Out-File to create the CSV files where for proper CSV output, you should use the Export-Csv cmdlet.
The following code should do what you want:
$myOUArray = "OU=Domain Controllers,DC=FABRIKAM,DC=COM", "OU=Computers,DC=FABRIKAM,DC=COM"
$myFilePath = "D:\VS-Code\Powershell\AD_Computer_Management\OUs" # just the path for the output files is needed
foreach ($OU in $myOUArray) {
# determine the file name from the OU we're in
$fileName = if ($OU -match 'OU=Domain Controllers') { 'Domain Controllers.csv' } else { 'Computers.csv'}
$filePath = Join-Path -Path $myFilePath -ChildPath $fileName
Write-Host "Getting computer info from OU '$OU'"
# get a string array of the computernames found in the OU
$computers = Get-ADComputer -Filter * -SearchBase $OU -SearchScope Subtree | Select-Object -ExpandProperty Name
# loop through this array to get the properties you want for
# each computer and store that as objects in the $result variable
$result = foreach($machine in $computers){
Get-WmiObject -Class:Win32_ComputerSystem -ComputerName $machine | Select-Object -Property Name, Manufacturer, Model
}
Write-Host "Creating file '$filePath'"
# save the CSV file to disk
$result | Export-Csv -Path $filePath -NoTypeInformation -Force
}
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.
Every time I run the script below I get
Cannot bind argument to parameter 'FilePath' because it is null.
It was working last night. No changes have been made and this morning it just fails. the funny thing is if i save the script and then run it, it works. However if I clear console then run it again it fails.
Am I missing something obvious?
New-Item -ItemType Directory -Force -Path C:\NonStandard_Services
set-location C:\NonStandard_Services
$Computers= Get-Content C:\computers.txt
$Report= $file
$file= $Computer
ForEach ($Computer in $Computers)
{
Get-WmiObject -ComputerName $Computer -class Win32_Service -ErrorAction SilentlyContinue |
Where-Object -FilterScript {$_.StartName -ne "LocalSystem"}|
Where-Object -FilterScript {$_.StartName -ne "NT AUTHORITY\NetworkService"} |
Where-Object -FilterScript {$_.StartName -ne "NT AUTHORITY\LocalService"} |
Select-Object -Property StartName,Name,DisplayName|
ConvertTo-Html -Property StartName,Name,DisplayName -head $HTML -body "<H2>Non- Standard Service Accounts on $Computer</H2>"| Out-File $Report -Append}
#Rename-Item c:\GP_Services\Report.htm $file
Get-ChildItem | Where-Object {$_.extension -ne ".htm"} | Rename-Item -newname { $_.name + '.htm' }
$Report= $file
$file= $Computer
ForEach ($Computer in $Computers)
{
...
}
You assign variables to other variables before they were assigned a value themselves. Change the above to this:
ForEach ($Computer in $Computers) {
$file = $Computer
$Report = $file
...
}
Or directly use $Computer in Out-File:
... | Out-File "$Computer.txt" -Append