Unable to pass formatted Get-ADOrganizationlUnit output into function in script - powershell

I'm unable to pass the results of Get-ADOrganizationalUnit into a function in a script.
I am storing the results in a variable that is being used to add the canonical name of the OUs returned by the cmdlet, to a dropdown list on a form.
I'm then attempting to use the same variable in a function that when called, will determine the distinguished of the OU based on the canonical name selected.
Since the variable is being set when the form loads so that the drop down in question will be populated with the various OUs, I added in a write-output $myVar just to make sure nothing strange was going on prior to being passed into the function. I've attempted to make $myVar global with $Global:varName and I've attempted to pass in the variable when calling the function: myFunction $myVar $myVar1. If I then use write-output $myVar within the function, there is no output, but I can use write-host $myVar and it will return a string of just the distinguishedNames of all the OUs in $myVar.
I tested this in the shell directly as well and I didn't have any issues with associating the canonical name back to the DN of the OU, but don't know what I'm doing incorrectly to cause it to not work when used in a script.
I'm using this to get the OU data for the dropdown list and function:
$userOUs = Get-ADOrganizationalUnit -SearchBase $ouRoot -Filter * -Properties CanonicalName | Where-Object {$_.Name -like '*user*'}
Note: The dropdown list is populated successfully using $userOUs.
I call the function with a button:
$myBtn.Add_Click({ myFunction $userID $userOUs})
The function I'm trying to pass it to:
function myFunction($userID, $userOUs) {
Write-Output $userOUs #returns nothing
Write-Host $userOUs #returns the string containing all of the OUs' distinguished names
$selectedOU = $OUList.SelectedItem
$targetOUCanonicalName = "$domainPrefix$selectedOU" #I remove the domain name from the canonical name for display in the dropdown but add it back here
$targetOu = $userOUs | Where-Object {$_.CanonicalName -eq $targetCanonicalName} | select -ExpandProperty distinguishedName
Get-ADUser -Identity $userID | Move-ADObject -TargetPath $targetOU
}
Ultimately, the goal is to be able to use the $userOus variable to determine the DN of the OU based on the selection made in a drop down of OU canonical names. I'd like to try and keep this more dynamic without having to define everything in a switch statement.
I expect once I get a bump in the right direction regarding why the variable is not passing into the function the way that I need it to, I'll be able to accomplish that.
Edit: I don't intend to use write-host or write-output since the script will have a form, I'm just using that to try and figure out what's going on.

I've tried your version and it seems to work just fine:
function myFunction($userID, $userOUs) {
Write-Output $userOUs #returns nothing
Write-Host $userOUs #returns the string containing all of the OUs' distinguished names
#$selectedOU = $OUList.SelectedItem
#$targetOUCanonicalName = "$domainPrefix$selectedOU" #I remove the domain name from the canonical name for display in the dropdown but add it back here
#$targetOu = $userOUs | Where-Object {$_.CanonicalName -eq $targetCanonicalName} | select -ExpandProperty distinguishedName
# Get-ADUser -Identity $userID | Move-ADObject -TargetPath $targetOU
}
$userOUs = Get-ADOrganizationalUnit -Filter * -Properties CanonicalName | Where-Object {$_.Name -like '*user*' }
$userOUs
myFunction -userID $UserID -userOUs $userOUs
$UserOus display data for me, when I execute your function as above I also get output. The only thing that comes to mind is that the issue is somewhere else.

Related

How to handle hyphenated names in powershell when getting active directory info

