PowerShell Script to sync members of distribution group with SharePoint group - powershell

Im in a SharePoint environment where:
distribution lists are in use instead of security lists
can not switch to security list
distribution lists are nested
no user profile sync
So to sync my new SharePoint group "something" I was thinking of running a PowerShell script regulary to fetch all the members hierarchically and put them in the SharePoint group, including checks. Unfortunately I cant install the AD add-in so I installed the Quest AD add-in.
update: I noticed that direct syncing took too long. So I have decided to split the solution: the first AD script output all nested distribution groups (Get-QADGroupMember -Indirect -Type 'group'), although this takes as long as getting all users, this feels safer, since probably this doesnt have to been run that much. Then script 2 read the groups and creates a csv file per group, this is certainly not unique, but with tens of thousands of users, this is the safest way. Then script 3 (underneath) reads the csv files with users and update the SharePoint user group. Then script 4 reads again the usergroup and deletes all the one where the date (in notes) is not equal to the most recent date.
<#
.SYNOPSIS
Syncs All Users in AD distribution Group To SharePoint group:
- Adds new Users
- Updates display name, email when changed
- Deleted Users no longer present
#>
# --------------------------------------------------------
# Variables to Change / Set:
# --------------------------------------------------------
$site = "http://abc-def-ghi:7777"
$SPgroup = "MYCOOLSPGROUP"
$ADgroup = "MYADGROUP"
# --------------------------------------------------------
# Add-SSuser : adds a user to Sharepoint Group
# --------------------------------------------------------
function Add-SSUser {
Param( [string]$account,
[string]$displayname,
[string]$email,
[string]$sitecollectionurl,
[string]$group,
[string]$date)
Get-SPWeb -Identity $sitecollectionurl | foreach-object {
$identityClaimWindowsNTAccount = "i:0#.w|$account"
[Microsoft.SharePoint.SPUser] $ssUser =
$_.EnsureUser($identityClaimWindowsNTAccount)
$_.Groups.GetByName($group).AddUser($ssUser)
$_.Groups.GetByName($group).Update()
Set-SPUser -Identity $ssUser -Web $_.Url -Group $group
# Update properties
$ssUser.Email = $email
$ssUser.Name = $displayname
$ssUser.Notes = "[[$date]]"
$ssUser.Update()
#Write-Host $ssUser.Xml
}
}

Related

MS Graph API - Group & membership info

I'm trying to pull out a listing of all groups in our Azure Active Directory org along with all the associated members (be them users, groups, contacts, etc).
Since I was unable to locate a method to do this through the various Microsoft portals with a simple export button I began the process of obtaining access to the Microsoft Graph API/SDK via Powershell.
I'm by no means a PowerShell expert as it's not one of my go-to scripts; however, from what I can tell the ability to pull group info in this fashion is fairly limited.
The following is what I've been able to accomplish thus far:
Pull in a list of the groups using Get-MgGroup -All
Use Get-MgGroupMembers to pull back a list of Directory Objects.
This is where I get stuck. From what I've read it looks like a Directory Object by default only returns the ID and the Deleted Date. I'd like to get a display Name for these objects; I can obviously do this by running the appropriate 'Get' cmdlet for the type of directory object (i.e. Get-MgUser); From what I can tell the type of directory object can't be gleaned via PowerShell with out 'trial-and-error'... This seems highly inefficient to simply get a displayName.
Is there a more effective way to determine either the displayName of a Directory Object via a PowerShell cmdlet or at the very least a type so I can write a case statement to run the right cmdlet on the first try?
For the record this is going to be incorporated in to a Powershell Script, the current iteration of which looks like this and sorta works okay... assuming the Id passed in $member.Id belongs to a User type directory object.
Connect-MgGraph
$groups=Get-mgGroup -All
ForEach ($group in $groups){
$members = #{}
$members = Get-MgGroupMember -GroupId $group.Id -All
ForEach ($member in $members){
$user = Get-MgUser $member.Id
Write-Output $object.ODataType
Write-output $group.DisplayName "," $member.Id "," $user.UserType"," $user.DisplayName "," $user.UserPrincipalName "," $user.Mail >> C:scripts\Azure_Groups.txt
}
}
Would appreciate any direction/assistance on this. Thanks in advance!
Not sure why its not returning all the details on the PowerShell query:
This is working fine in MS Graph Explorer with the results showing all the details of the members:
For more details:https://learn.microsoft.com/en-us/graph/api/group-list-members?view=graph-rest-1.0&tabs=http#example-1-get-the-direct-membership-in-a-group

