Putting together an ADSI LDAP query - powershell

I am searching through the active directory for users under a specific organisation unit, that I would like to change using ADSI.
# get all users from the organizational unit
$accounts = Get-ADObject -filter 'objectClass -eq "user"' -SearchBase $dsn
# iterate over user objects
foreach ($account in $accounts) {
# unfortunately we have to use ADSI over the set-aduser cmdlet as we neeed to touch remote desktop attribues
$user = [ADSI]"LDAP://" + ($account.DistinguishedName).ToString()
# get logon name
$SamAccountName = $user.psbase.InvokeGet("SamAccountName")
# Profile Attributes
$user.psbase.InvokeSet("ProfilePath", "")
$user.psbase.InvokeSet("ScriptPath", "DIR\Logon.cmd")
$user.psbase.InvokeSet("HomeDrive", "H:")
$user.psbase.InvokeSet("HomeDirectory", "\\host\users$\${SamAccountName}")
# Remote Desktop Services Attributes
$user.psbase.InvokeSet("TerminalServicesProfilePath", "")
$user.psbase.InvokeSet("TerminalServicesHomeDirectory", "\\host\users$\${SamAccountName}")
$user.psbase.InvokeSet("TerminalServicesHomeDrive", "H:")
# Write attributes back to global catalog
$user.SetInfo()
}
This all works fine, until it comes to the $user = [ADSI]"LDAP://" + ($account.DistinguishedName).ToString() part.
Method invocation failed because [System.DirectoryServices.DirectoryEntry] does not contain a method named 'op_Addition'.
At \\tsclient\D\SourceCode\PowerShell\Set-ADUserAttributes.ps1:37 char:5
+ $user = [ADSI]"LDAP://" + ($account.DistinguishedName).ToString()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (op_Addition:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Exception calling "InvokeGet" with "1" argument(s): "Unspecified error
"
At \\tsclient\D\SourceCode\PowerShell\Set-ADUserAttributes.ps1:40 char:5
+ $SamAccountName = $user.psbase.InvokeGet("SamAccountName")
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
It seems there is no query getting executed. However, $account.DistinguishedName contains the correct LDAP path (which I have tested manually).
So what am I doing wrong here?.

You're trying to append to an ADSI object by casting "LDAP://" as [ADSI] before you do the append.
Cat your strings first, then do the cast:
$user = [ADSI]("LDAP://" + $account.DistinguishedName)

The casting operation has higher precedence than the concatenation operation, so you need to do the concatenation in a subexpression, either like this:
[adsi]("LDAP://" + $account.DistinguishedName)
or like this:
[adsi]"LDAP://$($account.DistinguishedName)"
The distinguished name is automatically converted to a string here, so you don't need to manually call ToString().

Related

Separating values entered into a string

So I am trying to create a Powershell menu that when the user selects a choice, it will ask for the value or values its trying to search (ex. Ping Multiple Computers). I am currently having a hard time getting that to work. I will post pictures to show what I mean
When I type in one name to search the command executes fine shown below:
When I try with multiple values it doesn't work:
Here is a snap of the code I have:
Any help of course is much appreciated.
UPDATE - 11/13
This is what I currently have:
function gadc {
Param(
[Parameter(Mandatory=$true)]
[string[]] $cname # Note: [string[]] (array), not [string]
)
$cname = "mw$cname"
Get-ADComputer $cname
}
This is the output in the Console
cmdlet gadc at command pipeline position 1
Supply values for the following parameters:
cname[0]: imanuel
cname[1]: troyw
cname[2]: hassan
cname[3]:
Get-ADComputer : Cannot convert 'System.String[]' to the type
'Microsoft.ActiveDirectory.Management.ADComputer' required by parameter 'Identity'. Specified
method is not supported.
At line:32 char:19
+ Get-ADComputer $cname
+ ~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-ADComputer], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,Microsoft.ActiveDirectory.Management.Commands.G
etADComputer
Press Enter to continue...:
**And here is the other way with the same result:**
cmdlet gadc at command pipeline position 1
Supply values for the following parameters:
cname[0]: imanuel, troyw
Get-ADComputer : Cannot convert 'System.String[]' to the type
'Microsoft.ActiveDirectory.Management.ADComputer' required by parameter 'Identity'. Specified
method is not supported.
At line:32 char:19
+ Get-ADComputer $cname
+ ~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-ADComputer], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,Microsoft.ActiveDirectory.Management.Commands.G
etADComputer
Press Enter to continue...:
You need to declare your mandatory parameter as an array, then PowerShell's automatic prompting will allow you to enter multiple values, one by one - simply press Enter by itself after having submitted the last value in order to continue:
function gadc {
param(
[Parameter(Mandatory)]
[string[]] $cname # Note: [string[]] (array), not [string]
)
# Get-ADComputer only accepts one computer name at a time
# (via the positionally implied -Identity parameter), so you must loop
# over the names.
# The following should work too, but is slightly slower:
# $cname | Get-ADComputer
foreach ($c in $cname) { Get-ADComputer $c }
}

