Avoid duplicate Bulk-New-ADUser Creation via a csv file - windows

I have some modifications for this script. UserID is normally a user’s first name followed by their last name.
What I need to do is to compare the proxy address & SAMAccountName attribute associated with each Username before it create.
So I mean lets say Jack Sparrow , if jsparrow already in use then script will try as jasparrow (first and second letter of firstname) and in use jasparrow as well , will be jacsparrow and so on. I want to avoid duplicate usernames.
2-I decided that it would be better to make 'fun' passwords that use the first two letters of the FirstName , day/month , first two letters of the Lastname. The end result is that users get a password like "Ja1009Sp".
Firstname,LastName,Department,Manager,MobilePhone
Jack,Sparrow,IT,jsmith,1 88 635 5254-0551
John Smith,Sparrow,Finance,jsmith,188 635 5254-0554
Script :
Import-Module ActiveDirectory
$UserList = Import-CSV -Path C:\Temp\CreateUsers.csv
$targetOU='OU=usersOU,DC=My,DC=Domain,DC=org'
$upnDomain='sec.local'
foreach($Person in $UserList){
$useritems=#{
GivenName=$Person.Firstname
Surname=$Person.LastName
Department=$Person.Department
AccountPassword=ConvertTo-SecureString -String $Person.Password -AsPlainText -force
ChangePasswordAtLogon=$false
Enabled=$true
DisplayName="$($Person.Firstname) $($Person.Lastname)"
Manager=$Person.Manager
MobilePhone=$Person.MobilePhone
Name="$($Person.Firstname) $($Person.Lastname)"
SamAccountName="$($Person.Firstname+$Person.LastName.Substring(0,1))"
UserPrincipalName="$($Person.FirstName+$Person.LastName.Substring(0,1))#$upnDomain"
Company="Contoso"
}
New-ADUser #useritems -Path $targetOU
}

Try something like this.. I don't have AD available atm. to test the Get-ADUser-query used to look for existing account so it might need some tuning.
foreach ($Person in $UserList) {
#Reset counters
$i = 1
$n = 1
do {
if($i -le $person.Firstname.Length) {
$user = "$($Person.Firstname.Substring(0,$i)+$Person.LastName)"
$i++
} else {
#All combinations in use, adding number
$user = "$($Person.Firstname.Substring(0,1)+$Person.LastName+$n)"
$n++
}
} while ((Get-ADUser -Filter "(samAccountName -eq '$user') -or (proxyaddresses -like '$user*')"))
#Result username
#$user
#$useritems = #{
#.....
#SamAccountName=$user
#UserPrincipalName="$user#$upnDomain"
#....
#}
}
If all combinations are in use including jacksparrow, it tries jsparrow1 ++ until it finds a free number.
The password can be generated using:
$Password = "{0}{1}{2}" -f $Person.Firstname.Substring(0,2), (Get-Date).ToString("ddMM"), $Person.Lastname.Substring(0,2)

Related

How to display the installed applications in alphabetical order of application name using winget?

