How can I optimize my PowerShell - LDAP Query? - powershell

I have created a script that reads from a CSV (or other dataset, but not posting that side) and creates users in my AD environment.
Basically, whatever dataset is passed into the script will be processed, and then a user will be created if they do not exist. If the user exists in the AD already, then the script skips over the entry. This is a CREATE only script.
It's pretty slow, and I'd like to improve the performance whilst keeping the functionality. Can you give me any tips as to how I can make this perform better?
import-csv "c:\PSScripts\LDAP\ADMigrate.csv" | ForEach-Object {
# Define the User OU
$usersOU = [ADSI] "LDAP://ou=Students, dc=live,dc=tcicollege,dc=edu"
# Check for existing users
$existingUsers = ($usersOU.psbase.children | Where-Object {$_.psBase.schemaClassName -eq "User"} | Select-Object -expand Name)
$userQuery = $existingUsers -contains $_.'AccountName'
if ($userQuery) {
echo $_.'AccountName' " already exists in Directory."
} else {
# Create a new user
$newUser = $usersOU.create("user","cn=" + $_.'AccountName')
# Set Account AttributesAMAccountName
$newUser.Put("sAMAccountName", $_.'AccountName')
$newUser.Put("givenName", $_.'FirstName')
$newUser.Put("employeeID", $_.'StudentID')
$newUser.Put("sn", $_.'LastName')
$newUser.Put("department", $_.'Department')
$newUser.Put("company", $_.'SyStudentID')
$newUser.Put("UserPrincipalName", $_.'AccountName' + "#live.tcicollege.edu")
$newUser.Put("mail", $_.'AccountName' + "#live.tcicollege.edu")
$newUser.Put("displayName", $_.'LastName' + "," + " " + $_.'FirstName')
# First Commit
$newUser.SetInfo()
$newUser.userAccountControl="66048"
$newUser.Put("pwdLastset", -1)
$newUser.SetPassword($_.'Password')
# Final Commit
$newUser.SetInfo()
echo $_.'AccountName' " created successfully."
}
}
Thank you in advance for any help you can offer.

Try the static Exists() method to find if the user exists in the Students OU:
$user = [ADSI]::Exists("LDAP://cn=$($_.AccountName),ou=Students, dc=live,dc=tcicollege,dc=edu")
if(!$user)
{
"create code goes here"
}
The $usersOU value is static so you can take it out, place it before the import-csv command.

Related

How do I change the permissions of a user for 600+ folders in Outlook via PowerShell?

