Read from Excel and import into Active Directory - powershell

I have an Excel file which has two columns: 1st column(employees) and the second column (abbreviation).
The goal is to fill the initials in AD with the abbreviations oft he employees.
I think the PowerShell script should read the 1st column, check if the 1st column is the same as name in AD, then reads the employee's abbreviation and fill it in AD.
This is my first Powershell script and here is what I tried but of course it does not work:
$xl = New-Object -COM "Excel.Application"
$xl.Visible = $false
$wb = $xl.Workbooks.Open("C:\Users\user\Desktop\test.xlsx")
$ws = $wb.Sheets.Item(1)
for ($i = 2; $i -le 150) {
if ($ws.Cells.Item($i, 1).Value2 -ne $null) {
$Employees = $ws.Cells.Item($i, 1).Value2
$ abbreviation = $ws.Cells.Item($i, 2).Value2
write-host " Employees: "$ Employees
write-host " abbreviation: "$ abbreviation
}
$i++
}
get-aduser -Filter * -Properties initials | select name, initials
ForEach ($Employees in $name)
{
if ($Employees -eq $name)
{
#$Initials -eq $abbreviation
Set-ADUser -Initials $ abbreviation
}
}

If your just trying to update the initials with the abbreviation field from the file I would save the file as a CSV file initially and then do the following.
You can obviously add in more prompts and error checking along the way.
Get-Content test.csv | Select-Object -Skip 1 | ConvertFrom-Csv -Header Employees, abbreviation |
ForEach-Object {
Try
{
Get-ADUser $_.Employees | Set-ADUser -Initials $_.abbreviation
}
catch
{
Write-Host "Update Failed"
Write-Host "$Error[0]"
}
}
If there is no header in the file then you can drop the Select-object -skip 1
Or simpler
$Users = Import-Csv Test.csv
foreach ($User in $Users)
{
Get-ADUser $User.Employees | Set-ADUser -Initials $User.abbreviation
}

# This script reads Excel and update the Initials in AD
Import-Module ActiveDirectory;
$PathToXlsx = "C:\test.xlsx";
$xl = New-Object -COM "Excel.Application";
$xl.Visible = $false;
$wb = $xl.Workbooks.Open($PathToXlsx);
$ws = $wb.Sheets.Item(1);
for ($i = 2; $i -le 150;$i++) {
if ($ws.Cells.Item($i, 1).Value2 -ne $null) {
$Employee = $ws.Cells.Item($i, 1).Value2;
$Abbreviation = $ws.Cells.Item($i, 2).Value2;
Write-Host $Employee;
Write-Host $Abbreviation;
try
{Get-AdUser -Filter 'DisplayName -like $Employee' | Set-ADUser -Initials $Abbreviation;
$User = Get-AdUser -Filter 'DisplayName -like $Employee' -Properties 'Initials' | Select Name,Initials | Format-List;
Write-Host $User;
}
catch
{
Write-Host "Update Failed"
Write-Host "$Error[0]"
}
}
}
and in case of CSV.
Import-Module ActiveDirectory;
$PathTocsv = "C:\test.csv";
$Users = Import-Csv $PathTocsv -Delimiter ";";
foreach ($user in $Users){
$Employee = $user.Employee;
$Abbreviation = $user.Abbreviation;
Write-Host $Employee;
Write-Host $Abbreviation;
Get-AdUser -Filter 'DisplayName -like $Employee' | Set-AdUser -Initials $Abbreviation;
}

Related

Update EmployeeID Attribute based on csv

Here is the code I used to update the employeeID attribute based on csv.
I want to do verify the input value and then return the result of not matching items.
Any suggestions? Thanks!
Import-module ActiveDirectory
Import-CSV “C:\PSS\UserList.csv” | % {
$mail = $_.mail
$ID = $_.EmployeeID
$users = Get-ADUser -Filter {mail -eq $mail}
Set-ADUser $Users.samaccountname -employeeID $ID
}
No sense over-complicating it.
Import-module ActiveDirectory
$failedAccounts = #()
Import-CSV “C:\PSS\UserList.csv” | % {
$mail = $_.mail
$ID = $_.EmployeeID
$users = Get-ADUser -Filter {mail -eq $mail}
if ($users -ne $null){
Set-ADUser $Users.samaccountname -employeeID $ID
}
else {
$failedAccounts += $mail
}
}
Write-Host "Failed Accounts: $($failedAccounts.count)"
$failedAccounts