Using winget,
winget list command displays the list of the applications currently installed in my computer, but it doesn't display the applications in alphabetical order of application name just like in the control panel,
Is there a way to display the installed applications in alphabetical order of application name using winget?
Note: The two images are from different machines.
Thanks.
As Demetrius mentioned in his comment, there isn't an ability to sort built into the client currently. However, in your screenshot I see you are using PowerShell. You can use PowerShell variables and commands to effectively sort the output. By chaining a few commands together, it is possible to re-create the table. This seemed to work for me -
$a=winget list;$a|select -First 3;$a|select -Skip 3|Sort-Object|select -First 9
I was trying to see if there was a parameter/option to accompany the winget command, and really wanted to just comment on the answer by Trenly; I had been using a similar piped command (just shorter), so he should still get the credit!
However, apparently, I must have a certain reputation score to even comment on his (or any other) answer... Yet, I can provide an answer without any rating whatsoever; go figure. So, the shorter version, similar to his answer, but without the unnecessary nested piping:
winget list|Sort-Object
You can check for ConvertFrom-FixedColumnTable function at here to convert the result of winget list to a table.
I created a function winget_list_OrderBy in order to make it simple:
function winget_list_OrderBy {
<#
.EXAMPLE
winget_list_OrderBy
.EXAMPLE
winget_list_OrderBy -OrderBy 'Name' -Arguments "--id=Git.Git"
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[string[]]
$OrderBy = 'Name', # $OrderBy can be equal to 'Name'/'Id'/'Version'/'Source' (and 'Available' if exist).
[Parameter(ValueFromPipeline)]
[string[]]
$Arguments = ''
)
# Backup the original [Console]::OutputEncoding
$encoding = [Console]::OutputEncoding
# Make PowerShell interpret winget.exe's output as UTF-8
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
(winget list $Arguments) -match '^(\p{L}|-)' | # filter out progress-display lines
ConvertFrom-FixedColumnTable | # parse output into objects
Sort-Object $OrderBy | # sort by the ID property (column)
Format-Table # display the objects in tabular format
# Restore the original [Console]::OutputEncoding afterwards
[Console]::OutputEncoding = $encoding
}
Usage is simple: winget_list_OrderBy -OrderBy $OrderBy -Arguments $Arguments or winget_list_OrderBy.
thoughts on this? It may need a little clean up, but I just converted the results to an object Array.
$apps = #("Microsoft Visual Studio Code", "Microsoft Visual Studio Code Insiders", "Visual Studio Community 2022")
$global:foundapps = [System.Collections.Generic.List[object]]::new()
foreach ($app in $apps) {
$Applist = winget search $app
$header = $Applist[1]
$nameposition = $header.indexof('Name')
$idPosition = $header.indexof('Id')
$versionPosition = $header.indexof('Version')
$sourceposition = $header.indexof('Source')
$name = $header.substring($nameposition, $idPosition).replace(' ', '')
$id = $header.substring($idPosition, ($versionPosition - $idPosition)).replace(' ', '')
$iVersiond = $header.substring($versionPosition, ($sourceposition - $versionPosition)).replace(' ', '')
$source = $header.substring($sourceposition, ($header.length - $sourceposition)).replace(' ', '')
$appstoadd = $Applist | select-object -skip 3
foreach ($AppToAdd in $appstoadd) {
$foundapps.Add([PSCustomObject] #{
"Name" = $AppToAdd.substring($nameposition, $idPosition).replace(' ', '')
"Version" = $AppToAdd.substring($versionPosition, ($sourceposition - $versionPosition)).replace(' ', '')
"ID" = $AppToAdd.substring($idPosition, ($versionPosition - $idPosition)).replace(' ', '')
"Source" = $AppToAdd.substring($sourceposition, ($header.length - $sourceposition)).replace(' ', '')
})
}
}
$foundapps |fl

How to get the highest value from list of Device Names

