Display Data on Two Columns Within Excel - excel

I am trying to display data within an Excel document where Column A displays the server name and column B displays the .NET version. I'm running into an issue exporting to a .csv because it says that the file path does not exist. I would like some guidance on how I can resolve that issue and how I can display data on the two columns within Excel.
$Servers =
(
"test"
)
foreach ($Server in $Servers)
{
Invoke-Command -ComputerName $Server -ScriptBlock {
Write-Output "$(hostname)"
Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -Recurse | Get-ItemProperty -Name Version,Release -EA 0 | where { $_.PSChildName -match '^(?!S)\p{L}'} | select PSChildName, Version, Release | Select -ExpandProperty Version | Sort-Object Version | Export-Csv -Path C:\Users\User\Desktop\example.csv
}

The main issue is that you're using Export-Csv on the remote hosts since it is inside the Invoke-Command script block, and the likeable error is because the path you are using as export doesn't exist on those hosts.
It's also worth noting that Invoke-Command can run in parallel, -ComputerName as well as -Session can take an array, this removes the need for the foreach loop as well as it is much faster / efficient.
Invoke-Command -ComputerName $servers -ScriptBlock {
Write-Host "Working on $($env:COMPUTERNAME)..."
Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\NET Framework Setup\NDP' -Recurse |
Get-ItemProperty -Name Version, Release -EA 0 |
ForEach-Object {
if($_.PSChildName -notmatch '^(?!S)\p{L}') {
return # skip this
}
[pscustomobject]#{
HostName = $env:COMPUTERNAME
Version = $_.Version
}
} | Sort-Object Version
} -HideComputerName | Select-Object * -ExcludeProperty RunspaceID |
Export-Csv -Path C:\Users\User\Desktop\example.csv -NoTypeInformation

Related

Searching contents of text files on remote computers and exporting to excel file using powershell and export-excel

I'm trying to search the contents of text files on remote computers from computers.txt which includes
Pc1
Pc2
Pc3
Pc4
And export it using export-excel PowerShell module
using this code:
$directory = $PSScriptRoot
$computers = Get-Content -Path $directory\computers.txt
$searchwords = 'word1','word2','word3'
Foreach ($computer in $computers) {
$path = "\\$computer\C$\test\logs"
Foreach ($sw in $searchwords) {
$excel = Get-Childitem -path $path -recurse -Include "*.txt" |
Select-string -pattern "$sw" |
Select-object pattern, linenumber, line, path |
Export-excel $file -autosize -startrow 1 -tablename pattern -worksheetname "errors" -passthru
$ws = $excel.workbook.worksheets['errors']
$excel.save()
}
}
The problem is that it will only export the contents of pc4 which is the last in the computers.txt list.
Thanks in advance
Adding the -append switch on export-excel will get this working.
It was added as part of the release on 10/30/2017 - https://github.com/dfinke/ImportExcel#whats-new-in-release-52

LOCATING FILES USING MULTIPLE STRING AND DATE CRITERIA

I am trying to use a code as follows, to locate files with specific strings, extension and lastwritetime :
get-childitem C:\users\nila9\Downloads -filter *.mkv -recurse
| where-object { $_.Name -match ("*bluray*" -and "*1080*") -and $_.lastwritetime -match "11/20/2020" }
The code is meant to first filter all files in the downloads folder with the extension *.mkv and then further shortlist for filenames containing the string "bluray" and "1080" and modified after the specified date.
While this code does not return any error, it does however not execute and releases the control to the prompt.
Is there someplace I am getting it wrong?
Thanks
As Lee_Dailey pointed out, -match uses regex and needs a different syntax. Your code needs a different operator that handles wildcards (*) and for that, there is -like
Also, you should not try to compare a DateTime object with a string, so -match is no good for that either.
If a filename needs BOTH bluray AND 1080, you can change the code to
$refDate = (Get-Date -Year 2020 -Month 11 -Day 20).Date # set time part to all 0 (--> midnight)
Get-ChildItem -Path 'C:\users\nila9\Downloads' -Filter '*.mkv' -Recurse |
Where-Object { $_.Name -like "*bluray*" -and $_.Name -like "*1080*" -and $_.LastWriteTime -ge $refDate }
If however the file needs to have bluray OR 1080 in its name, you can use -match on that part:
$refDate = (Get-Date -Year 2020 -Month 11 -Day 20).Date # set time part to all 0 (--> midnight)
Get-ChildItem -Path 'C:\users\nila9\Downloads' -Filter '*.mkv' -Recurse |
Where-Object { $_.Name -match "bluray|1080" -and $_.LastWriteTime -ge $refDate }
The pipe symbol | in regex is the OR operator

