Active Directory and Powershell - windows

I'm currently rewriting a script that is in VB into a Powershell script.
What the script does is search our Active Directory for a user based on the script-users input.
Function PromptForName{
$nameInput = "*"
$nameInput += Read-Host ("Please enter a full or partial name.")
$nameInput += "*"
return $nameInput
}
Function FindUsers{
param ([string]$n)
$usersArray = Get-ADUser -f {DisplayName -like $n} | Select-Object Name
return $usersArray
}
This code will print out the correct list of names. What I then want to do is allow the user to choose one of those names, and have more information about that person. I'm stuck at allowing the script-user to select one of those names.
How can I prompt for another input; where the box will display a numbered list of all the names that FindUsers gave, and then return a number based on which user they chose? I'm completely lost.
This is currently how I am trying to do it, although I'm pretty sure it's completely wrong.
Function PrintUsers{
param $a
[int]$i, $j
[string]$userList
$j = 1
foreach($object in $array){
$userList += ($j + $array[$i])
$j++
}
return $userList
}
Function SelectUser{
param $list
$user = Read-Host ($list)
}
EDIT:
I have updated my functions to the following:
Function FindUsers{
param ([string]$n)
$usersArray = #(Get-ADUser -f {DisplayName -like $n} | Select-Object Name| Format-List)
return $usersArray
}
Function PrintUsers{
param ([String[]]$array)
$i
for($i = 1; $i -lt $usersArray.length; $i++){
Write-Host "$i. $($usersArray[$i-1].Name)"
}
}
The output after FindUsers is like this:
Name : xxxxx yyyyy
Name : xxxxx zzzzz
etc.
So the return of $usersArray is printing all that.
I don't want any printing until the PrintUsers function, and I want it to be in a numbered list type format like this:
1. xxxx yyyy
2. xxxx zzzz
etc.
I'm having the most difficult time figuring this out.

# get all users
$usersArray = #(Get-ADUser -f {DisplayName -like $n} )
# create menu
for($i=1; $i -le $usersArray.count; $i++){
Write-Host "$i. $($usersArray[$i-1].Name)"
}
# prompt for user number
$user = Read-Host Enter the user number to get more info
# display full info for selected user
$usersArray[$user-1] | Format-List *

Use Add-Member to add a unique identifier to each user. Let's treat processes as if they're user objects, for the sake of example:
$procs = gps;
$procs = $procs | % { $i=0; } {
Add-Member -MemberType NoteProperty -InputObject $_ -Name Number -Value $i -PassThru;
$i++;
};
$procs | select Number,Name;
$procid = Read-Host -Prompt 'Enter the number of the process you would like to operate on';
$proc = $procs | ? { $_.Number -eq $procid };
Write-Host -Object ('Operating on proc: {0}' -f $proc.Name);

Related

How to kill all RDP sessions using powershell script

I am writing powershell script like below but unable to read a sessionids from the query result then kill all the active sessions.
$queryResults = quser /server:$server; Write-Output "users : $users";
foreach($queryResult in $queryResults){
Write-Output "session ID : $queryResult.sessionid";
}
Any suggestion how to read a session ids from the queryResult then logoff all the sessions.
You need to parse the output of quser (it's not a PS command so the output is a simple string) - I've done it this way so that you receive an object that's more useful:
$server = 'myserver'
# Get list of logged in users
$queryResults = quser /server:$server
# Parse the quser output and store results in a new object. Skip first line as that's just headers.
$UserList = $queryResults | Select-Object -Skip 1 -ErrorAction Stop | ForEach-Object {
# Split up the current line into its elements
$CurrentLine = $_.Trim() -Replace '\s+',' ' -Split '\s'
# Create a new object from all the line elements. If session is disconnected different fields will be selected
If ($CurrentLine[2] -eq 'Disc') {
$SearchResult = [pscustomobject]#{
UserName = $CurrentLine[0];
SessionName = $null;
Id = $CurrentLine[1];
State = $CurrentLine[2];
IdleTime = $CurrentLine[3];
LogonTime = $CurrentLine[4..($CurrentLine.GetUpperBound(0))] -join ' '
}
}
Else {
$SearchResult = [pscustomobject]#{
UserName = $CurrentLine[0];
SessionName = $CurrentLine[1];
Id = $CurrentLine[2];
State = $CurrentLine[3];
IdleTime = $CurrentLine[4];
LogonTime = $CurrentLine[5..($CurrentLine.GetUpperBound(0))] -join ' '
}
}
# Output the custom object (stores it in $UserList)
$SearchResult
}
# Display a list of users/states/Id
$UserList | Select-Object UserName, Id, State, IdleTime
# To logoff everyone, you can just loop through
#$UserList | ForEach-Object {
# logoff $_.Id
#}
# Or you could just logoff the disconnected users:
#$UserList | Where-Object {$_.State -eq 'Disc'} | ForEach-Object {
# logoff $_.Id
#}