Generating a list of windows workstation computer names by reading the active directory and I need to find the highest number so that I can then assign a new device with the next available number - I am not having any success in doing this - how to do it? And as you can see from the list of names, I also have missing numbers in the sequence that ideally, I would like to fill in with new devices also...
The code I am using to get the list from AD is below.
((Get-ADComputer -Filter {operatingsystem -notlike "*server*" -and Name -like $NamingConvention -and enabled -eq "true"} -Credential $credential -server $ADServerIP).Name)
List of device names
PC01
PC28
PC29
PC30
PC31
PC32
PC33
PC34
PC35
PC36
PC37
PC38
PC40
PC41
PC42
PC43
PC44
PC45
PC46
PC47
PC27
PC48
PC26
PC24
PC179
PC18
PC180
PC181
PC182
PC183
PC184
PC185
PC186
PC187
PC188
PC189
PC19
PC190
PC191
PC192
PC21
PC22
PC23
PC25
PC178
PC49
PC51
PC77
PC78
PC79
PC80
PC81
PC83
PC84
PC85
PC87
PC88
PC89
PC90
PC91
PC92
PC93
PC94
PC95
PC96
PC97
PC76
PC50
PC75
PC72
PC52
PC53
PC54
PC55
PC56
PC57
PC59
PC60
PC61
PC62
PC63
PC64
PC65
PC66
PC67
PC68
PC69
PC70
PC71
PC73
PC98
PC177
PC175
PC115
PC116
PC117
PC118
PC119
PC12
PC120
PC121
PC122
PC123
PC124
PC125
PC126
PC127
PC128
PC129
PC13
PC130
PC131
PC114
PC132
PC113
PC111
PC02
PC03
PC04
PC06
PC08
PC09
PC10
PC100
PC101
PC102
PC103
PC104
PC105
PC106
PC107
PC108
PC109
PC11
PC110
PC112
PC176
PC133
PC135
PC158
PC159
PC16
PC160
PC161
PC162
PC163
PC164
PC165
PC166
PC167
PC168
PC169
PC17
PC170
PC171
PC172
PC173
PC174
PC157
PC134
PC156
PC154
PC136
PC137
PC138
PC139
PC14
PC140
PC141
PC142
PC143
PC144
PC145
PC146
PC147
PC148
PC149
PC150
PC151
PC152
PC153
PC155
PC99
Sort the pc names on their numeric values and select the last one:
$lastPC = (Get-ADComputer -Filter {operatingsystem -notlike "*server*" -and Name -like $NamingConvention -and enabled -eq "true"} -Credential $credential -server $ADServerIP).Name |
Sort-Object { [int]($_ -replace '\D+')} | Select-Object -Last 1
Here's a solution that will give you the highest number ($dataMax), the missing numbers ($dataMissing), and the next number to use ($dataNext). The next number to use will be either the 1st missing number, or if there are no missing numbers then it will be the highest number + 1
# load the computers list
$data = ((Get-ADComputer -Filter {operatingsystem -notlike "*server*" -and Name -like $NamingConvention -and enabled -eq "true"} -Credential $credential -server $ADServerIP).Name)
# create an array by splitting the data text using the "space" character as a delimiter
$data = $data.Split(" ")
# remove all the alpha characters ("PC"), leaving only the number values so it can be sorted easier
$dataCleaned = $data -replace "[^0-9]" , '' | sort { [int]$_ }
# after sorting the data, [-1] represents the last element in the array which will be the highest number
[int]$dataMax = $dataCleaned[-1]
# create a number range that represents all the numbers from 1 to the highest number
$range = 1..$dataMax | foreach-object { '{0:d2}' -f $_ }
# compare the created range against the numbers actually in the computer array to find the missing numbers
$dataMissing = #(compare $range $dataCleaned -PassThru)
# if there's a missing value, [0] represents the first element in the array of missing numbers
if ($dataMissing)
{
$dataNext = $dataMissing[0]
}
# if there's no missing values, the next value is the max value + 1
else
{
$dataMissing = "none"
$dataNext = $dataMax + 1
}
Write-Host "The highest number is:"('{0:d2}' -f $dataMax)
Write-Host "The missing numbers are: $dataMissing"
Write-Host "The next number to use is:" ('{0:d2}' -f $dataNext)
Assuming your list is exactly as it appears to be, then this appears to be one way to do it:
$List = 'PC01 PC28 PC29 PC30 PC31 PC32 PC33 PC34 PC35 PC36 PC37 PC38 PC40 PC41 PC42 PC43 PC44 PC45 PC46 PC47 PC27 PC48 PC26 PC24 PC179 PC18 PC180 PC181 PC182 PC183 PC184 PC185 PC186 PC187 PC188 PC189 PC19 PC190 PC191 PC192 PC21 PC22 PC23 PC25 PC178 PC49 PC51 PC77 PC78 PC79 PC80 PC81 PC83 PC84 PC85 PC87 PC88 PC89 PC90 PC91 PC92 PC93 PC94 PC95 PC96 PC97 PC76 PC50 PC75 PC72 PC52 PC53 PC54 PC55 PC56 PC57 PC59 PC60 PC61 PC62 PC63 PC64 PC65 PC66 PC67 PC68 PC69 PC70 PC71 PC73 PC98 PC177 PC175 PC115 PC116 PC117 PC118 PC119 PC12 PC120 PC121 PC122 PC123 PC124 PC125 PC126 PC127 PC128 PC129 PC13 PC130 PC131 PC114 PC132 PC113 PC111 PC02 PC03 PC04 PC06 PC08 PC09 PC10 PC100 PC101 PC102 PC103 PC104 PC105 PC106 PC107 PC108 PC109 PC11 PC110 PC112 PC176 PC133 PC135 PC158 PC159 PC16 PC160 PC161 PC162 PC163 PC164 PC165 PC166 PC167 PC168 PC169 PC17 PC170 PC171 PC172 PC173 PC174 PC157 PC134 PC156 PC154 PC136 PC137 PC138 PC139 PC14 PC140 PC141 PC142 PC143 PC144 PC145 PC146 PC147 PC148 PC149 PC150 PC151 PC152 PC153 PC155 PC99'
$NextNumber = ($List -split "\s" | ForEach-Object { if ($_ -match 'PC(?<Number>\d+)') { $Matches.Number } } | Measure-Object -Maximum).Maximum + 1
$NextNumber
"PC$NextNumber"

I want to search for values in a Table created with Powershell

So i have the following code to generate the entire Windows Update History:
$Session = New-Object -ComObject "Microsoft.Update.Session"
$Searcher = $Session.CreateUpdateSearcher()
$historyCount = $Searcher.GetTotalHistoryCount()
$Searcher.QueryHistory(0, $historyCount) | Select-Object Title, Description, Date,
#{name="Operation"; expression={switch($_.operation){
1 {"Installation"}; 2 {"Uninstallation"}; 3 {"Other"}
}}}
My question is how can i search in this table for updates containing KB. So for instance i want to know if the update KB4052623 has been installed. How can i write a simple piece of code to tell me KB4052326 has been found in the output of above code? Thanks in advance.
You need to assign the array returned by QueryHistory to a variable, then loop through that array to get the objects. Probably easiest to use Regex to find the KB numbers.
This code just list the found KB numbers, but you should be able test it as needed. Also, you may want to check the Description for KB numbers as well, it may tell you that it replaces old KB numbers or other important info.
$Session = New-Object -ComObject "Microsoft.Update.Session"
$Searcher = $Session.CreateUpdateSearcher()
$historyCount = $Searcher.GetTotalHistoryCount()
$MSUpdateHistory = $Searcher.QueryHistory(0, $historyCount) | Select-Object Title, Description, Date,
#{name="Operation"; expression={switch($_.operation){
1 {"Installation"}; 2 {"Uninstallation"}; 3 {"Other"}
}}}
$MSUpdateHistory | ForEach-Object {
if ($_.Title -match '(?<KBNum>KB\d+)') {
Write-Host "Found $($Matches.KBNum)"
#$_.Description
#$_.Date
#$_.Operation
}
}