I'm trying to give an assistant access to part of someone's O365 mailbox. She currently has foldervisible permissions on his inbox. And there is about 607 folders under the inbox that she needs access to without having anymore permissions to the inbox itself.
Below is the code I've tried to run. I've removed the domain name but otherwise the code is exact. I've run the code twice and gotten no errors and it runs for a quite a while. But once it's complete, there is no change in the permissions.
ForEach($f in (Get-EXOMailboxFolderStatistics -identity jjo | Where { $_.FolderPath.Contains("jjo:/Inbox/Case Files") -eq $True } ) ) {
$fname = "jjo:" + $f.FolderPath.Replace("/","\");
Add-MailboxFolderPermission $fname -User gka -AccessRights Owner
}
Try to narrow down what's working and not;
Get-EXOMailboxFolderStatistics -identity jjo -ErrorAction Stop | Where-Object { $_.FolderPath.Contains("jjo:/Inbox/Case Files") } | ForEach-Object {
Try {
$fname = "jjo:" + $_.FolderPath.Replace("/","\")
Write-Host "Amending: $fname ..."
Add-MailboxFolderPermission $fname -User gka -AccessRights Owner -ErrorAction Stop
Write-Host "Done"
}
Catch {
$_
}
}
Write-Host "Complete"
On the client side, Extended MAPI (C++ or Delphi) can be used modify the ACL table on the folder level. If using Redemption (I am its author) is an option (any language), you can use RDOFolder.ACL collection to modify the permissions. Something along the lines (VBA, off the op of my head):
ROLE_PUBLISH_EDITOR = &H4FB
set Session = CreateObject("Redemption.RDOSession")
Session.MAPIOBJECT = Application.Session.MAPIOBJECT
set AddressEntry = Session.AddressBook.GAL.ResolveName("The Other User Name")
set Folder = Session.GetDefaultFolder(olFolderInbox)
for each subFolder in Folder.Folders
set ACE = subFolder.ACL.Add(AddressEntry)
ACE.Rights = ROLE_PUBLISH_EDITOR
next

Powershell 5.1 if statement result is incorrect

I'm using Powershell 5.1 in constrained language mode with no access to additional modules.
I've created a script to return information from "net user query (username) /domain" that includes an If statement to return results if the end user I'm checking isn't a member of certain groups.
The If statement doesn't appear to be working correctly and I'm not sure where I'm going wrong.
1.
$enduser = read-host "please enter username"
Net user query $enduser /domain
$u = #{}; net user $enduser /domain | ConvertFrom-String -Delimiter '\s{2,}' -PropertyNames Name,
Value | ForEach-Object { $u[$_.Name] = $_.Value }
$globalgroups = $u_.'global group memberships'
if ($globalgroups) -inotlike '$_.EXAMPLEGROUPNAME'
{
write-output "user is not part of EXAMPLEGROUP"
}
I have also tried
2.
if ($globalgroups) -inotlike '*EXAMPLEGROUPNAME*'
{
write-output "user is not part of EXAMPLEGROUP"
}
3. as well as creating new variables
$EXAMPLEGROUP1 = { $u[$_.'global group memberships'] = $_.'EXAMPLEGROUPNAME' }
$EXAMPLEGROUPtest = { $u[$_.'global group memberships'] = $_.'value' }
and changing the if statement to;
if ($globalgroups) -inotlike '$examplegroup1'
{
write-output "user is not part of EXAMPLEGROUP"
}
the #2 snippet of code appears to work for one specific group which always appears as the first result in global group memberships, however if a different group name is added which appears later in the list of global group memberships, it always returns that the user isn't part of the group, even though I can see it there.
What can I change to make this work?
I am not sure why exaclty you are using Net user query. But as far as i can see, you try to make an If statement to return results if the end user you are checking isn't a member of certain groups in Active Directory.
I would rather than using Net user query do something like this:
$enduser = "TestUser"
$examplesgroupname = "Test-Group"
$groupmembership = Get-ADPrincipalGroupMembership $enduser
foreach ($membership in $groupmembership.name) {
if ($membership -ne $examplesgroupname) {
$membership_validated = $true
##statement Is true when user is not member of the examplegroup
} else {
$membership_unvalidated = $false
##statement Is false when user is member of the examplegroup
}
}
Write-Host $membership_validated
If you need to use Net user query for a certain reason, than give a short feedback and i will try to fix your script with Net user query included.

Using powershell to list AD users and making an interactive menu to remove specific user

so what i want to do is use powershell to list all ADusers into a basic interactive menu so that a specific user can be chosen and removed.
This is what I got so far, and this all users and allows me to select a specific one. But the Remove-ADUser -identity $ouEntry (on line: 18) runs right after I start the script and selects all the users for removal before I can select a specific one. I need it to run after i select an option, and with the correct user. I have been looking into a switch menu, but with poor results since I cant embed the ForEach properly.
Appreciate all help. I'm also open to alternate solutions
Clear-Host
$ouCounter = 1
$MenuArray = #()
$DomainName = ($env:USERDNSDOMAIN).split('.')[0]
$Tld = ($env:USERDNSDOMAIN).split('.')[1]
Write-Host "`nChoose the user you want to delete"
foreach ($ouEntry in ((Get-ADUser -SearchBase "DC=$DomainName,DC=$Tld" -Filter *).name))
{
$(" "+$ouCounter+".`t"+$ouEntry)
$ouCounter++
$MenuArray += $ouEntry + " was removed"
$MenuArray += Remove-ADUser -identity $ouEntry
}
do
{ [int]$menuSelection = Read-Host "`n Enter Option Number"}
until ([int]$menuSelection -le $ouCounter -1)
$MenuArray[ $menuSelection-1]
Output
Choose the user you want to delete
1. Administrator
2. Guest
3. user1
4. user2
5. user3
6. user4
7. user5
8. user6
9. Jon Snow
Enter Option Number:
Previous reference: Making a dynamic menu in Powershell
You might consider doing this in a Windows Forms GUI, so the list to choose from can have a scrollbar.
Having said that, the code you have now writes the entry on screen as menu item and immediately removes that user.
Below code first gets the users in an array once and creates a List object from that.
The reason for using a List object is because with that it is easy to remove an item (unlike with using an array).
$DomainName = ($env:USERDNSDOMAIN).Split('.')[0]
$Tld = ($env:USERDNSDOMAIN).Split('.')[1]
# get an array of users in the given OU, sorted on the Name property
$users = Get-ADUser -SearchBase "DC=$DomainName,DC=$Tld" -Filter * | Sort-Object Name
# store them in a List object for easy removal
$list = [System.Collections.Generic.List[object]]::new()
$list.AddRange($users)
# now start an endless loop for the menu handling
while ($true) {
Clear-Host
# loop through the users list and build the menu
Write-Host "`r`nChoose the user you want to delete. Press Q to quit." -ForegroundColor Yellow
$index = 1
$list | ForEach-Object { " {0}.`t{1}" -f $index++, $_.Name }
$menuSelection = Read-Host "`r`nEnter Option Number."
# if the user presses 'Q', exit the loop
if ($menuSelection -eq 'Q') { break }
# here remove the chosen user if a valid input was given
if ([int]::TryParse($menuSelection, [ref]$index)) {
if ($index -gt 0 -and $index -le $list.Count) {
$userToDelete = $list[$index - 1]
Write-Host "Removing user $($userToDelete.Name)" -ForegroundColor Cyan
Remove-ADUser -Identity $userToDelete.DistinguishedName
# remove the user from the list
$list.RemoveAt($index - 1)
}
}
# add a little pause and start over again
Start-Sleep -Seconds 1
}
Hope that helps

AD user creation with SamAccountName availability check loop

I'm learning Powershell by making a script that will hopefully automate everything that needs to be done when we get a new hire or a consultant. Currently I'm working on the part that will create the AD account. Below are the variables specific to this portion of the script.
#Variables for preliminary SamAccountName, modifiers and initials array
$PrelSamAccountName = ("se" + [string]$GivenName.Substring(0,3) + [string]$SurName.Substring(0,3)).ToLower()
$Modifier1 = 1
$Modifier2 = 0
$InitialsArray = #("x","y","z")
Here is the loop. I cut out a bunch of parameters on New-ADUser to make it less cluttered.
try {
#Checks if preliminary SamAccountName is taken or not
$ADCheck = Get-ADUser -Filter {SamAccountName -eq $PrelSamAccountName}
#Creates new user
New-ADUser -Name $Name -SamAccountName $PrelSamAccountName
} catch {
#Replaces final character in preliminary SamAccountName with "1++"
while (($ADCheck | Select-Object -ExpandProperty SamAccountName) -eq $PrelSamAccountName) {
$PrelSamAccountName = ([string]$PrelSamAccountName.Substring(0,7) + ($Modifier1++)).ToLower()
}
#Changes $Initials from $null to x/y/z if an existing user has identical name as new user
while (($ADCheck | Select-Object -ExpandProperty Name) -eq $Name) {
$Initials = $InitialsArray[$Modifier2++]
$Name = $GivenName + " " + $Initials + " " + $SurName
}
}
Everything is working as intended, except for the fact that a new user is created every other time I run the loop. Ideally I would want it to create a new user every time it is run. :)
I'm assuming it has something to do with the placement of the $ADCheck variable, but after having rewritten this portion multiple times I simply can't get it to work. Any ideas?
Thanks in advance.
You have some logical problems in here:
Follow this Approach:
Pseudocode:
while (user exist) {create new username}
new-aduser new username
PS Code:
function new-sam
{
#creates new sam
}
$sam = "username"
$a=$false
while (!$a)
{
try {
$a = [bool](Get-ADUser -Identity $sam )
}
catch {
$a = $false; $sam = new-sam
}
}
new-aduser ....

Effective permissions on remote share for domain users in Powershell

I searched and read some topics here but I didn't found what I am looking for.
Basically, I want to check the effective permissions for a specific user for several shares, I want a script such as :
$user = Read-Host "Enter username"
$shares = "\\serverABC\share2","\\serverABC\share1"
foreach ($share in $shares)
{
Cmdlet-EffectivePermissions $share
}
Output expected :
\\serverABC\share1
Full Control : No
Traverse folder / execute / file : YEs
List folder / read data : No
...
\\serverABC\share2"
Full Control : No
Traverse folder / execute / file : YEs
List folder / read data : No
...
In fact, I want to do in Powershell exactly the same way that effective permissions Tab.
Does it exist a built-in solution (without importing any modules, add-ins, ...) with .NET Method (GetUserEffectivePermissions) or with Get-ACL?
I'm not aware of a .NET/PowerShell way to do this natively. There is a PowerShell module here that should be able to do what you're looking for, though. After importing that, you should be able to modify your pseudo code to the following:
$user = Read-Host "Enter username"
$shares = "\\serverABC\share2","\\serverABC\share1"
foreach ($share in $shares) {
Get-EffectiveAccess -Path $share -Principal $user -ListAllRights
}
That returns PS objects instead of simple text. If the format isn't to your liking, you can use some of the utility commands to shape it however you like. Here are two examples of doing that:
First, a simple change to original that doesn't return the exact format you mentioned, but it's pretty close:
foreach ($share in $shares) {
$share
Get-EffectiveAccess -Path $share -Principal $user -ListAllRights | ForEach-Object {
"{0}: {1}" -f $_.Permission, $_.Allowed
}
""
}
Next, a more complicated change that formats the output exactly how you were asking (at least I think):
# Go through each FileSystemRights enum name and add them to a hash table if their value is
# a power of 2. This will also keep track of names that share a value, and later those can
# be combined to provide a friendly permission name
$Ht = #{}
foreach ($Name in [System.Enum]::GetNames([System.Security.AccessControl.FileSystemRights])) {
$Value = [System.Security.AccessControl.FileSystemRights]::$Name
if ($Value.value__ -band ($Value.value__ - 1)) {
# Not a power of 2, so ignore this
continue
}
if (-not $Ht.$Value) {
$Ht.$Value = #()
}
$Ht.$Value += $Name
}
# FullControl isn't a power of 2, but it's useful to test for access, so add it manually
$Ht.([System.Security.AccessControl.FileSystemRights]::FullControl) = "FullControl"
function YesNoTest {
param(
[System.Security.AccessControl.FileSystemRights] $EffectiveAccess,
[System.Security.AccessControl.FileSystemRights] $AccessToTest
)
if (($EffectiveAccess -band $AccessToTest) -eq $AccessToTest) {
"Yes"
}
else {
"No"
}
}
$shares | Get-EffectiveAccess -Principal $user | ForEach-Object {
$_.DisplayName
$EffectiveAccess = $_.EffectiveAccess
$Ht.GetEnumerator() | sort { $_.Key.value__ } -Descending | ForEach-Object {
"{0}: {1}" -f ($_.Value -join " / "), (YesNoTest $EffectiveAccess $_.Key)
}
""
}
Note that this won't be completely accurate if you run this against a remote system and the following conditions are met:
The security descriptor contains groups that are local to the remote system, i.e., non domain groups
The user(s) you're checking is a member of one of the local groups