Script is outputting an extra 0 - powershell

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
}

Related

how to use variable in parameter name

I am trying to read multiple files in powershell script in a for-loop always switching the file name dynamically.
I tried a different notation, but I'm still getting errors.
The code is like:
$fileName1 = "D:\file333"
$fileName2 = "D:\file444"
$datePattern =" C.{3} "
[Char[]]$buffer = new-object char[] 10000
for ($j=1; $j -le 2; $j++)
{
[string]$fileNumber = $j.ToString()
$inFile = new-object -TypeName System.IO.StreamReader -ArgumentList
$fileName($fileNumber)
[int]$bytesRead = $inFile.Read($buffer, 0, $buffer.Length)
while ($bytesRead -gt 0) {
[string]$bufferString = -join $buffer
$results = $bufferString | Select-String $datePattern -AllMatches
$results.Matches.Value
[int]$bytesRead = $inFile.Read($buffer, 0, $buffer.Length)
}
}
I have two questions:
What would be a right notation for the -ArgumentList, to get FileName1 and then FileName2?
Does the Read method applied on the inFile really requires a Char[] type as argument for $buffer? And Is it possible to get the [String] parameter instead?
Can someone give me a hint?
Okay,
I'm sure you were reminded when you first submitted the question, but please remember to put your code in a code block, like so:
$fileName1 = "D:\file333"
$fileName2 = "D:\file444"
$datePattern =" C.{3} "
[Char[]]$buffer = new-object char[] 10000
for ($j=1; $j -le 2; $j++)
{
[string]$fileNumber = $j.ToString()
$inFile = new-object -TypeName System.IO.StreamReader -ArgumentList
$fileName($fileNumber)
[int]$bytesRead = $inFile.Read($buffer, 0, $buffer.Length)
while ($bytesRead -gt 0)
{
[string]$bufferString = -join $buffer
$results = $bufferString | Select-String $datePattern -AllMatches
$results.Matches.Value
[int]$bytesRead = $inFile.Read($buffer, 0, $buffer.Length)
}
}
Now, I'm REALLY unsure of what you're trying to do here. Whatever it is, I strongly suggest you create a well founded question here around it.
In any case, powershell doesn't support the behavior you're trying to create. It's an easy newbie trap to fall into. Oh, I want to find files 1 and 2, so I make a loop and use the iterator, right? Wrong!
You need to loop over a collection containing the files. Powershell doesn't think of the files as part of a collection unless you put it in one.
Try this.
$files = #("D:\file3333", "D:\file444")
$datePattern =" C.{3} "
[Char[]]$buffer = new-object char[] 10000
foreach(file in files)
{
[int]$bytesRead = new-object -TypeName System.IO.StreamReader -ArgumentList $file | $_.Read($buffer, 0, $buffer.length)
while($bytesRead -gt 0)
{
[string]$bufferString = -join $buffer
$results = $bufferString | Select-String $datePattern -AllMatches
$results.Matches.Value
[int]$bytesRead = $inFile.Read($buffer, 0, $buffer.Length)
}
}
Here, we put the files into a collection, and iterate over that collection with a foreach loop. We can collapse your multiple lines to call the file into a buffer stream into one line by using the pipe operator.
I have NO IDEA what you're trying to do in the while loop, so I'd be happy to help you make it a bit more powershell friendly, but you'll have to ask the question you mean to ask before I can do so.

putting foreach loop into a csv file

im stuck on how to output my foreach loop into a csv or excel file. this script just get all computers off a local network and then test to see if that computer has a certain KBPatch. The script works just like I said I need help trying to make it output to a csv file. any tips/help is appreciated
Code Below
$strCategory = "computer"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$ObjSearcher.SearchRoot = $objDomain
$objSearcher.filter = ("(objectCategory=$strCategory)")
$colProplist = "name"
foreach($i in $colProplist)
{
$objSearcher.PropertiesToLoad.Add($i)
}
#Finds all operating systems and computer names
$colResults = $objSearcher.FindAll()
foreach($objResult in $colResults)
{
$objComputer = $objResult.Properties;
$names = $objComputer.name
# Define Hotfix to check
$CheckKBS = #(“patch#" , "patch#")
#Query the computers for the HotFix
foreach($name in $names)
{
foreach ($CheckKB in $CheckKBS) {
$HotFixQuery = Get-HotFix -ComputerName $name | Where-Object {$_.HotFixId -eq $CheckKB} | Select-Object -First 1;
if($HotFixQuery -eq $null)
{
Write-Host “Hotfix $CheckKB is not installed on $name”;
}
else
{
Write-Host “Hotfix $CheckKB was installed on $name by ” $($HotFixQuery.InstalledBy);
}
}}
}
To do this cleanly I usually use a custom object.
Before your foreach, instantiate an empty array:
$records = #()
and make an object template:
$tmpRecord = [PSCustomObject]#{
serverName = ''
missingKB = ''
}
Inside the foreach, clone the record obj:
$record = $tmpRecord.psobject.copy()
put your data into the record:
$record.serverName = $name
$record.missingKB = $CheckKb
Put the record into the array:
$records += $record
Then after the foreach, export to csv:
$records | export-csv yourcsv.csv
The need for an object template was confusing to me when I first learned this pattern. You need this because of the combination of scope and how objects are added to arrays (by reference).
If you try to get away with declaring an object inside the loop then that object will be scoped to the lifetime of the foreach loop. You'll then add a reference to that object to your $records array. After the foreach loop completes you will have an array full of references to objects that do not exist.

Get IIS log location via powershell?

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 }

Undesired Newlines in Output from AD Search

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

Powershell nested variables

Soo trying to do something like the following
$Computers = Get-Content .\~test.txt
ForEach ($Computer in $Computers) {
$computer.Attempts += 1
$computer.result = Successful
}
++ EDIT ++
The output i want is
$MyComputerName.Attempt += 1
$MyComputerName.result
So that each computer will have its own name creating a basic table within the script so that it doesn't have to keep reading some information from a text file.
where $computer is the computer name in the text file. I am trying to do this to create a faster method of checking valuables.
If I'm following you, you want to create new object for each computer:
$Computers = Get-Content .\~test.txt
ForEach ($Computer in $Computers) {
New-Object PSObject -Property #{
Name = $Computer
Attempts = 1
Result = 'Successful'
}
}
Alright eventually got this thing figured out and was as follows. Import-CSV is a great tool for creating a new object for each line.
$Computers = #(Import-CSV .\Computers.txt)
ForEach ($Computer IN $Computers) {
Write-host $Computer.Name
Write-Host $Computer.IP
Write-Host $Computer.Other
}
Computers.txt:
52123-9421,123.123.123.123,yea
HELLBOMBS-PC,123.123.123.123,yea
52123-942dv,123.123.123.123,yea
52123-942RM,123.123.123.123,yea