Exchange online - move messages with powershell

I have a huge mailbox (~50Gb) with plenty of messages in 'Inbox' . I'm looking for moving certain messages received before particular date to another folder at the same mailbox.
I have tried to do it from Outlook Windows app but it seems too slow and I can't do it for all the messages at once. Outlook just crash.
Is there any way to perform the task from Exchange Powershell ? I can certainly create server side rule but how to apply it to the messages already in 'Inbox' ?
New-InboxRule -Name Testmove2018 -Mailbox test -MoveToFolder "MailboxName:\2018" -ReceivedAfterDate "09/01/2015"
Oleg
The Exchange Online powershell module still allows you to copy and delete messages matching a search with Search-Mailbox, but you must copy them elsewhere first. Use an empty mailbox:
# First, check your search filter using EstimateOnly or LogOnly:
Search-Mailbox myUser#domain.com -SearchQuery 'Subject:Move Me' -EstimateResultOnly
# Copy the items to a temp mailbox and delete from the primary
# (EXO does not allow copying search results to same mailbox)
# param block just for readability:
$Params = #{
Identity = 'myUser#domain.com'
SearchQuery = 'Subject:Move Me'
TargetMailbox = 'temp#domain.com'
TargetFolder = 'Temp'
DeleteContent = $true
}
$TempResult = Search-Mailbox #Params
# Now move back to original mailbox
$Params.Identity = 'temp#domain.com'
$Params.TargetMailbox = 'myUser#domain.com'
$Params.TargetFolder = 'Moved'
$MoveResult = Search-Mailbox #Params
Then just make sure the number of emails is equal. EXO can take a while to get synced up, so if it's not finding all the email to move back, just give it a while and run the second search again:
If ($TempResult.ResultItemsCount -eq $MoveResult.ResultItemsCount) {
"We're good!"
} Else {
Write-Warning "Move results not equal!"
$TempResult
$MoveResult
}
Note that this is the 'Old' method, which is already retired and may be removed in the future. The new method is supposed to be using the New-ComplianceSearch style commands, but they have some limitations and aren't built for this purpose.
In my opinion, the "easiest" way is to open the mailbox in the browser > search for what you want > scroll down to load all the messages if needed > select all > move to > "move to a different folder..."

AzureAD Powershell Script to Bulk Change Manager Field

