Add incremented number to AD attribute based on last value - powershell

I'm trying to use PowerShell to search the AD attribute otherTelephone, and if the value is null then add the next number in the sequence.
For instance, if I add a new user called John, I'd like PowerShell to check AD and see that the last otherTelephone number used was 999 so it'll auto add 1000 for John.
So far I've managed to export a list of users in AD along with their respective otherTelephone numbers:
Get-ADUser -Filter * -Properties otherTelephone |
select name, #{L='otherTelephone'; E={$_.otherTelephone[0]}}, |
Export-Csv c:\aduser.csv -NoTypeInformation
but I'm not sure how to proceed from there.

As #Paul suggested in the comments to your question: read the phone numbers from AD, get the highest number, and increment it.
$nextPhoneNumber = Get-ADUser -Filter * -Properties otherTelephone |
? { $_.otherTelephone } |
select -Expand otherTelephone |
% { [int]$_ } | sort -Desc | select -First 1
$nextPhoneNumber++
Explanation:
? { $_.otherTelephone }: Ensure that only objects with a non-empty attribute are processed.
select -Expand otherTelephone: Expand the attribute so that you get a stream of phone numbers. This also takes care of multiple numbers assigned to a user (the attribute is multi-valued).
% { [int]$_ }: Convert each telephone number from a string to an integer, so they can be sorted numerically.
sort -Desc: Sort the list in descending order (first number is highest).
select -First 1: Select the first number from the list and discard the rest.
$nextPhoneNumber++: increment the number by 1.
If you need to do this for all existing users that don't already have the otherTelephone attribute populated, you could do something like this:
$users = Get-ADUser -Filter * -Properties otherTelephone
$nextPhoneNumber = $users | ? { $_.otherTelephone } |
select -Expand otherTelephone |
% { [int]$_ } | sort -Desc | select -First 1
$users | ? { -not $_.otherTelephone } | % {
$nextPhoneNumber++
Set-ADUser -Identity $_.DistinguishedName -Replace #{
'otherTelephone' = $nextPhoneNumber
}
}

Related

Sorting Office 365 user account / mailbox properties

I'm accessing my cloud Office 365 Exchange Server via Powershell. Showing all properties of an account can be done via Get-Mailbox 'username' | Select * (MS reference).
On most systems those properties are already sorted (e.g. Get-ADUser 'username' -Properties *). Is it possible to sort the Get-Mailbox output? I thought Get-Mailbox 'username' | Select * | Sort-Object would do the trick but it didn't make any difference (I guess a property doesn't constitute an object). What's the right command to sort the properties for a single user?
Note: Sorting properties of multiple accounts works fine e.g. Get-Mailbox -filter * | select Name, DisplayName | Sort-Object Displayname
Update:
I managed to get a sorted properties list using
(Get-Mailbox 'mailboxname' | select *).PSObject.properties | ForEach-Object {$_.Name} | Sort-Object Name
it gets me the following output:
AcceptMessagesOnlyFrom
AccountDisabled
AddressBookPolicy
ArchiveWarningQuota
...
$_.Name gives me the values but so far I couldn't combine both into one list, I'm trying to get s.th. like this:
AcceptMessagesOnlyFrom = {}
AccountDisabled = False
AddressBookPolicy =
ArchiveWarningQuota = 45 GB (48,318,382,080 bytes)
...
I'm not completely sure this is what you are after, let me know if I'm wrong. As in my comment, Sort-Object can handle sorting a list or an object[] by one or more of it's properties; but sorting one single object by it's properties, say alphabetically, would require a combination of accessing the object's properties with .PSObject.Properties.Name and then sorting this list with Sort-Object. And after that we can use Select-Object with this sorted list to display the object as we want.
Using the object below as an example as I have no idea how of the type Microsoft.Exchange.Data.Directory.Management.Mailbox looks.
$mailbox = [pscustomobject]#{
DisplayName = 'someuser'
UserPrincipalName = 'someuser#somedomain.com'
Mail = 'someuser#somedomain.com'
IsLicensed = $true
}
$properties = $mailbox.PSObject.Properties.Name | Sort-Object
$mailbox | Select-Object $properties
As you can see, object's properties are now sorted alphabetically:
DisplayName IsLicensed Mail UserPrincipalName
----------- ---------- ---- -----------------
someuser True someuser#somedomain.com someuser#somedomain.com
By looking at your edit, seems like you are looking for a one-liner, so this is how it could look:
Get-Mailbox 'mailboxname' | ForEach-Object {
$_ | Select-Object ($_.PSObject.Properties.Name | Sort-Object)
}

