Trying to match strings when importing a CSV with Powershell - powershell

I am trying to create a report showing older operating systems out of a CSV file. The program displays the output beautifully, but doesn't actually parse anything. I'm trying to separate out Windows 2000, 2003, XP, and yes, windows NT machines. The sample data is below:
Finding Title,IP Address / FQDN,Port,Service,OS,FQDN,Default Username,Default Password,Remediation Notes,Misc Notes,Misc Notes 2,Contact,Remediation
Default Password (yada) for 'yadayada' Account on Windows,fakefake,445,cifs,Microsoft Windows Server 2003 Service Pack 2,fakefake,yadayada,yadayada,Change the password.,,,,
Default Password (yadayada) for 'yadayada' Account on Windows,fakefake1,445,cifs,Microsoft Windows Server 2003 Service Pack 2,fakefake1,yadayada,yadayada,Change the password.,,,,
Default Password (yadayada) for 'yadayada' Account on Windows,fakefake2,445,cifs,Microsoft Windows Server 2003 Service Pack 1,fakefake2,yadayada,yadayada,Change the password.,,,,
Default Password (yadayada) for 'yadayada' Account on Windows,fakefake3,445,cifs,Microsoft Windows Server 2008 R2 Standard,fakefake3,yadayada,yadayada,Change the password.
The program I have so far is here:
$name = Read-Host 'CSV File?'
$csvs = import-csv $name
$object = $csvs.os
ForEach ( $object in $csvs ) {
if ($object -like '*2003*') { out-file -inputobject $object -append $name}
elseif ($object -like '*2000*') { out-file -inputobject $object -append $name}
elseif ($objects -clike '*NT*') { out-file -inputobject $object -append $name}
elseif ($object -clike '*XP*') { out-file -inputobject $object -append $name}
write-output $outfile
}
$a = "<style>"
$a = $a + "BODY{background-color:white;}"
$a = $a + "TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}"
$a = $a + "TH{border-width: 1px;padding: 2px;border-style: solid;border-color: black;background-color:thistle}"
$a = $a + "TD{border-width: 1px;padding: 2px;border-style: solid;border-color: black;background-color:white}"
$a = $a + "TD:Nth-Child(Odd) {Background-Color: #dddddd;}"
$a = $a + "</style>"
import-csv $name | ConvertTo-HTML -head $a| Out-File C:\Users\myuser\Downloads\scripts\Test.htm

I think you have a bug in there, you have 2 variables named object that you are using for different purposes:
$object = $csvs.os
ForEach ( $object in $csvs ) # object above getting overwritten
I would just get rid of the first object assignment and write the loop like this:
ForEach ( $object in $csvs ) {
if ($object.os -like '*2003*') { out-file -inputobject $object -append $name}
elseif ($object.os -like '*2000*') { out-file -inputobject $object -append $name}
elseif ($object.os -clike '*NT*') { out-file -inputobject $object -append $name}
elseif ($object.os -clike '*XP*') { out-file -inputobject $object -append $name}
}
Though you can avoid the repetition like this:
$csvs | where-object{ $_.os -like '*2003*' -or $_.os -like '*2000*' -or $_.os -clike '*NT*' -or $_.os -clike '*XP*'} | out-file -append $name

Related

How can I use Powershell to find when an SSL certificate expires for ONLY IIS for a list of servers from OU?