My goal would be to have a Powershell script that can import a CSV to bulk change a users manager field in AzureAD. The CSV would have 2 columns, one with the user and the other with their manager.
I've found scripts to export all users from AzureAD into a CSV, but this doesn't contain a column header for the manager field. I found an AzureAD script than can change the manager field using objectID but that's cumbersome, so ideally I could use an email address for the manager field.
I don't have code to show really, these were pretty basic scripts I found but I'm at best a non Powershell user.
Let us assume that you have below file :
In the left you have the username of the user and on the right you have the username of the new manager.
You could use the below snippet
#connecting to the Azure AD
Connect-AzureAD
#importing the CSV source which has the changes
$data = Import-Csv D:\Temp\Book1.csv
#Iterating through each row in the CSV
foreach ($row in $data)
{
#INFO in the Console
Write-Host "Updating the user :" $row.'User Username' " manager to " $row.'Manager Username' -ForegroundColor Yellow
#Updating the Manager
Set-AzureADUserManager -ObjectId (Get-AzureADUser -ObjectId $row.'User Username').Objectid -RefObjectId (Get-AzureADUser -ObjectId $row.'Manager Username').Objectid
#Completion info in the console for the specified row
Write-Host "Updated." -ForegroundColor Green
}
Explanation :
Step 1 :
Connecting to the Azure AD
Step 2:
Importing the CSV data that needs to be bulk updated
Step 3 :
Iterating through each row, updating the manager field using the commandlet Set-AzureADUserManager
Sample output :
Get-AzureADUserManager and Set-AzureADUserManager only accept ObjectID as input, similar to quite a few other AzureAD cmdlets.
You will need to have a multi step approach to achieve the outcome, below are the steps I would take
Get all Azure AD users, e.g. $AllAzureADUser = Get-AzureADUser -All
Use calculated property to populate manager field based on ObjectID of users you iterate through (essentially this is Foreach loop)
$AllAzureADUserWithManager = $AllAzureADUser | select *, #{ Name = "ManagerObjectId"; Expression = { Get-AzureADUserManager $_.ObjectId }}
Now you have all data required in $AllAzureADUserWithManager to make decisions and update the object. If you want to use UPN to update you can just look up the ObjectId based on UPN.
So say you iterating through an object import from CSV which has targetUserUPN and targetManagerUPN as columns:
$TargetUserObjectId = $AllAzureADUserWithManager | Where {$_.UPN -eq $row.targetUserUPN} | select -ExpandProperty ObjectId
$TargetManagerObjectId = $AllAzureADUserWithManager | Where {$_.UPN -eq $row.targetManagerUPN} | select -ExpandProperty ObjectId
Set-AzureADUserManager -ObjectId $TargetUserObjectId -RefObjectId $TargetManagerObjectId
If you need to run this on daily basis consider using a delta and export to csv previous runs and Filter down to only what is required if you have large number of users.

disable enumeration other user accounts in active directory

Huhu,
is it possible to disable the search for other Users in an AD? In this picture i am logged in as "normal" User.
Get-ADuser search
Here is a Picture of our AD structure.
AD Structure
So i don't want that a User can find another User in the Users OU by powershell (get-aduser)
is that possible?
I hope you have enough informations to understand my issue.
Regards
This is not really a thing in Windows proper. The default in AD is all users read. Secondly, anyone in AD is likely to have an email alias and thus search where they use PowerShell or not by their email alias/SMTP address, and should be for email lookups, so, IMHO, this is a futile use case.
One does not need Get-ADUser to find a user in AD. One has been able to do this since AD has been around and well before PowerShell was ever a thing using older scripting methods with .bat/.cmd/.vbs/WMI/ADSI or .Net directly and that is how it was done before PowerShell was ever a thing.
If you don't what a user using a specific cmdlet/cmdlets, then you need to implement restrictions via 'PowerShell just enough administration (JEA)'
Again, One does not need to use PowerShell to scan and get info from AD, a well documented and used thing, for folks who have PowerShell disabled (or tried to) in their environments.
Example:
How Can I Get a List of All the Users in an OU and Its Sub-OUs?
VBScript:
On Error Resume Next
Const ADS_SCOPE_SUBTREE = 2
Set objConnection = CreateObject(“ADODB.Connection”)
Set objCommand = CreateObject(“ADODB.Command”)
objConnection.Provider = “ADsDSOObject”
objConnection.Open “Active Directory Provider”
Set objCommand.ActiveConnection = objConnection
objCommand.Properties(“Page Size”) = 1000
objCommand.Properties(“Searchscope”) = ADS_SCOPE_SUBTREE
objCommand.CommandText = _
“SELECT Name FROM ‘LDAP://ou=finance,dc=fabrikam,dc=com’ WHERE objectCategory=’user'”
Set objRecordSet = objCommand.Execute
objRecordSet.MoveFirst
Do Until objRecordSet.EOF
Wscript.Echo objRecordSet.Fields(“Name”).Value
objRecordSet.MoveNext
Loop
Or
ADDS PowerShell (CMDLET, ADSI & .Net) to Expedite Your Tasks
ADSI:
<#
PowerShell ADSI(Active Directory Services Interface) commands
1. How to find the users property.
#>
$users1 = [ADSI]"LDAP://cn=copy,cn=users,dc=contoso,dc=com"
$users1 | select *
# 2. How to find the Group members for a Group.
$test = [ADSI]"LDAP://CN=test,CN=Users,DC=contoso,DC=com"
$test.Member |
ForEach-Object {[ADSI]"LDAP://$_"} |
select samccountname, samaccounttype
# 3. Listing an OU Contents
$ou=[ADSI]"LDAP://ou=tech,dc=contoso,dc=com"
$ou.PSBase.Children
$ou.PSBase.Children | Format-Table sAMAccountName
So this worked for me:
I just got it working by unchecking the "List Contents" from the "authenticated users" of the "Users" OU and I did not recognized any side effects so far.
Rights of Authenticated Users
And the "normal" User can't see the other users anymore by a query.
Tested with powershell: AD-GetUser and CMD "net user"
Query Result
So my problem is solved if there won't be any side effects in the future.
I will let you know.
Cheers

