Answered Working PS code fails in Script Block

  • 1 мая 2012 г. 17:36
     
      С кодом

    Hello all. The code in the script block runs fine by itself when it is the content of a powershell script. I am trying to get this Active Directory searcher to run when logged in as the local administrator. I thought I would call the code with the Invoke-Command so I can use credentials that have access to AD. I do not know why, but the code fails when it is used in the script block. Any ideas why?

    The error I get is:

    Exception calling "FindAll" with "0" argument(s): "An operations error occurred
    .
    "
        + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
        + FullyQualifiedErrorId : DotNetMethodException

    The Code:

    #Configure WinRM
    Enable-PSRemoting –force
    #New Powershell Session
    $s = New-PSSession -ComputerName . -Credential $cred
    #Run Commands
    Invoke-Command -Session $s -ScriptBlock {
    	Write-Host "Start of ScriptBlock"
    	Write-Host "Computer Name is: " $env:computername
    	$strFilter = "(&(objectCategory=computer)(Name=$env:computername))"
    	$objDomain = New-Object System.DirectoryServices.DirectoryEntry
    	$objSearcher = New-Object System.DirectoryServices.DirectorySearcher
    	$objSearcher.SearchRoot = $objDomain
    	$objSearcher.PageSize = 10
    	$objSearcher.Filter = $strFilter
    	$objSearcher.SearchScope = "Subtree"
    	
    	$colProplist = "name"
    	foreach ($i in $colPropList){$objSearcher.PropertiesToLoad.Add($i)}
    	
    	$colResults = $objSearcher.FindAll()
    }

