Why is Powershell & AD returning newlines? - powershell

I have this Powershell code:
$strFilter = "(&(objectCategory=User)(Description=*MD))"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = "Subtree"
$colProplist = "name", "department", "description"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}
$colResults = $objSearcher.FindAll()
foreach ($objResult in $colResults)
{$objItem = $objResult.Properties
$objItem.name + "|" + $objItem.department + "|" + $objItem.description
}
For some reason it's returning new lines. How can I stop this?
The output looks like:
Bob Dole
|
SOME DEPT
|
Bob description
Rick James
|
ANOTHER DEPT
|
Rick description
Changing the last block to:
foreach ($objResult in $colResults)
{$objItem = $objResult.Properties
$colResults.Properties|%{($_.name,$_.department,$_.description) -join "|"}
}
Doesn't give me anything, just a bunch of:
||
||
||
||
||
||
I also tried just:
$colResults.Properties|%{($_.name,$_.department,$_.description) -join "|"}
And only got:
||

You have to be aware that $objItem.attribute is always a collection. I suspect that this is root cause of the problems you are seeing. Try this:
foreach ($objResult in $colResults)
{$objItem = $objResult.Properties
$objItem.name[0] + "|" + $objItem.department[0] + "|" + $objItem.description[0]
}
The problem is that this can give your errors if any of these attributes is empty. To avoid it (and get the same experience) you can use unary -join (with -f to make it easier to read):
foreach ($objResult in $colResults)
{$objItem = $objResult.Properties
'{0}|{1}|{2}' -f #(
(-join $objItem.name)
(-join $objItem.department)
(-join $objItem.description)
)
}

You could just join them instead:
($objItem.Name,$objItem.Department,$objItem.Description) -join "|"
That should give you your desired output.
Alternatively you could use string formatting like this:
"{0}|{1}|{2}" -f $objItem.Name, $objItem.Department, $objItem.Description
Edit: Ok, this runs on my machine and returns the results you wanted:
$strFilter = "(&(objectCategory=User)(Description=*MD))"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = "Subtree"
$colProplist = "name", "department", "description"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}
$colResults = $objSearcher.FindAll()
$colResults.Properties|%{($($_['name']),$($_['department']),$($_['description'])) -join "|"}

Related

Powershell DirectorySearcher Null Output