Find users with a certain parameter, display the groups they belong to and count how many

I am trying to get a number of how many people with specific titles are in specific groups.
What my approach is:
I am looking for users with specific titles.
I am looping over those users and looking for their groups they are in
Then I am looping over each group and trying to add .csv entry when there is a new one for that specific title, if group is listed, I am trying to just increment the counter.
I think that my approach is slow - every time I export and import .csv file, but I am sure there is a way to work on a imported file.
Also I have strange error: when importing test.csv I have like 10 entries instead of one. How to fix that?
My code:
clear
$Roles = Get-Content 'C:\Users\DWodzinski-admin\Documents\Titles.txt'
$Users = #()
Foreach ($Item in $Roles){
$Users += Get-ADUser -Filter {title -like $Item} -properties title, SamAccountName | Select SamAccountName, title
}
Foreach ($User in $Users){
$AdGroups = Get-ADPrincipalGroupMembership $User.SamAccountName | Select Name
foreach ($thing in $AdGroups) {
$name = $thing.name
$csv = Import-Csv "C:\Users\DWodzinski-admin\Documents\test.csv"
foreach($i in $csv){
if($i.Group -eq $name -and $i.Title -eq $User.title) {
$i.Count += 1
Export-CSV "C:\Users\DWodzinski-admin\Documents\test.csv" -NoTypeInformation
} else {
$NewCsvEntry = #{
Title = $User.title
Group = $name
Count = 0
}
[PSCustomObject]$NewCsvEntry | Export-CSV "C:\Users\DWodzinski-admin\Documents\test.csv" -NoTypeInformation -Append
}
$i
}
}
$csv | Export-CSV "C:\Users\DWodzinski-admin\Documents\test.csv" -NoTypeInformation -Append
}
i changed it so that it only imports your csv once, at the start, and exports (overwrites) it at the end. maybe it also fixes your issue with the 10 entries, try it out.
clear
$csv = Import-Csv "C:\Users\DWodzinski-admin\Documents\test.csv"
$Roles = Get-Content 'C:\Users\DWodzinski-admin\Documents\Titles.txt'
$Users = #()
Foreach ($Item in $Roles) {
$Users += Get-ADUser -Filter { title -like $Item } -properties title, SamAccountName | Select SamAccountName, title
}
Foreach ($User in $Users) {
$AdGroups = Get-ADPrincipalGroupMembership $User.SamAccountName | Select Name
foreach ($thing in $AdGroups) {
$name = $thing.name
foreach ($i in $csv) {
if ($i.Group -eq $name -and $i.Title -eq $User.title) {
$i.Count += 1
}
else {
$newCsvEntry = [PSCustomObject]#{
Title = $User.title
Group = $name
Count = 0
}
$csv += $newCsvEntry
}
$i
}
}
}
$csv | Export-CSV "C:\Users\DWodzinski-admin\Documents\test.csv" -NoTypeInformation -Force
If anyone want to know how I did it, I created ArrayList with CustomObjects:
clear
$Roles = Get-Content 'C:\Users\DWodzinski-admin\Documents\Titles.txt'
$Users = #()
$List = New-Object System.Collections.ArrayList
$Object = [PSCustomObject]#{
Name = 'Pierwszy';
Title = 'Czarny';
Count = 0;
}
$List.add($Object)
Foreach ($Item in $Roles){
$Users += Get-ADUser -Filter {title -like $Item} -properties title, SamAccountName | Select SamAccountName, title
}
Foreach ($User in $Users){
$AdGroups = Get-ADPrincipalGroupMembership $User.SamAccountName | Select Name
foreach($Group in $AdGroups){
if($List | Where Name -eq $Group.name | Where Title -eq $User.title){
$temp = $List | Where Name -eq $Group.name | Where Title -eq $User.title
$temp.Count += 1
} else {
$Object = [PSCustomObject]#{
Name = $Group.Name;
Title = $User.title;
Count = 1;
}
$List.add($Object)
}
}
}
$FilePathLocation = "C:\Users\DWodzinski-admin\Documents\test.csv"
$List | Export-Csv -Path $FilePathLocation -NoTypeInformation