I have this section of code that if I can merely get the script to ONLY reply with Subject that exists (which indicates the IIS cert), then I can be done... (I have the OU enumeration, and the Invoke section down, and the email of the file for scheduling in a task):
[NOTE: I have the expiration set to 500 days so I can then use the script later to merely find specific expiration times]
[NOTE2: $day is set in my $profile to '$day = Get-Date -Format yyyyMMdd']
$serverlist = $serverListpath.Name
foreach($server in $serverlist){
if($server -like '#*')
{
continue
}
$threshold = 500 #Number of days to look for expiring certificates
$deadline = (Get-Date).AddDays($threshold) #Set deadline date
$p = ($c++/$server.count) * 100
Write-Progress -Activity "Checking $._" -Status "$p % completed" -PercentComplete $p;
if(Test-Connection -ComputerName $server -Count 2 -Quiet){
#$server = "KnownIISServerHostname" #<-- to test with a hostname
Invoke-Command -Verbose -ComputerName $server { Dir Cert:\LocalMachine\My } |`
foreach {
If ($_.NotAfter -le $deadline) {
$_ | Select *| select PSComputerName, Subject, NotAfter, #{Label="Expires In (Days)";Expression={($_.NotAfter - (Get-Date)).Days}} }
}|`
select PSComputerName,Subject, NotAfter, #{Label="Expires In (Days)";Expression={($_.NotAfter - (Get-Date)).Days}} |`
export-csv -Force -Append -Encoding ASCII -NoTypeInformation .\output\$day-ExpiringIISSSLCerts.csv
}
}
So where do I tweak this to get the reply to ONLY have existing "Subject" fields; Not to get the null subject field replies (which are RDP certificates)
Try to use this:
Import-Module WebAdministration
$CertAll=Get-ChildItem -Path Cert:\LocalMachine\My
$CertInUse=Get-Childitem -Path IIS:\SslBindings
$CertSame=Compare-Object -ReferenceObject $CertAll -DifferenceObject $CertInUse -Property ThumbPrint -IncludeEqual -ExcludeDifferent
$CertSame | foreach{Get-Childitem –path Cert:\LocalMachine\My\$($_.thumbprint)} | Select-Object -Property Subject, #{n=’ExpireInDays’;e={($_.notafter – (Get-Date)).Days}}
Since IIS certificates are your scope of concern here, I would suggest using the IIS PowerShell module to make sure you're selecting only certificates that are actually in use by IIS.
The following should pull certificates attached to sites with HTTPS(SSL). I don't currently have multiple sites on a single IIS server for testing, but theoretically this should find all of them, not just the "Default Web Site."
$serverlist = $serverListpath.Name
foreach($server in $serverlist){
if($server -like '#*')
{
continue
}
$threshold = 500 #Number of days to look for expiring certificates
$deadline = (Get-Date).AddDays($threshold) #Set deadline date
$p = ($c++/$server.count) * 100
Write-Progress -Activity "Checking $._" -Status "$p % completed" -PercentComplete $p;
if(Test-Connection -ComputerName $server -Count 2 -Quiet){
#$server = "KnownIISServerHostname" #<-- to test with a hostname
#Pull certificates from existing IIS bindings
$certificates = Invoke-Command -Verbose -ComputerName $server {
Import-Module IISAdministration
$sitebindings = Get-IISSite | foreach { Get-IISSiteBinding -Protocol HTTPS -Name $_ }
$thumbprints = $sitebindings.Attributes | where {$_.Name -match "certificateHash"} | Select-Object -ExpandProperty Value
$thumbprints | foreach {dir Cert:\LocalMachine\My\$_}
}
$certificates |`
foreach {
If ($_.NotAfter -le $deadline) {
$_ | Select *| select PSComputerName, Subject, NotAfter, #{Label="Expires In (Days)";Expression={($_.NotAfter - (Get-Date)).Days}} }
}|`
select PSComputerName,Subject, NotAfter, #{Label="Expires In (Days)";Expression={($_.NotAfter - (Get-Date)).Days}} |`
export-csv -Force -Append -Encoding ASCII -NoTypeInformation .\output\$day-ExpiringIISSSLCerts.csv
}
}
#Complete LOCAL run script. Call this in a Foreach Invoke-command.
$CertAll=GCI -Path Cert:\LocalMachine\My
$CertInUse= (GCI IIS:SSLBindings)
$CertSame=Compare-Object -ReferenceObject $CertAll -DifferenceObject $CertInUse -Property ThumbPrint -IncludeEqual -ExcludeDifferent
#$CertSame=Compare-Object -ReferenceObject $CertAll -Property ThumbPrint -IncludeEqual -ExcludeDifferent
$CertSame | foreach{GCI -filter "" –path Cert:\LocalMachine\My\$($_.thumbprint)} | Select-Object -Property Issuer, #{n=’ExpireInDays’;e={($_.notafter – (Get-Date)).Days}} -First 1
Thank you to #bruce-zhang
Similar to #bruce-zhangs's excellent answer but gets the certs in use first, then retrieves only those from the appropriate certificate stores (instead of only looking at the My cert store):
Import-Module WebAdministration
$CertsInUse = Get-Childitem -Path IIS:\SslBindings
$CertsInUse | foreach{Get-Childitem –path Cert:\LocalMachine\$($_.Store)\$($_.Thumbprint)} | Select-Object -Property FriendlyName,Subject, #{n=’ExpireInDays’;e={($_.notafter – (Get-Date)).Days}}
Here it is with a more verbose foreach:
Import-Module WebAdministration
$CertsInUse = Get-Childitem -Path IIS:\SslBindings
$CertsDetails = #()
foreach ($Cert in $CertsInUse) {
$CertsDetails += Get-ChildItem -Path Cert:\LocalMachine\$($Cert.Store)\$($Cert.Thumbprint)
}
$CertsDetails | Select-Object -Property FriendlyName,Subject, #{n=’ExpireInDays’;e={($_.notafter – (Get-Date)).Days}}
#checkCertExpDate-manual.ps1
$day = Get-Date -Format yyyyMMdd
$threshold = 5000 #Number of days to look for expiring certificates
$deadline = (Get-Date).AddDays($threshold) #Set deadline date
Dir Cert:\LocalMachine\My | foreach {
If ($_.NotAfter -le $deadline) { $_ | Select Issuer, Subject, NotAfter, #{Label="Expires In (Days)";Expression={($_.NotAfter - (Get-Date)).Days}} }
}
Then you just grep for the name:
.\checkCertExpDate-manual.ps1|Select-String -pattern "companyname"
Now, I can set the '$threshold' to whatever I want...
I invoke this remotely, after I copied to every server, and wrote the output to a log I then email to myself automatically every week from a scheduled task.
#D:\batch\checkCertExpDate.ps1
$day = Get-Date -Format yyyyMMdd
Set-Location d:\batch
$serverlist = gc ".\serverlist.txt"
foreach($server in $serverlist)
{
$threshold = 45 #Number of days to look for expiring certificates
$deadline = (Get-Date).AddDays($threshold) #Set deadline date
Invoke-Command $server { Dir Cert:\LocalMachine\My } | foreach {
If ($_.NotAfter -le $deadline) { $_ | Select Issuer, Subject, NotAfter, #{Label="Expires In (Days)";Expression={($_.NotAfter - (Get-Date)).Days}} }
}|select -expandproperty Subject|out-file .\output\$day-ExpiringIISSSLCerts.txt -Encoding ascii -Append
}
# Start mail send
$log = "d:\batch\output\$day-ExpiringIISSSLCerts.txt"
if(Test-Path -Path $log){
$smtpServer = "smtp.domain.com"
$messageSubject = "Verify SSL Cert Check Report - " + $env:computername
$message = New-Object System.Net.Mail.MailMessage
$message.From = "authorizedaccount#domain.com"
$message.To.Add("patrick.burwell#domain.com")
$message.Subject = $messageSubject
$message.IsBodyHTML = $true
$message.Body = "<head><pre>$style</pre></head>"
$message.Body += "Cert Check Report - " + $env:computername
$message.Body += Get-Date
$message.Body += "<br><b>Expiring Non-Prod Verify SSL Certificates Report from " + $env:computername + "</b>"
$message.Attachments.Add($log)
$smtp = New-Object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($message)
}
$result = Get-content $log
write-host $result |format-list -View table

Custom Foreach loop nslookup export to csv

I'm new to PowerShell scripting. I am trying to make a script which resolves a list of DNS names to their IPs and outputs them as a CSV file. When a server is unresolvable it should be output to a separate file.
Here is my code:
$location
$location = Read-Host "CSV Path: "
$Names = Import-Csv -Path $location
foreach($n in $Names)
{
try
{
$output = $n.NAME
$variable1 = [system.net.dns]::resolve($output) | Select
HostName,AddressList
$variable1
$IP = $variable1.AddressList | Select-Object IPAddressToString
$IPN = $IP.IPAddressToString
$csv+=New-Object psobject -Property #{IPAddresse= $IPN;Name=
$output} |
Export-Csv \\filepath\Working.csv -NoTypeInformation
}
catch
{
Write-host "$output is unreachable."
Write-Output "$output" | Export-Csv \\Filepath\Unreachable.csv -
Append -Encoding ASCII
}
}
edit: The code was working quite well but now it says there is a delimiter mistake with the import but i dont know why this all of a sudden comes up as the code worked didnt got any edit and suddenly doesnt work anymore
Here's a simplified/corrected version of your code:
$location = Read-Host "CSV Path: "
$Names = Import-Csv -Path $location
foreach($n in $Names)
{
try {
$Computer = [system.net.dns]::resolve($n.NAME) | Select HostName,AddressList
$IP = ($Computer.AddressList).IPAddressToString
New-Object PSObject -Property #{IPAddress=$IP; Name=$Computer.HostName} | Export-Csv \\filepath\Working.csv -NoTypeInformation -Append
} catch {
Write-Host "$($n.NAME) is unreachable."
Write-Output $n | Export-Csv \\Filepath\Unreachable.csv -Append -Encoding ASCII
}
}

PowerShell outtput to HTML

Using the following and would like to know how to output the results to HTML file
$Start = (Get-Date).AddMinutes(-5)
$Computername = gc C:\Temp\List.txt
$Events = gc C:\Temp\ErrorCodes.txt
Get-EventLog -AsString -ComputerName $Computername |
ForEach-Object {
# write status info
Write-Progress -Activity "Checking Eventlogs on \\$ComputerName" -Status $_
# get event entries and add the name of the log this came from
Get-EventLog -LogName $_ -EntryType Error, Warning -After $Start -ComputerName $ComputerName -ErrorAction SilentlyContinue |
Add-Member NoteProperty EventLog $_ -PassThru | Where-Object {$Events -contains $_.eventid}
} |
# select the properties for the report
Select-Object EventLog, EventID, TimeGenerated, EntryType, Source, Message
This will get it to print with pretty colors. You can tinker around with it and change whatever you want.
$a = "<style>"
$a = $a + "BODY{background-color:peachpuff;}"
$a = $a + "TABLE{border-width: 1px;border-style: solid;border-color:black;border-collapse: collapse;}"
$a = $a + "TH{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:thistle}"
$a = $a + "TD{border-width: 1px;padding: 0px;border-style: solid;border-color: black;background-color:palegoldenrod}"
$a = $a + "</style>"
$Variable = get-what_I_want
$Variable2 = get-what_I_want
$VariableHTML = $Variable | ConvertTo-HTML -head $a -body "<H2>Title I want</H2>"
$Variable2HTML = $Variable2 | ConvertTo-HTML -head $a -body "<H1>Header One</H1> <H2>Header Two</H2> <H3>Header Three</H3> <p>Paragraph<p/>"
$VariableHTML > C:\PoSH\My_Exported_HTML.html
I added a second variable. As you can see, you can add different levels of headers and a Paragraph. I found this helpful for single line items or if you want a better description.
If I would say: ConvertTo-Html is what you need, than probably that would be enough to add a comment.
Instead I will say: before asking questions outside PowerShell, ask them inside PowerShell first.
Get-Help *html*

PS script errors in first run: The object of type ".PowerShell.Commands.Internal.Format.FormatStartData" is not valid or not in the correct sequence

I have the following function that works correctly in 2012r2, when i run it in 2008R2 it throws the below error. The surprising thing is that if i execute it a second time, it works without any issue!!
# Function Reg-Stamp {
Param(
[Parameter(Mandatory=$True)]
[string]$Phase
)
$msg = "`nEntering: $((Get-Variable MyInvocation -Scope 0).Value.MyCommand.Name)" ; Write-Host -fore Gray $msg ; $msg | out-file -Append $log
$RegStampInfo = Build-Variable $RegStampCSV
$Version = ($ScriptVersionInfo | Where-Object {$_.Parameter -eq "Version" -and $_.Phase -eq $Phase }).Value
$DisplayName = ($ScriptVersionInfo | Where-Object {$_.Parameter -eq "DisplayName" -and $_.Phase -eq $Phase }).Value
$BuildDate = ($ScriptVersionInfo | Where-Object {$_.Parameter -eq "BuildDate" -and $_.Phase -eq $Phase }).Value
$RunDate = Get-Date
$Success = $(-not($CriticalError))
$msg = "`nUpdating registry with build information"; Write-Host -fore Gray $msg; $msg | out-file $log -Append;
$RegStampInfo | Where-Object {($_.Phase.ToLower()) -eq ($Phase.ToLower())} | foreach-Object {
$ValueData = $(get-variable -Name $($_.StampData) -ValueOnly -ErrorAction SilentlyContinue)
$msg = "Adding Key: $($_.StampKey) '$($_.StampValue)' '$ValueData'"; Write-Host -fore Green "$msg"; $msg | out-file $log -Append;
New-Item -Path $($_.StampKey) -ErrorAction SilentlyContinue
Set-ItemProperty -Path $_.StampKey -name $_.StampValue -Value $ValueData
}
$msg = "`nExiting: $((Get-Variable MyInvocation -Scope 0).Value.MyCommand.Name)"; Write-Host -fore DarkGreen $msg ; $msg | out-file -Append $log
#}
I get the following error:
out-lineoutput : The object of type "Microsoft.PowerShell.Commands.Internal.For
mat.FormatStartData" is not valid or not in the correct sequence. This is likel
y caused by a user-specified "format-table" command which is conflicting with t
he default formatting.
+ CategoryInfo : InvalidData: (:) [out-lineoutput], InvalidOperat
ionException
+ FullyQualifiedErrorId : ConsoleLineOutputOutOfSequencePacket,Microsoft.P
owerShell.Commands.OutLineOutputCommand
I have seen similar error in powershell when using format-table however i am not using fr here atleast directly.
Not sure what is wrong!
EDIT:
no, but it turns out that the issue was not in the above script at all.
It appears that caller script had a line involving format-table which had nothing to do with this script, was causing the issue.
$SupportFilesInfo = Import-csv $SupportFilesCSV | select-object
$SupportFilesInfo | ft ; $SupportFilesInfo | Out-File -append $log
I changed it to:
$SupportFilesInfo | ft | out-default; $SupportFilesInfo | Out-File -append $log
which resolved the error!!
However i am still at loss at why the error occurs ONLY during the first run.
I had hit this issue earlier too, but it was very consistent.
Any idea why?

Powershell: Calling a script from foreach in another script doesn't get the desired effect

In this script below. When I enter the whole script into the powershell command line then call it with a server name, it works fine. But when i call it from this script:
`sl C:\PowershellScripts
. ./psscript_Get-FreeSpaceFrag.ps1
$svl = gc 'C:\PowershellScripts\ebi_serverlist.txt'
$x = {foreach ($s in $svl) {write-host "Getting Disk Info for Server $s" -
foregroundcolor "Green"; Get-FreeSpaceFrag $s; start-sleep -s 5; }}
$x.invoke() | export-csv "C:\PowershellScripts\DiskInfo.csv" -NoTypeInformation`
It will not work, meaning that the csv file is empty after it processes for a while.
Function Get-FreeSpaceFrag ($s)
{
trap {write-host "Can't connect to WMI on server $s" -ForeGroundColor "Red"
continue
}
$dt = get-date
$Scope = new-object System.Management.ManagementScope "\\$s\root\cimv2"
$query = new-object System.Management.ObjectQuery "SELECT * FROM Win32_Volume"
$searcher = new-object System.Management.ManagementObjectSearcher $scope,$query
$SearchOption = $searcher.get_options()
$timeout = new-timespan -seconds 10
$SearchOption.set_timeout($timeout)
$SearchOption
$searcher.set_options($SearchOption)
$volumes = $searcher.get()
$fr = {foreach ($v in $volumes | where {$_.capacity -gt 0}){
$frag=($v.defraganalysis().defraganalysis).totalPercentFragmentation
$v | Add-Member -Name Frag -MemberType NoteProperty -Value $frag -Force -
PassThru
} }
$fr.invoke() | select #{N="Server";E={$_.Systemname}}, DriveLetter, Label,
Capacity, FreeSpace, #{N="PercentFree";E={"{0,9:N0}" -f
(($_.FreeSpace/1gb)/($_.Capacity/1gb)*100)}}, Frag, #{N="InfoDate";E={$dt}}
}
I think you're making this a bit harder than it should be i.e. I'm not sure why you need part of the code in an anoymous scriptblock? Try this instead:
. ./psscript_Get-FreeSpaceFrag.ps1
Get-Content 'C:\PowershellScripts\ebi_serverlist.txt' |
Foreach {Write-Host "Getting Disk Info for Server $_" -foregroundcolor "Green" `
Get-FreeSpaceFrag $_} |
Export-Csv "C:\PowershellScripts\DiskInfo.csv" -NoTypeInformation
Not sure if it will solve the problem but you can also simplify this part of your function:
$volumes | Where {$_.capacity -gt 0} | Foreach { $_ | Add-Member NoteProperty Frag `
($_.defraganalysis().defraganalysis.totalPercentFragmentation) -PassThru} |
Select #{N="Server";E={$_.Systemname}}, DriveLetter,Label,Capacity,FreeSpace,
#{N="PercFree";E={"{0,9:N0}" -f (($_.FreeSpace/1gb)/($_.Capacity/1gb)*100)}},
Frag, #{N="InfoDate";E={$dt}
BTW rather than invoking a scriptblock like this $fr.invoke() the canonical way in PowerShell is to use the call operator like so &$fr.
Might be your problem in the process block?
$x = {foreach ($s in $svl) {start-sleep -s 5;
write-host "Getting Disk Info for Server $s" -foregroundcolor "Green";
Get-FreeSpaceFrag $s;}}