I'm writing a powershell script that searches for users inside an Active Directory OU and allows me to reset passwords by choosing matches from a list. I found a Tutorial that uses the System.DirectoryServices.DirectoryEntry and System.DirectoryServices.DirectorySearcher, and modified it like so:
$objDomain = New-Object System.DirectoryServices.DirectoryEntry("LDAP:\\[REDACTED]")
##ReadSTDIN
$strSearch = Read-Host -Prompt "Search"
$strCat = "(&(objectCategory=User)(Name=*" + $strSearch + "*))"
## Search Object
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strCat
$objSearcher.SearchScope = "Subtree"
#Load Required Properties into the dynObjLink
$objSearcher.PropertiesToLoad.Add("name")
$objSearcher.PropertiesToLoad.Add("userPrincipalName")
$objSearcher.PropertiesToLoad.Add("SamAccountName")
##Magical Search Function
$colResults = $objSearcher.FindAll()
$colResults.PropertiesLoaded
#for every returned userID add them to a table
ForEach ($objResult in $colResults)
{$a++
$objResult.count
$objItem = $objResult.Properties
$objItem.name
$objItem.userPrincipalName
$results.Add($a, $objItem.name + $objItem.userPrincipalName + $objItem.SamAccountName)
}
#Print Table
$results | Format-Table -AutoSize
This works well enough, but when it prints data I can only get the "first name" value of anything that comes back. Everything else becomes NULL and I can't figure out why.
Name Value
---- -----
3 {James3 [REDACTED], $null, $null}
2 {James2 [REDACTED], $null, $null}
1 {James1 [REDACTED], $null, $null}
I've tried different kinds of authentication and manipulating values, but the DirectorySearcher object only seems to collect the "name" value of any record it returns, no matter what I load into it. Help?
Here's a bit shorter (and PowerShell v2-compatible) way of doing this:
#requires -version 2
param(
[Parameter(Mandatory=$true)]
[String] $SearchPattern
)
$searcher = [ADSISearcher] "(&(objectClass=user)(name=$SearchPattern))"
$searcher.PageSize = 1000
$searcher.PropertiesToLoad.AddRange(#("name","samAccountName","userPrincipalName"))
$searchResults = $searcher.FindAll()
if ( $searchResults.Count -gt 0 ) {
foreach ( $searchResult in $searchResults ) {
$properties = $searchResult.Properties
$searchResult | Select-Object `
#{Name = "name"; Expression = {$properties["name"][0]}},
#{Name = "sAMAccountName"; Expression = {$properties["samaccountname"][0]}},
#{Name = "userPrincipalName"; Expression = {$properties["userprincipalname"][0]}}
}
}
$searchResults.Dispose()
Note that there's no need to build a list and output afterwards. Just output each search result. Put this code in a script file and call it:
PS C:\Scripts> .\Searcher.ps1 "*dyer*"
If you omit the parameter, PowerShell will prompt you for it (because the parameter is marked as mandatory).
try using Properties matching to the PropertiesToLoad
$entry = new-object -typename system.directoryservices.directoryentry -ArgumentList $LDAPServer, "ldap", "esildap"
$entry.Path="LDAP://OU=childOU,OU=parentOU,DC=dc1,DC=dc2"
$searcher = new-object -typename system.directoryservices.directorysearcher -ArgumentList $entry
$searcher.PropertiesToLoad.Add('samaccountname')
$searcher.PropertiesToLoad.Add('mail')
$searcher.PropertiesToLoad.Add('displayname')
$objs = $searcher.findall()
foreach($data in $objs)
{
$samaccountname = $data.properties['samaccountname'][0] + ''
$mail = $data.properties['mail'][0] + ''
$displayname = $data.properties['displayname'][0] + ''
}
when accessing the properties of the resultset you get a System.DirectoryServices.ResultPropertyValueCollection type for each property
to get a string value for passing to a database the property value access the zero index of the object

Sum results of Windows Search query

I am trying to use PowerShell and Windows Search to get some statistics of my disk usage. This is what I came up for so now:
$sql = "SELECT System.FileExtension, System.Size FROM SYSTEMINDEX WHERE DIRECTORY = 'C:\Users\...\Documents' AND System.FileExtension = '.docx'"
$provider = "provider=search.collatordso;extended properties=’application=windows’;"
$connector = New-Object System.Data.OleDb.OleDbDataAdapter -Argument $sql, $provider
$dataset = New-Object System.Data.DataSet
if ($connector.fill($dataset)) {
$res = $dataset.Tables[0]
$res | Group-Object System.FileExtension |
Select-Object System.FileExtension,
#{Name='Total';Expression={($_.Group | Measure-Object SYSTEM.Size -Sum).Sum}} |
Format-Table
}
This works, but it looks kind of ugly. I also found and tried this:
$sql = "SELECT System.Size FROM SYSTEMINDEX WHERE DIRECTORY = 'C:\Users\...\Documents' AND System.FileExtension = '.docx'"
$provider = "provider=search.collatordso;extended properties=’application=windows’;"
$connector = New-Object System.Data.OleDb.OleDbDataAdapter -Argument $sql, $provider
$dataset = New-Object System.Data.DataSet
if ($connector.fill($dataset)) {
$dataset.Tables[0].Compute("Sum(SYSTEM.Size)")
}
which looks a lot nicer, but throws an error:
Cannot find an overload for "Compute" and the argument count: "1"

Using PowerShell to get all active-domain computers

I'm using this script(below) to get all computers on the domain; but it seems like it stops at 1000 on the dot, when I know there is clearly more. How would I go about getting the complete list?
$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)}
$colResults = $objSearcher.FindAll()
foreach ($objResult in $colResults)
{$objComputer = $objResult.Properties; $objComputer.name >> allcomps.csv}
try adding
$objSearcher.PageSize = 2000
http://technet.microsoft.com/en-us/library/ff730959.aspx

Filtering Powershell output

I am trying to filter the output of the following PS script;
We use server names like:
SRV-APP-001,
PRD-APP-001,
TST-APP-001
etc...
$strCategory = "computer"
$strOperatingSystem = "Windows*Server*"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objDomain
$objSearcher.Filter = ("OperatingSystem=$strOperatingSystem")
$colProplist = "name"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}
$colResults = $objSearcher.FindAll()
foreach ($objResult in $colResults)
{
$objComputer = $objResult.Properties;
$objComputer.name
}
The output of this script are all the servers in the Domain.
But I want only see the servers that start with "SRV" or "PRD"
the | where { $_name -like "SRV*"} is not realy working after the $objComputer.name part.
Thank you in advance
Change the filter to:
"(|(name=SRV*)(name=PRD*))(OperatingSystem=Windows*Server*)"

Powershell LDAP - physicalDeliveryOfficeName not showing up

Pretty straight forward question: I'm not sure why the "physicalDeliveryOfficeName" property is not showing up in my output. I've read that it is a non-standard property, but I have not been able to find a way to add it. Everything works perfectly except for the missing "physicalDeliveryOfficeName." Thanks for the help!
$Dom = 'LDAP://OU=XX;DC=XX;DC=local'
$Root = New-Object DirectoryServices.DirectoryEntry $Dom
$selector = New-Object DirectoryServices.DirectorySearcher
$selector.SearchRoot = $root
$selector.pagesize = 1000
$adobj= $selector.findall() | where {$_.properties.objectcategory -match "CN=Person"}
(Get-Content c:\FILENAME.txt) | Foreach-Object `
{ `
foreach ($person in $adobj){
$prop=$person.properties
if ($prop.cn -like "*" + $_.substring(1, 3) + "*")
{
$s1 = $_ -replace $_.substring(0, 4), $prop.cn
$s2 = $s1 -replace "AD_DEPT", $prop.department
$s3 = $s2 -replace "AD_BRANCH", $prop.physicalDeliveryOfficeName
add-content C:\FILENAME2.txt $s3
}
}
}
The AD_DEPT and AD_BRANCH are just placeholders in my original file.
EDIT
I read through JPBlanc's answer and did some more research and ended up with this working example. The key seems to be in specifying the properties to load. Thanks!
$strFilter = "(&(objectClass=Person)(department=*))"
$objDomain = New-Object System.DirectoryServices.DirectoryEntry
$objOU = New-Object System.DirectoryServices.DirectoryEntry("LDAP://OU=XX;DC=XX;DC=local")
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.SearchRoot = $objOU
$objSearcher.PageSize = 1000
$objSearcher.Filter = $strFilter
$objSearcher.SearchScope = "OneLevel"
$colProplist = "cn","department","physicaldeliveryofficename"
foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}
$colResults = $objSearcher.FindAll()
remove-item \\SERVER\FTPROOT\FOLDER\FILENAME.MODIFIED
(Get-Content \\SERVER\FTPROOT\FOLDER\FILENAME) | Foreach-Object `
{ `
foreach ($person in $colResults){
$prop = $person.properties
if ($prop.cn -like "*" + $_.substring(1, 3) + "*")
{
$s1 = $_ -replace $_.substring(0, 4), $prop.cn
$s2 = $s1 -replace "AD_DEPT", $prop.department
$s3 = $s2 -replace "AD_BRANCH", $prop.physicaldeliveryofficename
add-content \\SERVER\FTPROOT\FOLDER\FILENAME.MODIFIED $s3
break
}
}
}
Much things have to be said there.
1. The presence of the attribute
For an attribute to be queried, it first must be present in the SCHEMA of you directory. SCHEMA defines types and attributes that directory entries can contain. In the schema this attribute has to be defined as "MAY be" or "MUST be " present in a type. For example objectClass attribute MUST be present in all types.
If I have a look in the schema of my Windows 2K8 R2, I can see your attribute :
Now if I use Apache Directory Studio I can see that physicalDeliveryOfficeName is present 12 types (11 on a normal server forget SlxAuteur)
Conclusion of this first part : You probably (if you have enough rights) set this attribute on a user or an inetOrgPerson.
2. The way you search an attribute
You'll find here under a sample of usage of a directory searcher. I add the code to modify physicalDeliveryOfficeName attribute on a specified user.
$dn = New-Object System.DirectoryServices.DirectoryEntry ("LDAP://192.168.183.138:389/dc=societe,dc=fr","administrateur#societe.fr","blabla")
# Look for users
$Rech = new-object System.DirectoryServices.DirectorySearcher($dn)
$rc = $Rech.filter = "((objectCategory=person))"
$rc = $Rech.SearchScope = "subtree"
$rc = $Rech.PropertiesToLoad.Add("distinguishedName");
$rc = $Rech.PropertiesToLoad.Add("sAMAccountName");
$rc = $Rech.PropertiesToLoad.Add("ipphone");
$rc = $Rech.PropertiesToLoad.Add("telephoneNumber");
$rc = $Rech.PropertiesToLoad.Add("memberOf");
$rc = $Rech.PropertiesToLoad.Add("distinguishedname");
$rc = $Rech.PropertiesToLoad.Add("physicalDeliveryOfficeName"); # Your attribute
$liste = $Rech.findall()
foreach ($usr in $liste)
{
# Write-Host $usr.Properties["samaccountname"]
if ($usr.Properties["samaccountname"] -eq "massin")
{
Write-Host $usr.Properties["distinguishedname"]
$dnUser = New-Object System.DirectoryServices.DirectoryEntry ("LDAP://192.168.183.138:389/$($usr.Properties["distinguishedname"])","administrateur#societe.fr","blabla")
$dnUser.put("physicalDeliveryOfficeName", "1 rue de la source")
$res = $dnUser.setinfo()
$res
}
}
Here is the result :
Remarks : a Directory search is
The node where begin the search
the attributes you want (it's not mandatory, but it's a best practice) if you no give them you CAN'T be sure that they are retrieved.
The depth (base, onelevel, subtree)
The filter
If an attribute is not queried or is empty, it will not be present in the result