New to powershell, and I can't figure out why the SamAccountName column in the output file is empty. while the IsDisabled column has the account status results.
Import-Csv $filename | Foreach-Object{
$user = ([ADSISEARCHER]"(samaccountname=$($_.SamAccountName))").FindOne()
if($user)
{
New-Object -TypeName PSObject -Property #{
SamAccountName = $user.SamAccountName
IsDisabled = $user.GetDirectoryEntry().InvokeGet('AccountDisabled')
}
}
else
{
Write-Warning "Can't find user '$($_.SamAccountName)'"
}
} | Export-Csv $filename
You have to change the 6th line to:
SamAccountName = $user.Properties.samaccountname
You need to specify sAMAccountName as one of the properties to return. In addition you can specify the userAccountControl property and test the ADS_UF_ACCOUNTDISABLE bit to see if the user account is disabled. This will avoid the overhead of binding to each user object to test if disabled. Here is a short sample script:
param(
[String[]] $sAMAccountName
)
$ADS_UF_ACCOUNTDISABLE = 2
$sAMAccountName | foreach-object {
$searcher = [ADSISearcher] "(sAMAccountName=$($_))"
$searcher.PropertiesToLoad.AddRange(#("samaccountname","useraccountcontrol"))
$result = $searcher.FindOne()
if ( $result ) {
$samName = $result.Properties["samaccountname"][0]
$disabled = ($result.Properties["useraccountcontrol"][0] -band
$ADS_UF_ACCOUNTDISABLE) -ne 0
new-object PSObject -property #{
"Name" = $samName
"Disabled" = $disabled
}
} else {
write-warning "Can't find user $_"
}
}
Bill
IIRC this is a known issue (bug) with case-sensitive ldap property names. To avoid that you can use the hashtable syntax and not worry about case:
$user.Properties['SAMACCOUNTNAME']
Related
I am looking to update every user in AD both Telephone numbers from the general tab and pager, mobile and fax. What I am looking to update is any user in our AD from +44 (0) +44 0
I have tried with the following but did not work but can't see why
$UserSplat = #{
LDAPFilter = "(|(homephone=*)(othermobile=*)(mobile=*))"
Properties = #('homephone', 'othermobile', 'mobile')
SearchBase = 'OU=NoAdm,OU=Users,DC=xxxxx,DC=xxxxx,DC=xxxxx,DC=net'
}
Get-ADUser #UserSplat | ForEach-Object {
$CurrentUser = New-Object -TypeName PSCustomObject -Property #{
Name = $_.Name
HomePhone = (-join $_.homephone) -replace '\s'
OtherMobile = (-join $_.othermobile) -replace '\s'
Mobile = (-join $_.mobile) -replace '\s'
}
$CurrentUser
if ($CurrentUser.homephone -notmatch '^\(0)') {
$_ | Set-ADUser -Replace #{homephone = "0$($CurrentUser.HomePhone)" } -WhatIf
}
}
I unfortunately can't test this, I believe -LDAPFilter should be capable of filtering only those users that actually have their homephone, othermobile and mobile attributes starting with +44 (0) hence I added that to save a step. I also left the -WhatIf switch to be sure the script is doing what is supposed to do.
$startsWith = '+44 (0)'
$UserSplat = #{
LDAPFilter = "(|(homephone=$startsWith*)(othermobile=$startsWith*)(mobile=$startsWith*))"
Properties = #('homephone', 'othermobile', 'mobile')
SearchBase = 'OU=NoAdm,OU=Users,DC=xxxxx,DC=xxxxx,DC=xxxxx,DC=net'
}
$startsWith = [regex]::Escape($startsWith)
foreach($user in Get-ADUser #UserSplat)
{
$replaceHash = #{}
foreach($prop in 'homephone', 'othermobile', 'mobile')
{
if(($replace = $user.$prop) -match $startsWith) {
$replaceHash[$prop] = $replace -replace '\(|\)'
}
}
Set-ADUser $user -Replace $replaceHash -WhatIf
}
I am a powershell beginner and self learner. I finally made my script to get AD Users from NestedGroup. I now want to make a cache for them to make a quicker result when I run the script on the second time. I am totally lost on what I am doing and I want to ask for some approach from professionals here. Thank you for guidance.
Here is my code.
function Get-CachedGroupMembership($groupname){
$groupName = "cached_$($groupName)"
$cachedResults = Get-Variable -Scope Global -Name $groupName -ErrorAction SilentlyContinue
if($null -ne $cachedResults){
"found cached result"
$existing = write-host "Check: i found existing"
return $cachedResults, $existing
}
else{
$searching = write-host "Check: Searching"
$results = get-adgroup $groupname -properties memberof, members
Set-CachedGroupMembership -groupName $groupName -value $results
return $searching
}
}
Function Set-CachedGroupMembership($groupName,$value){
Set-Variable -Scope Global -Name $groupName -Value $value
return $value
}
function Get-ADUsers_cached {
param (
[Parameter(ValuefromPipeline = $true, mandatory = $true)][String] $GroupName
)
[int]$circular = $null
# result holder
$resultHolder = #()
$table = $null
$nestedmembers = $null
$adgroupname = $null
# get members of the group and member of
# $ADGroupname = get-adgroup $groupname -properties memberof, members
$ADGroupname = Get-CachedGroupMembership -GroupName $groupName
# 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
}
}
}
}
# 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 = "";
Nesting = $nesting;
DN = $nestedmember.distinguishedname;
Comment = ""
EmployeeNumber = "";
LastLogonDate = "";
PasswordLastSet = "";
}
# if member object is a user
if ($nestedmember.objectclass -eq "user") {
# saves all the properties in the table.
$nestedADMember = get-aduser $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, nesting, enabled, dn, comment , 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
}
# for circular output
#$table | select type, name, displayname, parentgroup, nesting, enabled, dn, comment
#calling function itself
$resultHOlder += Get-ADUsers_cached -GroupName $nestedmember.distinguishedName
}
else {
if ($nestedmember) {
$table = new-object psobject -property $props
$resultHolder += $table | select type, name, displayname, parentgroup, nesting, enabled, dn, comment, EmployeeNumber, LastLogonDate, PasswordLastSet
}
}
}
}
return $resultHOlder
}
function Get-NestedGroupUsers_cached {
param (
[Parameter(Mandatory = $true)][String]$FileName,
[Parameter(Mandatory = $true)][String]$searchFileURL
)
$storageHolder = #()
$groupList = Get-Content $searchFileURL
$groupList | ForEach-Object {
$allusers = Get-ADUsers_cached -GroupName $_
$storageHolder += $allusers
}
$storageHolder | select ParentGroup, Name, EmployeeNumber, Enabled, LastLogonDate, PasswordLastSet |Export-Csv -Path "C:\Users\***\Desktop\$FileName.csv" -NoTypeInformation -Force
}
You can make use of a global or module scoped variable to store the info. Then, check for the presence of the variable on second execution and pull from that, if present.
For instance, your query of AD Groups would be a good one to cache.
I would make two functions to make this easier:
function Get-CachedGroupMembership($groupname){
$groupName = "cached_$($groupName)"
$cachedResults = Get-Variable -Scope Global -Name $groupName -ErrorAction SilentlyContinue
if($null -ne $cachedResults){
"found cached result"
return $cachedResults
}
else{
"need to cache"
$results = get-adgroup $groupname -properties memberof, members
Set-CachedGroupMembership -groupName $groupName -value $results
}
}
Then to cache the results, I'd use this function too.
Function Set-CachedGroupMembership($groupName,$value){
Set-Variable -Scope Global -Name $groupName -Value $value
return $value
}
Finally, replace the call to $ADGroupname = get-adgroup $groupname -properties memberof, members with a call to Get-CachedGroupMembership -GroupName $groupName.
If you really want to persist the cache, you could even write results out to JSON and modify these functions to retrieve them, but then you could have an issue with stale results. So, you should always leave a way to force an update to the cache, for instance you could modify these functions to add a -Force switch to update your cached value.
With a little copy and paste, you could modify these functions into Get-CachedADUser as well, and have your entire script caching results.
I'm trying to create kind of a solution to create thousands of accounts in AD add them to specific group or for service accounts add them to specific OU. Keep a log of what was done and what the errors are.
The script ingest a csv file with the following headers.
SamAccountName,name,password,ou,domain,isAdded
$Domain = [system.directoryservices.activedirectory.domain]::GetCurrentDomain().Name
$NewUserADGroup = 'Print Operators'
$NewUsersList = Import-Csv .\bulk_user1.csv | Where-Object{$_.domain -like "$Domain"}
$NewUsersList | ForEach-Object{
$NewUserAttributes = #{
SamAccountName = $_.SamAccountName
name = $_.name
#path = $_.parentou
#UserPrincipalName = $_."samAccountName" + "#lovely.Local"
AccountPassword = (convertto-securestring "$NewUsersList.password" -AsPlainText -Force)
Enabled = $true
#Server = $dcname
#isAdded = $Issue
}
try{
#Create new User and add to specific group
New-ADUser $NewUserAttributes
Add-ADGroupMember -Identity $NewUserADGroup -Members $_.SamAccountName
#Delete Specific User
#Remove-ADUser -Identity $_.SamAccountName
}catch{
Write-Warning $_
$Issue = $_.ToString()
}
$count = $count + 1
Write-Host $_.SamAccountName " " $_.Name " " $_.SamAccountName.Enabled " Total:" $NewUsersList.Count + "Processed:" $count
$NewUserAttributes| Select-Object -Property SamAccountName,name,AccountPassword,Enabled,isAdded | Export-Csv ".\$Domain.NewAccountsCreatedStatus.csv"
}
I'm getting the following error:
WARNING: The name provided is not a properly formed account name
When I look at the variable
$NewUserAttributes
I do see the name and the value:
Name Value
---- -----
Enabled True
name bfmbsngfilexfer2
AccountPassword System.Security.SecureString
SamAccountName bfmbsngfilexfer2
As promised, below a rewrite of your code.
I have inserted comments to hopefully explain what the code does:
$Domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().Name
$NewUserADGroup = 'Print Operators'
$successCount = 0
$NewUsersList = Import-Csv .\bulk_user1.csv | Where-Object { $_.domain -eq $Domain } | ForEach-Object {
# capture the human readable password for output use
$password = $_.password
$userParams = #{
SamAccountName = $_.SamAccountName
Name = $_.name
Path = $_.parentou
UserPrincipalName = '{0}#lovely.Local' -f $_.SamAccountName
AccountPassword = ConvertTo-SecureString $_.password -AsPlainText -Force
Enabled = $true
#Server = $dcname
}
try{
# Create new User and add to specific group
$user = New-ADUser #userParams -PassThru -ErrorAction Stop
Add-ADGroupMember -Identity $NewUserADGroup -Members $user -ErrorAction Stop
# add the 'isAdded' element to the $userParams hashtable
$userParams['isAdded'] = $true
$successCount++
}
catch{
Write-Warning $_.Exception.Message
$userParams['isAdded'] = $false
}
# output a PsCustomObject with values taken from the Hashtable
# AccountPassword is a SecureString, which will be of no use to you..
# Output the human readable password instead so you can inform the new users.
[PsCustomObject]$userParams | Select-Object SamAccountName, Name,
#{Name = 'Password'; Expression = {$password}},
Enabled, isAdded
}
# output
# use '#($NewUsersList)' to force it as array, so the Count property is accurate
if (#($NewUsersList).Count) {
Write-Host ('Processed: {0} Succeeded: {1}' -f $NewUsersList.Count, $successCount) -ForegroundColor Green
$NewUsersList | Export-Csv ".\$Domain.NewAccountsCreatedStatus.csv" -NoTypeInformation
}
else {
Write-Host 'No users successfully processed!' -ForegroundColor Red
}
Hi I'm trying to create a script that will check if the list of AD Group I provided exist in AD but the script only returns the last object it found. Please disregard the previous sample code and refer to this one. The script seems to run but it is not returning all the ADGroup in the list even if they all exist as confirmed manually. It only returns the last object.
function CheckAD
{
$AD= "AA","BB"
foreach ($group in $AD)
{
$R_AD= #()
$R_AD=Get-ADGroup -filter {GroupCategory -eq "Security" -and Name -eq $group} -SearchBase "OU=Application,OU=SOO,OU=Groups,OU=UN,DC=B3,DC=in"|select -ExpandProperty name -Unique
if ($R_AD.count -gt 0)
{
$r = New-Object -TypeName psobject
$r|Add-Member -MemberType NoteProperty -Name ADGroup -Value $R_AD
}
}
return($r)
}
$Test=CheckAD
## Cleaned-up the formatting so I could read this
function CheckAD {
## Changed to a hash table; consider passing
## these in as parameters
$AD = #{"AA" = $false; "BB" = $false}
foreach ($group in $AD.keys) {
## You don't need this. Removing.
#$R_AD = #()
## In your post, you asked to know if the group existed.
if ( $(Get-ADGroup -filter {GroupCategory -eq "Security" -and Name -eq $group} -SearchBase "OU=Application,OU=SOO,OU=Groups,OU=UN,DC=B3,DC=in") ) {
$AD[$group] = $true
}
## You don't need this either
#if ($R_AD.count -gt 0) {
# $r = New-Object -TypeName psobject
# $r|Add-Member -MemberType NoteProperty -Name ADGroup -Value $R_AD
#}
}
#return($r)
return($AD)
}
$Test = CheckAD
I have list of accounts as below in a text file-
AccountName
Mahin\user1
Mahin\user2
Mahin\group5
user12
usert1
groupt3
This way, I have around 400 accounts (mix of various users and some groups),
as you can see from above, some accounts have format as <Domain>\<AccountName>
and some with just <AccountName>.
I was trying to find a way to segregate users and groups from this list,
how can I achieve this using powershell?
Looking for something like
AccountName, IsUser, IsGroup, IsExists
Mahin\user1,1,0,1
Mahin\user2,1,0,1
Mahin\group5,0,1,1
user12,1,0,1
usert1,,,0 //-> This Account DOES NOT EXIST, so, IsUser, IsGroup and IsExists (0) can be empty or some distiguishable number
groupt3,,,0 //-> This Account DOES NOT EXIST, so, IsUser, IsGroup and IsExists (0) can be empty or some distiguishable number
So far, I know how to get members of various groups using below script, but not sure of above part.
$groups = Get-Content "C:\AD\groups.txt"
$resultsarray =#()
foreach ($group in $groups) {
$resultsarray += Get-ADGroupMember -Id $group | select
samaccountname,name,#{Expression={$group};Label="Group Name"}
}
$resultsarray| Export-csv -path "C:\AD\output\GroupMembers.csv" -notypeinformation
Note: Im not very familiar with the Active Directory Cmdlets, there is probably a better solution for this.
However, I would retrieve all users and groups first, then iterate over your text file and try to find any user or group:
$users = Get-ADUser -filter *
$groups = Get-ADGroup -filter *
Get-Content 'YOUR_FILE_PATH'| ForEach-Object {
$adObject = $_ -replace '(.*)\\(.*)', '$2#$1' # fix search string
if ($users | Where { $_.SamAccountName -eq $adObject -or
$_.UserPrincipalName -like "$adObject*"} )
{
[PSCustomObject]#{
AccountName = $_
IsUser = $true
IsGroup = $false
Exist = $true
}
}
elseif ($groups | Where Name -eq $adObject)
{
[PSCustomObject]#{
AccountName = $_
IsUser = $false
IsGroup = $true
Exist = $true
}
}
else
{
[PSCustomObject]#{
AccountName = $_
IsUser = $false
IsGroup = $false
Exist = $false
}
}
} | Export-csv -path "C:\output\GroupMembers.csv" -notypeinformation
On my AD I have to transform Mahin\group5 to group5#Mahin and search for group5#Mahin*, thats why I replace the string within the script.