System.DirectoryServices.DirectorySearcher works if called from PowerShell but not if called from cmd.exe - scripting

I wrote an script for PowerShell 1.0 (now using 2.0) that executes a search on my Active Directory. The code is the following:
$filter = "some filter"
$rootEntry = New-Object System.DirectoryServices.DirectoryEntry
$searcher = New-Object System.DirectoryServices.DirectorySearcher
$searcher.SearchRoot = $rootEntry
$searcher.Filter = $filter
$searcher.SearchScope = "Subtree"
$colResults = $searcher.FindAll()
After calling FindAll() method of the DirectorySearcher instance, I print the results to see what I got.
The thing is, if I start PowerShell.exe and call the script on the prompt I'm able to see results. But if I try to call it using cmd.exe using the same filter I don't see any results. FindAll() returns an empty result set.
I'm running this on a Windows 2003 Server. It did not came with PowerShell 1.0 so I downloaded it and installed it on the server. It does have .Net Framework 2.0.
Any suggestions?
Thanks a lot.

By defaul your $rootEntry point on the root of you local AD i you are running on a server, and this with the credetial of the current process. you don't show what is your filter and how you use your result.
Here is a small sample of an ADSI search from PowerShell
Clear-Host
# ADSI Bind with current process credentials
#$dn = [adsi] "LDAP://192.168.30.200:389/dc=dom,dc=fr"
# ADSI Bind with specific credentials
$dn = New-Object System.DirectoryServices.DirectoryEntry ("LDAP://192.168.183.138:389/dc=societe,dc=fr","administrateur#societe.fr","test.2011")
# 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()

Finally got it working by doing two things:
Upgrade to PowerShell 2.0.
Run with -File option.
So the command was run like this:
>>powershell -file ./script.ps1 "dn" "uid"
I'm not sure what the difference between the -File and -Command options are (does anyone?) but it worked.
Thanks.

Related

DSC Script Resource - executes .exe, but doesn't wait until completion

Question)
How do I get a DSC script resource to wait until the code has completed before moving on?
(The code is invoke-expression "path\file.exe")
Details)
I am using powershell version 5
and am trying to get DSC setup to handle our sql server installations.
My manager has asked me to use the out of the box DSC components.
i.e. no downloading of custom modules which may help.
I have built up the config file that handles the base server build - everything is good.
The script resource that installs sql server is good.
It executes, and waits until it has installed completely, before moving on.
When I get up to the script resource that installs the sql server cumulative update, I have issues.
The executable gets called and it starts installing (it should take 10-15 minutes), but the dsc configuration doesn't wait until it has installed, and moves on after a second.
This means that the DependsOn for future steps, gets called, before the installation is complete.
How can I make the script resource wait until it has finished?
Have you tried the keyword "DependsOn" like that ?
Script MyNewSvc
{
GetScript = {
$SvcName = 'MyNewSvc'
$Results = #{}
$Results['svc'] = Get-Service $SvcName
$Results
}
SetScript = {
$SvcName = 'MyNewSvc'
setup.exe /param
while((Get-Service $SvcName).Status -ne "Running"){ Start-Sleep 10 }
}
TestScript = {
$SvcName = 'MyNewSvc'
$SvcLog = 'c:\svc.log'
If (condition) { #like a a running svc or a log file
$True
}
Else {
$False
}
}
}
WindowsFeature Feature
{
Name = "Web-Server"
Ensure = "Present"
DependsOn = "[Script]MyNewSvc"
}
Invoke-Expression doesn't seem to wait until the process has finished - try this in a generic PowerShell console and you'll see the command returns before you close notepad:
Invoke-Expression -Command "notepad.exe";
You can use Start-Process instead:
Start-Process -FilePath "notepad.exe" -Wait -NoNewWindow;
And if you want to check the exit code you can do this:
$process = Start-Process -FilePath "notepad.exe" -Wait -NoNewWindow -PassThru;
$exitcode = $process.ExitCode;
if( $exitcode -ne 0 )
{
# handle errors here
}
Finally, to use command line arguments:
$process = Start-Process -FilePath "setup.exe" -ArgumentList #("/param1", "/param2") -Wait -PassThru;
$exitcode = $process.ExitCode;

powershell v2 - how to get process ID