Все ответы

  • 1 мая 2012 г. 17:48
     
     
    I tried your code as is against a remote DC, and it works fine.  Cannot reproduce your error.

    Grant Ward, a.k.a. Bigteddy

    What's new in Powershell 3.0 (Technet Wiki)

  • 1 мая 2012 г. 18:29
     
     

    Are you calling a remote system with RSAT installed?

    That will not likely work.  If you call remotely into a DC with WinRM you can query AD.  You cannot do it if the RSAT machine is not a DC (!?!) or if it is not 'Trusted for Delegation'.

    If you have WS2008R2 then remote into the R2 DC and it will work.


    ¯\_(ツ)_/¯

  • 1 мая 2012 г. 22:46
     
     
    Bigteddy, thanks for giving it a shot. I am not sure what the problem is here. I am sure the code works too since I can use it while in it's own ps1 file. I just assumed that the script block was causing the problem since it works fine outside of the script block. Any idea's what might be causing my issued?
  • 1 мая 2012 г. 22:57
     
     
    To the best of my knowledge I am calling the local machine. I use this script to install applications. Our SCCM installer will take a three letter flag to know what site to install SCCM against. For instance I would SCCMInstaller.exe /G_SAC to install SCCM for the Sacramento SCCM Site. I get the site by querying AD and then running if then statments to figure out what OU the computer is in. Currently the programs works great. I image a PC with MDT. When MDT is all done I log into the machine (network credentials & administrator) and run the PS script and everything works great. It grabs the OU and installs SCCM with the right site code. I am trying to have MDT run this PS file, but the problem is that MDT does not have network credentials when running a PS script. Right now I am trying to get the PS script to run from the local administrator account with my network credentials by providing them to a PSCredential object used with the PSSession or Invoke-Command.

    Why is PowerShell telling me that the FindAll method needs arguments when it doesn't even take arguments? Or is it saying that the error gave 0 arguments?
  • 1 мая 2012 г. 23:13
     
     

    The 'FindAll; error is one you get back on a null find. It is saying that nothing was found.

    Use 'Enter-Pssession $s '  and try to tun the same set of commands.

    Try this:
    $strFilter = "samaccountname=yoursamname"

    Put in your samaccountname and see if it still fails.


    ¯\_(ツ)_/¯

  • 1 мая 2012 г. 23:22
     
     Отвечено

    The following is all you need to find one computer.

    $objSearcher=[adsisearcher]"samaccountName=$($env:computername)`$"
    $objSearcher.FindOne()

    Try just that bit of code and do it by using Enter-PSSession.


    ¯\_(ツ)_/¯

    • Помечено в качестве ответа Russel_ 2 мая 2012 г. 2:22
    •  
  • 2 мая 2012 г. 2:34
     
     

    Thank you so much for the help. I went into my code and added "Enter-PSSession" after "$s = New-PSSession -ComputerName . -Credential $cred" and then I also took out the Invoke-Command call but left the code that was in a script block. I also changed my code to the two lines you gave me:

    $objSearcher=[adsisearcher]"samaccountName=$($env:computername)`$"
    $objSearcher.FindOne()

    If you don't mind, I have a slew of questions. Why did this work over the other method? What is [adsisearcher]? Does it have the same properties/attributes/methods as System.DirectoryServices.DirectorySearcher? If I wanted to just get the "name" attribute for the SearchResult object, how would I do that? When you use the FindAll() method it will return all the attributes by default unless you specify, is the same true for FindOne() since it is only going to return a SearchResult instead of a SearchResultCollection?

  • 2 мая 2012 г. 3:52
     
     

    Never mind, answered my own questions. [adsisearcher] is a type accelerator . http://blogs.technet.com/b/heyscriptingguy/archive/2010/08/24/use-the-powershell-adsisearcher-type-accelerator-to-search-active-directory.aspx

    Pipe [adsisearcher] to Get-Member for Methods/Properties.

    To get the name attribute for the search result you have to get the property of the properties

    objSearcher=[adsisearcher]"samaccountName=$($env:computername)`$"
    $x = $objSearcher.FindOne()

    $x.Properties.name

    $x.Properties.operatingsystem

  • 2 мая 2012 г. 5:00
     
      С кодом

    Thank you so much for the help. I went into my code and added "Enter-PSSession" after "$s = New-PSSession -ComputerName . -Credential $cred" and then I also took out the Invoke-Command call but left the code that was in a script block. I also changed my code to the two lines you gave me:

    $objSearcher=[adsisearcher]"samaccountName=$($env:computername)`$"
    $objSearcher.FindOne()

    If you don't mind, I have a slew of questions. Why did this work over the other method? What is [adsisearcher]? Does it have the same properties/attributes/methods as System.DirectoryServices.DirectorySearcher? If I wanted to just get the "name" attribute for the SearchResult object, how would I do that? When you use the FindAll() method it will return all the attributes by default unless you specify, is the same true for FindOne() since it is only going to return a SearchResult instead of a SearchResultCollection?

    Wow!  So many questions.

    Ok. I will try.  I asked you to do it that way to see if the remoting system was functional and also to get away from th linear code setting.  By changing things we have chased away th gremlins temporarily.

    It looks like something you were doing has a mistake that is buried inside th characters in such a way that when I or someone else runs it it will work.  Te problem i not yet solved.

    Next we want to do the second test with this.

    $sb={([adsisearcher]"samaccountname=$env:computername`$").FindOne()}
    $cred=Get-Credential DOMAIN\USERID
    Invoke-Command -Credential $cred -ScriptBlock $sb -computer .


    What I am doing isreducing complexityy so you can see the mechanics of the system.  I am also trying to get us away from  he old legacy constructs like $objSearcher.  Code like this is converted VBScript code.  It misses the point of both VBScipt and PowerShelll.  the naming convention that adds a three character or longer prefix to a variable was invented many years ago for 'C' programmers so they could remembeer the types of the variables.  It was also extremely useful in early Bsic programming since all variables in Basic are not typed.

    In modern times the compilers and intellisense handle all of this for us.  Besides, most  here would not even begin to know how to troubleshoot a subtle type coersion  issue if it was happening in PowerShell or VBScript.

    PowerShell will convert types when necessary and you will almost never need to know that it is happening.  Besides, using 'obj' to prefix a PowerShell variable is redudant because everything in PowerShell is an object. 

    That said lets just use names that are meningful and that contribute to the readability of the code.  Nominclature in coding should be for humans and not for computers.

    $searcher-=[adsisearcher<filter>
    OR
    $adsearcher=[adsisearcher]<filter>

    are more readable and more meaning full. I tend to use $se=[adsisearcher] or:

    $computer=([adsisearcher]"samaccountname=$env:computername`$").FindOne()

    So what is really different besides forcing you  to refocus on the code and away from the problem.

    Invoke-Command is not the issue. It would have worked your original way except for a number of things that might have caused an issue.

    In scripting or other coding we want to alter the system only when it is required.  By setting all parameters each time we create many issues for ourselves when it comes to understandablility of the code. 

    The code you posted was legacy.  It was created along time ago from the demo code that Microsoft pubilshed right down to the variable names.  People have stuck with that naming for over ten years without ever asking why.

    In formal programming we have learned not to do things this way because it presents extra lines of code that need to be managed and debugged.  No correctly trained programmer would do this today.

    All of the settings you were adding to the search setup were either defaults or unnecessary.

    Start with the 'searcher' class instance.  We can get it using New-Object but we can also use its 'type accelerator' or use its type in the raw form.

    These are all equivalent:
    $searcher=New-Object System.DirectoryServices.DirectorySearcher(<filter>)
    $searcher=[System.DirectoryServices.DirectorySearcher]<filter>
    $searcher=[adsisearcher]<filter>

    They all create a directory searcher instance set to the <filter>.  This is because the type accelerator can take one object as an initializer.

    $x=[int]99  # System.int
    $s=[string]'Hello World!'  #System.String
    $xml=[xml]'<root/>'   #System.Xml.XmlDocument

    All of these are type accellerators.

    [adsisearcher] is the type accelerator for the directory searcher.  These TAs are all built into PowerShell.  We can create new ones if we need them.

    Using type accellerators simplify coding and reduce the visual confusion making PowerShell code easier to read and easier to debug.  Use the full New-Object when you need to create a raw object or you need to use one of the alternate constuctors.

    Example:

    # template
    $searcher=New-Object System.DirectoryServices.DirectorySearcher(<filter>,<searchroot>)
    # search an ou for a user
    $searcher=New-Object System.DirectoryServices.DirectorySearcher('samaccountname=user01','LDAP://OU=someeou,DC=mydomain,DC=com')
    $OR
    #user=(New-Object System.DirectoryServices.DirectorySearcher('samaccountname=user01','LDAP://OU=someeou,DC=mydomain,DC=com')).FindOne()

    Lots of choices.  I present them to get you to see PowerShell in a new way.  Don't worry too much about completely understanding how this works.  That will come with experience.  Just ask whether you understand the code you are writing and learn to use PowerShell to troubleshoot your code for you.

    So what about all of the other lines that I changed or threw out?  They were mostly redundant.

    1.  SearchRoot is always set to the domain so there is no need to set it.
    2.  PageSize is meaningless whrn you are asking for one object or fewer that 1500.
    3.  SearchScope is always 'SubTree' unless you change it.
    4.  Using the PropertiesToLoad attribute on a singleton is not really necessary.

    Change only the options that you need to change.  If you were to follow the implied convention you would have to set every one of a couple of teo dozen parameters (23).  Wouldn't that be a pain?

    To debug code that is not behaving well we need to start by simplifying the problem.  By starting with less code we are already closer to a solution.

    We have not yet discovered why you couldn't get the original code to work.   Both BigTeddy and myself ran it succesfully.  Of course we both had to make subtle changes to get it to work in out domain.  My copy is so generic that it can be run anywhere without any changes.  That may prevent subtle things from happening.

    Lastly - I changed the filter. You know the NetBIOS name you are looking for.  In the case of computer objects the object CN or Name is the NetBIOS name and the samaccuontname is the NetBIOS name plus a $.  The samaccoutnname is unique within a domain. I just changed the filter to search on that one attribute. That is much faster and less prone to errors.

    Eventually you will learn more about Active Directory and the Net Framework and all of this will become second nature.


    ¯\_(ツ)_/¯

  • 2 мая 2012 г. 5:02
     
     
    SOrry I wrote a book.  I didn't intend to but there is no simple answer to your questions.  I didn't want to just say "because" or "it works that way". 

    ¯\_(ツ)_/¯

  • 2 мая 2012 г. 16:10
     
     
    Sorry? Yeah right, that response was amazing. I think I should have to pay you for a response as good as that. Let me give that stuff you mentioned a try. Thanks for the explanation, I appreciate it.
  • 2 мая 2012 г. 17:28
     
      С кодом

    I ran:

    $sb={([adsisearcher]"samaccountname=$env:computername`$").FindOne()}
    $cred=Get-Credential DOMAIN\USERID
    Invoke-Command -Credential $cred -ScriptBlock $sb -computer .

    And got the orginal error about 0 arguments

  • 2 мая 2012 г. 17:38
     
      С кодом

    I ran:

    $sb={([adsisearcher]"samaccountname=$env:computername`$").FindOne()}
    $cred=Get-Credential DOMAIN\USERID
    Invoke-Command -Credential $cred -ScriptBlock $sb -computer .

    And got the orginal error about 0 arguments

    I can't reproduce your error.  See the following transcript:

    PS C:\Users\administrator> $sb={([adsisearcher]"samaccountname=$env:computername`$").FindOne()}

    Invoke-Command -Credential contoso\administrator -ScriptBlock $sb -computer london

     

    PSComputerName     : london
    RunspaceId         : a74ccc0f-0c4a-48df-9fcd-d652120e8983
    PSShowComputerName : True
    Path               : LDAP://CN=LONDON,OU=Domain Controllers,DC=contoso,DC=local
    Properties         : {operatingsystem, adspath, cn, lastlogoff...}

    PS:  "London" is the name of my DC.


    Grant Ward, a.k.a. Bigteddy

    What's new in Powershell 3.0 (Technet Wiki)

  • 2 мая 2012 г. 18:23
     
      С кодом

    I ran:

    $sb={([adsisearcher]"samaccountname=$env:computername`$").FindOne()}
    $cred=Get-Credential DOMAIN\USERID
    Invoke-Command -Credential $cred -ScriptBlock $sb -computer .

    And got the orginal error about 0 arguments

    Saying you got an error without teh full error message is not helpful.

    Are you running this by pasting it or from a script file?


    ¯\_(ツ)_/¯

  • 3 мая 2012 г. 17:08
     
      С кодом

    Sorry about that, I got the error message I posted earlier. I posted it again below. I ran it from both a script file and by typing it into the command prompt and got the same error both times.

    Exception calling "FindOne" with "0" argument(s): "An operations error occurred
    .
    "
        + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
        + FullyQualifiedErrorId : DotNetMethodException

    Do I need to disable something to maybe make this work? Is there some type of restriction that won't let script blocks run?
  • 3 мая 2012 г. 17:53
     
      С кодом

    Sorry about that, I got the error message I posted earlier. I posted it again below. I ran it from both a script file and by typing it into the command prompt and got the same error both times.

    Exception calling "FindOne" with "0" argument(s): "An operations error occurred
    .
    "
        + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
        + FullyQualifiedErrorId : DotNetMethodException

    Do I need to disable something to maybe make this work? Is there some type of restriction that won't let script blocks run?

    What code did you paste?  Please paste it back so we can see it.

    I suspect that when you are copying it from the web it is getting damaged.

    Try copying and pasting into notepad then copying from notepad and back into the session.

    Before pasting close PowerShell and restart so you do not have issues.  DO NOT USE PowerShellISE.

    Start a new PowerSHell instance from the start menu and then paste the scritp from NOTEPAD not from the web directly.

    If your account is not trusted for delegation then no amount of playing around like this will get you connected.

    I can reproduce the error by using a workstation that is not trusted event though I am logged in as a domain admin.  If the computer is a workstation it will not allow delegated conenctions. You must use remoting through a machine trusted for delegation.  By default RSAT does not elevate a machine to a trusted level.  Only DCs are trusted. If you logon through WinRM (Invoe-Command with credentials is doing just that) you will not be able to reach out to another machine.  Only direct connections are allowed.


    ¯\_(ツ)_/¯

  • 3 мая 2012 г. 20:25
     
     Предложенный ответ С кодом

    I ran this code by putting it in a script and by typing it into the command line:

    $sb={([adsisearcher]"samaccountname=$env:computername`$").FindOne()}
    $cred=Get-Credential DOMAIN\USERID
    Invoke-Command -Credential $cred -ScriptBlock $sb -computer .

    I had originally copied it into a text document and back out for both the script and when running it straight through the command prompt. I don't use the PS ISE, I just open the PS command line by Start->Run-> Powershell . I don't understand the information about account delegation. If the code below works, why would the code above not work? The only difference is essentially the code block? Does your account have to be trusted for deligation to run script blocks? I am not familiar with RSAT either, but I will do some research. How can I tell if my account/machine are trusted for deligation? I have any access to DCs so that option is out the window.

    #Start Transcript and echo script info
    Start-Transcript -Append -Path $env:WINDIR\Temp\MDT_Powershell_log.txt -NoClobber
    Write-Output $MyInvocation.MyCommand.Path
    $script = Split-Path $MyInvocation.MyCommand.Path
    #Get Credentials
    $f1 = $script + "\r.txt"
    $f2 = $script + "\rkey.xml"
    $key = Import-Clixml $f2
    $password = Get-Content $f1 | ConvertTo-SecureString -key $key
    $cred = New-Object system.Management.Automation.PSCredential("ABC\russ", $password)
    #Configure WinRM
    Enable-PSRemoting –force
    #New Powershell Session
    $s = New-PSSession -ComputerName . -Credential $cred
    Enter-PSSession $s
    #AD Searcher
    $ADSearcher=[adsisearcher]"samaccountName=$($env:computername)`$"
    $R = $ADSearcher.FindOne()
    $loc = [String]$R.Path
    Write-Host $R.Properties.operatingsystem
    #Corporite Sites
    if ($loc.Contains("Sacramento")) {$site="SAC"}
    elseif ($loc.Contains("Denver")) {$site="DVR"}
    elseif ($loc.Contains("Chicago")) {$site="CHI"}
    elseif ($loc.Contains("Nashville")) {$site="NSH"}
    Write-Host "THe site is: " $site
    \\SERVER01\Apps\SCCM2007ClientTool\Setup.exe /G_$site /NOREBOOT
    Exit-PSSession
    Stop-Transcript

    Thanks again for your continued support, I appreciate the help.
  • 3 мая 2012 г. 21:47
     
     

    You are not able to delegate.  Workstatrions are set that way.

    If you are not a domain admin then you are not likely to get your code to work as you will not have enough rights to access AD as a delagated session.

    Delegation si like forwarding.  YOu are using one account to gain the enhanced capabilities of another acount.  For security accounts, bot machine and user, are not trusted.  If we need to trust a machine or user then we need to be very careful.  DCs are trusted buy default becuse they have act instead of or inplace of machine accounts and users for GP and AD.  Event admins are not usually trusted for delegation.  Some service accounts may be trusted.

    What you are trying to do cannot be made to work because you have a limited user account in the domain.

    Perhaps if you explain why you need to do this we can find a better approach.


    ¯\_(ツ)_/¯

  • 5 мая 2012 г. 9:33
     
     

    It just hit that we are going about this all wrong.

    I am so busy trying to answer you question that I forgot to step back and ask "Why?".

    To get to AD just start  PowerShell as an alteernate user then run your AD queries. 

    RunAs /user:dom/account powershell

    Now yu can just connect directly to AD with no issues.  YOu will be running as a domain users and you can query AD without remoting.


    ¯\_(ツ)_/¯

  • 10 мая 2012 г. 11:52
     
     

    Hello there, sorry I was away for a couple of days. If I use RunAs, can I pass in the password? Securely? My overall goal is to just find out what OU a computer is during the MDT application install process. MDT images a machine, adds it to the domain, installs the progrograms and then is done. I just need to query AD to find out what OU the computer is in so I know how to install a particular program. Is there a better way to do this? Thanks again for your help

  • 10 мая 2012 г. 14:01
     
     

    Hello there, sorry I was away for a couple of days. If I use RunAs, can I pass in the password? Securely? My overall goal is to just find out what OU a computer is during the MDT application install process. MDT images a machine, adds it to the domain, installs the progrograms and then is done. I just need to query AD to find out what OU the computer is in so I know how to install a particular program. Is there a better way to do this? Thanks again for your help

    RunAs can take a password.   It does not pass anything.  It starts a shell as a different user. It is  a full shell.  Admins have been using RunAs for administration since the earliest days of XP.

    Go to a prompt and type "runas /?"


    ¯\_(ツ)_/¯

  • 10 мая 2012 г. 15:04
    Модератор
     
     
    Hello there, sorry I was away for a couple of days. If I use RunAs, can I pass in the password?

    No.

    Why doesn't the RunAs program accept a password on the command line?

    Bill

  • 10 мая 2012 г. 22:14
     
     
    RunAs won't work if I can't script it out. That is my main objective. Script the install. I already have a script that works, so long as the admin is logged in to the machine.
  • 10 мая 2012 г. 22:25
     
     
    RunAs won't work if I can't script it out. That is my main objective. Script the install. I already have a script that works, so long as the admin is logged in to the machine.

    The MDT does all of this for you.  WHy try to reinvent the wheel?

    YOu can try to use

    $item=New-Object System.Management.DirectoryServices.DirectoryEntry(<adspath>,>useername,password)

    Do not try to use Invoke ot anything else with this. Just supply the adspath to the object you want to inspect.


    ¯\_(ツ)_/¯

  • 10 мая 2012 г. 22:33
     
     
    Thanks again for the help, I will give that code you posted a shot. I am more interested in your previous statement though. If MDT can do it for me, can you tell me how so we can just set that up instead? The last thing I want to do is re-invent the wheel.
  • 11 мая 2012 г. 1:52
     
     
    Thanks again for the help, I will give that code you posted a shot. I am more interested in your previous statement though. If MDT can do it for me, can you tell me how so we can just set that up instead? The last thing I want to do is re-invent the wheel.

    http://technet.microsoft.com/en-us/solutionaccelerators/dd407791


    ¯\_(ツ)_/¯