I'm trying to get the password expiration date in active directory using powershell for users with hyphenated names (IE firstname.last-name) and on the hyphenated names it gives an invalid cmdlet error. How do I query the hyphenated names?
The current command I have is
net user $username /DOMAIN | find "Password expires"
Maybe use the ActiveDirectory module instead of the net commands:
$MaxPwdAge = (Get-ADDefaultDomainPasswordPolicy).MaxPasswordAge.Days
Get-ADUser -Filter { Name -like "*-*" } -Properties 'PasswordLastSet', 'DisplayName' |
Select-Object Name,DisplayName,
#{ Name = 'PasswordExpires'; Expression = { $_.PasswordLastSet.AddDays( $MaxPwdAge ) } }
If needed You can change the filter to look at DisplayName instead -Filter { DisplayName -like "*-*" }
You may need to adjust the properties you're retrieving depending on what you want to include in the output. This is just an example but it works, and can be used to plot a path forward. It does seem like you have to calculate the expiration date. But I can work on that and see if there's a better way.
If you want to Query for a specific user:
Get-ADUser Name-Name -Properties 'PasswordLastSet',DisplayName |
Select-Object Name,DisplayName,
#{ Name = 'PasswordExpires'; Expression = { $_.PasswordLastSet.AddDays( $MaxPwdAge ) } }
This assumes the Hyphenated name is a samAccountName. If you need to search by DisplayName you'll have to resort back to filter, even if you are looking for only the one user.
Get-ADUser -Filter { DisplayName -eq "Name-Name" } -Properties 'PasswordLastSet',DisplayName |
Select-Object Name,DisplayName,
#{ Name = 'PasswordExpires'; Expression = { $_.PasswordLastSet.AddDays( $MaxPwdAge ) } }
Note: That you have to change the "Name-Name". Also in the last example I changed to using the -eq operator instead of -like. Obviously this assumes you know exactly what you're looking for. Though you can use -Like with DisplayName or even the surName attribute if you like.

Powershell Script to query Active Directory