I have an Application, that runs multiple instances of itself. e.g
AppName.exe instance1
AppName.exe instance2
AppName.exe instance3
Using Powershell v2 I am trying to create a simple script that given an array of AppNames and instances, it loops through them, checks if they are running, and then shuts them down.
I figured the best way to do this would be check for each instance, if found capture it's processID, and pass that to the stop-process cmdlet.
BUT, I can't figure out how to get the process id.
So far I have:
$appName = "AppName.exe"
$instance = "instance1"
$filter = "name like '%"+$appName+"%'"
$result = Get-WmiObject win32_process -Filter $filter
foreach($process in $result )
{
$desc = $process.Description
$commArr = $process.CommandLine -split"( )"
$inst = $commArr[2]
$procID = "GET PROCESS ID HERE"
if($inst -eq $instance)
{
Stop-Process $procID
}
}
Can anyone tell me where to get the process ID from please?
you can use the get-process cmdlet instead of using wmi :
$procid=get-process appname |select -expand id
$procid=(get-process appname).id
When using Get-WmiObject win32_process ..., the objects returned have an attribute named ProcessId.
So, in the question, where you have:
$procID = "GET PROCESS ID HERE"
use:
$procID = $process.ProcessId
You could also use that in the $filter assignment, e.g.
$filter = "ProcessId=1234"

Upload a file to Sharepoint 2010 with powershell 2.0

