Lync Powershell enable group members - powershell

I'm trying to automate enabling members of a group for Enterprise Voice, I have the following (pretty rough I know), which work if there's only a single member:
$telephoneNumber = Get-ADGroupMember -Identity "LyncEnterpriseVoice" | Get-ADUser -Properties telephoneNumber | Select-Object -ExpandProperty telephoneNumber
$ext = Get-ADGroupMember -Identity "LyncEnterpriseVoice" | Get-ADUser -Properties otherTelephone | Select-Object -ExpandProperty othertelephone
Get-ADGroupMember -Identity "LyncEnterpriseVoice" | foreach {Get-ADUser $_.SamAccountName | foreach {Set-CSuser -identity $_.UserPrincipalname –EnterpriseVoiceEnabled $True –LineUri "tel:$telephoneNumber;ext=$ext"}}
I can't figure out how to do a foreach otherTelephone and telephoneNumber. Can someone please point me in the right direction.

Not a definite answer, but should help you nonetheless:
You are calling Get-ADGroupMember -Identity "LyncEnterpriseVoice" three times in a row. Don't do that - it's inefficient and can even lead to inconsistencies if you don't get the same result back each time. Just call it once and assign it to a variable, then use that variable each time. This will also make your code much more readable, if you give that variable a good name.
Understand what you want to achieve exactly - what objects are you collecting, what properties do they have and what do you want to iterate over? I can barely get that from your code and it seems you don't have a solid understanding of it either. Once you understand that, the rest is easy.
Edit:
I'm not sure what you mean by "I get the results I want". I'm pretty sure your variables do not contain what you think they do. It's helpful to build a script line by line - examining for each step what objects you are dealing with. Otherwise you just make assumptions and you will inevitably be wrong often.
For example in line 1, you seem to get an array of telephone numbers and in line 2 an array of additional phone numbers ($telephoneNumber and $ext). But in line 3 you use these complete arrays, instead of a single element, to build that LineUri string for each user, which can't possibly be correct.
Also in line 3 you use AD user objects as input and then do a foreach and get get the AD user object again for each user - a clear sign that you are not aware of the objects you are dealing with.
So for example:
Get the users we want to manipulate, including the telephone numbers. Otherwise you are dealing with three separate arrays and don't know how to match up everything - it's just messy.
$users = Get-ADGroupMember -Identity LyncEnterpriseVoice | Get-ADUser -Properties telephoneNumber, othertelephone
Let's see what we have:
$users
Ok, it's an array of objects with all the information we need, but just out of curiosity, what kind of objects are they exactly?
$users[0].GetType()
Not very surprisingly, they are ADUser objects. Let's set some properties:
$users | foreach { Set-SCUser -Identity $_.UserPrincipalName –EnterpriseVoiceEnabled $true –LineUri "tel:$($_.telephoneNumber);ext=$($_.othertelephone)" }
Note the subexpressions $(...) in the string - these are necessary, because else the variable $_ would be expanded and the rest would literally be ".telephoneNumber".
One additional thing: you write "tel:...;ext=..." which is inconsistent and seems weird - maybe a typo?

Related

How to query the Active Directory using a list of users in a text file for a specific attribute with PowerShell

I'm somewhat basic to Powershell and use one-liner commands only to keep it short and basic.
I would like to do the following: I have a list of users in a text file in the form of UserPrincipalName. I'd like to query this list of users if their accounts are still active/enabled or not. To do so, I'm trying to run the following command, which just reveals nothing in the end (blank output):
gc .\users.txt | foreach {get-aduser -server "corp.xxx.com"
-f 'name -like "$_"' -properties *}| select displayname,enabled
As mentioned, the output is blank with no errors or whatsoever.
I read that aduser doesn't work with pipelines, but I need to find a solution.
Kindly request your support :)
Thanks
Your use of single quotes in your filter is not allowing the expansion of the variable. Double-quotes should be wrapping the filter expression so as to allow the interpolation of the automatic variable $_:
Get-ADUser -Filter "name -like '$_'" ...
Single-quoted strings:
A string enclosed in single quotation marks is a verbatim string. The string is passed to the command exactly as you type it. No substitution is performed.
Also note, you mention in your question that the file has the user's UserPrincipalName attribute, yet you're querying the Name attribute, if that's the case, the filter should be:
Get-ADUser -Filter "UserPrincipalName -eq '$_'" ...
Note the use of -eq instead of -like, for exact matches you should always use this operator, see about_ActiveDirectory_Filter for usage details and examples of each operator.
If you're only interested in DisplayName and Enabled for your output, there is no reason in querying all the user's attributes, -Properties * should be just -Properties DisplayName since Enabled is already part of the default attributes returned by Get-ADUser.
Finally, the -Identity parameter can be bound from pipeline, and this parameter accepts a UserPrincipalName as argument, hence ForEach-Object is not needed in this case:
Get-Content .\users.txt |
Get-ADUser -server "corp.xxx.com" -Properties DisplayName |
Select-Object DisplayName, Enabled