Setting IDs per PowerShell script

I have a PowerShell script, which fills the Usrid custom attribute with EMP$ID value. $ID is a continuous number, which is normally stored in a text file and continuously written after setting the ID and set it one up. First I get with Get-ADUser all users without a ID, then will set the ID into Usrid attribute.
My problem is: I want to check if the ID or value exists.
If ID exists → $ID++
else → set ID from File and write it to file again
The if part in the script looks like this:
# I'm calling the content or the last ID
$lastid = Get-Content "C:\startid.txt"
# Convert the content into a decimal string
$Usrid = [System.Decimal]::Parse($lastid)
# Find out all userProxyFull Object without an ID
Get-ADObject -Filter {(objectClass -eq "userProxyFull") -and (-not(Userid -like "*"))} -Searchbase "DC=MY,DC=SEARCHBASE" -Searchscope subtree -Server myIP |
ForEach-Object {
# Then the problem part here, see description above
if ({Usrid -eq "EMP$ID"}) {
$ID++
Set-ADObject $_ -Partition "DC=my,DC=partition" -Add #{Usrid="EMP$ID"}
} else {
Set-ADObject $_ -Partition "DC=my,DC=partition" -Add #{Usrid="EMP$ID"}
}
}
But the script doesn't check. Or how can I check the highest ID and set the highest ID into Usrid attribute?
Assuming that Usrid needs to be unique you need to compare your input value against the already existing attribute values.
$existing = #{}
Get-ADUser -LDAPFilter '(UserId=*)' -Property Usrid | ForEach-Object {
$existing[$_.Usrid] = $true
}
while ($existing.ContainsKey("EMP$ID") {
$ID++
}
At this point you have a $ID where none of the existing accounts has a Usrid attribute with the value EMP$ID. Then you can go ahead and assign the attribute on all account objects where it doesn't have a value yet while incrementing $ID after each assignment:
Get-ADObject ... | ForEach {
Set-ADObject $_ -Partition "DC=my,DC=partition" -Add #{Usrid="EMP$ID"}
$ID++
}
Note, however, that the above is assuming that there are no gaps in your numbering (i.e. you don't have a situation where you have attribute values ..., EMP25, EMP26, EMP28, ...). If that assumption doesn't apply you're better off determining the next available ID by getting the highest already assigned ID and incrementing that value by 1:
$ID = Get-ADUser -LDAPFilter '(Userid=*)' -Property Usrid |
ForEach-Object { [int]($_.Usrid -replace '^EMP') } |
Sort-Object |
Select-Object -Last 1
$ID++

Using PowerShell to generate a unique username in AD

I'm writing a script to be used by help desk staff to quickly (and accurately) create AD user accounts by inputting basic employee information. We use portions of a persons name in order to create the samAccountName. Which I've achieve by the following.
$GivenName = "Joe"
$Surname = "Smith"
$SamAccountName = $Surname.substring(0, [System.Math]::Min(5, $Surname.Length)) + $GivenName.Substring(0,1)
This works great as long as there isn't already a "Joe Smith" (or John or Jennifer Smithers, etc). The solution would be to add a number to the end. When manually creating accounts the help desk would search AD look at what number suffix to use if necessary. I'm trying to figure out how PowerShell can do that for us. I've gone through several ideas with help from what I've found online but so far I've been unsuccessful.
My first thought was to do something like this.
$SamSuffix = 2
If ((Get-ADUser -LDAPFilter "(SamAccountName=$samAccountName)")-eq $Null)
{
"$SamAccountName does not exist in AD" #out result for testing.
}
Else{
do
{
Get-ADUser -LDAPFilter "(SamAccountName=$SamAccountName + $SamSuffix++)"
}
until (Get-ADUser -LDAPFilter "(SamAccountName=$SamAccountName + $SamSuffix)")-eq $Null)
}
This obviously doesn't work. Even if it did I don't know how I'd get to the 'next step' to create the account.
I also tried pulling the existing names into a list
$SamExist = (Get-ADUser -LDAPFilter "(SamAccountName=$SamAccountName*)" | Select SamAccountName)
do {$SamAccountName + $SamSuffix++}
until ($SamExist -notcontains $SamAccountName -or $SamAccountName + $SamSuffix)
This also doesn't work but if it did I can see that it would automatically add the suffix even if it wasn't needed.
You approach where you get all the existing matches first would be where I would start. Lets assume $SamAccountName is smithj
$existingAccounts = Get-ADUser -Filter "samaccountname -like '$SamAccountName*'" -ErrorAction SilentlyContinue | Select-Object -ExpandProperty samaccountname
So $existingaccounts that have a samaccountname's starting with smithj. If there are not any then $existingAccounts would be null and we can test for that.
if($existingAccounts){
# Figure out what the suffix will be
$highestValue = $existingAccounts -replace "^$SamAccountName" |
ForEach-Object{[int]$_} |
Measure-Object -Maximum |
Select-Object -ExpandProperty Maximum
} else {
# Create the user as normal.
}
Pretending there are some accounts that exist we trim the leading characters from the samaccountname, convert the remaining to an integer and select the highest from that. So $highestValue is the last number used in a conflicting account.
Add one to that and you have a guaranteed username you can create assuming nothing changes in those moments i.e. two users making to smithj accounts.
If you are looking to fill gaps where a user might have left and you want to use the next available after 1 then you could do this.
$existingAccounts = "smithj1", "smithj5", "smithj10", "smithj2", "smithj3"
# Figure out what the next unused suffix will be
$existingSuffixes = $existingAccounts -replace "^$SamAccountName" | ForEach-Object{[int]$_}
# Once again determine the maximum one in use
$highestValue = $existingSuffixes | Measure-Object -Maximum | Select-Object -ExpandProperty Maximum
# Find the first gap between 1 and the max suffix
$nextAvailableSuffix = 1..($highestValue + 1) | Where-Object{$existingSuffixes -notcontains $_} | Sort-Object desc | Select -First 1
$nextAvailableSuffix would contain 4 using the above example. We add 1 to highest value in case the only other one is 2 so that way there will only be an answer to $nextAvailableSuffix

Insert parameter data from one output into another

This is directed towards Exchange/Office 365 powershell but can apply for other types as well.
If i have a list of users for example, and I use UserPrincipalName for their identity - now in all my scripts I can pull data using UserPrincipalName for the identity, but in most UserPrincipalName isn't actually an exportable value. So, if you take my two examples below I can pull data in both using UserPrincipalName for the identity, but in the 2nd one the ouput would be blank.
How can I insert for example the identity value UserPrincipalName into the 2nd. This would apply for more than the two scripts mentioned below.
$CSV | foreach {
Get-Mailbox -resultsize unlimited -Identity $_.UserPrincipalName
} | select userprincipalname, server |out-gridview
Example output:
UPN#something.com, server101
$csv | ForEach {
Get-ActiveSyncDeviceStatistics -Identity:$_.UserPrincipalName
} | select DeviceFriendlyName,UserPrincipalName |out-gridview
Example output:
2nd: DeviceIOS4,-
I don't have an Exchange at hand, but most likely the objects returned by Get-Mailbox do have a UserPrincipalName property, while the objects returned by Get-ActiveSyncDeviceStatistics don't. To preserve the UPN in the output of the second script, you can do something like this:
$csv | ForEach {
$upn = $_.UserPrincipalName
Get-ActiveSyncDeviceStatistics -Identity:$upn
} | select DeviceFriendlyName,#{n='UserPrincipalName';e={$upn}} |out-gridview

AD nested group membership powershell reporting

We have following naming convention for shared resources:
sg_ShareName1_RO
sg_ShareName1_RW
sg_ShareName2_RO
sg_ShareName2_RW
I would like to get report in following format in Excel/csv:
ShareName1 ShareName2 ...
User1 RW NA
User2 NA RO
I'm fighting how to output Shared names to row in csv file instead of column.
Here is come code I've already done:
$users = GetADUser - filter {name like '*'} | sort name | select name
$sharegroups = Get-AdGroup -filter {name like 'sg_*'} | sort name
$shares = Get-AdGroup -filter {name like 'sg_*'} | sort name | foreach {$_} | select #{N='Share Name'; E={$_.Name.Replace('sg_', '').Replace('_', '').Replace('RO','').Replace('RW','')}} -Unique
Tnen to avoid trips to AD each time to check group membership first i would like to store members of each group in array
$sharegroupmembers = #{}
foreach ($group in $sharegroups)
{
$sharegroupmembers[$group.name] = Get-ADGroupMember $group.name -Recursive | select name
}
After that I'm stuck on howe to make correct projection of shares to columns, users to rows and RW/RO/NA to values based on group membership
Your number of columns is going to be the maximum number of group memeberships any user has. Those are in the values of $sharegroupmembers, so:
$shargroupmembers.values |
sort count |
select -last 1
That's how many rows you'll have, and how many share membership properties you'll need to create on your objects you're going to export.