I am trying to query all users in multiple OUs of the same name. Get the SamAccountName attribute and then check for a file at a specific location with that name.
Here is what I have so far:
$ous = Get-ADOrganizationalUnit -Filter "Name -eq 'Admin-User-Accounts'"
$ous | ForEach-Object {
$AccountName = Get-ADUser -Filter * -SearchBase $_.DistinguishedName |
Select SamAccountName
Test-Path "\\domain.net\SYSVOL\domain.net\IA\$AccountName.pdf"
}
If a file is not found. I want to add the user to a group, however here is the kicker. The account has to be added to the non-compliance group for the organization that the account belongs to.
I.E an admin account found under:
OU=Admin-User-Accounts,OU=Administration,OU=ORG1,OU=ORGS,DC=domain,DC=net
would be added to the group named 'ORG1 IA - Non-Compliant Users' located under:
OU=Groups,OU=ORG1,OU=Information Assurance,OU=ORGS,DC=domain,DC=net
Well your post is a bit confusing, and no way to really validate because I have nothing setup like this.
Yet, querying for users in all OU or the enterprise is a common everyday thing.
However, an OU name, just like any other AD object name, must be unique. So, querying for the same OU name is not a thing, in a single AD forest / domain. If you meant querying every OU for the same username, then alrighty then.
By stepping thru how you are explanation for your use case, that you have laid out.
(though maybe you want to edit your post to make it's more clear, well to me anyway...)
Using pseudo code, then trying to map that out... and with no real way to determine what you mean by several things in your post/sample. So, the below is a rough first example of how I'd do approach this... again this is untested, so, I leave that homework to you.
# query all users in multiple OUs
(Get-ADOrganizationalUnit -Filter *).DistinguishedName |
ForEach{
# Collect all members of the current OU
$AccountNames = Get-ADUser -SearchBase $PSItem -Filter *
# Process each member in the current OU collection
ForEach($AccountName in $AccountNames)
{
"Processing $($AccountName.SamAccoutnName)`n"
# Initialize properties needed for processing
$UserOrg = $AccountName.DistinguishedName.split(",")[1]
$MemberCheckOU = "OU=Admin-User-Accounts,OU=Administration,OU=ORG1,OU=$UserOrg,DC=domain,DC=net"
$NonCompliantOU = "OU=Groups,OU=ORG1,OU=Information Assurance,OU=$UserOrg,DC=domain,DC=net"
# Validate user file existence for the current user
If(-Not (Test-Path -LiteralPath "\\domain.net\SYSVOL\domain.net\IA\$($AccountName.SamAccoutnName).pdf)"))
{
# if no file Process the user groupmebership modification
"Processing $($AccountName.SamAccoutnName)"
# Notify that the file was not found and processing is required
Write-Warning -Message "$($($AccountName.SamAccoutnName).pdf) not found. Process group modify actions"
# If the current user is in the MemberCheckOU, add to the NonCompliantOU
If(Get-ADPrincipalGroupMembership -Identity $($AccountName.SamAccoutnName) | Where-Object -Property DistinguishedName -Match $MemberCheckOU )
{ Add-ADGroupMember -Identity $NonCompliantOU -Members $($AccountName.SamAccoutnName) }
Else
{
# Do something else
}
}
Else
{
# Notify that the file was found and no processing required
Write-Host "$($AccountName.pdf) found. No further actions taken" -ForegroundColor Green }
}
}
It seems that one of the variables is incorrect because PowerShell is giving me the following:
Get-ADPrincipalGroupMembership : Cannot validate argument on parameter 'Identity'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command
again.
Okay, so here is what I have so far based on your post above Postanote:
# query all users in multiple OUs
(Get-ADOrganizationalUnit -Filter "Name -eq 'Admin-User-Accounts'") |
ForEach{
# Collect all members of the current OU
$AccountNames = Get-ADUser -SearchBase $PSItem -Filter *
# Process each member in the current OU collection
ForEach($AccountName in $AccountNames)
{
"Processing $($AccountName.SamAccoutnName)`n"
# Initialize properties needed for processing
$UserOrg = $AccountName.DistinguishedName.split(",")[1]
$MemberCheckOU = "OU=Admin-User-Accounts,OU=Administration,OU=$UserOrg,OU=ORGS,DC=domain,DC=net"
$NonCompliantOU = "OU=Groups,OU=$UserOrg,OU=Information Assurance,OU=ORGS,DC=domain,DC=net"
# Validate user file existence for the current user
If(-Not (Test-Path -LiteralPath "\\domain.net\SYSVOL\domain.net\IA\$($AccountName.SamAccoutnName).pdf)"))
{
# if no file Process the user groupmebership modification
"Processing $($AccountName.SamAccoutnName)"
# Notify that the file was not found and processing is required
Write-Warning -Message "$($($AccountName.SamAccoutnName).pdf) not found. Process group modify actions"
# If the current user is in the MemberCheckOU, add to the NonCompliantOU
If(Get-ADPrincipalGroupMembership -Identity $($AccountName.SamAccoutnName) | Where-Object -Property DistinguishedName -Match $MemberCheckOU )
{ Add-ADGroupMember -Identity "$UserOrg IA - Non-Compliant Users" -Members $($AccountName.SamAccoutnName) }
Else
{
# Do something else
}
}
Else
{
# Notify that the file was found and no processing required
Write-Host "$($AccountName.pdf) found. No further actions taken" -ForegroundColor Green }
}
}
Looking at the original script fragment:
$ous = Get-ADOrganizationalUnit -Filter "Name -eq 'Admin-User-Accounts'"
$ous | ForEach-Object {
$AccountName = Get-ADUser -Filter * -SearchBase $_.DistinguishedName |
Select SamAccountName # note 1
Test-Path "\\domain.net\SYSVOL\domain.net\IA\$AccountName.pdf" # note 2
}
Note 1: Your going to end up with $accountname.accountname holding your value. I think your going to want to expand this instead.
Note2: Powershell may be getting confused and thinking your looking for the variable $accountname.pdf
Instead, try this...
$ous = Get-ADOrganizationalUnit -Filter "Name -eq 'Admin-User-Accounts'"
$ous | ForEach-Object {
$AccountName = $(Get-ADUser -Filter * -SearchBase $_.DistinguishedName).SamAccountName
Test-Path "\\domain.net\SYSVOL\domain.net\IA\$($AccountName).pdf"
}
here, we save the value of just .SamAccountName for the query to the $AccountName, and by adding $($accountname) we make clear the variable we want, and that .pdf is not part of the variable name.
Now, note as well, this doesn't save the results anywhere, it will just flash them to screen.

Multiple rows in a grid [duplicate]

This question already has answers here:
Export hashtable to CSV with the key as the column heading
(2 answers)
Closed 4 years ago.
I'm trying to list all ad group memberships of specific users. The input would be a string of logins split with a comma 'login1,login2'.
So I go over each user and list their memberships with the username as title. Somehow it only shows the first entry. Also it shows the user groups in one row and I don't know how to change that.
Code below:
$users = $logon -split ','
$q = #()
foreach ($user in $users) {
$usernm = Get-ADUser -Filter 'samAccountName -like $user' | select Name
$useraccess = Get-ADPrincipalGroupMembership $user | Select-Object Name
$userobj = New-Object PSObject
$userobj | Add-Member Noteproperty $usernm.Name $useraccess.Name
$q += $userobj
}
Expected output would be something like:
fullnameuser1 fullnameuser2 list of users goes on...
------------- ------------- ------------------------
adgroup1 adgroup3 ...
adgroup2 adgroup4
... ...
In principle this would also mean that if i typed $q.'fullnameuser1' output would be:
fullnameuser1
-------------
adgroup1
adgroup2
...
Whenever the code is ran, it will only ever add the first user's access, also returning all groups on one row. So somehow I need to go over all the group memberships and add a row for each one.
First and foremost, PowerShell does not expand variables in single-quoted strings. Because of that Get-ADUser will never find a match unless you have a user with the literal account name $user. Also, using the -like operator without wildcards produces the same results as the -eq operator. If you're looking for an exact match use the latter. You probably also need to add nested quotes.
Get-ADUser -Filter "samAccountName -eq '${user}'"
Correction: Get-ADUser seems to resolve variables in filter strings by itself. I verified and the statement
Get-ADUser -Filter 'samAccountName -eq $user'
does indeed return the user object for $user despite the string being in single quotes.
If you want a fuzzy match it's better to use ambiguous name resolution.
Get-ADUser -LDAPFilter "(anr=${user})"
You may also want to avoid appending to an array in a loop, and adding members to custom objects after creation. Both are slow operations. Collect the loop output in a variable, and specify the object properties directly upon object creation.
$q = foreach ($user in $users) {
...
New-Object -Type PSObject -Property {
$usernm.Name = $useraccess.Name
}
}
Lastly, I'd consider using the user's name as the property name bad design. That would be okay if you were building a hashtable (which is mapping unique keys to values), but for custom objects the property names should be identical for all objects of the same variety.
New-Object -Type PSObject -Property {
Name = $usernm.Name
Group = $useraccess.Name
}
Basily query all the users and store it in $users, example:
Get-ADUser -Filter * -SearchBase "dc=domain,dc=local"
And then you can export the results as csv or a table.
To Export as CSV :
Get-ADPrincipalGroupMembership <Username> | select name, groupcategory, groupscope | export-CSV C:\data\ADUserGroups.csv`
To Format the result as Table in the console itslef :
Get-ADPrincipalGroupMembership <Username> | select name, groupcategory, groupscope | Format-Table

Powershell Pipeline Argument not gettings passed to Set-ADUser

I've been banging my head against the wall for the past few hours, I'm sure it's because I don't quite understand something about how #{} and $_ work.
First the code:
Get-ADUser username -Properties mail | Set-ADUser -replace #{"proxyaddresses"="SMTP:"+$_.mail}
As you can see I'm trying to update the proxyaddresses fields with the user's email address.
Instead only the string is pulled:(output: proxyaddresses : {SMTP:}) and the pipeline is ignored, I'm assuming it's because it's empty for some reason, but it's not clear to me why.
I've tried variations such as "proxyaddresses="SMTP:$($_.mail)" I tried using default properties that are always sent with Get-ADUser such as UserPrincipalName
I know that something like this is possible because of this http://www.itprotoday.com/management-mobility/more-flexible-active-directory-one-liner and mutiple answers on SO using some variation of the linked example.
When I assign a variable to SMTP:$_.mail and then use that in the field instead like so:
Get-ADUser username -Properties mail | %{ $smtp = "SMTP:"+$_.mail
$_|Set-ADUser -replace #{"proxyAddresses"=$smtp}
This works (output: proxyaddresses :{SMTP:emailaddress#domain.com}). If I leave out the string like so: #{"proxyaddresses"=$_.mail}
I get the following error:
Set-ADUser : Cannot bind parameter 'Replace' to the target. Exception setting "Replace": "Object reference not set to an instance of an object."
At line:1 char:58
I'm not sure what this means.
I'd like some variation of my initial idea to work, but I'll settle for the workaround using an extra variable if there's no other way.
EDIT: There seems to be some confusion about what I'm asking, so I'll clarify:
Is there a way to use the pipeline variable $_ without a script block inside a hashtable, (inside a script block requires double piping like #TheIncorrigible1 suggested in his first answer.)?
EDIT: Based on this it seems this should not be having any issues.
You need to use ForEach-Object to access the pipeline in the way you're trying, otherwise it doesn't know what your pipeline object ($_) is:
Get-ADUser -Identity username -Properties mail |
ForEach-Object {
$_ | Set-ADUser -Replace #{ 'proxyaddresses' = 'SMTP:' + $_.mail }
}
Or the -PipelineVariable common parameter which explicitly assigns $_ to a variable:
Get-ADUser -Identity username -Properties mail -PipelineVariable user |
Set-ADUser -Replace #{ ProxyAddresses = "SMTP:$($user.mail)" }
ProxyAddresses is an array where the Primary Email address is set like SMTP:primary#example.com but there can and will be other elements there too like alias email addresses (that have the lowercase smtp: prefix), SIP: addresses etc.
NEVER try to simply overwrite whatever is already there by a single string found in the mail attribute of the user object, but merge them with the ones you want to add. Selectively replace the ones you want to be changed and build an array of valid addresses.
Basically you do
$oldErrorAction = $ErrorActionPreference
$ErrorActionPreference = 'Stop'
$user = Get-AdUser -Identity $SAMAccountName -Properties mail
$primaryEmailAddress = $user.mail
$externalAddress = "smtp:<WHATEVER ALIAS YOU WOULD LIKE FOR THE USER">
$mailProxies = #("SMTP:$primaryEmailAddress", "smtp:$externalAddress")
# add more to this array if need be
$newProxies = #{'ProxyAddresses' = $mailProxies}
try {
$user | Set-ADUser -Clear ProxyAddresses
$user | Set-ADUser -Add $newProxies
}
catch {
Write-Warning "Could not set ProxyAddresses: $($_.Exception.Message)"
}
$ErrorActionPreference = $oldErrorAction
You may want to try set the variable first and then call on it in the script.
something like this,
$user = Get-ADUser username -Properties mail | select-object mail
Then write your script and call on the variable you may need to use foreach with a if statement to get it to function the way you want.

Powershell comparison with attributes

I'm beginner in powershell and I need your help.
I need to compare the department attribute from the AD containing some text amd replacing by another value.
But it doesn't work. Do I made a mistake below? Cheers
//Find the user and save the user in the variable
$member = get-Aduser -f {GivenName -eq 'Jack'}
//check if the Departement field match with "Dep20 "
if($member.department -eq "Dep20")
{
//Set "Dep21" in department field
$member.Department = 'Dep21';
set-AdUser -f {GivenName -eq $member.givenName} -departement $member.Department;
}
Some issues with your initial script
First
Get-AdUser won't give you the property Department by default.
You could have confirmed this by actually looking at the output of your Get-AdUser statement. You do need to add it to the list of properties explicitely.
get-Aduser -f {GivenName -eq 'Jack'} -Properties Department
Also, you did make a mistake in the Set-AdUser cmdlet. The parameter name you have written, at the time of my answer, is -departement. Instead, you need to set -department.
Finally, Get-AdUser could return multiple users (or none).
Therefore, you need to account for that by checking how many $member were returned or to do a foreach to process none (if 0) or all of them the same.
At least, that part is subjective to what you need but here would be my approach.
$member = get-Aduser -Filter 'GivenName -like "Jack*"' -Properties Department
$member | foreach {
if ($member.Department -eq 'Dep20')
{
$_.Department = 'Dep21'
set-AdUser $_ -Department $_.Department;
}
}
Edit:
I modified my answer to switch the Filter parameter from a scriptblock (as your question) for a string filter as per mklement0 comment.
Because the Filter parameter is actually a string, giving it a script block will create problems on multiple occasions and you are better restrict yourself to the string type for this parameter.
See this for a more detailed explanation on the matter.