Fastest way to load Active Directory with dummy data?

In preparation for a test, we need to load a Windows Server VM with up to 400,000 users and 100,000 groups, and various mappings between them.
A powershell script has been written to achieve this, running on a Server 2012 R2 VM (4 cores, 8GB RAM). However, at the rate the script is running, it's looking like it could take more than a month to complete.
We've tried the script using both the net command and the Add-AD commands to see if there's any speed increase. There doesn't seem to be. The script uses several For loops to iterate through creating users, creating groups, and adding certain users numbers to group numbers.
Command examples were:
#net users $userName mypassword /add
#New-ADUser -Name $userName -SamAccountName $userName -DisplayName $userName -AccountPassword mypassword -Enabled $true
and
net group $groupName $userName /add
#Add-ADGroupMember -Identity $groupName -Members $userName
Any suggestions on the fastest way to load an AD with a mass of new users/groups/mappings?
Thanks
The PowerShell cmdlets for AD are convenient, but they are not efficient.
Using ADSI directly will likely be faster because it gives you more control of what's going on. PowerShell has a shortcut notation of [ADSI]"LDAP://thepath" to create objects (they're technically DirectoryEntry object, but the examples here use the IADs methods).
There are instructions on creating users here, but I can summarize it:
[ADSI]$OU = "LDAP://OU=IT,OU=Departments,OU=Employees,DC=Globomantics,DC=Local"
$new = $OU.Create("user","CN=Ginger Snaps")
$new.put("samaccountname","gsnaps")
$new.setinfo()
#Account is created disabled, so we need to enable and set a password
#(the password can't be set until it's created)
$new.put("userAccountControl",544)
$new.setpassword("P#ssw0rd")
$new.setinfo()
You use $new.put() for whatever other attributes you want to set. You can also create groups this way too, just use "group" instead of "user" in the Create() method.
This is still going to take a while. It's the network connections that will hurt you the most. So you have to:
Get as physically close to a DC as you can (run it on a DC if you can), and
Keep the number of network requests down
If you do run this on a DC, then (if the domain has more than one DC) make sure to target the DC that you're on. You can do that by injecting the DC name into the LDAP:// strings, like this:
"LDAP://dc1.domain.com/OU=IT,OU=Departments,OU=Employees,DC=Globomantics,DC=Local"
Number 2 is limited by the fact that you have to do 2 requests per new user (one to create, one to set password). But you can do other things to keep the number down, like create all the users first and store the distinguishedName of each new user, which you can calculate yourself (rather than asking AD for it) because it's the CN=user that you pass to Create() plus the OU. So for the example above, the DN of the new user is:
CN=Ginger Snaps,OU=IT,OU=Departments,OU=Employees,DC=Globomantics,DC=Local
Once you have all those, you can create the groups and add all the members in one go. For example:
[ADSI]$OU = "LDAP://OU=IT,OU=Departments,OU=Employees,DC=Globomantics,DC=Local"
$new = $OU.Create("group","CN=group1")
$new.put("samaccountname","group1")
$members = #("CN=Ginger Snaps,OU=IT,OU=Departments,OU=Employees,DC=Globomantics,DC=Local", `
"CN=Another User,OU=IT,OU=Departments,OU=Employees,DC=Globomantics,DC=Local")
$new.put("member", $members)
$new.setinfo()
Where $members is your array of the distinguishedName for each member.
This way you have one network request that creates the whole group with the members already set, rather than one network request for each member.