ADAccount inactive accounts piping through ADUser issue

I did some PowerShell script to find inactive users in AD that are 90 days old looping through all DCs to also get LastLogon attribute. I also need some extra attributes that only ADUser can bring out. I've got an error when running my script with the piping in the $users = Search-ADAccount line.
Import-Module ActiveDirectory
function Get-ADUsersLastLogon() {
$dcs = Get-ADDomainController -Filter {Name -like "*"}
$OUs = #()
$OU += "ou=Users-A,ou=Users,ou=Items,dc=mydc,dc=com"
$OU += "ou=Users-B,ou=Users,ou=Items,dc=mydc,dc=com"
$time = 0
$exportFilePath = "c:\tmp\lastLogon-test $(get-date -f dd-MM-yyyy).csv"
$columns = "name;username;whencreated;whenchanged;DNname;datetime"
#InactiveTest
$InactiveFilter = #{
UsersOnly = $true
AccountInactive = $true
TimeSpan = New-Timespan -Days 90
}
#EndInactiveTest
Out-File -FilePath $exportFilePath -Force -InputObject $columns
foreach ($OU in $OUs) {
$users = Search-ADAccount #InactiveFilter |
Get-ADUser -Filter * -SearchBase $OUs -Property displayName, whenCreated, whenChanged
foreach ($user in $users) {
foreach($dc in $dcs) {
$hostname = $dc.HostName
$currentUser = Get-ADUser $user.SamAccountName |
Get-ADObject -Server $hostname -Properties lastLogon
if ($currentUser.LastLogon -gt $time) {
$time = $currentUser.LastLogon
}
}
$dt = [DateTime]::FromFileTime($time)
$row = $user.displayName + ";" + $user.SamAccountName + ";" +
$user.whenCreated + ";" + $user.whenChanged + ";" +
$user.distinguishedName + ";" + $dt
Out-File -FilePath $exportFilePath -Append -NoClobber -InputObject $row
$time = 0
}
}
}
Get-ADUsersLastLogon
I think iterating through DC's and OU's and then collecting only the inactive users last logon dates could best be done using a Hashtable object as intermediate storage.
This helps avoiding duplicate entries and gives the opportunity to compare the LastLogonDate properties.
For the final output, it uses one single cmdlet called Export-Csv.
Below my (untested) code:
function Get-ADUsersLastLogon {
# get your ad domain
$DomainName = (Get-ADDomain).DNSRoot
# get all DC hostnames as string array
$DCs = Get-ADDomainController -Filter * -Server $DomainName | Select-Object -ExpandProperty Hostname
# create an array of OU distinghuished names used as SearchBase
$OUs = "OU=Users-A,OU=Users,OU=Items,DC=mydc,DC=com", "OU=Users-B,OU=Users,OU=Items,DC=mydc,DC=com"
$exportFilePath = "c:\tmp\lastLogon-test $(Get-Date -Format dd-MM-yyyy).csv"
$InactiveFilter = #{
UsersOnly = $true
AccountInactive = $true
TimeSpan = New-Timespan -Days 90
}
# use a lookup Hashtable to eliminate duplicates and collect only the latest logon dates
$lookup = #{}
# loop through the list of dc's
foreach ($dc in $DCs) {
# loop through the list of OU's
foreach ($ou in $OUs) {
$users = Search-ADAccount #InactiveFilter -SearchBase $ou -Server $dc
foreach($user in $users) {
# get the properties we want from the AD User.
# using the PowerShell property names, we get the dates already converted into DateTime objects.
$usr = Get-ADUser -Identity $user.DistinguishedName -Server $dc -Properties DisplayName, Created, Modified, LastLogonDate |
Select-Object #{Name = 'Name'; Expression = {$_.DisplayName}},
SamAccountName,
#{Name = 'WhenCreated'; Expression = {$_.Created}},
#{Name = 'WhenChanged'; Expression = {$_.Modified}},
#{Name = 'DistinguishedName'; Expression = {$_.DistinguishedName}},
#{Name = 'LastLogon'; Expression = {$_.LastLogonDate}}
if ($usr) {
if ($lookup.ContainsKey($($user.DistinguishedName))) {
# we have collected this user before
$lastLogon = $lookup[$($user.DistinguishedName)].LastLogon
if ($lastLogon) {
if (($usr.LastLogon) -and $lastLogon -lt $usr.LastLogon) {
# only store this new instance if the $user.LastLogon property is of a later date
$lookup[$($user.DistinguishedName)] = $usr
}
}
}
else {
# this is a new user, so add the object to the HashTable
$lookup[$($user.DistinguishedName)] = $usr
}
}
else {
# should never happen..
Write-Warning "User $($user.SamAccountName) not found."
}
}
}
}
# export the objects contained in the $lookup Hashtable as CSV
($output = foreach ($key in $lookup.Keys) {
$lookup.$key
}) | Export-Csv -Path $exportFilePath -NoTypeInformation -Delimiter ';' -Encoding UTF8 -Force
}
Hope that helps
#voilier Sorry, I don't understand how it works for you. Pasted your code and Get-ADUser cmdlet expects filter value. If you use get-help get-aduser -full you will see that searchbase parameter can only be used with Filter or LDAPFilter parameters. More than that neither of them accept pipeline input. Identity parameter accepts pipeline input by value only. so you need to use the distinguishedname property from Search-ADAccount #InactiveFilter for example distinguishedname and pass it to filter
$users = Search-ADAccount #InactiveFilter | %{Get-ADUser -filter {distinguishedname -eq $_.distinguishedname} -SearchBase $OU -Property displayName, whenCreated, whenChanged}
I replaced your $users=... part with the code above and now I see no errors and CSV file created successfully.
Replace your foreach $ou in $ous with this and check the csv file. it works on my computer
Foreach ($ou in $ous){
$users = (Search-ADAccount #InactiveFilter | %{Get-ADUser -filter {distinguishedname -eq $_.distinguishedname} -SearchBase $OU -Property displayName, whenCreated, whenChanged})
foreach ($user in $users) {
foreach($dc in $dcs) {
$hostname = $dc.Name
$last_logon_time=((Get-ADUser $user.SamAccountName | Get-ADObject -Server "$hostname" -Properties lastLogon) |?{$_.lastlogon -gt $time}) | select -ExpandProperty lastlogon
}
$dt = [DateTime]::FromFileTime("$last_logon_time")
$row = $user.displayName + ";" + $user.SamAccountName + ";" +
$user.whenCreated + ";" + $user.whenChanged + ";" +
$user.distinguishedName + ";" + $dt
Out-File -FilePath $exportFilePath -Append -NoClobber -InputObject $row
$last_logon_time = 0
}
}
I hope it helps you

Modifying PowerShell to export Windows Update to CSV file for specific OU only not working

I need to get the list of the server last Windows Update patch from multiple different OU and then export it as in CSV file with the below column and its sample result I gather manually running Get-HotFix locally on each server:
ServerName, Last Time Update Installed, KB Number, KB Update Name, InstalledBy
PRODSQL01-VM, 31/12/2018 02:46:55, KB4462930, Cumulative Update, NT AUTHORITY\SYSTEM
PRODSQL02-VM, 18/12/2018 12:00:00 AM, KB4471324, Security Update, DOMAIN\SVC_SCCM
PRODDC01-VM, 16/1/2019 02:16:31, KB4343669, Cumulative Update, DOMAIN\SVC_SCCM
PRODDC02-VM, 13/1/2018 03:00:00 AM, KB4457146, Security Update, DOMAIN\Admin-Staff1
This is the modified script for multiple OU processing, but somehow the result is still a blank CSV file:
$CsvFile = 'C:\Result.csv'
$key = 'SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Install'
$keytype = [Microsoft.Win32.RegistryHive]::LocalMachine
$OUList = #(
"OU=TEST Servers,OU=MyDomain Testing,DC=MyDomain,DC=com"
"OU=PROD Servers,OU=Servers,OU=MyDomain Sydney,DC=MyDomain,DC=com"
"OU=PROD Servers,OU=Servers,OU=New Company,DC=MyDomain,DC=com"
)
$OUList | ForEach-Object {
$OU = $_
$Computers = Get-ADComputer -Filter {Enabled -eq $True -and OperatingSystem -like "*Server*"} -SearchBase $OU |
Select-Object -ExpandProperty DNSHostName |
ForEach-Object {
If (Test-Connection $_ -Count 1 -Quiet) {
$_
}
Else {
Write-Host "Cannot reach $($_)" -ForegroundColor Red
}
}
ForEach ($computer in $Computers) {
Try {
$remoteBase = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($keytype, $computer)
$regKey = $remoteBase.OpenSubKey($key)
$keyValue = $regkey.GetValue('LastSuccessTime')
Write-Host ''
Write-Host "$($computer): last time updates were installed was $($keyValue)"
}
Catch {
$ | Write-Error
}
Finally {
If ($regKey) {$regKey.Close()}
}
}
} | Export-Csv -Path $Csvfile -NoTypeInformation
As others have mentioned, you're doing write-host rather than adding anything to your CSV file.
Note that I haven't tested any of the below code - it's just a bit of rearranging.
$CsvFile = 'C:\Result.csv'
$Results = #() #object to hold the output
$key = 'SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\Results\Install'
$keytype = [Microsoft.Win32.RegistryHive]::LocalMachine
$OUList = #(
"OU=TEST Servers,OU=MyDomain Testing,DC=MyDomain,DC=com"
"OU=PROD Servers,OU=Servers,OU=MyDomain Sydney,DC=MyDomain,DC=com"
"OU=PROD Servers,OU=Servers,OU=New Company,DC=MyDomain,DC=com"
)
ForEach ($OU in $OUList) {
Get-ADComputer -Filter {Enabled -eq $True -and OperatingSystem -like "*Server*"} -SearchBase $OU |
Select-Object -ExpandProperty DNSHostName |
ForEach-Object {
If (Test-Connection $_ -Count 1 -Quiet) {
Try {
$remoteBase = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($keytype, $_)
$regKey = $remoteBase.OpenSubKey($key)
$keyValue = $regkey.GetValue('LastSuccessTime')
# Add result to Results array. Not Result
$results += [pscustomobject]#{
Computer = $_
LastSuccessTime = $KeyValue
}
}
Catch {
$ | Write-Error
}
Finally {
If ($regKey) {$regKey.Close()}
}
}
Else {
Write-Host "Cannot reach $($_)" -ForegroundColor Red
}
}
}
#export result object to CSV
$Results | Export-Csv -Path $Csvfile -NoTypeInformation