Creating PowerShell Script templates for easy group management

First of all thanks for the help to the other users, because of that I learned a lot.
I have a problem that there are lots of user templates in my company (Lots of different group settings depending operation). Because of that I want to make it easy for my colleges to assign user to Operations.
I think about a solution that My colleges enter the user and Group to a CSV file, then the script goes trough the CSV lines, detects the Operation, and go to the operations TXT file to get the group info, then add the user.
The files are:
UserAndOperation.csv and it includes 2 columns, first one is user second one is Operation
Then the TXT files are added, in them the Groups are added for every line (I also wanted to make only one Operation csv that first column is operation name and the second one is groups that has to be added, and separated by "," but that scared my eye :D ).
this is the Frankenstein code that I created:
Import-Csv ".\UserAndOperation.csv" | ForEach-Object {get-aduser $_.User | if($_.Operation = "Operation1"){
$Groups = Get-Content .\operation1.txt
foreach($group in $groups)
{Add-ADPrincipalGroupMembership -Identity $_.User -MemberOf $Group}
}
elseif ($_.Operaiton = "Operation2"){
$Groups = Get-Content .\operation2.txt
foreach($group in $groups)
{Add-ADPrincipalGroupMembership -Identity $_.User -MemberOf $Group}
}
And goes for each operation
}
It gives an error that it don't recognize the if and elseif statements.
I don't know how to proceed, could anyone help me with fixing it ?
Thanks and best Regards.
You can't pipe results into an if statement.
e.g.
get-aduser $_.User | if($_.Operation
You also have a typo in:
$_.Operaiton
I would suggest to clean up the code a bit by trying to put statements one per line, so as to not confuse yourself, outputting debug statements, etc..
As has already been pointed out, you can't shove arbitrary control flow statements like if into the middle of a pipeline statement.
Beyond that, your intended if/else approach will mean a lot of repeated code, making the script harder to maintain.
You could use a hashtable (#{}) to "map" the operation name to the relevant file name, this way you only need to write the loop to add the group memberships once:
# map the Operation column value to the relevant file path
$operations = #{
'Operation1' = 'path\to\operation1.txt'
'Operation2' = 'path\to\operation2.txt'
'Operation3' = 'path\to\operation3.txt'
}
Import-Csv ".\UserAndOperation.csv" | ForEach-Object {
$User = Get-ADUser $_.User
$Groups = Get-Content $operations[$_.Operation]
foreach($Group in $Groups){
Add-ADPrincipalGroupMembership -Identity $User -MemberOf $Group
}
}

How to move multiple users to multiple OUs importing users from CSV and filtering by department

New to powershell and scripting in general. Trying to improve automation in our onboarding process, we have to move multiple user accounts to multiple OUs every couple of months.
Import-Module ActiveDirectory
$dept1 = "OU=Path1,DC=SOMEWHERE,DC=OUTTHERE"
$dept2 = "OU=Path2,DC=SOMEWHERE,DC=OUTTHERE"
Import-Csv "C:\Scripts\Incoming.csv" | ForEach-Object {
$samAccountName = $_."samAccountName"
Get-ADUser -Identity $samAccountName -Filter {Department -eq "Dept1"} | Move-ADObject -TargetPath $dept1
Get-ADUser -Identity $samAccountName -Filter {Department -eq "Dept2"} | Move-ADObject -TargetPath $dept2
}
This actually moves ALL users with the department marked into the path I have set.. but I only want it to look at those users in the csv and then filter their departments from AD - not the CSV. I feel like I'm missing a step but I've searched everywhere and tried the get-help. I feel like I need to get the identity, then search/filter against something else but I'm not quite sure how. Would appreciate any help.
edit
Okay, if I run the following:
Get-ADUser -Filter {Department -eq "Dept1"} -Properties Department
It returns everyone that fits that property but how do I compare those to the $samAccountName and ONLY try to move those accounts or run the commands against the accounts on the list? When I ran the second half of the command it tried to move them all and failed.
Move-ADObject $samAccountName -Target $dept1
I feel dumb.
It's ok to feel dumb. You're not and everyone feels that way at times when trying to learn a new thing. You're also here asking for help, so you're ahead of the game compared to a lot of others.
#Lee_Daily's comment is correct that Get-ADUser doesn't support using both -Identity and -Filter in the same command. They're part of different parameter sets. You can tell from the syntax output of Get-Help Get-ADUser or the online docs. Both show 3 different sets of parameters and Identity and Filter are not in the same one. What's odd is that your original script should have thrown an error because you tried to use both in the same command. No need to worry about that now though.
Here's a typical way one might approach this task. First, you query the user's details including the department you want to make a decision on. Then, you write your condition and perform the appropriate action. Doing it this way means you're only querying AD once for each user in your CSV rather than twice like your original script which is good for script performance and load on your AD. The inside of your ForEach-Object loop might look something like this. Note the addition of -Properties department in Get-ADUser. We need to ask for it explicitly because department isn't returned in the default result set.
# all of this goes inside your existing ForEach-Object loop
$u = Get-ADUser -Identity $_.samAccountName -Properties department
if ($u.Department -eq 'Dept1') {
$u | Move-ADObject -TargetPath $dept1
} elseif ($u.Department -eq 'Dept2') {
$u | Move-ADObject -TargetPath $dept2
}
Now let's talk about some alternative ways you might approach this.
The first way sort of flips things around so you end up only calling Get-ADUser once total, but end up doing a lot more filtering/processing on the client side. It's not my favorite, but it sets things up to understand my preferred solution. In particular, the Get-ADUser call uses the -LDAPFilter parameter. LDAP filter syntax is a little strange if you've never seen it before and this particular example could use the more common -Filter syntax just as easily. But in the next example it would be much more difficult and learning LDAP filter syntax enables you to query AD from anything rather than just PowerShell.
# instead of immediately looping on the CSV we imported, save it to a variable
$incoming = Import-Csv "C:\Scripts\Incoming.csv"
# then we make a single AD query for all users in either Dept1 or Dept2
$users = Get-ADUser -LDAPFilter '(|(department=Dept1)(department=Dept2))' -Properties department
# now we filter the set of users from AD by department and whether they're in the CSV and do the moves as appropriate
$users | Where-Object { $_.department -eq 'Dept1' -and
$_.samAccountName -in $incoming.samAccountName } | Move-ADObject -TargetPath $dept1
$users | Where-Object { $_.department -eq 'Dept2' -and
$_.samAccountName -in $incoming.samAccountName } | Move-ADObject -TargetPath $dept2
The benefit of this method is the single AD round trip for users rather than one for each in the CSV. But there are a lot more local loops checking the samAccountNames in the results with the samAccountNames from the CSV which can get expensive cpu-wise if your CSV and/or AD is huge.
The final example tweaks the previous example by expanding our LDAP filter and making AD do more of the work. AD is ridiculously good at returning LDAP query results. It's been fine-tuned over decades to do exactly that. So we should take advantange of it whenever possible.
Essentially what we're going to do is create a giant 'OR' filter out of the samAccountNames from the CSV so that when we get our results, the only check we have to do is the check for department. The way I verbalize this query in my head is that we're asking AD to "Return all users where SamAccountName is A or B or C or D, etc and Department is Dept1 or Dept2. The general form of the filter will look like this (spaces included for readability).
(& <SAM FILTER> <DEPT FILTER> )
# Where <DEPT FILTER> is copied from the previous example and
# <SAM FILTER> is similar but for each entry in the CSV like this
(|(samAccountName=a)(samAccountName=b)(samAccountName=c)...)
So let's see it in action.
# save our CSV to a variable like before
$incoming = Import-Csv "C:\Scripts\Incoming.csv"
# build the SAM FILTER
$samInner = $incoming.samAccountName -join ')(samAccountName='
$samFilter = "(|(samAccountName=$samInner))"
# build the DEPT FILTER
$deptFilter = '(|(department=Dept1)(department=Dept2))'
# combine the two with an LDAP "AND"
$ldapFilter = "(&$($samFilter)$($deptFilter))"
# now make our single AD query using the final filter
$users = Get-ADUser -LDAPFilter $ldapFilter -Properties department
# and finally filter and move the users based on department
$users | Where-Object { $_.department -eq 'Dept1' } | Move-ADObject -TargetPath $dept1
$users | Where-Object { $_.department -eq 'Dept2' } | Move-ADObject -TargetPath $dept2
There are more efficient ways to build the LDAP filter string, but I wanted to keep things simple for readability. It's also a lot easier to read with better PowerShell syntax highlighting than StackOverflow's. But hopefully you get the gist.
The only limitation with using this method is when your incoming CSV file is huge. There's a maximum size that your LDAP filter can be. Though I'm not sure what it is and I've never personally reached it with roughly ~4000 users in the filter. But even if you have to split up your incoming CSV file into batches of a few thousand users, it's still likely to be more efficient than the other examples.

Grabbing lots of data and passing it to CSV from mutiple dependent Get-Aduser queries

We have users with minor but annoying differences in naming standards that are loosely followed making scripting a pain. The company has been around a while and depending on who was working in IT when the employee was hired the account could follow any naming convention if followed at all.
In one forest we have accounts that start with a-, the manager attribute of that account is the DN of the account owners other/main account without many other common attributes populated. I then need to look up the managers account and grab their SamAccountName. Then I need to add s- to the SamAccountName and do search for that to see if it exists.
Then I need to write the original SamAccountName, the second SamAccountName and the s-SamAccountName and something like a check box if its a valid account name all to a CSV.
without rewriting the script and passing everything to/from a var and then processing that I dont see a way to do it. This script looks up roughly 800 users and processes that three time, so it already takes a while to run without slowing it down with a bunch of var transfers.
$test = get-aduser -ldapFilter "(SamAccountName=a-*)" -Server XXX.int:3268 -Properties manager |
Select -ExpandProperty manager | Get-ADUser -Server XXX.int:3268 |
Select -ExpandProperty samaccountname
$test
You can do something like this to get you started.
$test = get-aduser -ldapFilter "(SamAccountName=a-*)" -Server XXX.int:3268 -Properties manager |
Select-Object SamAccountName,
#{n='Manager';e={Get-ADUser $_.Manager -Server XXX.int:3268 | Select -Expand SamAccountName}},
#{n='s-Manager';e={Get-ADUser "s-$($_.Manager)" -Server XXX.int:3268 | Select -Expand SamAccountName}}
$test
I am not clear if you need to add s- to the manager's or the original account's SamAccountName. The script above adds s- to the manager's SamAccountName.
In scripts where multiple AD lookups are required, sometimes it is best to do one larger lookup and then filter that result for what you need.
Explanation:
The main technique of the script makes use of delayed-script binding with the Select-Object command. Each user object found by the original Get-ADUser command is piped into Select-Object and is accessible by the pipeline variable $_. The hash table select can be updated fairly easily to meet your needs.

How to pass a variable to -Filter

I have come across a very strange situation in PS.
In a script I have, there is a cmdlet (Get-Mailbox) which pulls back a few mailboxes and stores them in $mailboxes.
I then loop through this as follows to find a matching AD account.
foreach ($user in $mailboxes) {
Get-ADUser -Filter {UserPrincipalName -eq $user.UserPrincipalName}
}
When I run this it errors saying that it can't find the property UserPrincipalName on $user.
I have debugged the script and tested it thoroughly. At the point where it errors if I type $user.UserPrincipalName it outputs a list of UPNs and their date type is string so the property is there and has data.
I came to the conclusion that for some reason -Filter can't see the $user variable - as if it is isolated inside the {} brackets which I have heard can be the case with functions. However if I modify the code like so it works.
foreach ($user in $mailboxes) {
$name = $user.UserPrincipalName
Get-ADUser -Filter {UserPrincipalName -eq $name}
}
Although this fixes my problem I'd like to learn why the first example doesn't work. Can anyone explain?
Something worth noting is the get-mailbox actually connects to Exchange Online first and returns a data type of:
Deserialized.Microsoft.Exchange.Data.Directory.Management.Mailbox
but when the Get-ADUser command errors it says the the object is of type PSCustomobject. I think this maybe part of the problem.
Get-ADUser -Filter "userprincipalname -eq '$($user.userprincipalname)'"
I don't know why, but there's some more discussion here about which syntaxes do and don't work with Get-ADUser, and how the scriptblock syntax you are using works with a full user object but not with a PSCustomObject, here:
http://www.powershellish.com/blog/2015-11-17-ad-filter