I am having an issue with the out put; I wanted a second look to see another way of getting my output to not have return or new lines in it.
Could someone take a look for me please?
$objDomain = New-Object System.DirectoryServices.DirectoryEntry("LDAP://OU=Workstations");
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher;
$objSearcher.SearchRoot = $objDomain;
$objSearcher.PageSize = 100000;
$objSearcher.SearchScope = "Subtree";
$dateMonth = Get-Date -Format "MM";
$dateDay = Get-Date -Format "dd";
$dateYear = Get-Date -Format "yyyy";
$colProplist = "name"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}
$colResults = $objSearcher.FindAll();
foreach ($objResult in $colResults)
{
$objItem = $objResult.Properties;
$computer = $objItem.name | Select-String -Pattern 'NSC';
Write-Host $computer;
#Add-Content "C:\PowerShell\Reports\Computer Report - $dateMonth-$dateDay-$dateYear.csv" "$computer";
}
Example output:
NSCNPR02
NSCNPR05
NSCNPR01
NSCNPR03
Expected Output:
NSCNPR03
NSCNPR05
NSCNPR01
NSCNPR03
Try this:
$date = Get-Date -Format "MM-dd-yyyy"
$filename = "C:\PowerShell\Reports\Computer Report - $date.csv"
...
$objSearcher.FindAll() | ? {
$_.Properties.Name -like 'NSC*'
} | Add-Content $filename
The problem with your code is that
Add-Content "C:\Pow...csv" "$computer"
always adds a new line, even if $computer is $null. This behavior is due to the double quotes around $computer. Without them the problem wouldn't exist (but my suggestion would still be a cleaner solution ;).
Add-Content adds new lines.
As a workaround you may out text into the string and then write it into the file when the cycle is done.
Can you use the LDAP filtering instead of pulling all the records and then searching?
Instead of
"LDAP://OU=Workstations"
Use
"LDAP://OU=Workstations?(&(objectCategory=computer)(name=NSC*))"
For more information: http://social.technet.microsoft.com/wiki/contents/articles/5392.active-directory-ldap-syntax-filters.aspx
Related
I want to collect all the event logs since a defined timestamp. Here there is my chunck code:
$StartTime = (Get-Date).AddMinutes(-5)
$rawdata = Get-WinEvent -ListLog *
$eventlogs = #{}
foreach ($record in $rawdata) {
if ($record.LastWriteTime -gt $StartTime) {
$eventlogs[$record.GetHashCode()] = #{
'LogType' = $record.LogType
'Name' = $record.LogName
'Provider' = $record.OwningProviderName
'Path' = $record.LogFilePath
'Mode' = $record.LogMode
'Time' = $record.LastWriteTime
}
}
}
In addition to the above info, how can I retrieve a full extended description of each event log? I would like to avoid parsing each single .evtx file
Best way to do that is to use FilterXml parameter of Get-WinEvent.
You can actually create your filter by creating a filter using Event Viewer:
After that you copy > paste that in PowerShell. Also you will need to escape single quotation marks that surround time '2021-11-23T10:00:00.000Z' should become ''2021-11-23T10:00:00.000Z'' . NB, those are 2 * ', not double quote ".
Of course if you want to pass dates via variable, you can do that by using Get-Date
$filterXml = '
<QueryList>
<Query Id="0" Path="Application">
<Select Path="Application">*[System[TimeCreated[#SystemTime>=''2021-11-23T10:00:00.000Z'' and #SystemTime<=''2021-11-23T14:03:00.999Z'']]]</Select>
</Query>
</QueryList>'
Get-WinEvent –FilterXml $filterXml
Not sure about reading extended descriptions, but you can read the event log with the following. Note logname allows wild cards, thus the *.
$time = (get-date).AddMinutes(-5)
Get-WinEvent –FilterHashtable #{logname='*'; starttime=$time}
I found a solution with a chunk of code like this one I have written:
# get events log
Write-Host -NoNewline "Retrieving events log... "
$logtime = (Get-Date).AddMinutes(-15)
$eventlogs.Clear()
$string = ''
$ErrorActionPreference= 'SilentlyContinue'
foreach ($logtype in ('System', 'Security','Application')) {
$rawdata = Get-WinEvent -FilterHashTable #{LogName=$logtype; StartTime=$logtime}
foreach ($record in $rawdata) {
$logkey = '[' + $record.LogName + ']_'
$logkey += Get-Date $record.TimeCreated -format "yyyy-MM-dd_HH-mm-ss-fff"
$record.Message -match "^(.*)\r+" > $null
if ($matches[1]) {
$string = $matches[1]
$matches[1] = $null
} else {
$record.Message -match "^(.*)\n+" > $null
if ($matches[1]) {
$string = $matches[1]
$matches[1] = $null
} else {
$string = $record.Message
}
}
$eventlogs[$logkey] = #{
'Name' = $record.LogName
'Time' = Get-Date $record.TimeCreated -format "yyyy-MM-dd_HH-mm-ss"
'Id' = $record.Id
'Message' = $string
'Type' = $record.LevelDisplayName
}
}
}
$ErrorActionPreference= 'Inquire'
$logfile = $logpath + '\' + $timestamp + '_EventLogs.csv'
'ID;LOGTYPE;NAME;TIME;MESSAGE' | Out-File $logfile -Encoding UTF8 -Append
foreach ($item in ($eventlogs.Keys | Sort-Object)) {
$new_record = #(
$eventlogs[$item].Id,
$eventlogs[$item].Type,
$eventlogs[$item].Name,
$eventlogs[$item].Time,
$eventlogs[$item].Message
)
$new_string = [system.String]::Join(";", $new_record)
$new_string | Out-File $logfile -Encoding UTF8 -Append
}
Write-Host -ForegroundColor Green 'DONE'
For convenience I will catch only the first line of the message. In order to get this I have adopted the following block:
$record.Message -match "^(.*)\r+" > $null
if ($matches[1]) {
$string = $matches[1]
$matches[1] = $null
} else {
$record.Message -match "^(.*)\n+" > $null
if ($matches[1]) {
$string = $matches[1]
$matches[1] = $null
} else {
$string = $record.Message
}
}
Such solution is due to the fact that some messages use \r\n, others only \n and still others I don't know. This last block does not satisfy me, even if it works.
Simply getting all recent logs, working within the windows api limits. For some reason it still says ProviderName in the headers.
$time = (get-date).AddMinutes(-5)
get-winevent -listlog * | % { Get-WinEvent #{logname=$_.logname;
starttime=$time} -ea 0} | ft -GroupBy logname
ProviderName: Microsoft-Windows-WMI-Activity/Operational
TimeCreated Id LevelDisplayName Message
----------- -- ---------------- -------
11/27/2021 10:52:54 AM 5857 Information Win32_WIN32_TERMINALSERVICE_Prov provider started with result c...
11/27/2021 10:52:54 AM 5857 Information CIMWin32 provider started with result code 0x0. HostProcess = w...
11/27/2021 10:51:59 AM 5857 Information StateMessageProvider provider started with result code 0x0. Hos...
% -parallel works even better in ps 7.
get-winevent -logname *
Get-WinEvent: Log count (458) is exceeded Windows Event Log API limit (256). Adjust filter to return less log names.
$time = (get-date).AddMinutes(-5)
get-winevent -listlog * | % -parallel { Get-WinEvent #{logname=$_.logname;
starttime=$using:time} -ea 0} | ft -GroupBy logname
I have the following code
function ping-test($hosts) {
$conn = [System.Collections.ArrayList]#($hosts)
[int]$hostsamount = $conn.Count
foreach($co in $conn)
{
$check = Test-Connection $co -Count 3 -ErrorAction SilentlyContinue
$zugriffzeit = $check | select ResponseTime | Measure-Object ResponseTime -Average
$avg = [system.math]::Round($zugriffzeit.Average)
$zeit = Get-Date -Format HH:mm:ss
if($check -eq $null)
{
$pcre = Write-Output $co
$pire = Write-Output 'False'
$zure = $null
}
else
{
$pcre = Write-Output $co
$pire = Write-Output 'True'
$zure = Write-Output "$avg ms"
$zure = $zure.Replace(' ','')
}
[void]$re.Add([PSCustomObject] #{PCName=$pcre; PingResult=$pire; Zugriffszeit=$zure; Zeit=$zeit} )
**$log = "Host:{0} Ping: {1} Zugriffszeit: {2} Zeit: {3}" -f $pcre, $pire, $zure, $zeit
$log >> $logpath**
[int]$recount = $re.Count
[int]$eff = $recount - $hostsamount
try {
$re.RemoveRange(0, $eff)
}
catch{
Write-Host $Error
}
}
return $re
}
I use the following code(is in that function)
$log = "Host:{0} Ping: {1} Zugriffszeit: {2} Zeit: {3}" -f $pcre, $pire, $zure, $zeit
$log >> $logpath
the Question is: I want to form a table with the Colums "Host", "Ping", "Zugriffszeit", and "Zeit".
How can I form this table and save as a .txt or .log file somewhere??
Thx for the help
Use the same data as you're outputting!
To export to csv (if you want to re-use the data programmatically later):
$re |Export-Csv $logpath -NoTypeInformation
If you want to ever format it in a nice table again, it's as easy as:
Import-Csv $logpath |Format-Table
If you simply want nicely tabulated output in your log file:
$re |Format-Table |Out-String |Out-File $logfile
#MathiasR.Jessen showed import and export to csv.
But if you are bound to use .txt or .log files (As the aspect of your question says) then use PSCustomObject and Out-File
[PSCustomObject]#{
Host = $pcre
Ping = $pire
Zugriffszeit = $zure
Zeit = $zeit
} | Out-File $logpath
Later import it like:
Get-Content $logpath
Im working on a Powershell script that can put lines from a text file into a list box, but only lines that start with a date ../../....
The add list box part is working. But id like to delete older lines from the text file first.
I tried this but it not correct. Can anyone help me code this correctly.
I will rephrase the question to hopefully make it clearer.
The text file look like this:
text.txt
//Regular Roster | Only update if making a permanent change.
!The order for the entry must be: Day time,number,name,email(optional)
!With a comma ‘,’ separating the values.
Monday 9AM-2PM,0400499449,BILL
Monday 2PM-6PM,074477464,Terry
Monday 6PM-9PM,040484747,Leanne
Tuesday 9AM-2PM,07123456,BILL
Tuesday 2PM-6PM,054647383,Barbara
Tuesday 6PM-9PM,03937464,Mandy
//Changes to Regular Roster. This section will override the above.
!The date order of the entries below is not important but each line must follow this pattern:
!date(Dayxx/Monthxx/Yearxxxx),time(9AM,2PM or 6PM),number,name,email(optional)
!Date slash must be forward facing '/'.
!The only times that can be entered are 9AM,2PM or 6PM.
!With a comma ‘,’ separating the values.
01/01/2019,6AM,0400012345,Kurt,kurt#outlook.com
02/01/2019,6AM,0412345676,Bill,bill#outlook.com
03/01/2019,6AM,0400012345,Sam,Sam#outlook.com
04/01/2019,6AM,0412345676,Barry,barry#outlook.com
05/01/2019,6AM,0400012345,Kurt,kurt#outlook.com
If I try this code in Powershell assuming the current date is 04/01/2019 (4th January 2019) it works fine
$CurrentDate = "04/01/2019"
$Text = "05/01/2019,6AM,0400012345,Kurt,kurt#outlook.com"
$Text | ForEach-Object {
$Roster1Date,$Roster2Time,$Roster3Number,$Roster4Name,$Roster5Email = $_.split(",")
$Newdate = "$Roster1Date"
$CurrentDate = [datetime]::ParseExact("$CurrentDate", 'dd/MM/yyyy', $null)
$Newdate = [datetime]::ParseExact("$Newdate", 'dd/MM/yyyy', $null)
if ($Newdate -ge $CurrentDate) {
write-output "NewDate is bigger than or equal to"
}
else {
write-output "CurrentDate is bigger"
}
};
When I try to build a Listbox with this line it works fine
#Add Listbox
$ShiftsListBox = New-Object System.Windows.Forms.ListBox
$ShiftsListBox.Location = New-Object System.Drawing.Size(400,40)
$ShiftsListBox.Size = New-Object System.Drawing.Size(360,20)
$ShiftsListBox.Height = 160
Get-Content $RosterPath | select-string -Pattern '../../...' | ForEach-Object {[void] $ShiftsListBox.Items.Add($_)};
$FormCreateNewShift.Controls.Add($ShiftsListBox)
When I tried to combine the two it just comes up blank
#Add Listbox
$TextFile = text.txt
$ShiftsListBox = New-Object System.Windows.Forms.ListBox
$ShiftsListBox.Location = New-Object System.Drawing.Size(400,40)
$ShiftsListBox.Size = New-Object System.Drawing.Size(360,20)
$ShiftsListBox.Height = 160
Get-Content $TextFile | select-string -Pattern '../../...' | ForEach-Object {
$Roster1Date,$Roster2Time,$Roster3Number,$Roster4Name,$Roster5Email = $_.split(",")
$Newdate = "$Roster1Date"
$CurrentDate = [datetime]::ParseExact("$CurrentDate", 'dd/MM/yyyy', $null)
$Newdate = [datetime]::ParseExact("$Newdate", 'dd/MM/yyyy', $null)
if ($Newdate -ge $CurrentDate) {
[void] $ShiftsListBox.Items.Add($_)
}
else {
}
};
$FormCreateNewShift.Controls.Add($ShiftsListBox)
The idea is that if the date is greater than or equal to the current date display the line in the list box, else don't. But then I will try to delete the line by adding in a replace line.
#Add Listbox
$TextFile = text.txt
$ShiftsListBox = New-Object System.Windows.Forms.ListBox
$ShiftsListBox.Location = New-Object System.Drawing.Size(400,40)
$ShiftsListBox.Size = New-Object System.Drawing.Size(360,20)
$ShiftsListBox.Height = 160
Get-Content $TextFile | select-string -Pattern '../../...' | ForEach-Object {
$Roster1Date,$Roster2Time,$Roster3Number,$Roster4Name,$Roster5Email = $_.split(",")
$Newdate = "$Roster1Date"
$CurrentDate = [datetime]::ParseExact("$CurrentDate", 'dd/MM/yyyy', $null)
$Newdate = [datetime]::ParseExact("$Newdate", 'dd/MM/yyyy', $null)
if ($Newdate -ge $CurrentDate) {
[void] $ShiftsListBox.Items.Add($_)
}
else {
((Get-Content -path $TextFile) -replace $_,"") | Set-Content -Path $TextFile;
}
};
$FormCreateNewShift.Controls.Add($ShiftsListBox)
Based on new sample file, this script distinguishes
between lines with a leading date
a date from today or later(keep)
a date before today (skip)
other lines (keep)
## Q:\Test\2019\01\01\SO_53992011.ps1
$Rosterpath = ".\rosterpath"
$CurrentDate = (Get-Date).Date # to have a datetime starting today at 00:00:00
$RegEx = [RegEx]"^(?<day>\d{2})\/(?<mon>\d{2})\/(?<year>\d{4})"
$UpdatedContent = Get-Content $RosterPath | ForEach-Object {
If ($_ -match $RegEx){
If((Get-Date -Year $Matches.year -Month $Matches.mon -Day $Matches.day) -ge $CurrentDate){
# yes keep this line
$_
} else {
#drop this line
}
} else {
# line without a date, keep
$_
}
}
$UpdatedContent | Set-Content $RosterPath
I'm writing a script that I'd like to be able to easily move between IIS servers to analyze logs, but these servers store the logs in different places. Some on C:/ some on D:/ some in W3SVC1, some in W3SVC3. I'd like to be able to have powershell look this information up itself rather than having to manually edit this on each server. (Yeah, I'm a lazy sysadmin. #automateallthethings.)
Is this information available to PowerShell if I maybe pass the domain to it or something?
I found this to work for me since I want to know all of the sites log directory.
Import-Module WebAdministration
foreach($WebSite in $(get-website))
{
$logFile="$($Website.logFile.directory)\w3svc$($website.id)".replace("%SystemDrive%",$env:SystemDrive)
Write-host "$($WebSite.name) [$logfile]"
}
Import-Module WebAdministration
$sitename = "mysite.com"
$site = Get-Item IIS:\Sites\$sitename
$id = $site.id
$logdir = $site.logfile.directory + "\w3svc" + $id
Thanks for Chris Harris for putting the website ID idea in my head. I was able to search around better after that and it led me to the WebAdministration module and examples of its use.
Nice... I updated your script a little bit to Ask IIS for the log file location.
param($website = 'yourSite')
Import-Module WebAdministration
$site = Get-Item IIS:\Sites\$website
$id = $site.id
$logdir = $site.logfile.directory + "\w3svc" + $id
$time = (Get-Date -Format "HH:mm:ss"(Get-Date).addminutes(-30))
# Location of IIS LogFile
$File = "$logdir\u_ex$((get-date).ToString("yyMMdd")).log"
# Get-Content gets the file, pipe to Where-Object and skip the first 3 lines.
$Log = Get-Content $File | where {$_ -notLike "#[D,S-V]*" }
# Replace unwanted text in the line containing the columns.
$Columns = (($Log[0].TrimEnd()) -replace "#Fields: ", "" -replace "-","" -replace "\(","" -replace "\)","").Split(" ")
# Count available Columns, used later
$Count = $Columns.Length
# Strip out the other rows that contain the header (happens on iisreset)
$Rows = $Log | where {$_ -like "*500 0 0*"}
# Create an instance of a System.Data.DataTable
#Set-Variable -Name IISLog -Scope Global
$IISLog = New-Object System.Data.DataTable "IISLog"
# Loop through each Column, create a new column through Data.DataColumn and add it to the DataTable
foreach ($Column in $Columns) {
$NewColumn = New-Object System.Data.DataColumn $Column, ([string])
$IISLog.Columns.Add($NewColumn)
}
# Loop Through each Row and add the Rows.
foreach ($Row in $Rows) {
$Row = $Row.Split(" ")
$AddRow = $IISLog.newrow()
for($i=0;$i -lt $Count; $i++) {
$ColumnName = $Columns[$i]
$AddRow.$ColumnName = $Row[$i]
}
$IISLog.Rows.Add($AddRow)
}
$IISLog | select #{n="DateTime"; e={Get-Date ("$($_.date) $($_.time)")}},csuristem,scstatus | ? { $_.DateTime -ge $time }
I have the following powershell script which binds to an active directory OU and lists the computers. It seems to work fine except that it ouputs an extra 0 - I'm not sure why. Can anyone help?
$strCategory = "computer"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry("LDAP:// OU=Computers,OU=datacenter,DC=ourdomain,DC=local")
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher($objDomain)
$objSearcher.Filter = ("(objectCategory=$strCategory)")
$colProplist = "name"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}
$colResults = $objSearcher.FindAll()
foreach ($objResult in $colResults)
{
$objComputer = $objResult.Properties;
$objComputer.name
}
Output:
0
Server1
Server2
Server3
You need to capture (or ignore) the output of the PropertiesToLoad.Add method, otherwise you'll get a value for each property in $colPropList.
foreach ($i in $colPropList){[void]$objSearcher.PropertiesToLoad.Add($i)}
You can simplify and shorten your script and load a bunch of properties in one call without using a foreach loop. Another benefit of the AddRange method is that it doesn't output the length of requested properties, so there's no need to capture anything.
$strCategory = "computer"
$colProplist = "name","distinguishedname"
$searcher = [adsisearcher]"(objectCategory=$strCategory)"
$searcher.PropertiesToLoad.AddRange($colProplist)
$searcher.FindAll() | Foreach-Object {$_.Properties}
I suspect your foreach loop is outputting the result when calling PropertiesToLoad.Add.
Try piping to out-null, like so:
foreach ($i in $colPropList){
$objSearcher.PropertiesToLoad.Add($i) | out-null
}