Export reg value to csv

i have 1 question:
i need verify 3 reg key on 20 pc and export result on csv file.
I used this string
Get-ItemProperty -Path hklm:"\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\" -Name "keyname" | Export-csv -path "csvpath"
and recive the all value for thi key but i don't need see the "PSPath, PSParentPath, PSChildName, PSDrive, PSProvider.
now i was thinking of making a script with variables to simplify it, but at this point i would like it to tell me even if the key was not found and the basic thing i can run it from the DC to all machines (about 20).
this could be a starting point
$key1 = name key 1
$key2 = name key 2
$key3 = name key 3
$hostname= hostname
$regkey= get-itemprperty -path ecc....
and now i'm seeing how you implement the verification loop and export everything to csv
thx
To verify the key existence, use Test-Path.
Computer names and Key names as arrays of strings.
No experience with remoting, I think you'll be using Invoke-Command, but this should give you an idea of looping and getting all non-PS properties:
Computer1
Computer2
Computer3
'# -split '\n'
$keyNames = #'
KeyName1
KeyName2
KeyName3
`# -split '\n'
ForEach ( $Comoputer in $Computers) {
ForEach ( $KeyName in $KeyNames ) {
If ( Test-Path $KeyName )
{
$AllProps = ($key = Get-Item $KeyName).Property
(Get-ItemProperty $key).PSobject.Properties | where name -in $AllProps | select Name , Value
<< Create output >>
}
Else
{
"$ComputerName: $KeyName not found."
}
}
} | Export-Csv "\\Path\to\CsvFile"
To probe multiple computers for 3 registry properties and output the result in a CSV file, you can use Invoke-Command like below:
$computers = 'pc01','pc02','pc03' # etc. the 20 computers you want to probe
$propertynames = 'property1','property2','property3' # you may use wildcards here
# loop over the computers
$result = foreach ($computer in $computers) {
if (!(Test-Connection -ComputerName $computer -Count 1 -Quiet)) {
Write-Warning "Computer '$computer' is not responding"
continue # skip this computer and proceed with the next
}
Invoke-Command -ComputerName $computer -ScriptBlock {
$regPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
# create a temporary Hashtable to store the items
$hash = [ordered]#{}
# loop over the properties
foreach ($prop in $using:propertynames) {
$entry = Get-ItemProperty -Path $regPath -Name $prop -ErrorAction SilentlyContinue
if ($entry) {
$hash['ComputerName'] = $using:computer
$entry = $entry | Select-Object * -ExcludeProperty PS*
# use a loop in case you have used wildards for the property names
foreach ($item in $entry.PsObject.Properties) {
$hash[$item.Name] = $item.Value
}
}
else {
Write-Warning "Could not find property '$prop'"
}
}
if ($hash.Count) {
# output the hash converted to PSObject
[PsCustomObject]$hash
}
}
}
# remove the properties added by Invoke-Command
$result = $result | Select-Object * -ExcludeProperty PS*,RunspaceId
# output to gridview
$result | Out-GridView
# output to CSV file
$result | Export-Csv -Path 'X:\Path\To\TheResults.csv' -NoTypeInformation

Get-ADUser - output a blank line fo CSV file when no data is found

I've cobbled together (from various online sources) a script that can return data about AD users from a CSV file. It can clearly mark when a user has NOT been found, but perhaps just as important to me is a way of outputting a blank line to the CSV file when the input CSV file also has a blank. That would save a lot mucking around in Excel to make sure that all the blank lines correspond to each other (and subsequently where user data has been found). A 1-to-1 comparsion is the goal.
Here is what I have...
$ImportCSV = "C:\Users\x\Desktop\testCSV_in.csv"
$Names = Import-Csv -Path $ImportCSV
$Results = foreach ($Name in $Names) {
$filter = $Name.samAccountName
$User = Get-ADUser -Filter "SamAccountName -like '$filter'" -Properties Samaccountname, Givenname, Surname, EmailAddress, Name
#blank lines input CSV
if ( $User.SamAccountName -eq "" ) {
# please help
}
# found user
if ( $User ) {
$User |
Select-Object -Property Samaccountname, Givenname, Surname, EmailAddress, Name
}
# not found user
else {
[pscustomobject]#{
SamAccountName = 'MISSING ACCOUNT'
}
}
}
$Results | Export-Csv -Path "C:\Users\x\Desktop\testCSV_out.csv" -NoTypeInformation -Encoding UTF8
All the possible combinations that I can think of for $User.SamAccountName -eq "" just return back a CSV file that doesn't reflect the gaps that I purposely introduced (to mimic real use-cases).
I am sure, it's a just a line or two code that's needed. Thanks.
If you want a normalized export you need a normalized Object, meaning, all columns for your CSV must exist for all lines, even if they're $null. Try this code, see if it works:
$out = {
param($samAccountName)
[pscustomobject]#{
Samaccountname = $samAccountName
Givenname = $User.GivenName
Surname = $User.SurName
EmailAddress = $User.EmailAddress
Name = $User.Name
}
}
Import-Csv -Path 'C:\Users\x\Desktop\testCSV_in.csv' | ForEach-Object {
if([string]::IsNullOrWhiteSpace($_.samAccountName)) {
return & $out -samAccountName 'User Not Found on CSV'
}
$params = #{
Properties = 'Samaccountname', 'Givenname', 'Surname', 'EmailAddress', 'Name'
LDAPFilter = "(SamAccountName={0})" -f $_.samAccountName
}
$user = Get-ADUser #params
if(-not $user) {
return & $out -samAccountName 'User Not Found on AD'
}
& $out -samAccountName $user.samAccountName
} | Export-Csv -Path "C:\Users\x\Desktop\testCSV_out.csv" -NoTypeInformation -Encoding UTF8
Edit: Import-Csv, same as ConvertFrom-Csv will skip empty lines. At least one column has to be populated.
I found a way to get this same functionality and also add manager email and calculated fields as well:
Import-CSV C:\test\CSV_in.csv |
ForEach-Object {
Try{
$u = Get-ADUser $_.samaccountname -Properties * -ErrorAction Stop | Select GivenName, Surname, samaccountname, Title, Department, #{N='Manager';E={(Get-ADUser $_.Manager).Name}},#{N="ManagerEmail";E={(Get-ADUser -Property emailaddress $_.Manager).emailaddress}}
[PSCustomObject]#{User = $_.name; samaccountname = $u.samaccountname; Manager = $u.Manager; ManagerEmail = $u.ManagerEmail}
}
Catch{
[PSCustomObject]#{User = $_.name; samaccountname = 'Not Found'; Manager = 'N/A'; ManagerEmail = 'N/A'}
}
} | Export-CSV c:\test\CSV_out.csv -NoTypeInformation

Programming a powershell script to read emails from a CSV and then returning a CSV with Email addresses and GUid's

I am trying to write a script that should take in a CSV file which contains a list of Email addresses of old mailboxes on Exchange. I want to then search each address and return the corresponding Guid with each mail box into a CSV so I can keep a record when it comes to deleting mailboxes that have been backed up.
$UserAddresses = Import-Csv -Path “C:// File path”
$results = foreach ($address in $UserAddresses) {
Get-MailboxDatabase |
Get-MailboxStatistics |
Where { $_.Identity -eq $address } |
Select-Object DisplayName, MailboxGuid, Database
}
$results | Export-Csv C://ReturnedFilePath
Write-Output "Finished"
This code returns a blank CSV file.
Edit.
$UserAddresses = Import-CSV -Path C:/Temp/XXXXX
$counter
$results = Foreach ($address in $UserAddresses){
$Email = $($address.EmailAddresses)
Get-Mailbox -Identity $Email | Select-Object Name,ExchangeGuid
$counter += 1
if ($counter >= 5)
{
Write-output "Finished"
continue
}
}
$results | Export-CSV C:/Temp/ReportOfGuids.Csv

Prefix filename with sequential number. Be prompted (powershell)

I am trying the insert a number as a prefix "n__" for each filename in the CWD (not recursively). n is a number with increments of +1, user needs to enter the first value. To number, the files must be pre-sorted in lowest-value-first order based on the filename.
$i = Read-Host 'Enter first file number'
$confirmation = Read-Host "Re-enter first file number"
if ($confirmation -eq '=') {
# proceed
}
Get-ChildItem | Sort-Object | Rename-Item -NewName { "$i+1" + "__" + $_.Name }
What I am missing at the i+ and in order to ensure that the files are sorted for numbering?
Intended result:
101__121.ext
102__211.ext
103__375.ext
# putting [int] before the variable will insure that your input is an integer and not a string
[int] $i = Read-Host 'Enter first file number'
[int] $confirmation = Read-Host "Re-enter first file number"
# Your if statement seemed to not make sense. This is my take
if ($confirmation -eq $i)
{
Get-ChildItem | Sort-Object | Foreach-Object -Process {
$NewName = '{0}__{1}' -f $i,$_.Name # String formatting e.x. 1__FileName.txt
$_ | Rename-Item -NewName $NewName # rename item
$i ++ # increment number before next iteration
}
}
else
{
Write-Warning -Message "Your input did not match"
}
Assuming files are numeric names (121.ext,211.ext,etc.)
[Int]$UserInput = Read-Host -Prompt 'Enter first file number'
[Int]$Confirmation = Read-Host -Prompt 'Re-enter first file number'
If ($Confirmation -ne $UserInput) { Exit }
$Collection = Get-ChildItem | Sort-Object -Property #{Expression={[Int]$_.BaseName}}
While (($UserInput - $Confirmation) -lt $Collection.Count)
{
$Collection[$UserInput - $Confirmation] |
Rename-Item -NewName "$($UserInput)__$($_.Name)"
$UserInput++
}

Resources