Get all AD Users and their group memberships (recursively) using Powershell

I have a script from a previously answered question, but don't have enough reputation to comment. I tried to run that script and came across this error message:
Export-CSV : Cannot append CSV content to the following file: C:\users.csv. The appended object does not have a property that corresponds to the following column: User;Group. To continue with mismatched properties, add the -Force parameter, and then retry the command.
How can I debug this script to resolve this issue?
Function Get-ADGroupsRecursive{
Param([String[]]$Groups)
Begin{
$Results = #()
}
Process{
ForEach($Group in $Groups){
$Results+=$Group
ForEach($Object in (Get-ADGroupMember $Group|?{$_.objectClass -eq "Group"})){
$Results += Get-ADGroupsRecursive $Object
}
}
}
End{
$Results | Select -Unique
}}
import-module activedirectory
$users = get-aduser -Filter {Name -Like "*"} -Searchbase "OU=Sample Accounts,DC=domain,DC=com" -Properties MemberOf | Where-Object { $_.Enabled -eq 'True' }
$targetFile = "C:\users.csv"
rm $targetFile
Add-Content $targetFile "User;Group"
foreach ($user in $users)
{
$Groups = $User.MemberOf
$Groups += $Groups | %{Get-ADGroupsRecursive $_}
$Groups | %{New-Object PSObject -Property #{User=$User;Group=$_}}|Export-CSV $targetfile -notype -append
}
try this function
function Get-InChainGroups
{
param (
[parameter(mandatory = $true)]
$user,
$domain)
$user1 = (get-aduser -filter { name -eq $user } -server $domain).distinguishedname
Write-verbose "checking $user"
$ldap = "(&(objectcategory=group)(groupType:1.2.840.113556.1.4.803:=2147483648)(member:1.2.840.113556.1.4.1941:=$user1))"
try { Get-ADobject -LDAPFilter $ldap -server $domain | select #{ n = 'Identity'; e = { $user } }, Name, #{ n = 'DN'; e = { $_.distinguishedname } } | ft -a }
catch { "Exception occurred" }
}