I am stuck with a piece of code.
We have 2 AD domains with users and groups in them.
I am trying to run a script that will check if the user is a member of a group to disable EV access and if they are not a member of that group add them to the EV enable group.
I have this working for 1 domain but I can't get it to work across the 2 domains we have.
I want the script to check domain1 and add it to the group in domain1 but if it doesn't find the user check domain2 and add it to the group in domain2.
Below is an extract of the code I have but I am struggling to get it to recognise the domain controller so that it looks in the right domain for the user.
foreach ($u in $Users){
Foreach($domain in $Domainlist)
{
$dom =get-addomain $domain.name
$dm = $dom.distinguishedname
$dname = $dom.name
$DomName = $dom.DNSRoot
$ADdc = Get-addomaincontroller -discover -domain $domName
$dc = $ADdc.hostname
$User = Get-ADUser $u.name -server $dc
$Enablegroup = "cn=evenable,ou=users and computers," + $dom
$disablegroup = "cn=evdisable,ou=users and computers," + $dom
if ((Get-ADUser $u.name -server $dc -Properties memberof).memberof -eq $disablegroup)
{
$name = $u.name
$dm = $domain.name
Write-host "$name is a member of the $dm EV disable group" -f Yellow
}
This is not hard at all!
Basically we just replace one line with the following, and we use PowerShell's try/catch syntax. Try this first chunk of code, if you have an error, then do the second part instead.
From
$User = Get-ADUser $u.name -server $dc
To
try{ $User = Get-ADUser $u.name -server $dc -ErrorAction STOP}
catch {$User = Get-ADUser $u.name -server OtherDC -ErrorAction STOP
$dom =get-addomain OtherDomain}
The whole goal of a structure like this is to handle the branching logic of try this/if it fails, do this instead, and ensure that the script will keep running after that point. So, because you're using $dom in the later part of the script to resolve the full path of the Security Group, you also need to change the $dom value in the Catch block, as I've done above.
Just tweak these values as appropriate for your domain and it should just work. I used an approach very similar to this for a task of my own, and Try/Catch/Finally was exactly the tool to get the job done.
Related
With PowerShell, I'm trying to get an ADUser account with Get-ADUser with LdapFilter using the employeeid attribute. I'm using a GC server of the domain as the sourcing server for faster results. However, I'm not getting the matching ADUser account(s). However, I'm able to retrieve results using a DirectorySearcher object. Please refer to the tried code snippets below,
###
#1 DirectorySearcher
$empid = "123456"
$ldapcn = "GC://dc=mydomain,dc=net"
$ldapfilter = "(&(ObjectCategory=Person)(objectclass=user)(employeeid=" + $empid + "))"
$objent = new-object System.DirectoryServices.DirectoryEntry($ldapcn)
$objsearch = new-object System.DirectoryServices.DirectorySearcher
$objsearch.SearchRoot = $objent
$objsearch.SearchScope = "subtree"
$objsearch.Filter = $ldapfilter
$objsearch.pagesize = 1000
$properties = "employeeid","givenname","sn","samaccountname"
$objsearch.propertiestoload.addrange($properties)
$results = $objsearch.Findall()
# Working
# $results contains matching user records
######################################################
#2 Get-ADUser
$empid = "123456"
$Server_AD_GC = (Get-ADDomainController -Server mydomain.net | select -exp hostname) + ":3268"
$ldapfilter = "(&(ObjectCategory=Person)(objectclass=user)(employeeid=" + $empid + "))"
$results = Get-ADUser -LdapFilter $ldapfilter -Properties employeeid, givenname, sn, samaccountname -Server $Server_AD_GC
# NOT WORKING!
# $results DOES NOT CONTAIN matching user records
What am I missing here?! Any help would be highly appreciated.
UPDATE 1
I just verified the Partial Attribute Set (PAS) with the code below and DO NOT SEE employeeid included in the list
$Domain = "mydomain.net"
# $schemaNamingContext = "cn=Schema,cn=Configuration,dc=mydomain,dc=net"
$schemaNamingContext = (Get-ADRootDSE -Server $Domain).SchemaNamingContext
Get-ADObject -SearchBase $schemaNamingContext -LDAPFilter "(isMemberOfPartialAttributeSet=TRUE)" -Properties ldapDisplayName | Select ldapDisplayName | sort ldapDisplayName
For more background, I'm running the 'DirectorySearcher' code block to search the source mydomain.net and running it from a W2012R2 server joined to a trusted domain, say mycaller.net, which is from a different forest. Importantly, the calling trusted domain mycaller.net's PAS CONTAINS employeeid. However, as already said Get-ADUser is unable to fetch the record(s).
Below is a screenshot of results observed with different environments,
Now, if not for a solution, I'd be glad if at least someone is able to reproduce this behavior.
Query:
In my example,
DirectorySearcher's $ldapcn = "GC://DC=mydomain,DC=net"
vs
Get-ADUser's $Server_AD_GC = (Get-ADDomainController -Server $Domain | select -exp hostname) + ":3268"
I expected both to work in a similar fashion. I see that I haven't specified a host for DirectorySearcher but have given one for Get-ADUser. Is this something to be looked into?
Import-Module activedirectory
$Name = "Larry Page"
$Searcher = [ADSISearcher]"(&(objectCategory=person)(objectClass=user)(cn=$Name))"
[void]$Searcher.PropertiesToLoad.Add("sAMAccountName")
$Results = $Searcher.FindAll()
ForEach ($User In $Results)
{
$NTName = $User.Properties.Item("sAMAccountName")
$CompanyName = $User.Properties.Item("company")
$NTName + " " + $CompanyName
[string]$userName = $NTName.properties.name
Get-ADUser "L2371732" -Properties company,PasswordExpired, PasswordLastSet, PasswordNeverExpires
}
This is my code so far. I am trying to substitute $userName for L2371732 in the following line but I am getting a different error so I hard coded the username in the Get-ADUser.
I only wan the fields I specified however I am getting everything (company, distinguishedname,enabled, etc)
Just trying to focus on the title portion of the question.
As per documentation -Properties does the following:
Specifies the properties of the output object to retrieve from the server. Use this parameter to retrieve properties that are not included in the default set.
So you would be seeing what you asked for in addition to the default set. If you don't want those properties you can drop the by piping to Select-Object and ask for only what you need.
$props = 'company', 'PasswordExpired', 'PasswordLastSet', 'PasswordNeverExpires'
Get-ADUser "L2371732" -Properties $props | Select-Object $props
If you wanted a default property returned as well e.g. samaccountname you can add that to the list with no issue.
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.
I'm just wondering if anyone of you is using powershell to set a users P.O. Box via
Set-ADUser
Im comparing domainaccounts between two different domains and let my script write the differences back to one of the DCs. At the first run, the script detects that there's a different POBox on DC1 than on DC2 and writes the one from DC1 back to DC2.
This works perfectly well with all of the atributes except the postOfficeBox.
As stated above, on the first run, the script detects the changes and writes the changes back. That does not apply to the postOfficeBox however. If I open the userobject on DC2, it remains blank! What is even more strange is, that the script doesn't detect that the POBox is blank if I run it again! I have to modify the postOfficeBox manually in order for the script to see any changes again. (and with blank I mean not even spaces)
All other attributes are working fine. Is this a possible bug on Win Server 2012 and WPS?
Here's the code:
$attributes = "c","co","company","countryCode","department","displayName","postOfficeBox","sAMAccountName"
$user1 = Try{Get-ADUser -Identity $_.SamAccountName -Properties $attributes}
catch{}
$user2 = try{Get-ADUser -Identity $_.SamAccountName -Properties $attributes -Credential $AdminCredentials -Server $dc2}
catch{}
if ($user1.SamAccountName -eq $user2.SamAccountName) {
$chk_modified = #{}
$attributes | ? { $user1.$_ -ne $user2.$_ } | % {
$chk_modified[$_] = $user2.$_
}
if ($chk_modified.Count -ge 1) {
Set-ADUser -Identity $_.SamAccountName -Replace $chk_modified
}
}
I see a field in Active Directory called 'POBox' is this the field you mean?
Here is how I would update it
Get-ADUser $userName | Set-ADUser -POBox $pobox -Credential $cred
or this:
'Get-ADUser -Identity $_.SamAccountName | Set-ADUser -postOfficeBox "the new box 42" -credential $cred'
Hope this helps.
Basically, in our environment, we have a ton of security groups. Security groups that are nested within other groups etc. So it is a real PITA to find out why a setting is applying to a user, because of one of the nested groups they may or may not be a part of
For e.g. If you add a user to group X, they suddenly have a published application in Citrix. Citrix is configured for security group Y. Attempting to find the link between X and Y is very time consuming but can be automated.
I want to create a script where, you enter a user and the end security group (group Y from above), and the script outputs the intermediary groups that connects the user to the final group. If this makes sense?
Something like this:
function get-grouprelationship($username, $knownsecuritygroup)
{
$getallgroups = get-adgroupmember $knownsecuritygroup | where-object {$_.ObjectClass -eq "Group" | select-object SamAccountName | foreach-object {get-adgroupmember $_.SamAccountName}
}
(The above variable takes your group, and loops through all members of that group, printing their members)
$usergroups = (get-aduser -identity $username -Properties memberof | select-object memberof).memberof
(The above gets all groups that a user is in)
$usergroups1 = $usergroups.split(",")
$usergroups2 = $usergroups1[0]
$usergroups3 = $usergroups2.substring(3)
(the above formats the text nicely)
if ($usergroups3 -contains $groupname){write-host "$username is directly in $groupname}
From here, I am quite stuck as I basically need to nest multiple for loops, depending on how many groups are in each group. Then do a condition check that
if ($groupname -eq $currentgroup){write-host "connected by $groupname and $currentgroup}
I'm also stuck with the $getallgroups variable, because it only checks 1 level down. It would then need another foreach loop inside that, which would need another one inside that etc.
Having no prior coding experience, I am really struggling to get my head around an easy way to achieve my goal.
EDIT:
I found this script here - script. Below basically works, except it is way to verbose:
import-module activedirectory
$username = read-host "What's their username?"
Function RecurseUsersInGroup {
Param ([string]$object = "", [int]$level = 0)
$indent = "-" * $level
$x = Get-ADObject -Identity $object -Properties SamAccountName
if ($x.ObjectClass -eq "group") {
Write-Host "# $($x.SamAccountName)"
$y = Get-ADGroup -Identity $object -Properties Members
$y.Members | %{
$o = Get-ADObject -Identity $_ -Properties SamAccountName
if ($o.ObjectClass -eq "user" -and $o.SamAccountName -eq $username) {
Write-Host "-> $($o.SamAccountName)"
} elseif ($o.ObjectClass -eq "group") {
RecurseUsersInGroup $o.DistinguishedName ($level + 1)
}
}
} else {
Write-Host "$($object) is not a group, it is a $($x.ObjectClass)"
}
}
$thegroup = read-host "What's the Group?"
RecurseUsersInGroup (get-adgroup $thegroup).DistinguishedName
That works fine, but appears to output every security group, oppose to the connecting ones. Certainly a step in the right direction though! If I find the source I will post the credit as well.
The following version is not less verbose (could probably be written a lot more terse, but I'm hoping the script is at least semi-readable), but it does a search for the group and returns the Active Directory group objects for each group along the branch in which the group was found.
function Get-GroupConnection
{
[CmdletBinding()]
PARAM (
$Username,
$GroupName
)
$User = Get-AdUser -Identity $Username -Properties MemberOf
if (-Not ($User))
{
return;
}
$SearchedGroups = #()
function Find-GroupBranches
{
[CmdletBinding()]
PARAM (
$GroupNameList,
$SearchForGroupName
)
$ADGroups = $GroupNameList | Foreach { Get-ADGroup -Identity $_ -Properties MemberOf }
foreach($group in $ADGroups)
{
Write-Verbose "Testing if either '$($Group.SamAccountName)' or '$($Group.DistinguishedName)' are equal to '$SearchForGroupName'"
if ($Group.SamAccountName -eq $SearchForGroupName -OR $Group.DistinguishedName -eq $SearchForGroupName)
{
Write-Verbose "Found $($Group.DistinguishedName)"
Write-Output $Group
return
}
}
Write-Verbose "No match in current collection, checking children"
foreach ($currentGroup in $ADGroups)
{
if ($SearchedGroups -Contains $currentGroup.DistinguishedName)
{
Write-Verbose "Already checked children of '$($currentGroup.DistinguishedName)', ignoring it to avoid endless loops"
continue
}
$SearchedGroups += $currentGroup.DistinguishedName
if ($currentGroup.MemberOf)
{
Write-Verbose "Checking groups which $($currentGroup.DistinguishedName) is member of"
$foundGroupInTree = Find-GroupBranches -GroupNameList $currentGroup.MemberOf -SearchForGroupName $SearchForGroupName
if ($foundGroupInTree)
{
Write-Output $currentGroup
Write-Output $foundGroupInTree
break
}
}
else
{
Write-Verbose "$($currentGroup.DistinguishedName) is not member of any group, branch ignored"
}
}
}
Write-Verbose "Searching immediate group membership"
Find-GroupBranches -GroupNameList $User.MemberOf -SearchForGroupName $GroupName
}
Get-GroupConnection -Username MyUser -GroupName SubSubGroup -Verbose
A description of how it searches follows.
Given the following Active Directory structure:
MyUser
Domain Admins
AnotherSubGroup
Other Group
DirectMemberGroup
Domain Admins (same group as MyUser is direct member of, above)
AnotherSubGroup (which is of course the same as above too)
SubGroup
SubSubGroup
Some Other Group
If we search for the connection between MyUser and 'SubSubGroup' the script would search first the direct membership of the MyUser user, i.e. 'Domain Admins', 'Other Group', 'DirectMemberGroup' and 'Some Other Group'. None of these match the 'SubSubGroup' we search for, so it starts checking 'child'groups.
'Domain Admins' is member of 'AnotherSubGroup', but that does not match 'SubSubGroup'. 'AnotherSubGroup' is not member of any group, so that branch is ignored.
'Other Group' is not member of any group, so that branch are ignored.
'DirectMemberGroup' is member of other groups, so it iterates through those groups. It has already checked 'Domain Admins' for children, so that group is skipped to avoid getting stuck in a circular search. Therefore it check 'SubGroup'.
'SubGroup' does not match 'SubSubGroup' so it check the groups which 'SubGroup' is member of. 'SubGroup' is member of 'SubSubGroup', so it checks that group.
'SubSubGroup' does match 'SubSubGroup' and will therefore be chosen as a match.
In the above example, the output group object will be branches which lead to the 'SubSubGroup' group, in the following order:
DirectMemberGroup
SubGroup
SubSubGroup
Observe that this method will return the first connection it finds between the user and the group. If, for example, the 'Some Other Group' group would also be a member of 'SubSubGroup' this would not change the output, nor the search process, mentioned above.