PowerShell - drive variable - powershell

Hello Stackoverflow users,
I am a noob at powershell and this is part of my 1st script I am creating :). I am lost on how I would run a script that is dependent on a drive. I have script that runs task on the d: drive but some hosts does not have a D: drive but has an F: drive instead. What is the best way of adding this variable into the script?
Sample of the script is below
mkdir -Force -Path D:\Apps\NetprobeNT\Auto-monitor
Copy-Item D:\Apps\NetprobeNT\selfannounce.xml -Destination D:\Apps\NetprobeNT\Auto-monitor -Force
$removecomment = Get-Content D:\Apps\NetprobeNT\Auto-monitor\selfannounce.xml
$removecomment = $removecomment -replace "<!--<type>automonitor-windows</type>-->","" -replace "<!-- Autogenerated types -->","" -replace "<!--End of autogenerated types -->",""
$removecomment | ?{$_.Trim() -ne ""}
$removecomment | Out-File D:\Apps\NetprobeNT\Auto-monitor\selfannounce.xml -Encoding default
[xml]$selfannounceXml = Get-Content -Path D:\Apps\NetprobeNT\Auto-monitor\selfannounce.xml
$newCommentstart = $selfannounceXml.CreateComment('Autogenerated types')
$startupNode = $selfannounceXml.netprobe.selfAnnounce.managedEntity.types
$startupNode.InsertAfter($newCommentstart, $startupNode.LastChild)
$selfannounceXml.Save("D:\Apps\NetprobeNT\Auto-monitor\selfannounce.xml")
#Get IIS application path
Import-Module webadministration
$a = Get-Website | Select-Object Name
$a | ForEach-Object {
$_.name = $_.name.replace(" ","")
}
#Export file as .txt
$a | Format-Table -HideTableHeaders | Out-File D:\Apps\NetprobeNT\Auto-monitor\Website.txt
$b = Get-Content -Path D:\Apps\NetprobeNT\Auto-monitor\Website.txt
$b | ForEach {$_.TrimEnd()} | ? {$_.trim() -ne '' } > D:\Apps\NetprobeNT\Auto-monitor\Website.txt
$b = Get-Content -Path D:\Apps\NetprobeNT\Auto-monitor\Website.txt
#(ForEach ($a in $b) {$a.Replace(' ', '')}) > D:\Apps\NetprobeNT\Auto-monitor\Website.txt
#Get XML and add IIS path to 'types'
#Stop-Service -DisplayName NetprobeNT_DES
[xml]$xmlSA = Get-Content D:\Apps\NetprobeNT\Auto-monitor\selfannounce.xml
$b | ForEach-Object {
$tempchild = $xmlSA.CreateElement("type")
$tempchild.set_InnerText($_)
$newType = $xmlSA.netprobe.selfAnnounce.managedEntity.types.AppendChild($tempchild)
}
#$Newcommentstart =
$xmlSA.Save("D:\Apps\NetprobeNT\Auto-monitor\selfannounce.xml")
[xml]$selfannounceXml = Get-Content -Path D:\Apps\NetprobeNT\Auto-monitor\selfannounce.xml
$newCommentstart = $selfannounceXml.CreateComment('End of Autogenerated types')
$startupNode = $selfannounceXml.netprobe.selfAnnounce.managedEntity.types
$startupNode.InsertAfter($newCommentstart, $startupNode.LastChild)
$selfannounceXml.Save("D:\Apps\NetprobeNT\Auto-monitor\selfannounce.xml")
As you can see everything is dependent on D:\Apps.... but in some cases it might be F:\Apps..... How would I put some logic in or variable to know which drive is present? thank you for any help in advance.
Update:
From some help below, I can use the following method for now
$Path = "F:\Apps\NetprobeNT\"
$PathExists = Test-Path $Path
If ($PathExists -eq $True)
{
$DeviceID = "F:"}
Else
{
$DeviceID = "D:"}
How could I do something similar to the script above that would scan all drives and test-path to determine the $DeviceID? Note - must work for PowerShell 2.0 (windows 2003 host).
Thanks Again.
Update 2 -
I think the best method is the following as it will cater for any drive but I can not get it working. I know I am making a simple mistake -
Get-WmiObject win32_logicaldisk -Filter "DriveType=3 AND DeviceID!='C:'" | Select DeviceID | Format-Table -HideTableHeaders > c:\DeviceID.txt -Force
$DeviceID = Get-Content C:\DeviceID.txt
$DeviceID | ForEach {$_.TrimEnd()} | ? {$_.trim() -ne '' } > c:\DeviceID.txt
$DeviceID = Get-Content C:\DeviceID.txt
$Path = "$_\Apps\NetprobeNT\"
$PathExists = Test-Path $Path
foreach ($DeviceID in $DeviceID)
{
If ($PathExists -eq $True)
{
$DeviceDrive = $DeviceID}
Else
{
$DeviceDrive = "C:"}
}
I think the following line is the problem
$Path = "$_\Apps\NetprobeNT\"
Any ideas on to get this working?
Thank you

