Creating PowerShell Script templates for easy group management - powershell

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
}
}

Related

Get samaccountname from display name - but with extras

First time poster but site has helped me so much in the past.
We are an MSP and regularly get requests from clients to pull various details off a list of users they send us. Unfortunately though their lists rarely (if ever) contain any unique identifiers for AD such as samAccountName or even e-mail.
So typically I only get their first and last names, job titles etc. and use a slight variation on the below to try and get the required samAccountNames to work in batch modify scripts.
Get Samaccountname from display name into .csv
The problem comes (and caused a big headache recently) when I try to put that output back into a table to line up with the displaynames. As if the script can't find the displayname it just moves onto the next one in the list and puts the samAccountName directly below the last one it found. making it out of line with the displayname column I've put it beside.
My question is is there something I can add to the below script that when an error occurs it simply inputs null or similar into the samAccountName output csv so I could spot that easily in an excel sheet.
Similarly some users have multiple accounts like an admin and non-admin account with the same display name but different samAccountName so it pulls both of them, which is less of a problem but also if there was any way to have the script let me know when that happens? That would be super useful for future.
Import-Module activedirectory
$displayname = #()
$names = get-content "c:\temp\users.csv"
foreach ($name in $names) {
$displaynamedetails = Get-ADUser -filter { DisplayName -eq $name } -server "domain.local"| Select name,samAccountName
$displayname += $displaynamedetails
}
$displayname | Export-Csv "C:\temp\Samaccountname.csv"
So the problem lies in that you rely on Get-ADUser to provide you with user objects and when it doesn't you have gaps in your output. You instead need to create an object for every name/line in your "csv" regardless of whether Get-ADUser finds anything.
Get-Content 'c:\temp\users.csv' |
ForEach-Object {
$name = $_
$adUser = Get-ADUser -Filter "DisplayName -eq '$name'" -Server 'domain.local'
# Create object for every user in users.csv even if Get-ADUser returns nothing
[PSCustomObject]#{
DisplayName = $name # this will be populated with name from the csv file
SamAccountName = $adUser.SamAccountName # this will be empty if $adUser is empty
}
} | Export-Csv 'C:\temp\Samaccountname.csv'

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.

Is there a way to export the variable in a ForEach loop to my CSV output on each line

I am attempting to run a script against our AD that spits out the AD Users in a group, for all the groups in a list. Essentially attempting to audit the groups to find the users. I have a functioning script, except I have no way to determine when the output of one group ends, and where the output of the next begins.
I have tried looking for previous examples, but nothing fits exactly the method I am using, and with me just dipping my toes into powershell I have not been able to combine other examples with my own.
$groups = Get-Content "C:\Folder\File_with_lines_of_ADGroup_Names.txt"
foreach ($group in $groups) { Get-ADGroupMember -Identity "$group" | Where-Object { $_.ObjectClass -eq "user" } | Get-ADUser -Property Description,DisplayName | Select Name,DisplayName,Description,$group | Export-csv -append -force -path "C:\Folder\File_of_outputs.csv" -NoTypeInformation }
Right now the problem lies with getting the $group variable to be exported along with the Name, DisplayName, and Description of each user returned. This is the best way I can think of to tag each user's group and keep all the results in a single file. However, only the first line of results works which is the HEADERS of the CSV, and everything after it is either listed as "Microsoft.ActiveDirectory.Management.ADPropertyValueCollection"or simply blank after the first group of results.
Hoping someone can show me how to easily add my variable $group to the output for each user found for filtering/pivoting purposes.
Thanks and let me know if you have questions.
I believe what you are after are calculated properties on your select statement.
Select Name,DisplayName,Description,$group
Should Probably be something like
Select Name,DisplayName,Description,#{n='Group'; e={$group};}
See also https://serverfault.com/questions/890559/powershell-calculated-properties-for-multiple-values

Getting distribution group details with PowerShell

I need to recreate hundreds of distribution groups in a new (O365) environment. I’ve no access to the source system other than to work with their techs to provide them scripts that they’ll run for me.
I wrote a script to spit out the names of all the lists that my users are members of (security and distribution). I’d like to write another to cycle through each distribution group and provide me with details of that group. I don’t see how to do that.
I see that Set-DistributionGroup will happily let me set the AcceptMessagesOnlyFromDLMembers (and a million other fields) but I don’t see that Get_DistributionGroup will output those values for me. How do I do this to ensure I’m not recreating an open group for HR that should have been MemberJoinRestriction enabled (for example)?
Thanks in advance.
Okay so I'm not a PowerShell guy (obvs.), but here's what I wrote that works. I'm sure I'm not taking advantage of PowerShell at all here, so wanna suggest how I could improve? Like why did I have to use $temp? Quicker to check the array first before I Get-ADGroup again? etc.
# Start with a user list, get the groups eash is a part of, get information about the group
# if it is mail-enabled then add it to an array, remove duplicates, then store all the
# information about that DistributionGroup into a .csv
$groups = #()
ForEach ($user in $(Get-Content c:\Users\sid.sowder\Desktop\CEGusers.txt)) {
$MyGroups = Get-ADuser $user -Properties * | select memberof -ExpandProperty memberof
ForEach ($MyGroup in $MyGroups) {
$temp = Get-ADGroup $MyGroup -Properties *
if ($temp.mail -ne $null) {
$groups += $temp.SamAccountName
}
}
}
$groups = $groups | sort -unique
Foreach ($group in $groups) {
Get-DistributionGroup -Identity $group |
Select * |
Export-CSV C:\Users\Sid.Sowder\Desktop\distlistdetail.csv -NoTypeInformation -Append
}

Lync Powershell enable group members

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?