I'm struggling since a couple of days to upload files to Sharepoint 2010 with powershell.
I'm on a win7 machine with powershell v2 trying to upload to a SP 2010 site.
I'm having 2 major issues
$Context.web value is always empty even after Executequery() and no
error is shown. My $Context variable gets the server version (14.x.x.x.x) but nothing more
$Context.Load($variable) which always returns the error Cannot find an overload for "Load" and the argument count: "1".
I copied Sharepoint DLLs to my Win7 machine and I import the reference to my script.
The below script is a mix of many parts I took from the net.
I'v already tried unsuccessfully to add an overload on the clientcontext defining Load method without Type parameter suggested in the following post
http://soerennielsen.wordpress.com/2013/08/25/use-csom-from-powershell/
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")
$site = "https://Root-of-my-site"
$listname = "My-folder"
$context = New-Object Microsoft.SharePoint.Client.ClientContext($site)
[Microsoft.SharePoint.Client.Web]$web = $context.Web
[Microsoft.SharePoint.Client.List]$list = $web.Lists.GetByTitle($listName)
$Folder = "C:\temp\Certificates"
$List = $Context.Web.Lists.GetByTitle($listname)
Foreach ($File in (dir $Folder))
{
$FileCreationInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$FileCreationInfo.Overwrite = $true
$FileCreationInfo.Content = get-content -encoding byte -path $File.Fullname
$FileCreationInfo.URL = $File
$Upload = $List.RootFolder.Files.Add($FileCreationInfo)
$Context.Load($Upload)
$Context.ExecuteQuery()
}
The error is
Cannot find an overload for "Load" and the argument count: "1".
At C:\temp\uploadCertToSharepoint.ps1:48 char:14
+ $Context.Load <<<< ($Upload)
+ CategoryInfo : NotSpecified: (:) [], MethodException
+ FullyQualifiedErrorId : MethodCountCouldNotFindBest
Can someone please help me sorting this issue?
I'll need to upload around 400 files with ad-hoc fields to a sharepoint site in a couple of weeks and at the moment I'm completely stuck. Running the script server side is unfortunately not possible.
Thanks,
Marco
This error occurs since ClientRuntimeContext.Load is a Generics Method:
public void Load<T>(
T clientObject,
params Expression<Func<T, Object>>[] retrievals
)
where T : ClientObject
and Generics methods are not supported natively in PowerShell (V1, V2) AFAIK.
The workaround is to invoke a generic methods using MethodInfo.MakeGenericMethod method as described in article Invoking Generic Methods on Non-Generic Classes in PowerShell
In case of ClientRuntimeContext.Load method, the following PS function could be used:
Function Invoke-LoadMethod() {
param(
$clientObjectInstance = $(throw “Please provide an Client Object instance on which to invoke the generic method”)
)
$ctx = $clientObjectInstance.Context
$load = [Microsoft.SharePoint.Client.ClientContext].GetMethod("Load")
$type = $clientObjectInstance.GetType()
$clientObjectLoad = $load.MakeGenericMethod($type)
$clientObjectLoad.Invoke($ctx,#($clientObjectInstance,$null))
}
Then, in your example the line:
$Context.Load($Upload)
could be replaced with this one:
Invoke-LoadMethod -clientObjectInstance $Upload
References
Invoking Generic Methods on Non-Generic Classes in PowerShell
Some tips and tricks of using SharePoint Client Object Model in
PowerShell. Part 1
It throws the error because in powershell 2.0 you cannot call generic method directly.
You need to create closed method using MakeGenericMethod. Try to use code below.
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint.Client.Runtime")
$site = "http://server"
$listname = "listName"
$Folder = "C:\PS\Test"
$context = New-Object Microsoft.SharePoint.Client.ClientContext($site)
[Microsoft.SharePoint.Client.Web]$web = $context.Web
[Microsoft.SharePoint.Client.List]$list = $web.Lists.GetByTitle($listName)
$method = $Context.GetType().GetMethod("Load")
$closedMethod = $method.MakeGenericMethod([Microsoft.SharePoint.Client.File])
Foreach ($File in (dir $Folder))
{
$FileCreationInfo = New-Object Microsoft.SharePoint.Client.FileCreationInformation
$FileCreationInfo.Overwrite = $true
$FileCreationInfo.Content = (get-content -encoding byte -path $File.Fullname)
$FileCreationInfo.URL = $File
$Upload = $List.RootFolder.Files.Add($FileCreationInfo)
$closedMethod.Invoke($Context, #($Upload, $null) )
$Context.ExecuteQuery()
}

How to test authentication to remote IP address

I have one powershell script that invokes another powershell script.
The first script is invoked with an ip address, which gets passed to the second script. The second script is supposed to return the userId in form Domain\User
The first script uses ProcessStartInfo and Process to get elevated credentials to call the second script
# part of first script
$startInfo = New-Object System.Diagnostics.ProcessStartInfo
$startInfo.FileName = "powershell.exe"
$startInfo.Arguments = "C:\script\second_script.ps1 "
$startInfo.RedirectStandardOutput = $true
$startInfo.UseShellExecute = $false
$startInfo.CreateNoWindow = $false
$startInfo.Username = Service_Account
$startInfo.Domain = Domain
$startInfo.Password = password
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $startInfo
$process.Start() | Out-Null
$standardOut = $process.StandardOutput.ReadToEnd()
$process.WaitForExit()
The second script has many try-catch blocks, such as checking whether we can ping the machine, checking whether we can access WMI
# part of second
# Can we ping the machine?
try{
Test-Connection $Sender_IP -count 1 -ErrorAction Stop | out-null
}
catch [Exception]
{
$userId = "Unknown/CannotPing "
return $output = "userId=" + $userId
}
try
{
<#Gather information on the computer corresponding to $Sender_IP#>
$Win32OS = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Sender_IP -ErrorAction Stop
}
catch [Exception]
{
$userId = "Unknown/CannotDetectOS "
return $output = "userId=" + $userId
}
The script was unable to access WMI of many IP addresses. And when I was trying to troubleshoot by manually remoting into the IP address with the service account, I was unable to.
Now, I am trying to figure out a way for the script to check whether it can authenticate to the IP address. If the script is unable to authenticate to the IP address, it should throw and exception and not even check whether it can access WMI.
What cmdlets can help with this?

Powershell Pull Current OU

I need to be able to pull the current machine OU. I found some VB code that could do this,but I would like to just be able to do in the script with out having to call VB. Any ideas, the VB code is below.
Set objSysInfo = CreateObject("ADSystemInfo")
DN = objSysInfo.ComputerName
WScript.Echo DN
-Josh
You can get the ADSystemInfo with this function.
function Get-LocalLogonInformation
{
try
{
$ADSystemInfo = New-Object -ComObject ADSystemInfo
$type = $ADSystemInfo.GetType()
New-Object -TypeName PSObject -Property #{
UserDistinguishedName = $type.InvokeMember('UserName','GetProperty',$null,$ADSystemInfo,$null)
ComputerDistinguishedName = $type.InvokeMember('ComputerName','GetProperty',$null,$ADSystemInfo,$null)
SiteName = $type.InvokeMember('SiteName','GetProperty',$null,$ADSystemInfo,$null)
DomainShortName = $type.InvokeMember('DomainShortName','GetProperty',$null,$ADSystemInfo,$null)
DomainDNSName = $type.InvokeMember('DomainDNSName','GetProperty',$null,$ADSystemInfo,$null)
ForestDNSName = $type.InvokeMember('ForestDNSName','GetProperty',$null,$ADSystemInfo,$null)
PDCRoleOwnerDistinguishedName = $type.InvokeMember('PDCRoleOwner','GetProperty',$null,$ADSystemInfo,$null)
SchemaRoleOwnerDistinguishedName = $type.InvokeMember('SchemaRoleOwner','GetProperty',$null,$ADSystemInfo,$null)
IsNativeModeDomain = $type.InvokeMember('IsNativeMode','GetProperty',$null,$ADSystemInfo,$null)
}
}
catch
{
throw
}
}
You can't use ADSystemInfo directly in Powershell (or at least it's not easy) according to this page
Well, OK, that’s not entirely true; it is possible to use ADSystemInfo from within PowerShell; however, the process is far from easy and even farther from being intuitive. That’s because ADSystemInfo is lacking a “wrapper” that makes it easy to access the object from a .NET language like Windows PowerShell. That results in a lot of gyrations involving .NET Reflection classes, the InvokeMember method, and, as near as we can tell, a lot of prayer.
But the page does provide examples for performing AD queries using the System.DirectoryServices.DirectorySearcher .NET object. Here's an example from the page slightly modified to match your VB script:
$strName = $env:computername
$strFilter = "(&(objectCategory=Computer)(Name=$strName))"
$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
$objSearcher.Filter = $strFilter
$objPath = $objSearcher.FindOne()
$objPath.GetDirectoryEntry().distinguishedname