You can use WMI Filter, Filter only disk volumes which are not C Drive
$DeviceID = Get-WmiObject win32_logicaldisk -Filter "DriveType=3 AND DeviceID!='C:'" |
Select DeviceID
Then change the target:
mkdir -Force -Path "$DeviceID\Apps\NetprobeNT\Auto-monitor"
*Also, it's best practice to use one variable and call it each time, more readable, and easier, like this:
$TargetPath = "$DeviceID\Apps\NetprobeNT\Auto-monitor"
mkdir -Force -Path $TargetPath

I was able to achieve this from another post I posted. The below will make the $DeviceDrive C: drive by default. It will then search all volumes for the path and if true it will assign the drive to $DeviceDrive. Note - if two drives has the same path it will assign the last drive it finds to the variable.
$DeviceDrive = "C:"
Get-WmiObject win32_logicaldisk -Filter "DriveType=3 AND DeviceID!='C:'" |
Where-Object { Test-Path "$($_.DeviceID)\Apps\NetprobeNT\" } |
Foreach-Object {
$DeviceDrive = $_.DeviceID

Related

PSCustomObject Check Multiple Computers for folder

I am trying to check for presence of a particular folder "appdata\Local\Packages\ActiveSync" in each of the profile folders that are returned for each of the computer by the below script.Searching through various forums I got the script below and need further assistance to eventually output it to a file with results of Test-Path against each computer name and corresponding profile path.
e.g. \\Computer1\C:\users\John\appdata\Local\packages\ActiveSync False
Invoke-Command -Computer (get-content c:\temp\servers.txt) -ScriptBlock {
Get-childItem 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList' |
% {Get-ItemProperty $_.pspath }} | Select pscomputername,profileimagepath |
Where-Object { $_.ProfileImagePath -like "C:\users*" } | Out-File c:\temp\profiles.csv
For this, I think I would use a loop to go through all user path strings like below:
Invoke-Command -ComputerName (Get-Content -Path 'c:\temp\servers.txt') -ScriptBlock {
$regPath = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*'
Get-ItemPropertyValue -Path $regPath -Name 'ProfileImagePath' -ErrorAction SilentlyContinue |
Where-Object { $_ -like 'C:\Users*' } | ForEach-Object {
# combine the value with the rest of the path to form a LOCAL path
$path = Join-Path -Path $_ -ChildPath 'AppData\Local\Packages\ActiveSync'
[PsCustomObject]#{
ComputerName = $env:COMPUTERNAME
Path = '\\{0}\{1}' -f $env:COMPUTERNAME, ($path.Replace(":", "$")) # UNC format
Exists = Test-Path -Path $path -PathType Container
}
}
} | Export-Csv -Path 'c:\temp\profiles.csv' -NoTypeInformation
Please note that if the output should be a structured CSV file, you need to use Export-Csv on the resulting objects instead of Out-File.
Also you may need to append parameter -Credential to the Invoke-Command call where you can give it administrative credentials.

command 'search-index' does not appear to be working

here is the script I've created. I have downloaded the 'PSSearch' package and when I goto commands 'Search-Index' is one of the available commands
$computers = #([some computer])
$destination = "[some path]"
foreach ($computer in $computers) {
$Path = Set-Location [path on computer]
$keywords= #('"word 1"','word2','word3','word4')
$dirlist = Get-ChildItem -Recurse -Force $Path -ErrorAction Continue
foreach($word in $keywords) {
$SearchResults = Search-Index $word
$dirlist | Where-Object {$_.Name -match $SearchResults} | Select-Object Name,FullName | format-Table * -AutoSize |
Export-Csv $destination\FoundFiles.csv -nti -Append
$cui = ($dirlist | Where-Object {$_.Name -match $SearchResults})
Copy-Item $cui -Destination $destination - Append
}
}
What is happening is I'm getting all files and folders from the location (not just the ones I'm searching for)
The problem could be that I don't know how this line should be scripted
$cui = ($dirlist | Where-Object {$_.Name -match $SearchResults})

powershell truncates file fullnames ONLY as automated task