How to export the Certificate details related to the particular IIS site using Powershell script

I wrote a script where in it will export all the SSL certificate details from my machine to an Excel sheet, but I need to export the Certificates which are mapped to the particular site in IIS and then I need to export those details with Site name and the Certificate details to an Excel sheet.
Code
#Clearing the Console host in PS
Clear-Host
#Installing the Excel module to the Powershell
Install-Module -Name ImportExcel
#List of Servers
$computers = Get-Content "C:\TEMP\servers.txt"
#Number of days to look for expiring certificates
$threshold = 300
#Set deadline date
$deadline = (Get-Date).AddDays($threshold)
Invoke-Command -ComputerName $computers {
Get-ChildItem -Path 'Cert:\LocalMachine\My' -Recurse |
Select-Object -Property #{n='ServerName';e={$env:COMPUTERNAME}},Issuer, Subject, NotAfter,
##{Label = 'ServerName';Expression = {$env:COMPUTERNAME}}
#{Label='Expires In (Days)';Expression = {(New-TimeSpan -Start (Get-Date) -End $PSitem.NotAfter).Days}}
} | Export-Excel -Path C:\users\$env:username\documents\MultipleServer_Certificate_Expiry_Details.xlsx`
This is a very common thing, with many articles and samples all over the web on this IIS use case. This is what the web administration module is used for.
<#
Get all IIS bindings and SSL certificates
On a local or remote IIS PowerShell Session
#>
Import-Module -Name WebAdministration
Get-ChildItem -Path IIS:SSLBindings |
ForEach-Object -Process {
if ($_.Sites)
{
$certificate = Get-ChildItem -Path CERT:LocalMachine/My |
Where-Object -Property Thumbprint -EQ -Value $_.Thumbprint
[PsCustomObject]#{
Sites = $_.Sites.Value
CertificateFriendlyName = $certificate.FriendlyName
CertificateDnsNameList = $certificate.DnsNameList
CertificateNotAfter = $certificate.NotAfter
CertificateIssuer = $certificate.Issuer
}
}
}
Customize the above to fit your output needs.
Note if you happen to be on a legacy version of PowerShell:
[PsCustomObject]#{} will not work in PS 2.0 but you may replace it by New-Object -TypeName PSObject
Update
You've asked for a sample script to run on multiple servers. However, you already have the code in your post. Just put that Invoke-Command inside a ForEach loop and pass in a list of computers.
$Computers |
ForEach {
Invoke-Command -ComputerName $PSItem -ScriptBlock {
Get-ChildItem -Path 'Cert:\LocalMachine\My' -Recurse |
Select-Object -Property #{n='ServerName';e={$env:COMPUTERNAME}},Issuer, Subject, NotAfter,
#{Label='Expires In (Days)';Expression = {(New-TimeSpan -Start (Get-Date) -End $PSitem.NotAfter).Days}}
} | Export-Excel -Path "C:\users\$env:username\documents\MultipleServer_Certificate_Expiry_Details.xlsx"
}
Of course, you'll need to add in that sample for the Web Admin block to your cert data points

Passing PowerShell Output to CSV/Excel

I previously had asked a question regarding adding together files and folders with a common name and having them summed up with a total size (Sum of file folder size based on file/folder name). This was successfully answered with the PS script below:
$root = 'C:\DBFolder'
Get-ChildItem "$root\*.mdf" | Select-Object -Expand BaseName |
ForEach-Object {
New-Object -Type PSObject -Property #{
Database = $_
Size = (Get-ChildItem "$root\$_*\*" -Recurse |
Measure-Object Length -Sum |
Select-Object -Expand Sum ) / 1GB
}
}
This now leaves me with a list that is ordered by the 'Database' Property by default. I have attempted to use a Sort-Object suffix to use the 'Size' property with no joy. I have also attempted to use Export-Csv with confounding results.
Ideally, if I could pass the results of this script to Excel/CSV so I can rinse/repeat across multiple SQL Servers and collate the data and sort within Excel, I would be laughing all the way to the small dark corner of the office where I can sleep.
Just for clarity, the output is looking along the lines of this:
Database Size
-------- ----
DBName1 2.5876876
DBName2 4.7657657
DBName3 3.5676578
Ok, it was one pipe character that I had missed when using the Export-csv function. This resolved my problem.
$root = 'C:\DB\Databases'
Get-ChildItem "$root\*.mdf" | Select-Object -Expand BaseName |
ForEach-Object {
New-Object -Type PSObject -Property #{
Database = $_
Size = (Get-ChildItem "$root\$_*\*" -Recurse |
Measure-Object Length -Sum |
Select-Object -Expand Sum ) / 1GB
}
} | Export-Csv 'C:\Test\test.csv'

Decreased output with PowerShell multithreading than with singlethread script

I am using PowerShell 2.0 on a Windows 7 desktop. I am attempting to search the enterprise CIFS shares for keywords/regex. I already have a simple single threaded script that will do this but a single keyword takes 19-22 hours. I have created a multithreaded script, first effort at multithreading, based on the article by Surly Admin.
Can Powershell Run Commands in Parallel?
Powershell Throttle Multi thread jobs via job completion
and the links related to those posts.
I decided to use runspaces rather than background jobs as the prevailing wisdom says this is more efficient. Problem is, is I am only getting partial resultant output with the multithreaded script I have. Not sure if it is an I/O thing or a memory thing, or something else. Hopefully someone here can help. Here is the code.
cls
Get-Date
Remove-Item C:\Users\user\Desktop\results.txt
$Throttle = 5 #threads
$ScriptBlock = {
Param (
$File
)
$KeywordInfo = Select-String -pattern KEYWORD -AllMatches -InputObject $File
$KeywordOut = New-Object PSObject -Property #{
Matches = $KeywordInfo.Matches
Path = $KeywordInfo.Path
}
Return $KeywordOut
}
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, $Throttle)
$RunspacePool.Open()
$Jobs = #()
$Files = Get-ChildItem -recurse -erroraction silentlycontinue
ForEach ($File in $Files) {
$Job = [powershell]::Create().AddScript($ScriptBlock).AddArgument($File)
$Job.RunspacePool = $RunspacePool
$Jobs += New-Object PSObject -Property #{
File = $File
Pipe = $Job
Result = $Job.BeginInvoke()
}
}
Write-Host "Waiting.." -NoNewline
Do {
Write-Host "." -NoNewline
Start-Sleep -Seconds 1
} While ( $Jobs.Result.IsCompleted -contains $false)
Write-Host "All jobs completed!"
$Results = #()
ForEach ($Job in $Jobs) {
$Results += $Job.Pipe.EndInvoke($Job.Result)
$Job.Pipe.EndInvoke($Job.Result) | Where {$_.Path} | Format-List | Out-File -FilePath C:\Users\user\Desktop\results.txt -Append -Encoding UTF8 -Width 512
}
Invoke-Item C:\Users\user\Desktop\results.txt
Get-Date
This is the single threaded version I am using that works, including the regex I am using for socials.
cls
Get-Date
Remove-Item C:\Users\user\Desktop\results.txt
$files = Get-ChildItem -recurse -erroraction silentlycontinue
ForEach ($file in $files) {
Select-String -pattern '[sS][sS][nN]:*\s*\d{3}-*\d{2}-*\d{4}' -AllMatches -InputObject $file | Select-Object matches, path |
Format-List | Out-File -FilePath C:\Users\user\Desktop\results.tx -Append -Encoding UTF8 -Width 512
}
Get-Date
Invoke-Item C:\Users\user\Desktop\results.txt
I am hoping to build this answer over time as I dont want to over comment. I dont know yet why you are losing data from the multithreading but i think we can increase performace with an updated regex. For starters you have many greedy quantifiers that i think we can shrink down.
[sS][sS][nN]:*\s*\d{3}-*\d{2}-*\d{4}
Select-String is case insensitive by default so you dont need the portion in the beginning. Do you have to check for multiple colons? Since you looking for 0 or many :. Same goes for the hyphens. Perhaps these would be better with ? which matches 0 or 1.
ssn:?\s*\d{3}-?\d{2}-?\d{4}
This is assuming you are looking for mostly proper formatted SSN's. If people are hiding them in text maybe you need to look for other delimiters as well.
I would also suggest adding the text to separate files and maybe combining them after execution. If nothing else just to test.
Hoping this will be the start of a proper solution.
It turns out that for some reason the Select-String cmdlet was having problems with the multithreading. I don't have enough of a developer background to be able to tell what is happening under the hood. However I did discover that by using the -quiet option in Select-String, which turns it into a boolean output, I was able to get the results I wanted.
The first pattern match in each document gives a true value. When I get a true then I return the Path of the document to an array. When that is finished I run the pattern match against the paths that were output from the scriptblock. This is not quite as effective performance wise as I had hoped for but still a pretty dramatic improvement over singlethread.
The other issue I ran into was the read/writes to disk by trying to output results to a document at each stage. I have changed that to arrays. While still memory intensive, it is much quicker.
Here is the resulting code. Any additional tips on performance improvement are appreciated:
cls
Remove-Item C:\Users\user\Desktop\output.txt
$Throttle = 5 #threads
$ScriptBlock = {
Param (
$File
)
$Match = Select-String -pattern 'ssn:?\s*\d{3}-?\d{2}-?\d{4}' -Quiet -InputObject $File
if ( $Match -eq $true ) {
$MatchObjects = Select-Object -InputObject $File
$MatchOut = New-Object PSObject -Property #{
Path = $MatchObjects.FullName
}
}
Return $MatchOut
}
$RunspacePool = [RunspaceFactory]::CreateRunspacePool(1, $Throttle)
$RunspacePool.Open()
$Jobs = #()
$Files = Get-ChildItem -Path I:\ -recurse -erroraction silentlycontinue
ForEach ($File in $Files) {
$Job = [powershell]::Create().AddScript($ScriptBlock).AddArgument($File)
$Job.RunspacePool = $RunspacePool
$Jobs += New-Object PSObject -Property #{
File = $File
Pipe = $Job
Result = $Job.BeginInvoke()
}
}
$Results = #()
ForEach ($Job in $Jobs) {
$Results += $Job.Pipe.EndInvoke($Job.Result)
}
$PathValue = #()
ForEach ($Line in $Results) {
$PathValue += $Line.psobject.properties | % {$_.Value}
}
$UniqValues = $PathValue | sort | Get-Unique
$Output = ForEach ( $Path in $UniqValues ) {
Select-String -Pattern '\d{3}-?\d{2}-?\d{4}' -AllMatches -Path $Path | Select-Object -Property Matches, Path
}
$Output | Out-File -FilePath C:\Users\user\Desktop\output.txt -Append -Encoding UTF8 -Width 512
Invoke-Item C:\Users\user\Desktop\output.txt

Resources