I am learning powershell, and a looking to query couple of AD groups and determine whether a user is member of AD group.
Part 1 : Query a AD group which has 10 nested AD groups
Part 2: Query user's AD group and pull a list
Part 3: Not posted, but to compare output of Part 1 and Part 2
Searched online and found some tits and bits. I am aware of Active Directory module, but avoid using it, since this script would be executed by user who is non-technical and avoid installing RSAT just for that.
I have Powershell version 2 and Windows 7
Part 1
group1 is the AD group which has 10 nested AD groups.
Write-Host "Fetching information from groups.Please wait.."
Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$ct = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$group=[System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity ($ct,'group1')
$group1 = $group.GetMembers | Where {$_.StructuralObjectClass -eq "group"} | Select SamAccountName
$group1 = $group1 -replace("=", " ") -replace("{", " ") -replace("#", " ") -replace ("}", " ") -replace("SamAccountname", " ") -replace '\s+', ' '
$ADGroups = foreach ($l in group1) {$l.trim()}
To ensure the AD group information is fetched by program or exit the script
if (($ADgroups | out-string) -like $null) {
Write-Host "Unable to fetch AD groups information" -foreground "red"
Start-sleep 10
break
}
Part 2
Temp location where files can be written
$location = "C:\AD"
$ct = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$user = [System.DirectoryServices.AccountManagement.UserPrincipal]::FindByIdentity($ct, $username)
If ($user -like $null) {
Write-host "User does not exist in AD" -foreground "magenta"
start-sleep 10
break
}
Write-Host "Please wait...Looking user group membership..."
$usergroups = $user.GetGroups()
Removing file if exists.
Remove-Item $location\useradgroups.txt
$usergroups | select SamAccountName | Out-File $location\useradgroups.txt -append
$testr = gc $location\useradgroups.txt
if (($testr | out-string) -like $null) {
Write-Host "Unable to fetch user AD groups information" -foreground "red"
Start-sleep 10
Remove-Item $location\useradgroups.txt
break
}
$useradgroups = foreach ($l in $testr ) {$l.trim()}
$useradgroups | Select-String -Pattern "\w" | out-file $location\useradgroups.txt
$useradgroups = gc $location\useradgroups.txt
Question:
Unless I trim the output, cannot compare it. so had to write the script as shown above:
Avoid writting the output to text file
Avoid -replace("=", " ") -replace("{", " ") in Part 1
Simplify the code.
Any suggestion from Powershell gurus would be welcomed. It would help me in learning process
So what you're trying to do is recursively get ADGroupMembers? Sort of like this or this? There is always more than one way to do things. You can use the [ASDI] type accelerator.
$groups = [adsi]'LDAP://CN=GroupName,OU=Groups,OU=Place,DC=Tomorrow,DC=Today'
$group | Get-Member
foreach($group in $groups){
$members = $group.member
foreach($member in $members){
#Is this a group or a user?
#If group Get the Group members
#If a user say the user is part of the group
}
}
Are you trying to format the distinguishedName of a user?
$user = "CN=Huck Finn,OU=Users,OU=Today,OU=Tomorrow,DC=Yesterday,DC=com"
$splits = ($user -split ',')
[PSCustomObject]#{Username=$splits[0].substring(3);OUPath=($splits[1..($splits.Count-1)] | % {$_.substring(3)}) -join '\'}
Hope I helped.
Untested, but can't you use something like this for part 1?
Import-Module ActiveDirectory
Get-ADGroupMember "group1" | select sAMAccountName
Related
having a heck of a time with this. Also, wanted to preface this with I'm not the best at PowerShell as I'm just starting out. I have a CSV file that I'm trying to read the first column which happens to be 'AssetName'. These are AD joined computers.
#Get Computer
$Computers = Import-csv -Delimiter ";" -Path 'C:\Path\to\File.csv' | Select-Object AssetName
$Group = "Sec Group Name"
# Set the ErrorActionPreference to SilentlyContinue, because the -ErrorAction
# option doesn't work with Get-ADComputer or Get-ADGroup.
$ErrorActionPreference = "SilentlyContinue"
# Get the computer and group from AD to make sure they are valid.
$ComputerObject = Get-ADComputer $Computer
$GroupObject = Get-ADGroup $Group
Foreach ($Computer in $Computers){
if ($GroupObject) {
# If both the computer and the group exist, remove the computer from
# the group.
Remove-ADGroupMember $Group `
-Members (Get-ADComputer $Computer).DistinguishedName -Confirm:$False
Write-Host " "
Write-Host "The computer, ""$Computer"", has been removed from the group, ""$Group""." `
-ForegroundColor Yellow
Write-Host " "
}
else {
Write-Host " "
Write-Host "I could not find the group, ""$Group"", in Active Directory." `
-ForegroundColor Red
Write-Host " "
}
}
else {
Write-Host " "
Write-Host "I could not find the computer, $Computer, in Active Directory." `
-ForegroundColor Red
Write-Host " "
}
Upon doing so, I want to remove that Asset from a specific security group. Whenever I run my script, I get this error. I don't know why it's reading it with the "#{AssetName=CompName}".
The computer, "#{AssetName=CompName}", has been removed from the group, "Sec Group Name".
Any help would be much appreciated.
With your first line you are saving a list of PSObjects to $Computers.
$Computers = Import-csv -Delimiter ";" -Path 'C:\Path\to\File.csv' | Select-Object AssetName
These Objects look like this #{AssetName=Computername}
When you iterate these objects you need to specify that you only want the value of the AssetName parameter
Get-ADComputer $Computer.AssetName
Another (in my opinion better) way would be to stop using Select-Object (which storing the returned objects in $computers) and only storing a list of AssetNames in $computers like this:
$Computers = (Import-csv -Delimiter ";" -Path 'C:\Path\to\File.csv').AssetName
EDIT:
You can also use -ExpandProperty with your Select-Object:
$Computers = Import-csv -Delimiter ";" -Path 'C:\Path\to\File.csv' | Select-Object -ExpandProperty AssetName
My PowerShell script to retrieve the last sign in date of Azure AD users doesn't work when using variables within the filter for UPN. This is a well discussed topic when it comes to AzureAD PowerShell cmdlets, with the most common answer being to wrap the variable in additional quotes. However, none of the examples are for this exact cmdlet. In any case, this does not work for me. To break things down I am now using the approach of building my filter as a variable ($filter).
If I use
$filter = "UserPrincipalName eq 'user123#domain.com'"
the Get-AzureADAuditSignInLogs cmdlet passes me a result. If I use $filter = "UserPrincipalName eq '$($row.UserPrincipalName)'", Get-AzureADAuditSignInLogs gives a $null result.
In BOTH cases, Write-Host $filter outputs the exact same string so there is no problem with retrieving the entry from the CSV file. There is something "special" about the way MS's Azure AD cmdlets implement filters.
Has anyone come across this before? Is there a way I can force my variable to be as if I "hand typed" it?
Connect-AzureAD
$Result = $null
$output = $null
$filter = $null
$csv = Import-csv -Path c:\temp\UPNs.csv
ForEach($row in $csv)
{
$filter = "UserPrincipalName eq '$($row.UserPrincipalName)'"
#$filter = "UserPrincipalName eq 'user123#domain.com'"
$filter = $filter.ToString()
Write-Host $filter
$Result = Get-AzureADAuditSignInLogs -Filter $filter -Top 1 | Select-Object CreatedDateTime, UserPrincipalName
$output = ($Result.UserPrincipalName.ToString() + "," + $Result.CreatedDateTime.ToString())
$output | Out-File C:\temp\HighwaysSignInInfo.txt -Append
Write-host $output
}
CSV file:
UserPrincipalName
user123#domain.com
user1234#domain.com
user12345#domain.com
user12356#domain.com
user1234567#domain.com
This filter works just fine:
Get-AzureADAuditSignInLogs -Filter "startsWith(userPrincipalName,'name#Contoso.com')"
Although some websites say you can use Get-AzureADAuditSignInLogs -Filter "UserPrincipalName eq 'name#Contoso.com'" it doesn't seem to work for me.
The full script (with handling for bandwidth throttling)
#GetAADUserSignIns v1.0
#This script will obtain the last sign in date for each supplied UPN via a source csv file. The biggest issue with such queries is bandwidth throttling. This is handled within the script using a simple
#Try, Catch combined with a Function that can be used to make nested calls.
#IMPORTANT: Review the below 3 options to generate a source file (Option 3 is recommended)
#1. This attempts dump the entire audit log to text file. However you are likely to be throttled before the export completes so don't bother
#Get-AzureADAuditSignInLogs -Filter -All:$true | Out-File C:\Temp\AuditLogsAll.txt
#2. To get a list of all AAD accounts exported to csv (This will capture guest accounts too, the list will be huge, therefore not recommended to run Get-AzureADAuditSignInLogs with)
#Get-AzureADUser -All $true | Export-Csv C:\Temp\AADUsers.csv -NoTypeInformation
#3. Obtain a list of on-prem AD accounts that have a valid UPN
#Get-ADUser -Filter {userPrincipalName -like "*#customdomain.com"} | Select-Object userPrincipalName | Export-Csv C:\Temp\UPNs.csv -NoTypeInformation
Connect-AzureAD
Function GetLastSignInDate {
param (
[string]$upn
)
$filter = "startsWith(userPrincipalName,'" + $upn + "')"
Try
{
$Result = Get-AzureADAuditSignInLogs -Filter $filter -Top 1 | Select-Object CreatedDateTime, UserPrincipalName
$output = ($upn + "," + $Result.CreatedDateTime.ToString())
$output | Out-File C:\temp\SignInInfo.txt -Append
Write-host $output -ForegroundColor Green
}
Catch
{
$message = $_
if ($message -like "*Too Many Requests*")
{
Write-host "Sleeping for 10 seconds due to throttling limitations..." -ForegroundColor Yellow
sleep 10
#Nested function call to retry the entry that was throttled
GetLastSignInDate $upn
}
elseif ($message -like "*This request is throttled*")
{
Write-host "Sleeping for 10 seconds due to throttling limitations..." -ForegroundColor Yellow
sleep 10
#Nested function call to retry the entry that was throttled
GetLastSignInDate $upn
}
elseif ($message -like "*null-valued*")
{
$output = ($upn + ", Not Found")
$output | Out-File C:\temp\SignInInfo.txt -Append
Write-host $output -ForegroundColor Gray
}
elseif ($message -like "*Invalid filter clause*")
{
$output = ($upn + ", Invalid character")
$output | Out-File C:\temp\SignInInfo.txt -Append
Write-host $output -ForegroundColor Gray
}
elseif ($message -like "*Error reading JToken*")
{
$output = ($upn + ", Script stopped due to authentication token timeout")
Write-host $output -ForegroundColor White -BackgroundColor Red
exit
}
else
{
$output = ($upn + ",Error - " + $message.ToString().SubString(0,15))
$output | Out-File C:\temp\SignInInfo.txt -Append
Write-host $output -ForegroundColor Red
}
}
}
$csv = $null
$Result = ""
$output = ""
$filter = ""
$i = $null
$csv = Import-csv -Path C:\temp\upns.csv
ForEach($row in $csv)
{
$upn = $row.UserPrincipalName.ToLower().Replace('`r`n','').Replace('`r','').Replace('`n','').Trim()
GetLastSignInDate $upn
}
I am creating powershell script to create skype for business user account.
I am trying to find the avaialble number from CSV file which looks for Status, Business Unit and location to find correct LineUri to be used.
I am using following code which always use the first SPARE number and doesn't validate the location and business unit to find the line uri.
$path = Split-Path -parent $MyInvocation.MyCommand.Definition
$newpath = $path + "\PhoneData.csv"
$LineUri = #()
$Status = #()
$BusinessUnit = #()
$Location = #()
$csv = Import-Csv $newpath -Delimiter ","
$csv |`
ForEach-Object {
$LineUri += $_.LineUri
$Status += $_.Status
$BusinessUnit +=$_.BusinessUnit
$Location +=$_.Location
}
$currentStatus = "Spare"
$userBusinessUnit = "Support"
$userLocation = "WA"
if ($Status -eq $currentStatus -And $BusinessUnit -eq $userBusinessUnit -And $Location -eq $userLocation )
{
$Where = [array]::IndexOf($Status, $currentStatus)
$AvailableURI = $LineUri[$Where]
#Write-Host "Next Uri Available: " $LineUri[$Where]
$changeWhere = [array]::IndexOf($LineUri, $AvailableURI)
#Write-Host "Next Uri Available: " $Status[$changeWhere]
Try
{
Enable-CsUser -identity sudip.sapkota -RegistrarPool "s4b-fe01.tapes.com" -SipAddressType SamAccountName -sipdomain "tapes.com"
Set-CsUser -Identity sudip.sapkota -EnterpriseVoiceEnabled $true -LineURI $AvailableURI
Grant-CsDialPlan -Identity sudip.sapkota -PolicyName 'DialPlan'
Grant-CsVoicePolicy -Identity sudip.sapkota -PolicyName 'My VoicePolicy'
Write-Host "[INFO]`t Lync Enterprise account for user sudip.sapkota has been created with sip : $AvailableURI" -ForegroundColor "Green"
"[INFO]`t Lync Enterprise account for user sudip.sapkota has been created with sip : $AvailableURI" | Out-File $log -append
$i = $changeWhere
$csv[$i].Status = 'Used'
$csv | Export-Csv -Path $newpath -NoTypeInformation
Write-Host "[INFO]`t PhoneData CSV has been updated" -ForegroundColor "Green"
"[INFO]`t PhoneData CSV has been updated" | Out-File $log -append
}
catch
{
Write-Host "[ERROR]`t Oops, something went wrong: $($_.Exception.Message)`r`n" -ForegroundColor "Red"
"[WARNING]`t Oops, something went wrong: $($_.Exception.Message)" | Out-File $log -append
}
}
else
{
Enable-CsUser -identity sudip.sapkota -RegistrarPool "s4b-fe01.tapes.net" -SipAddressType SamAccountName -sipdomain "tapes.net"
Write-Host "[INFO]`t No Spare LineURI, user has been created as PC-to-PC only" -ForegroundColor "Yellow"
"[INFO]`t No Spare LineURI, user has been created as PC-to-PC only" | Out-File $log -append
}
My CSV looks like this.
Name LineUri Status BusinessUnit Location
Test 1 tel:+61396176100;ext=6100 Spare Sales VIC
Test 2 tel:+61396176101;ext=6101 Spare Sales VIC
Test 2 tel:+61396176102;ext=6102 Used Sales NSW
Test 2 tel:+61396176103;ext=6103 Spare Support WA
Test 2 tel:+61396176104;ext=6104 Spare Support WA
Test 2 tel:+61396176105;ext=6105 Used Action VIC
Test 2 tel:+61396176106;ext=6106 Spare Suppot VIC
Test 2 tel:+61396176107;ext=6107 Spare Action VIC
Can someone help to find my mistake?
As I am manually feeding the input, the test input are
$currentStatus = "Spare"
$userBusinessUnit = "Support"
$userLocation = "WA"
So I need to find the LineURI which is SPARE, whose location is WA and whose BusinessUnit is Support.
I should get tel:+61396176103;ext=6103 as LineURI
I reworked your logic a bit. I felt that you could do all of this inside the foreach loop. For clarity, this does gather the data in the loop and then leaves you with an array to work with. You could just nest your actions inside the loop directly and break out or you could keep it like this and just execute your work against the array item after.
# Array of spare numbers that meet your criteria
$firstAvailableSpare
# criteria
$currentStatus = "Spare"
$userBusinessUnit = "Support"
$userLocation = "WA"
$csv |`
ForEach-Object {
$LineUri += $_.LineUri
$Status += $_.Status
$BusinessUnit +=$_.BusinessUnit
$Location +=$_.Location
$currentStatus = "Spare"
$userBusinessUnit = "Support"
$userLocation = "WA"
if ( ($_.Status -eq $currentStatus) -And ($_.BusinessUnit -eq $userBusinessUnit) -And ($_.Location -eq $userLocation) ) {
$firstAvailableSpare = $_ # Assign the first
$_.Status = "Used"
continue # break the loop so we don't get additional hits
}
}
$csv | Export-Csv -Path $newpath -NoTypeInformation
$firstAvailableSpare # <-- your first available spare
$firstAvailableSpare.LineUri # <-- the URI you want
edit 1: Updated for CSV export requirement
edit 2: Changed break to continue. WARNING: For some reason this breaks the ISE's debugging functionality. Stepping will cease and future break points will be ignored. I don't know why this is but it is out of scope for your goal.
My script is hitting the active directory to pull AD groups and their subgroups.
I will be adding in Get-ADGroupMember Where-Object { $_.ObjectClass -eq "user" }) to the script but right now the script runs endlessly.
I checked the CSV file and data is being pushed into it, I just do not know why this is running endlessly.
I am still new to Powershell and may have overlooked something.
Do I need to add some sort of catch or exit? I read that exit closes the loop before it can execute completely.
My Script:
Clear-Host
#Import-Module ActiveDirectory
Write-Host -ForegroundColor Green "Starting"
$logfile = "C:\temp\logfile.csv"
"Domains,Groups,Nested" | Add-Content $logfile
$grp_list = Import-Csv "C:\temp\Copy_lp.csv"
$domains = #()
$dom = Import-Csv "C:\temp\Copy_lp.csv" |
Select-Object -Unique -ExpandProperty "Domain Group"
foreach ($domain in $dom) {
$domains += $domain.Split('\')[0]
}
$domains = $domains | Select-Object -Unique
foreach ($domain in $domains) {
Write-Host "Domain:" $domain
Get-ADGroup -Filter * -Server $domain |
ForEach-Object {
$group = $_.Name
$grps = (Get-ADGroupMember $group -Server $domain |
Where-Object { $_.ObjectClass -eq "Group" })
$grps | ForEach {
Write-Host "Group: " $_.Name
"$domain,$($_.Name),$group" | Add-Content $logfile
}
}
}
Write-Host -ForegroundColor Green "Done"
Update
This was answered by Tomer. I had taken the output CSV from a previous script that was executed to look at user permissions at the database level. So when a user was mapped to multiple databases (user also belonged to different groups and subgroups) within the instance and would appear numerous times. So as my script went through the list it would re-run the same AD Group(s) over and over. I still do not understand why it would continuously run but once I altered the CSV file to have just a domain list it executed and spit out the output I was looking for. I did not put this as the answer because I felt Tomer deserved the credit. Thank you all for your help.
In Active Directory, membership self-loops are permitted. So for example, group A may contain another group B which may contain group A again. Therefore, simple enumeration of the member-of relation can lead to an infinite loop.
An easy way to solve it is to store the DNs you've already seen, and not going over them again.
I'm trying to export some group memberships all to one CSV file so I can find users who are not in our domain. Everything works great, but when all the outputs get appended I can't see what group each entry is in. Here's what I have now.
$Groups = import-csv "C:\users\USER\desktop\secgroupinput2.csv"
foreach($item in $Groups)
{
Get-ADGroupMember -Server "SERVERDC" -Identity $item.directoryname | export-csv "C:\users\USER\desktop\realexport.csv" -Append
}
How can I add a row between appends with the group name, likely from the import?
Thanks!
I did something similar in the past. Hope this code helps :
Function ADGroupMembers
{
$group = get-content C:\Pshell\PM\group.txt
$i =25
do{
if (get-QADgroup $group[$i] -Empty 0)
{write-output ''; Write-Output -inputobject "The $($group[$i]) group members :"; write-output '';
get-QADGroupMember $group[$i] -IncludeAllProperties | Format-Table -AutoSize DisplayName, Type, Office, Company, Department, Title, WhenCreated | Out-String -Width 4096;
write-output ''}
else {Write-Output -inputobject "*** Member not found in the $($group[$i]) group!"; write-output ''}
$i +=1}
while ($i -ne $group.length)
}
ADGroupMembers | out-File 'c:\Pshell\PM\groupmemberdetails.txt'