Speed up CSV Powershell script

I've got a Powershell script that functions but it takes ages to complete. I'm a newbie with Powershell and i can't find a solution to speed up the proces. Hopefully somebody can show me to the right direction.
Example. I've got 2 csv files.
CSV 1:
CI Name,Last Logon Account
Computer1, User1
Computer2, User2
Computer3, User3
CSV 2:
Device Display Label,Subscriber Employee Id
Computer1, User1
Computer2, User2
Computer3, User6
I want to have all the Ci names in the first column with the last logon account in the second column and match subscriber employee id with ci name from the first file.
Resulting in:
Ci name, Last logon Account, Subscriber Employee Id
Computer1,User1,User1
Computer2,User2,User2
Computer3,User3,User6
I have the following script in Powershell:
$Data = Import-csv 'C:\Temp\Excel\CSV\file1.csv'
$Data2 = Import-Csv 'C:\Temp\Excel\CSV\file2.csv'
$combine = #()
foreach ($first in $Data) {
foreach ($second in $Data2) {
if ($second.'Device Display Label' -eq $first.'CI Name') {
$match = New-Object PSObject
$match | Add-Member Noteproperty "Ci Name" $first.'CI Name'
$match | Add-Member Noteproperty "Last Logon Account" $first.'Last Logon Account'
$match | Add-Member Noteproperty "Subscriber Employee Id" $second.'Subscriber Employee Id'
$combine += $match
}
}
}
$Combine
It works and it gives the desired result.
The only problem is that both csv files have 15000 lines. So it takes ages to finish the script.
Is there a way to speed up the proces. I hope somebody can point me to the right direction.
Use a hashtable to build an index out of one of the CSV files - this way you don't need the nested loops and the runtime should drop significantly:
# Build index/reference table from first data set
$DataTable = #{}
Import-csv 'C:\Temp\Excel\CSV\file1.csv' |ForEach-Object {
$DataTable[$_.'CI Name'] = $_
}
# No need to store the second data set in an intermediate variable
$combine = Import-Csv 'C:\Temp\Excel\CSV\file2.csv' |ForEach-Object {
if($DataTable.ContainsKey($_.'Device Display Label')){
# Take the existing object from the first data set
# and add the subscriber from the second data set
$DataTable[$_.'Device Display Label'] |Add-Member NoteProperty "Subscriber Employee Id" $_.'Subscriber Employee Id' -PassThru
}
}

How to change the loop Powershell content from the csv file automatically?

I want to change the loop PowerShell content from the CSV file automatically, is there someone know how to achieve the goal?
I want to run the loop PowerShell as below,I export the AD Group Members UPN with CSV file like One#contoso.com. I want to change the Userlist with the UPN automatically because there have more than 1000 members in the group and I have to change the upn from one#contoso.com to 'one#contoso.com',it’s easy to make a miss, is there has a smart way can achieve the goal,thanks.
Export-ADGroupmember UPN:
Get-ADGroupmember -identity adgroup | % { get-aduser $_.samaccountname | select userprincipalname } | export-csv upn.csv -notypeinformation
Loop PowerShell:
Loop PowerShell:
$UserList = #(
'One#contoso.com'
'Two#contoso.com'
'Three#contoso.com'
'Four#contoso.com'
'Five#contoso.com'
)
foreach ($UL_Item in $UserList)
{
$ARAGU_Params = #{
TenantName = "contoso"
HostPoolName = "contosoHostPool"
AppGroupName = "Desktop Application Group"
UserPrincipalName = $UL_Item
}
Add-RdsAppGroupUser #ARAGU_Params
}
You cant change content in your $UserList .More precisely, it is possible but affect the performance since You will need to read from the file and break it into segments. The best way is to download all users at once.You can do it like this:
$UserList=Get-ADGroupmember -identity adgroup | % { get-aduser $_.samaccountname | select userprincipalname }
foreach ($UL_Item in $UserList)
{
$ARAGU_Params = #{
TenantName = "contoso"
HostPoolName = "contosoHostPool"
AppGroupName = "Desktop Application Group"
UserPrincipalName = $UL_Item.userprincipalname
}
Add-RdsAppGroupUser #ARAGU_Params
}

Resources