How can I remove duplicates before exporting to a csv?
foreach ($group in $ADGroups) {
$ADResult += [PSCustomObject]#{
ADGroup = $group.name
ADuser = $user.name
}
#Remove-ADGroupMember -Identity $Group.DistinguishedName -Members $UserInfo.DistinguishedName -Confirm:$false
$group = $null
}
In the above bit of code the result comes out like
aduser | adgroup
x | a
x | s
x | v
The aduser is the same for each group. How can I post the user once
so it comes out like the following without the duplicate user?
aduser | adgroup
x | a
| s
| v
This will create the output as you requested. You'll lose the ability to sort and filter if you plan to work with the output data in Excel but it's your choice.
#Users Group Membership
$ADGroups = Get-ADGroup -Filter *
$groupMembership = foreach ($group in $ADGroups) {
$members = Get-ADGroupMember -Identity $group | Where-Object { $_.objectClass -eq 'user' }
foreach ($member in $members) {
[PSCustomObject]#{
ADuser = $member.name
ADGroup = $group.name
}
}
}
#Get Unique Users
$usersUnique = $groupMembership | Select-Object -Property ADuser -Unique | Sort-Object -Property ADuser
#Loop each Unique user and add a new NoteProperty ADuser2. This will be populated with ADuser if its the first occurrence, else blank.
$results = foreach ($user in $usersUnique) {
$firstOccurrence = $true
$userMembership = $groupMembership | Where-Object { $_.ADuser -eq $user.ADuser } | Sort-Object -Property ADGroup
foreach ($membership in $userMembership) {
if ($firstOccurrence) {
$membership | Add-Member -MemberType NoteProperty -Name ADuser2 -Value $user.ADuser -PassThru
$firstOccurrence = $false
}
else {
$membership | Add-Member -MemberType NoteProperty -Name ADuser2 -Value "" -PassThru
}
}
}
#Export to CSV
$results | Sort-Object -Property ADuser, ADGroup | Select-Object ADuser2, ADGroup | Export-Csv -Path C:\support\user-group_membership_report.csv -NoTypeInformation
I would go with something like this:
$i = 0
foreach ($group in $ADGroups) {
if($i -eq 0){
$ADResult += [PSCustomObject]#{
ADGroup = $group.name
ADuser = $user.name
}
$i = 1
}
else {
$ADResult += [PSCustomObject]#{
ADGroup = $group.name
ADuser = ""
}
}
}
Related
I need to find AD group members from given groups in csv file as input. the groups contains Users and Groups also. below is sample input data
I wrote the below code. for users I am getting the output (i.e. for the first entry), but for the second one, as they are groups within group, I am not able to fetch the email.
$GroupCollection= Import-csv -Path "C:\Groups.csv"
$Report = #()
Foreach($Group in $GroupCollection){
$MemberGroup=#()
$Group = $Group.'OPE DLs'
if($Group -match '#')
{
$pos = $Group.IndexOf("#")
$leftPart = $Group.Substring(0, $pos)
}
else
{
$leftPart = $Group
}
$MemberGroup = Get-ADGroupMember -identity $leftPart -recursive | Get-ADUser -Properties mail | Select-Object mail
$MemberGroups = ($MemberGroup.mail) -join "`r`n"
if($MemberGroups -ne ""){
$Out = New-Object PSObject
$Out | Add-Member -MemberType noteproperty -Name 'Contract Details' -Value $Group.'Customer subset'
$Out | Add-Member -MemberType noteproperty -Name 'Group Name' -Value $leftPart
$Out | Add-Member -MemberType noteproperty -Name 'Member Groups' -Value $MemberGroups
$Report += $Out
}
}
$Report | Sort-Object Name | FT -AutoSize
$Report | Sort-Object Name | Export-Csv -Path ‘C:\Group-MemberGroups-Report.csv’ -NoTypeInformation
Please let me know how to get the details. Is the approach is fine or there is any other way to do this.
expected output
As commented, if the object returned by Get-ADGroupMember is a group, you won't get results by piping it through to Get-ADUser, because... it is a group, not a user.
You need to loop over the results and depending on what type the object is (group, user or computer) you use either Get-ADUser or Get-ADGroup (not interested in computer objects).
Try
$Report = foreach ($Group in $GroupCollection){
$groupName = ($Group.'OPE DLs' -split '#')[0]
$groupMembers = Get-ADGroupMember -Identity $groupName -Recursive | ForEach-Object {
$adObject = $_
switch ($adObject.objectClass) {
'group' { ($adObject | Get-ADGroup -Properties mail).mail }
'user' { ($adObject | Get-ADUser -Properties EmailAddress).EmailAddress }
}
}
if (#($groupMembers).Count) {
[PsCustomObject]#{
'Group Name' = $groupName
'Contract Details' = $Group.'Customer subset'
'Member Groups' = $groupMembers -join [environment]::NewLine
}
}
}
$Report = $Report | Sort-Object 'Group Name'
$Report | Format-Table -AutoSize
$Report | Export-Csv -Path 'C:\Group-MemberGroups-Report.csv' -NoTypeInformation
Notes:
adding to an array with += is extremely wasteful because the entire array needs to be rebuilt in memory on each iteration
To take the group name as the part left of the # character, I simply use the -split operator and take the first element ([0])
To output an object, I'm using a [PsCustomObject]#{..} construct rather than the old (pre PowerShell 3.0) New-Object PSObject method
I am trying to read group membership of computers from a particular OU and write to a CSV file. The input criteria for the group membership is like if the computer is part of say "admin" and i need the csv file in the below format
---------------------------------------------------------
Computer Group1 Group2 Group3 Group4
ABCD admin1 admin2 admin3 admin4
EFGH admin2 admin3
XYZZ admin1 admin4
--------------------------------------------------------------
but end up like this.
---------------------------------------------------------
Computer Group1 Group2 Group3 Group4
ABCD admin1 admin2 admin3 admin4
EFGH admin2 admin3
XYZZ admin1 admin4
--------------------------------------------------------------
The code is like this
$All_computer = Get-ADComputer -Filter * -Property * -SearchBase $ou -Server $server | Select-object Name,DNSHostName,Description,memberof
$computerExport = $All_computer |
ForEach-Object {
$ComputerName = $_.Name
$Description = $_.description
$DNSHostname = $_.DNSHostName
$memberof = $_.memberof
$groups = $memberof.where{$_ -like "*$unput_group*"}
$Group_member = [pscustomobject]#{
Workstation = $ComputerName
Comp_Description = $Description
DNS_Hostname = $DNSHostname
}
$i = 0
foreach($group in $Groups)
{
$i++
$member = $group.split('=')[1].split(',')[0]
$Group_member | add-member -MemberType NoteProperty -Name "Group$i" -Value $member
}
$Group_member
}
}
$computerExport | export-csv .\Membership_status.csv -NoTypeInformation
What do i need to do to get the group membership to populate to proper column.
Well of course it does. I mean it's doing what you're asking it to do.
You're only adding the number of properties to the custom object that is found from the where object query. I am really struggling to understand what you're trying to do this for but I THINK what you really want is for each object to have all the possible properties but to have null values for those that don't match that particular computer or better yet to use a boolean.
So... Maybe like this:
[string]$GroupSearch = "admin"
$All_computer = Get-ADComputer -Filter * -Property DNSHostName, Description, memberof -SearchBase $ou -Server $server | Select-Object Name, DNSHostName, Description, memberof
$MatchedGroups = $All_Computer.MemberOf | Sort -Unique | ?{$_.Name -match $GroupSearch}
$computerExport = ForEach ($C in $All_computer) {
$Group_member = [pscustomobject]#{
Workstation = $($C.Name)
Comp_Description = $($C.Description)
DNS_Hostname = $($C.DNSHostName)
}
ForEach ($group in $MatchedGroups) {
[string]$GrpName = $($group.split('=')[1].split(',')[0])
If ($C.MemberOf -contains $group) {
$Group_member | Add-Member -MemberType NoteProperty -Name $GrpName -Value $true
} else {
$Group_member | Add-Member -MemberType NoteProperty -Name $GrpName -Value $false
}
}
$Group_member
}
$computerExport | Export-Csv .\Membership_status.csv -NoTypeInformation
If I understand the question, you need to get all computers from a certain OU that are member of group(s) with a similar partial name.
To do that, I would suggest creating an array of computer objects at first with a temporary extra property called 'Groups' in which the group names that match the partial name are stored.
Later, we'll put these in the correct order as new properties called 'Group1', 'Group2' etc.
# the partial groupname to search for
$unput_group = 'admin'
# Get-ADComputer by default already returns these properties:
# DistinguishedName, DNSHostName, Enabled, Name, ObjectClass, ObjectGUID, SamAccountName, SID, UserPrincipalName
# get an array of computer objects that are member of 'admin*' group(s) with the desired properties
# one extra temporary property is added which contains an array of 'admin*' group names
$All_computer = Get-ADComputer -Filter * -Property Description, MemberOf -SearchBase $ou -Server $server |
Where-Object { $_.MemberOf -match $unput_group} |
Select-Object #{Name = 'Workstation'; Expression = {$_.Name}},
#{Name = 'Comp_Description'; Expression = {$_.Description}},
#{Name = 'DNS_Hostname'; Expression = {$_.DNSHostName}},
#{Name = 'Groups'; Expression = { #($_.MemberOf |
Where-Object { $_ -match "CN=($unput_group[^,]+)" } |
ForEach-Object { $matches[1] }) }}
# get all the group names from the computers we have collected and sort unique
$All_Groups = $All_computer.Groups | Sort-Object -Unique
# build a lookup hashtable with property names ('Group1', 'Group2' etc)
$hash = [ordered]#{}
for ($i = 1; $i -le $All_Groups.Count; $i++) {
$hash["Group$i"] = $All_Groups[$i - 1]
}
# now loop through the collection and add the group properties in order
$result = foreach ($computer in $All_computer) {
foreach ($propertyName in $hash.Keys) {
$group = if ($computer.Groups -contains $hash[$propertyName]) { $hash[$propertyName] }
$computer | Add-Member -MemberType NoteProperty -Name $propertyName -Value $group
}
# output the updated object and remove the temporary 'Groups' property
$computer | Select-Object * -ExcludeProperty Groups
}
# finally, save the results to disk
$result | Export-Csv -Path .\Membership_status.csv -NoTypeInformation
I am trying to store the value of a function value with add-member on it on my $GroupStorage variable but it does not store on the variable? Anyone knows how to do it? My goal is to add it force to my $storageHolder
$NestedGroupUsers = Get-ADUsers_cachedV2 -GroupName $nestedmember.distinguishedName
$GroupStorage = $NestedGroupUsers | Add-Member -NotePropertyName MainParentGroup -NotePropertyValue $ADgroupname.name -Force
$resultHOlder += $GroupStorage
==============================================================================================
This is my whole code. This code is Getting all the users from nested Group.
function Get-CachedADUSER($AduserCached) {
# $AduserCached = $resultHOlder
$cachedResults = Get-Variable -Scope Global -Name $AduserCached -ErrorAction SilentlyContinue
if ($null -ne $cachedResults) {
$existing = write-host "Check: i found existing"
return $cachedResults.value , $existing
}
else {
$searching = write-host "Check: Searching"
$results = get-aduser $AduserCached -properties * #enabled, displayname, EmployeeNumber, LastLogonDate, PasswordLastSet
Set-CachedADUSER -setAduserCached $AduserCached -value $results
return $results.value , $searching
}
}
Function Set-CachedADUSER($setAduserCached, $value) {
Set-Variable -Scope Global -Name $setAduserCached -Value $value
return $value
}
function Get-ADUsers_cachedV2 {
param (
[Parameter(ValuefromPipeline = $true, mandatory = $true)][String] $GroupName
)
[int]$circular = $null
# result holder
$resultHolder = #()
$table = $null
$nestedmembers = $null
$adgroupname = $null
$GroupStorage = #()
$NestedGroupUsers = #()
# get members of the group and member of
$ADGroupname = get-adgroup $groupname -properties memberof, members
# $ADGroupname = Get-CachedGroupMembership -GroupName $groupName -properties memberof, members
# list all members as list (no headers) and save to var
$memberof = $adgroupname | select -expand memberof
if ($adgroupname) {
if ($circular) {
$nestedMembers = Get-ADGroupMember -Identity $GroupName -recursive
$circular = $null
}
else {
$nestedMembers = Get-ADGroupMember -Identity $GroupName | sort objectclass -Descending
# if get adgroupmember returns nothing, it uses the members for ordinary getADGroup
if (!($nestedmembers)) {
$unknown = $ADGroupname | select -expand members
if ($unknown) {
$nestedmembers = #()
foreach ($member in $unknown) {
$nestedmembers += get-adobject $member #-ErrorAction SilentlyContinue
}
}
}
}
# loops through each member
ForEach ($nestedmember in $nestedmembers) {
# creates the properties into a custom object.
$Props = #{
Type = $nestedmember.objectclass;
Name = $nestedmember.name;
DisplayName = "";
ParentGroup = $ADgroupname.name;
Enabled = "";
EmployeeNumber = "";
LastLogonDate = "";
PasswordLastSet = "";
}
# if member object is a user
if ($nestedmember.objectclass -eq "user") {
# saves all the properties in the table.
$nestedADMember = Get-CachedADUSER -AduserCached $nestedmember.Name -properties * #enabled, displayname, EmployeeNumber, LastLogonDate, PasswordLastSet
$table = new-object psobject -property $props
$table.enabled = $nestedadmember.enabled
$table.name = $nestedadmember.samaccountname
$table.displayname = $nestedadmember.displayname
$table.EmployeeNumber = $nestedadmember.EmployeeNumber
$table.LastLogonDate = $nestedadmember.LastLogonDate
$table.PasswordLastSet = $nestedadmember.passwordLastSet
#save all in 1 storage
$resultHOlder += $table #| select type, name, displayname, parentgroup, enabled, EmployeeNumber, LastLogonDate, PasswordLastSet
}
# if member object is group
elseif ($nestedmember.objectclass -eq "group") {
$table = new-object psobject -Property $props
# if circular, meaning the groups member of list contains one of its members.
# e.g. if group 2 is a member of group 1 and group 1 is a member of grou 2
if ($memberof -contains $nestedmember.distinguishedname) {
$table.comment = "Circular membership"
$circular = 1
}
$NestedGroupUsers = Get-ADUsers_cachedV2 -GroupName $nestedmember.distinguishedName
$GroupStorage = $NestedGroupUsers | Add-Member -NotePropertyName MainParentGroup -NotePropertyValue $ADgroupname.name -Force
$resultHOlder += $NestedGroupUsers
}
else {
if ($nestedmember) {
$table = new-object psobject -property $props
$resultHolder += $table #| select type, name, displayname, parentgroup, enabled, EmployeeNumber, LastLogonDate, PasswordLastSet
}
}
}
}
return $resultHOlder
}
function Get-NestedGroupUsers_cachedV2 {
param (
[Parameter(Mandatory = $true)][String]$FileName,
[Parameter(Mandatory = $true)][String]$searchFilePath
)
$storageHolder = #()
$groupList = Get-Content $searchFilePath
$groupList | ForEach-Object {
$allusers = Get-ADUsers_cachedV2 -GroupName $_
$storageHolder += $allusers
}
$storageHolder | Export-Csv -Path "C:\Users\***\***\$FileName.csv" -NoTypeInformation #-Force
}
Likely your $GroupStorage variable is coming out empty?
Add -PassThru to the Add-Member command which will send the modified object down the pipe to $GroupStorage. Add-Member does not return any objects without that switch.
$GroupStorage = $NestedGroupUsers | Add-Member -NotePropertyName MainParentGroup -NotePropertyValue $ADgroupname.name -PassThru
I have a script that gives me all members of a group with certain desired information. I want this same format but for all groups that a specified username belongs to. I want information about each group, such as group type (ie security, distribution list). How would I do this? I want a different row for each group, with information about each group in the columns.
Add-PSSnapin Quest.ActiveRoles.ADManagement
$myCol = #()
ForEach ($Group in (Get-QADGroup "CN=research,OU=Security,OU=Groups,DC=xxx,DC=com" -GroupType Security))
{
ForEach ($Member in (Get-QADGroupMember $Group -SizeLimit 0))
{
$myObj = "" | Select Group, Type, Member, Email, Username, Department
$myObj.Group = $Group.Name
$myObj.Type = $Group.groupType
$myObj.Member = $Member.Name
$myObj.Email = $Member.Email
$myObj.Department = $Member.Department
$myObj.Username = $Member.sAMAccountName
$myCol += $myObj
}
}
$myCol | Export-Csv -Path "C:\Users\sdevito\Desktop\test.csv" -NoTypeInformation
or. there is this code that i found that does something similar, but each group is in the same row, different column. i cannot figure out how to edit this code to make each group on a new row.
$alist = "Name`tAccountName`tDescription`tEmailAddress`tLastLogonDate`tManager`tTitle`tDepartment`tCompany`twhenCreated`tAcctEnabled`tGroups`n"
$userlist = Get-ADUser sdevito -Properties * | Select-Object -Property Name,SamAccountName,Description,EmailAddress,LastLogonDate,Manager,Title,Department,Company,whenCreated,Enabled,MemberOf | Sort-Object -Property Name
$userlist | ForEach-Object {
$grps = $_.MemberOf | Get-ADGroup | ForEach-Object {$_.Name} | Sort-Object
$arec = $_.Name,$_.SamAccountName,$_.Description,$_.EmailAddress,$_LastLogonDate,$_.Manager,$_.Title,$_.Department,$_.Company,$_.whenCreated,$_.Enabled
$aline = ($arec -join "`t") + "`t" + ($grps -join "`t") + "`n"
$alist += $aline
}
$alist | Out-File C:\Users\sdevito\Desktop\testt.csv
How about something like:
#Requires -Version 3.0
Add-PSSnapin Quest.ActiveRoles.ADManagement
function Get-UsersGroups {
[cmdletbinding()]
param (
[Parameter(Position=0,Mandatory)][string]$Identity,
[Parameter(Position=1)][ValidateSet('all','nested','normal')][string]$MemberType
)
$user = Get-QADUser -Identity $Identity
switch ( $MemberType ) {
'all' { $groups = $user.AllMemberOf }
'nested' { $groups = $user.NestedMemberOf }
default { $groups = $user.MemberOf }
}
foreach ( $group in $groups ) {
$groupinfo = Get-QADGroup -Identity $group
$props = [ordered]#{
Group = $groupinfo.Name
Type = $groupinfo.GroupType
Member = $user.Name
Email = $user.Email
Department = $user.Department
Username = $user.sAMAccountName
}
$obj = New-Object -TypeName PSObject -Property $props
$obj
}
}
Get-UsersGroups -Identity bob | Export-Csv -Path "C:\Users\sdevito\Desktop\test.csv" -NoTypeInformation
i wrote a script that gonna disabled old users...
and i need to do an exclude list to it...
the exclude list should be .csv, with 3 columns "Name","SamaccountName","Reason"...
i'm kind of stuck with the exclude list filtering...
i tried to do -notmatch and -notcontains and nothing worked for me...
i even try to do a foreach with if but the same...
Function Get-ADLockOldUsers {
param ()
begin{
[datetime]$myDate = '01/01/1601 02:00:00'
$colObj = #()
$AllUsers = (Get-ADUser -Filter * -Properties lastLogonTimestamp | ? {$_.Enabled} | Select-Object Name,SamAccountName,#{N="LastLogon";E={[datetime]::FromFileTime($_.lastLogonTimestamp)}})
$AllUsers = $AllUsers | ? {(Get-Date).AddDays(-30) -gt $_.LastLogon -and -not ($_.LastLogon -eq $myDate)}
}
process {
$AllUsers | % {
$obj = New-Object psobject
$obj | Add-Member noteproperty 'Name' $_.Name -Force
$obj | Add-Member noteproperty 'SamAccountName' $_.SamAccountName -Force
$obj | Add-Member noteproperty 'LastLogon' $_.LastLogon -Force
$obj | Add-Member noteproperty 'NeedDisabled' $true -Force
$colObj += $obj
}
}
end { return $colObj }
}
Function Set-ADLockUser {
param()
begin{
if (Test-Path '.\excludeusers.csv') {
$excludeUsers = Import-Csv '.\excludeusers.csv'
$DUser = #()
$colUsers = Get-ADLockOldUsers
$colUsers | ? {$_.SamAccountName -notcontains $excludeUsers} | % {Set-ADUser -Identity $_.SamAccountName -Enabled $false -WhatIf }
}
else { Write-Output "Error! excludeusers.csv cannot be found, stop script"; break }
}
process {
}
end{}
}
Set-ADLockUser
A string value can never contain an array, so
$_.SamAccountName -notcontains $excludeUsers
will always evaluate to $true. You need to reverse the check and make the reference an array of strings (the CSV import produces an array of custom objects). Selecting only the field SamaccountName from the imported CSV and switching the arguments should do what you want:
$excludeUsers = Import-Csv '.\excludeusers.csv' | % { $_.SamaccountName }
...
$colUsers | ? { $excludeUsers -notcontains $_.SamAccountName } | ...
As a side note, you could simplify the the code for finding obsolete accounts like this:
$myDate = Get-Date '01/01/1601 02:00:00'
$limit = (Get-Date).AddDays(-30)
$colObj = Get-ADUser -Filter * -Properties * `
| ? { $_.Enabled } `
| select Name,SamAccountName,#{n="NeedDisabled";e={$true}},
#{n="LastLogon";e={[datetime]::FromFileTime($_.lastLogonTimestamp)}} `
| ? { $limit -gt $_.LastLogon -and $_.LastLogon -ne $myDate }
This is the final solution...
<#
.Synopsis
Get All Users in the Domain and check the last logon Date
.Example
Set-ADLockUser -ReportOnly:$true
Get all users that didn't logon for a 30 days and write a report to the current directory
.Example
Set-ADLockUser -ReportOnly:$false
Get all users that didn't logon for a 30 days and disabled them
.Description
Get All Users in the Domain and check the last logon Date, and exclude some users from a list .\excludeusers.csv
.Parameter ReportOnly
Specifies if the script is in reportmode or active mode if ReportOnly=$false all the relevant users will lock
.Outputs
PSObject[]
.Notes
Name: Set-ADLockUser
Author: Ohad Halali
Date: 14.07.2013
.Link
#>
Function Get-ADLockOldUsers {
param ()
begin{
[datetime]$myDate = '01/01/1601 02:00:00'
$colObj = #()
$AllUsers = (Get-ADUser -Filter * -Properties lastLogonTimestamp | ? {$_.Enabled} | `
Select Name,SamAccountName,#{N="LastLogon";E={[datetime]::FromFileTime($_.lastLogonTimestamp)}}) | `
? {(Get-Date).AddDays(-30) -gt $_.LastLogon -and -not ($_.LastLogon -eq $myDate)}
}
process {
$AllUsers | % {
$obj = New-Object psobject
$obj | Add-Member noteproperty 'Name' $_.Name -Force
$obj | Add-Member noteproperty 'SamAccountName' $_.SamAccountName -Force
$obj | Add-Member noteproperty 'LastLogon' $_.LastLogon -Force
$obj | Add-Member noteproperty 'NeedDisabled' $true -Force
$colObj += $obj
}
}
end { return $colObj }
}
Function Set-ADLockUser {
param([bool]$ReportOnly=$true)
begin{
if (Test-Path '.\excludeusers.csv') {
$excludeUsers = Import-Csv '.\excludeusers.csv'
$colUsers = Get-ADLockOldUsers | ? {$excludeUsers.SamAccountName -notcontains $_.SamAccountName}
if ($ReportOnly) {
$colUsers | Export-Csv '.\Report.csv' -NoClobber -NoTypeInformation -Encoding ASCII -Force
}
else {
$colUsers.SamAccountName | Set-ADUser -SamAccountName $_ -Enabled:$False -Replace #{info="Disabled after no login for 30 days (Script)"} -WhatIf
}
}
else { Write-Output "Error! excludeusers.csv cannot be found, stop script"; break }
}
process {}
end{}
}
Set-ADLockUser