I am trying to get all members of a distribution group in office365 that are in two groups one. The first group "Precon" filters users that are in a specific group. The next group "Office" gets the office distribution group that the memebers are in. The end goal is that I would like to get the office location for each member in the precon group.
I have tried a couple of things but dont know if I have the correct syntax or if what I was trying even works the way I think it does.
So far this is what I have
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $cred -Authentication Basic -AllowRedirection
Import-PSSession $Session -DisableNameChecking
$groupmembers = foreach($g in Get-DistributionGroup -Filter {name -like "Precon"}) {
Get-DistributionGroupMember -Identity $g.SamAccountName | select Name
}
$office = foreach($o in Get-DistributionGroup -Filter {name -like "*Office"})
{
Get-DistributionGroupMember -Identity $o.SamAccountName | select Name
}
$match = $groupmembers | ForEach {
if ($_ -imatch $office){
Write-Output $_ "matches"
}
}
$match
Any help would be appreciated.
What is the output?
For the last block of code I'd look at:
$match = $groupmembers | where $office -contains $_
$match
I found a method that works
$precon = #()
foreach($i in Get-DistributionGroup -Filter {Name -like '*Precon'}) {
$precon += ( Get-DistributionGroupMember -Identity $i.samAccountName | foreach {$_.Name} )
}
$office = #{}
foreach($i in Get-DistributionGroup -Filter {Name -like '*Office'}) {
$office[$i.name] += ( Get-DistributionGroupMember -Identity $i.samAccountName | foreach {$_.Name} )
}
$uber = #()
foreach ($key in $office.Keys) {
$preconOffice = $office[$key] | Where { $precon -Contains $_ }
foreach ($name in $preconOffice) { $uber += ( New-Object -TypeName PSObject -Prop (#{'Office' = $key ;'Name' = $name}) ) }
}
$uber
Related
Below Function is not running the lines "If ($script:AADServersTable)" down... I have put a write-host and that is working, however the variables are not working below that
Function get-AADVariables
{
$script:AADServersTable = New-Object 'System.Collections.Generic.List[System.Object]'
$scriptBlockaadinstalled = {
Get-WmiObject -Class Win32_Product |
where name -eq "Microsoft Azure AD Connect synchronization services" | select Name, Version}
##$scriptBlockaadinstalled = {hostname}
Import-Module ActiveDirectory
$servers = Get-ADComputer -Filter 'operatingsystem -like "*server*" -and enabled -eq "true"' | Select-Object -Property Name -ExcludeProperty Release
## just a single invoke-command
foreach ($server in $servers) {
$aadinstalledresults = Invoke-Command -ComputerName $server.name -ScriptBlock $scriptBlockaadinstalled ##-HideComputerName
$aadinstalledresults = $aadinstalledresults.PSComputerName
$script:AADServersTable.Add($aadinstalledresults)
}
If ($script:AADServersTable) {
Write-Host 'working'
$AADServers = ($script:AADServersTable | Group-Object -NoElement).Name | Get-Unique
$AADServers = $AADServers -split "`n"
$AADServer = $AADServers[0]
$StandByAADServer = $AADServers[1]
$SecondStandByAADServer = $AADServers[2]
}
}
The following script works perfectly, but I think it's way too complex and slow for what it needs to do.
Basically, for a list of users in a variable (manually or obtained from Get-ADUser, doesn't matter), I want to query all Domain Controllers and get the LastLogonDate for each user. I'll later use it for bad password etc.
Any suggestions on cleaning it up please that would improve my coding skills?
$UserList = "User1", "User2"
$DCs = (Get-ADDomainController -Filter *).Name
$Combined = foreach ($User in $UserList)
{
$DCarray = [ordered] #{}
foreach ($DC in $DCs)
{
$DCresponse = Get-ADUser $User -Properties DisplayName, LastLogonDate -Server $DC | Select-Object Name, DisplayName, LastLogonDate
if( -not $DCarray.Contains("Name")) { $DCarray.Add("Name",$DCresponse.name) }
if( -not $DCarray.Contains("DisplayName")) { $DCarray.Add("DisplayName",$DCresponse.DisplayName) }
if( -not $DCarray.Contains($DC)) { $DCarray.Add($DC,$DCresponse.LastLogonDate) }
}
$Return = New-Object -TypeName psobject
foreach ($Key in $DCarray.keys)
{
$Each = $DCarray[$Key]
$Return | Add-Member -MemberType NoteProperty -Name $Key -Value $Each
}
$Return
}
$Combined | Format-Table -AutoSize
I think the logic is mostly the same but this should be easier to understand and maintain. In addition, the use of the LDAPFilter should improve the runtime a bit.
$UserList = "User1", "User2", "User3"
$filter = "(|(name={0}))" -f ($UserList -join ')(name=')
# LDAP query string would look like: (|(name=User1)(name=User2)(name=User3))
$DCs = (Get-ADDomainController -Filter *).Name
$props = #{
Properties = 'DisplayName', 'LastLogonDate'
LDAPFitler = $filter
}
$result = foreach($dc in $DCs)
{
$props.Server = $dc
$users = Get-ADUser #props
foreach($user in $users)
{
# If this User's LastLogonDate attribute is NOT null
if($user.LastLogonDate)
{
[pscustomobject]#{
DomainController = $dc
UserName = $user.Name
DisplayName = $user.DisplayName
LastLogonDate = $user.LastLogonDate
}
}
}
}
$result | Sort-Object UserName, LastLogonDate | Out-GridView
I can't leave a comment, so posting as an Answer instead.
For a non-Powershell solution have a look at this tool which will retrieve the the last logon time from all DC in the forest\domain for a list of users, based on a list of samaccountnames.
https://nettools.net/last-logon-time/
Gary
List all users that have mailboxes but are not in a group called Metalogix*. I need a PowerShell script that will check whether specific user is a part of certain group or not and if the user is part of any of those groups.
I already have working script:
Import-Module ActiveDirectory
$Users = Get-Mailbox -ResultSize "unlimited"
$Group = "Metalogix*"
foreach ($user in $Users) {
$Check = Get-ADPrincipalGroupMembership -Identity $User.sAMAccountName |
? { $_.Name -like $Group }
if ($Check -eq $null) {
Write-Output "$User.sAMAccountName is NOT part of this group"
} else {
$Results = Get-Mailbox -Identity $User.sAMAccountName |
select Name, sAMAccountName, PrimarySmtpAddress, Database |
Export-csv "c:\results1.csv" -NTI -Append
}
}
But script doesn't list groups recursively, e.g tester4-6 are members of 'Test Group 2', which is a member of 'Test Group 1'. The rest are direct. Just I can see direct membership, not recursive membership.
2nd question : I want to get all users with samaccountname that begins with "STR" prefix.
Test Group 1
tester1
tester2
-> Test Group 2
tester4
tester6
I'd probably use a recursive function. Something like this:
function Test-GroupMembership {
Param(
[Parameter(Mandatory=$true)]
[string]$Identity,
[Parameter(Mandatory=$true)]
[string]$Name
)
$groups = Get-ADPrincipalGroupMembership -Identity $Identity
if ($groups | Where-Object { $_.Name -like $Name }) {
return $true
} elseif ($groups -ne $null) {
foreach ($g in $groups) {
if (Test-GroupMembership -Identity $g -Name $Name) {
return $true
}
}
}
return $false
}
Get-ADPrincipalGroupMembership isn't recursive, but Get-ADGroupMember is.
$Users = Get-Mailbox -ResultSize "unlimited"
$Group = 'Metalogix*'
$GroupMembers = Get-ADGroupMember -Identity $Group | Get-ADGroupMember -Recursive | Select-Object -ExpandProperty samAccountName
foreach ($User in $Users) {
if ($User -in $GroupMembers) {
Write-Output "User $User is in group $Group."
}
else {
Write-Output "User $User is not in group $Group."
}
}
This is also more efficient because you're only fetching group membership once.
I'm away from my servers, so treat the above as pseudocode.
I have a script from a previously answered question, but don't have enough reputation to comment. I tried to run that script and came across this error message:
Export-CSV : Cannot append CSV content to the following file: C:\users.csv. The appended object does not have a property that corresponds to the following column: User;Group. To continue with mismatched properties, add the -Force parameter, and then retry the command.
How can I debug this script to resolve this issue?
Function Get-ADGroupsRecursive{
Param([String[]]$Groups)
Begin{
$Results = #()
}
Process{
ForEach($Group in $Groups){
$Results+=$Group
ForEach($Object in (Get-ADGroupMember $Group|?{$_.objectClass -eq "Group"})){
$Results += Get-ADGroupsRecursive $Object
}
}
}
End{
$Results | Select -Unique
}}
import-module activedirectory
$users = get-aduser -Filter {Name -Like "*"} -Searchbase "OU=Sample Accounts,DC=domain,DC=com" -Properties MemberOf | Where-Object { $_.Enabled -eq 'True' }
$targetFile = "C:\users.csv"
rm $targetFile
Add-Content $targetFile "User;Group"
foreach ($user in $users)
{
$Groups = $User.MemberOf
$Groups += $Groups | %{Get-ADGroupsRecursive $_}
$Groups | %{New-Object PSObject -Property #{User=$User;Group=$_}}|Export-CSV $targetfile -notype -append
}
try this function
function Get-InChainGroups
{
param (
[parameter(mandatory = $true)]
$user,
$domain)
$user1 = (get-aduser -filter { name -eq $user } -server $domain).distinguishedname
Write-verbose "checking $user"
$ldap = "(&(objectcategory=group)(groupType:1.2.840.113556.1.4.803:=2147483648)(member:1.2.840.113556.1.4.1941:=$user1))"
try { Get-ADobject -LDAPFilter $ldap -server $domain | select #{ n = 'Identity'; e = { $user } }, Name, #{ n = 'DN'; e = { $_.distinguishedname } } | ft -a }
catch { "Exception occurred" }
}
I'm trying to find some way of flexibly altering/substituting pipeline elements in PowerShell:
Function Where-DirectlyReportsTo {
Param (
[Parameter(
ValueFromPipeline = $true,
HelpMessage = "The ADUser object to be tested"
)]
[Microsoft.ActiveDirectory.Management.ADUser] $ADUser,
[Parameter(
Mandatory = $true,
ValueFromPipeline = $false,
Position = 0
)]
[String] $mgrDN
)
Process {
If ($ADUser) {
If ($ADUser.Manager -eq $mgrDN) { Return $ADUser }
}
}
}
$Properties = #("Manager")
$users = Get-ADUser -Filter * -SearchBase $OU -Properties $Properties
[ScriptBlock] $sb = {Where-DirectlyReportsTo "CN=Colonel Foobar,$OU"}
$DNs = $users | $sb | %{$_.DistinguishedName}
Which I want to return the DNs of all the users that report to Colonel Foobar, but it gives me the error Expressions are only allowed as the first element of a pipeline.
This is a trivial example, but I'd ultimately like to be able to put the pipeline step inside a loop and pass it different ScriptBlocks to get different sets of users, or use more complicated ScriptBlocks (e.g.: {Where-IsEmployee | Where-IsInDepartment "Finance" | Where-LikesIceCream}).
I realize that I may be going about this all wrong, and I would very much appreciate being pointed in the right direction.
EDIT: To clarify, here's a rough outline of what I'd like to accomplish:
[ScriptBlock[]] $arrBlocks = #( # lots of different cases
)
ForEach ($sb In $arrBlocks) {
$DNs = $users | $sb | %{$_.DistinguishedName}
# Then do something with the DNs
}
Realistically, this will probably involve a hash table instead of an array, so that I know what to do with each set of results.
The syntax error is simple:
# Wrong:
$DNs = $users | $sb | %{$_.DistinguishedName}
# Correct; note the call operator in front of $sb:
$DNs = $users | &$sb | %{$_.DistinguishedName}
This still leaves the matter of your pipeline hitting a dead end. You don't need to get fancy here; just pipe $Input along to the next function:
[ScriptBlock] $sb = {$Input | Where-DirectlyReportsTo "CN=Colonel Foobar,$OU"}
You shouldn't have $sb itself enumerate the input. That's extra overhead, and if you use param(), bumps the script block up to a cmdlet. You really don't need that.
In fact, you could simplify this whole thing down to four lines, or even one really long line:
$properties = #("Manager")
$managers = #(
"CN=Colonel Foobar,$OU"
"CN=Sergeant Foobar,$OU"
"CN=Random Manager,$OU"
)
$users = Get-ADUser -Filter * -SearchBase $OU -Properties $properties
$DNs = $users | ?{ $managers -contains $_.Manager } | %{ $_.DistinguishedName }
I tested that with this code:
$OU = 'OU=test'
$users = #(
#{
Manager = "CN=Colonel Foobar,$OU";
DistinguishedName = "Show me!"
}
#{
Manager = "CN=Anon Y Mous,$OU";
DistinguishedName = "Don't show me!"
}
'rabbit'
42
$null
#{
DistinguishedName = "Don't show me, either!"
}
#{
Manager = "CN=Random Manager,$OU";
DistinguishedName = "Show me, too!"
}
)
$managers = #(
"CN=Colonel Foobar,$OU"
"CN=Sergeant Foobar,$OU"
"CN=Random Manager,$OU"
)
$DNs = $users | ?{ $managers -contains $_.Manager } | %{ $_.DistinguishedName }
$DNs | Write-Host
You could make it a bit more verbose, if you wanted to:
$properties = #("Manager")
$managers = #(
"CN=Colonel Foobar,$OU"
"CN=Sergeant Foobar,$OU"
"CN=Random Manager,$OU"
)
$filter = { $managers -eq $_.Manager }
$selector = { $_.DistinguishedName }
$users = Get-ADUser -Filter * -SearchBase $OU -Properties $properties
$DNs = $users | ?{ &$filter } | %{ &$selector }
You sound like you want to eventually have multiple filters. This is pretty easy, too:
$properties = #("Manager")
$managers = #(
"CN=Colonel Foobar,$OU"
"CN=Sergeant Foobar,$OU"
"CN=Random Manager,$OU"
)
$filters = #(
{ $managers -contains $_.Manager }
{ ![string]::IsNullOrWhiteSpace($_.DistinguishedName) }
)
$selector = { $_.DistinguishedName }
$users = Get-ADUser -Filter * -SearchBase $OU -Properties $properties
$DNs = $users | ?{ $filters.Invoke() -notcontains $false } | %{ &$selector }
There needs to be something at the head of the pipeline in your scriptblock and you have to define your scriptblock as taking pipeline input.g.:
[scriptBlock]$sb = {[CmdletBinding()]param([Parameter(ValueFromPipeline=$true)]$obj) `
process {
foreach ($o in $obj) {
$o | Where-DirectlyReportsTo "CN=Colonel Foobar,$OU"}}}
You also can't throw $sb into the pipeline quite like that, try this:
$users | &$sb | %{$_.DistinguishedName}
In PS 2.0 this somewhat more concise (readable?) syntax works. (doesn't work in PS 3.0, where setting isFilter throws an exception)
$filterSet =
{$_.property -eq "desired value"},
{$_.method() -eq "I like the return value"},
{$_.gettype() -eq [special.type.I.like]},
{arbritraryBoolPSfunction $_}
$filterSet | % {$_.isFilter = $true} # make 'em all filters
ForEach ($filter In $filterSet) {
$DNs = $users | & $filter | %{$_.DistinguishedName}
# Then do something with the DNs
}