How to get this PowerShell command output into an iterable collection or array

This answer shows how to output a list of computer-names on the domain:
(([adsi]"WinNT://$((Get-WMIObject Win32_ComputerSystem).Domain)").Children).Where({$_.schemaclassname -eq 'computer'})
If the Pathproperty is appended
... .Where({$_.schemaclassname -eq 'computer'}).Path
it outputs a list in this format:
WinNT://{domainname}.net/{machine-name}
How would that succession of fully qualified machine names be made into an array or a collection that could be iterated by the script found here?
Can that command (i.e. its output) simply be assigned to an array variable?
[array]$ComputerNames = ... (([adsi]"WinNT://$((Get-WMIObject <snip> ).Path
I suspect not, since when I try that
$ComputerNames = ... (([adsi]"WinNT://$((Get-WMIObject <snip> ).Path
foreach($computer in $ComputerNames) {
if($regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $computer)) {
I get an error:
Exception calling "OpenRemoteBaseKey" with "2" argument(s): "The network path was not found."
At C:\Users\{myusername}\Get-NetFrameworkVersion.ps1:40 char:8
+ if($regKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('Lo ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : IOException
My ultimate goal is to obtain the .NET versions installed on all PCs on the domain.

Add xml object to xml file in PowerShell

I am trying to create an RDCMan file (.rdg xml file) using PowerShell. I have started by defining this template
$newFileTemplate = '<?xml version="1.0" encoding="utf-8"?>
<RDCMan programVersion="2.7" schemaVersion="3">
<file>
<properties>
<expanded>False</expanded>
<name>Office Servers</name>
</properties>
<displaySettings inherit="None">
<liveThumbnailUpdates>True</liveThumbnailUpdates>
<allowThumbnailSessionInteraction>False</allowThumbnailSessionInteraction>
<showDisconnectedThumbnails>True</showDisconnectedThumbnails>
<thumbnailScale>1</thumbnailScale>
<smartSizeDockedWindows>True</smartSizeDockedWindows>
<smartSizeUndockedWindows>False</smartSizeUndockedWindows>
</displaySettings>
</file>
</RDCMan>
'
before creating an xml object like so
$File = 'D:\Test.rdg'
Set-Content $File $newFileTemplate
[XML]$XMLFile = [XML](Get-Content $File)
I would then like to define a function for adding a group of servers
# This function adds a new group element
Function Add-NewGroup($GroupName,$RDCManFile) {
[xml]$GroupXML = #"
<group>
<properties>
<expanded>False</expanded>
<name>$GroupName</name>
</properties>
</group>
"#
$Child = $RDCManFile.ImportNode($GroupXML.group, $true)
$RDCManFile.Configuration.AppendChild($Child)
}
And call it by running
Add-NewGroup('DCs',$XMLFile)
This would allow me to populate the xml file with all of the OUs in AD.
Is anyone able to tell me where I am going wrong?
Thanks
Update: The error I am getting is
You cannot call a method on a null-valued expression.
At D:\Users\user\Desktop\Projects\RDCMan\Create-RDG.ps1:179 char:5
+ $Child = $RDCManFile.ImportNode($GroupXML.group, $true)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
You cannot call a method on a null-valued expression.
At D:\Users\user\Desktop\Projects\RDCMan\Create-RDG.ps1:180 char:5
+ $RDCManFile.Configuration.AppendChild($Child)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
And I am trying to do what was suggested here https://stackoverflow.com/a/29693625/2165019
The property 'Configuration' cannot be found on this object. Verify that the property exists. error at $RDCManFile.Configuration.AppendChild($Child).
If I can accept a .rdg file format and structure as defined in the Script to create a Remote Desktop Connection Manager group from Active Directory Technet article then I'd use
$RDCManFile.RDCMan.file.AppendChild($Child)
Follow the Theo's answer about calling a function.
Pass parameters as positional
Add-NewGroup 'DCs' $XMLFile
or as named
Add-NewGroup -GroupName 'DCs' -RDCManFile $XMLFile
This error (and second) most likely means that $RDCManFile is null.
You cannot call a method on a null-valued expression.
At D:\Users\user\Desktop\Projects\RDCMan\Create-RDG.ps1:179 char:5
+ $Child = $RDCManFile.ImportNode($GroupXML.group, $true)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Try adding $RDCManFile | Out-Host at the top of the Add-NewGroup function to check what's contained in that variable.
You need to change the way you are calling the function.
Because of the comma in between the parameters, PowerShell sees this as one array value, so only parameter $GroupName will receive something.
Furthermore, you should not use brackets around the params, call the function like this:
Add-NewGroup 'DCs' $XMLFile
or send the parameters using their names:
Add-NewGroup -GroupName 'DCs' -RDCManFile $XMLFile

Concatenate network path + variable (name of the folder)

How can I concatenate this network path with this variable entered by the user (it will be a full network path)?
So the user type the new folder name, for example: Folder-123 (will be stored in the variable $pjname)
Copy-Item "\\SERVER\Work_3rd\R Drive Structure\Project No\MDCXXXX" -Destination "\\SERVER\Work_3rd" -Recurse
write-host "Folder has been created. Press any key to continue..."
[void][System.Console]::ReadKey($true)
Write-Host "Please enter the project name: "
$pjname = Read-Host
Write-Output "New Folder will be: $pjname"
Rename-Item -Path "\\SERVER\Work_3rd\MDCXXXX" -NewName $pjname
write-host "Folder has been renamed. Press any key to continue..."
[void][System.Console]::ReadKey($true)
$pathToTemplate = '\\SERVER\Work_3rd\R Drive Structure\Project No\MDCXXXX'
$rootPath2 = '\\SERVER\Work_3rd\'
$rootPath = -join ($rootPath2, $pjname) # this concatenates the new project
name on to the root folder path**
# $rootPath += $pjname # this concatenates the new project name on to the
root folder path
If(Test-Path $rootPath)
{
$CurrentACL = (Get-Item $pathToTemplate).GetAccessControl('Access')
$CurrentACL | Set-Acl -Path $rootPath
}
This new folder stored in $pjname should have a network path like \\\SERVER\Work-3rd\ + FOLDER NAME. For example \\\SERVER\Word-3rd\Folder-123
The PowerShell is not finding the final path of the new folder so the permission is not being applied on it.
I'm trying in a test area and getting this problem below:
Folder has been renamed. Press any key to continue...
Get-Acl : Cannot find path '\\SERVER\test-area\Test-123' because it does not exist.
At C:\Users\felipe.sa\Desktop\Script\NewProjectFolder\NewProject-WP_-
_ProductionV3.ps1:279 char:8
+ $acl = Get-Acl $NewNetworkPath
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (:) [Get-Acl], ItemNotFoundException
+ FullyQualifiedErrorId :
GetAcl_PathNotFound_Exception,Microsoft.PowerShell.Commands.GetAclCommand
You cannot call a method on a null-valued expression.
At C:\Users\felipe.sa\Desktop\Script\NewProjectFolder\NewProject-WP_-
_ProductionV3.ps1:282 char:1
+ $acl.SetAccessRule($rule)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Are you trying to create a new directory in that share path, or rename a directory? It looks like you're trying to rename a directory.
My guess it isn't working because you are missing the trailing "\" in your path. Here is some sample code that adds a user supplied variable to a network path:
$MyRootPath = "\\SomeServer\Dir1\Dir2\"
Write-Host "Enter Dir Name"
$myAnswer = Read-Host
I typed "hello" when prompted for new directory..
$finalAnswer = $myAnswer.Trim()
$NewNetworkPath = ("{0}{1}" -f $MyRootPath, $finalAnswer)
$NewNetworkPath
returns:
\\SomeServer\Dir1\Dir2\hello
Whenever you are concatenating paths, escpecially those supplied by end users, stick to a utility that can do most of the heavy lifting. Use the combine method as string concatenation has several pitfalls that have to be needlessly mitigated.
[io.path]::combine('\\server\share', 'newfolder')
The combine method will take the path parts, as an array, and build a proper path. Note this does not validate if the path exists. It can deal with trailing path separators well. These next commands yield the same result.
[io.path]::combine('\\server\share\', 'newfolder')
[io.path]::combine('\\server\share', 'newfolder')
Beware leading path separators though:
If the one of the subsequent paths is an absolute path, then the combine operation resets starting with that absolute path, discarding all previous combined paths.
Thanks Matt & oze4! A weird problem happened now. I used the solution of oze4 and sometimes it works and sometimes not. Would it be the string entered by the user?
When it worked the folder name was 'MDX1111 - XXXX Xxxxxxx Xxxxxxxxx' - 32 characters. I ran the code again using the folder name 'MDX1112 - Xxxxxx Xxxxxxx Xxxxxxxxxx' - 35 characters and got this error below:
Get-Acl : Cannot find path '\\SERVER\Work_Block_A\MDX1112 - Xxxxxx Xxxxxxx
Xxxxxxxxxx' because it does not
exist.
At C:\Users\felipe.sa\Desktop\Script\NewProjectFolder\NewProject-WP_-
_ProductionV1.ps1:125 char:8
+ $acl = Get-Acl $NewNetworkPath
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (:) [Get-Acl], ItemNotFoundException
+ FullyQualifiedErrorId :
GetAcl_PathNotFound_Exception,Microsoft.PowerShell.Commands.GetAclCommand
You cannot call a method on a null-valued expression.
At C:\Users\felipe.sa\Desktop\Script\NewProjectFolder\NewProject-WP_-
_ProductionV1.ps1:128 char:1
+ $acl.SetAccessRule($rule)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
Any thoughts?
Thank you.

Method Invocation failed system.string error

Morning,
I'm trying to use a CSV file with a list of users and automate the process to set an AD users extensionAttribute15 back to the "notset" value.
I use a similar code to populate the attribute, the CSV file consists of just two things, the users LAN ID and the value for the attribute.
Populating the field is not the problem, changing the values back to "not set" has been.
Here is the code I am using.
Import-module ActiveDirectory
Import-CSV "code.csv" | % {
$User = $_.cn
$user.Put(“extensionAttribute15”, #())
$user.SetInfo()
}
and here are the errors.
Method invocation failed because [System.String] doesn't contain a method named 'Put'.
At attribute.ps1:4 char:10
+ $user.Put <<<< (“extensionAttribute15”, #())
+ CategoryInfo : InvalidOperation: (Put:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Method invocation failed because [System.String] doesn't contain a method named 'SetInfo'.
At attribute.ps1:5 char:14
+ $user.SetInfo <<<< ()
+ CategoryInfo : InvalidOperation: (SetInfo:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Any ideas what the problem could be?
Thanks,
When you read in a CSV file, the resulting objects are just simple property bags. They don't support any special methods, they just hold flat data. There is nothing in these objects that isn't present in the text of the CSV file itself.
If you want to obtain a rich object which has Active Directory context and capabilities, you will need to obtain one from a cmdlet in the ActiveDirectory module.
Something like this is probably along the lines you need
Import-module ActiveDirectory
Import-CSV "code.csv" | % {
$user = Get-ADUser $_.cn # get a rich object from the AD module, by passing a string
$user.Put(“extensionAttribute15”, #())
$user.SetInfo()
}