We have a file server that processes files that are received. When a file fails to process for whatever reason, it is moved into a failure folder. I've written a script to iterate through every possible one of these folders and spit out the FullName of the file into an e-mail which it sends to me.
Now when I run it manually, it works fine. However, when I set it as a scheduled task (running as Local System), the script still runs successfully, but the e-mail contains paths like \\blahblah\blah\blahblahblah\bl.....
I've tweaked the script a bunch of different ways and every time the output ends up the same. When I run it manually, it works as intended, when it runs as an automated script, it truncates the FullNames. I've found other people with this issue, but not as an automated task.
This is the relevant code of the script.
$emailFileList = ""
$filelist = #()
try {
GCI $topLevelPath -Recurse |
? { $_.PSIsContainer } |
ForEach-Object {
dir $_.FullName |
Where-Object {$_.FullName -like $unableToProcess} | ForEach-Object {
$filelist += dir $_.FullName
}
}
$emailFileList = Out-String -InputObject $($filelist | Select-Object FullName | Format-Table -AutoSize)
$emailBody = $emailBody + $emailFileList
}
EDIT:
I used the HTML method below but it added a bunch of junk markup. I added 4 lines to replace the markup with quotes. The inside of the try block now looks like this, and it works even as scheduled tasks.
GCI $topLevelPath -Recurse |
? { $_.PSIsContainer } |
ForEach-Object {
dir $_.FullName |
Where-Object {$_.FullName -like $unableToProcess} | ForEach-Object {
$filelist += dir $_.FullName
}
}
$emailFileList = $filelist | Select-Object FullName | ConvertTo-Html -fragment
$emailFileList = [regex]::Replace($emailFileList, "<table>.+</th></tr>", "")
$emailFileList = $emailFileList -replace '<tr><td>', '"'
$emailFileList = $emailFileList -replace '</td></tr>', """`r`n"
$emailFileList = $emailFileList -replace '</table>', ''
$emailBody = $emailBody + $emailFileList
I guess I also technically used regex on html what have I done noooooooo
Edit: Regardling answer "duplication" the problem above is SPECIFICALLY an interaction between powershell and the windows scheduled tasks.
This gives the kind of output you would probably expect
[command] | Format-Table -AutoSize | Out-String -Width 10000 #| clip.exe
Since you were using Format-Table -Autosize it was probably truncating due to the the amount of characters per line in the powershell instance. You can use the ConvertTo-Html function with the -Fragment command to create an HTML table.
Try something like this:
$emailFileList = ""
$filelist = #()
try {
Get-ChildItem $topLevelPath -Recurse `
| Where-Object -Property PSIsContainer -EQ -Value $True `
| ForEach-Object {
Get-ChildItem $_.FullName |
Where-Object -Property FullName -Like -Value $UnableToProcess `
| ForEach-Object {
$filelist += Get-ChildItem $_.FullName
}
}
$emailFileList = $filelist | Select-Object FullName | ConvertTo-Html -Fragment
$emailBody = $emailBody + $emailFileList
}
catch
{
}
Your problem is the formatter truncating because the console host can't render the full strings. Here's a solution where you'll get a txt with a list of names that can be used however you want
General rule of thumb: filter left, format right.
#Requires -Version 3
Try
{
Get-ChildItem -Path $TopLevelPath -Recurse -Folder |
## If you're on version 2, replace the -Folder switch with the following:
#Where-Object { $_.PSIsContainer }
ForEach-Object {
## If version 2, remove #().FullName and replace with
# | Select-Object -ExpandProperty FullName
$FileList = #(Get-ChildItem -Path $_.FullName -Filter "*$UnableToProcess*").FullName
}
$EmailBody += #($FileList)
}
Catch
{
}

A more efficient way to import multiple CSV files from a directory

There must be a more efficient way to import multiple CSV files from a directory where the name of the file contains Services_Results*.csv into one variable with unique entries. I'm thinking of looping through all the files in the directory that match the file name with a wildcard then just importing the lines where Success is on the field.
$Success0 = Import-Csv -Path "\\FILE05\Users\USER001\+Projects\Chrome\Services_Results.csv" | Where-Object {$_.Status -eq "Success"}
$Success1 = Import-Csv -Path "\\FILE05\Users\USER001\+Projects\Chrome\Services_Results_150601.csv" | Where-Object {$_.Status -eq "Success"}
$Success2 = Import-Csv -Path "\\FILE05\Users\USER001\+Projects\Chrome\Services_Results_150602.csv" | Where-Object {$_.Status -eq "Success"}
$Success3 = Import-Csv -Path "\\FILE05\Users\USER001\+Projects\Chrome\Services_Results_150602_b.csv" | Where-Object {$_.Status -eq "Success"}
$Success4 = Import-Csv -Path "\\FILE05\Users\USER001\+Projects\Chrome\Services_Results_150602_c.csv" | Where-Object {$_.Status -eq "Success"}
$Success5 = Import-Csv -Path "\\FILE05\Users\USER001\+Projects\Chrome\Services_Results_150603_a.csv" | Where-Object {$_.Status -eq "Success"}
$PCList = $Success0 + $Success1 + $Success2 + $Success3 + $Success4 + $Success5
$PCList = $PCList.PC | sort -Unique
Write-host "PCList" $PCList.count
From Get-Help Import-CSV:
-Path <String[]>
Specifies the path to the CSV file to import. You can also pipe a path to Import-Csv.
Required? false
Position? 1
Default value None
Accept pipeline input? true (ByValue)
Accept wildcard characters? false
So Import-CSV will accept multiple values for -Path from the pipeline:
$PCList =
Get-ChildItem '\\FILE05\Users\USER001\+Projects\Chrome\Services_Results*.csv' |
Select -ExpandProperty FullName |
Import-CSV |
Where-Object {$_.Status -eq "Success"}
How about using PowerShell Background Jobs to speed up the process? I haven't tested this code, but try to read an understand the pattern. I think you'll appreciate how much it can speed up the process, if you're importing large CSV files.
### Create an empty array to hold Background Jobs.
$JobList = #();
### Get a list of CSV files.
$FileList = Get-ChildItem -Path \\FILE05\Users\USER001\+Projects\Chrome\*Services_Results*.csv;
foreach ($File in $FileList) {
### Start a new Background Job for each file
$JobList += Start-Job -ScriptBlock { Import-Csv -Path $args[0] | Where-Object -FilterScript { $PSItem.Status -eq 'Success'; } } -ArgumentList $File.FullName -Name $File.Name;
}
### Wait for all the jobs to complete.
Wait-Job -Job $JobList;
### Receive the results of the jobs.
$ResultList = Receive-Job -Job $JobList;
I think you can do what you want quite neatly with the way you're already doing it, but using a wildcard and a ForEach loop:
$files = Get-ChildItem "\\FILE05\Users\USER001\+Projects\Chrome\*.csv"
$PCList = $files | ForEach { (Import-Csv $_ | Where Status -eq "Success") }
$PCList = $PCList | sort -Unique -Property PC
Write-Output $PCList
Write-Output $PCList.Count

Write parts from filename including the first line in merged txt / csv -file with powershell

I'm currently working on a PowerShell script that reads out the default printer on several workstations and write the information in a textfile to a network drive. My last question regarding some text replacements inside the script was successfully solved. But now I work on the second part.
Get-WmiObject -Class Win32_Printer -Filter "Default = $true" | % {
$_.Name -replace '(?:.*)\\NDPS-([^\.]+)(?:.*)', 'PS-$1'
} | Out-File -FilePath "H:\daten\printer\$($env:COMPUTERNAME)_defaultprinter.txt"
Get-WmiObject Win32_Printer -Filter "Default = $true" `
| Select-Object -expandProperty Name `
| Out-File -FilePath "P:\batch\migration\Printer\$($env:COMPUTERNAME)_$($env:USERNAME)_defaultprinter.txt"
The last line of the provided code writes the default printer to the network drive. Now I have there nearly 1500 single txt-files. For better analysis I use the following PowerShell script to merge all the single txt files to one big file.
Get-ChildItem -path \\samplepath\prgs\prgs\batch\migration\Printer -recurse | ? {
! $_.PSIsContainer
} | ? {
($_.name).contains(".txt")
} | % {
Out-File -filepath \\samplepath\prgs\prgs\batch\migration\Printer\gesamt_printer.txt -inputobject (get-content $_.fullname) -Append
}
I receive a file wich contains the default printer information from every txt-file but I need the $($env:USERNAME)-part from the filename as a separate value in addition to the printer information in on line to use the data in Excel. Can someone please provide me a tip how to insert the part from filename in the merged file?
You could extract the username part from the file name like this:
$_.Name -match '^.*?_(.*)_.*?\.txt$'
$username = $matches[1]
The group in the regular expression (accsisible via $matches[1]) contains the text between the first and the last underscore in the filename.
You could use it like this:
$root = "\\samplepath\prgs\prgs\batch\migration\Printer"
$outfile = "$root\gesamt_printer.txt"
Get-ChildItem $root -Recurse | ? {
-not $_.PSIsContainer -and $_.Extension -eq ".txt"
} | % {
$_.Name -match '^.*?_(.*)_.*?\.txt$'
$username = $matches[1]
$content = Get-Content $_.FullName
"{0},{1}" -f ($content, $username) | Out-File $outfile -Append
}
You could also directly create a CSV:
$root = "\\samplepath\prgs\prgs\batch\migration\Printer"
$outfile = "$root\gesamt_printer.txt"
$getPrinter = { Get-Content $_.FullName }
$getUser = { $_.Name -match '^.*?_(.*)_.*?\.txt$' | Out-Null; $matches[1] }
Get-ChildItem $root -Recurse `
| ? { -not $_.PSIsContainer -and $_.Extension -eq ".txt" } `
| select #{n="Username";e=$getUser},#{n="Printer";e=$getPrinter} `
| Export-Csv $outfile -NoTypeInformation
Note that these code sample don't contain any checks to exclude file names that don't have at least two underscores in them.