I am trying to combine the below snippets into the existing code for updating user AD attributes.
Add-Type -AssemblyName 'Microsoft.VisualBasic'
Do
{
Write-Host -Object 'Enter a sAMAccountName / Alias "First.Lastname", or nothing (Press Enter) to leave; wildcards and a space separated list are NOT supported.' -ForegroundColor Yellow
$UserInput = [Microsoft.VisualBasic.Interaction]::InputBox('Enter the User AD account to check', 'sAMAccountName / Alias "First.Lastname"', $UserInput)
If ($UserInput)
{
$(ForEach ($Username in $UserInput.Split(' ', [StringSplitOptions]::RemoveEmptyEntries))
{
If ($ADUser = Get-ADUser -Filter { samAccountName -like $UserName } -Properties DisplayName)
{
Write-Verbose -Message "Processing $($ADUser.DisplayName)"
Write-Host "The sAMAccountName $($UserInput) matching to the AD account '$($ADUser.DisplayName)'" -ForegroundColor Green
}
Else
{
Write-Host "Could not find a user with a sAMAccountName matching '$($UserName)' !" -ForegroundColor Red | Write-Warning
}
})
}
}
Until (-not $UserInput)
The snippets above are working for validating user input against the Active Directory user account, to see if the AD account is valid or not.
This is the main menu item code which is used for updating the AD attributes based on the https://answers.microsoft.com/en-us/msoffice/forum/msoffice_o365admin-mso_exchon-mso_o365b/recipient-type-values/7c2620e5-9870-48ba-b5c2-7772c739c651
# Set The attributes value for Remote Regular User Mailboxes
$replace = #{
msExchRemoteRecipientType = 4
msExchRecipientDisplayType = -2147483642
msExchRecipientTypeDetails = 2147483648
}
# Set The attributes value for Remote Shared Mailboxes
$replace = #{
msExchRemoteRecipientType = 100
msExchRecipientDisplayType = -2147483642
msExchRecipientTypeDetails = 34359738368
}
Set-ADUser -Identity $ADUser -Replace $replace -WhatIf
This is the main menu item code, but without the input check validation section:
If (!(Get-Module "*ActiveDirectory*")) {
Try { Import-Module ActiveDirectory -ErrorAction Stop }
Catch { Write-Warning "Unable to load Active Directory module because $($Error[0])"; Exit }
}
Add-Type -AssemblyName 'Microsoft.VisualBasic'
$Input = [Microsoft.VisualBasic.Interaction]::InputBox('Enter the User AD account to check', 'sAMAccountName / Alias "First.Lastname"', $Input)
$properties = 'Name,msExchRemoteRecipientType,msExchRecipientDisplayType,msExchRecipientTypeDetails,proxyAddresses' -split ','
$ADUserAttributesValues = Get-ADUser -identity $Input -Properties $properties |
Select-Object Name,
msExchRemoteRecipientType,
msExchRecipientDisplayType,
msExchRecipientTypeDetails
$menuCaption = "Hybrid AD User account Exchange attribute modification"
$menuMessage = "Please select the action to be applied to the user $($Input) `n $($ADUserAttributesValues)"
## Format: "Menu Text" = "Help Text"
## "Menu Text" must match the options in the Switch statement below
## "&" marks the character to use as hotkey
$menu = [ordered]#{
'Remote &Shared Mailbox' = "Convert $($Input) as Remote Shared Mailbox"
'Remote &User Mailbox' = "Convert $($Input) as Remote User Mailbox"
'&Quit' = 'Leave without changes'
}
$menuChoices = #()
$menu.Keys | ForEach-Object {
$choice = [System.Management.Automation.Host.ChoiceDescription]$_
$choice.HelpMessage = $menu[$_]
$menuChoices += $choice
}
$answer = $host.UI.PromptForChoice($menuCaption , $menuMessage , $menuChoices, ($menu.Count - 1))
Switch ($menuChoices[$answer].Label) {
'Remote &Shared Mailbox' {
Clear-Host
Write-Host "You selected to convert $($Input) as Remote Shared Mailbox" -ForegroundColor Yellow
# Set The attributes value for Remote Shared Mailboxes
$replace = #{
msExchRemoteRecipientType = 100
msExchRecipientDisplayType = -2147483642
msExchRecipientTypeDetails = 34359738368
}
Set-ADUser -Identity $ADUserAttributesValues.Name -Replace $replace -WhatIf
# Check the attributes value
Get-ADUser -identity $Input -Properties $properties |
Select-Object Name,
msExchRemoteRecipientType,
msExchRecipientDisplayType,
msExchRecipientTypeDetails,
(#{Label = 'Email Address'; Expression = {($_.proxyAddresses | Where-Object {($_ -like 'SMTP*') -and ($_ -notlike '*onmicrosoft.com') } | Sort-Object -CaseSensitive -Descending | ForEach-Object {$_.Split(':')[1]}) -join ', ' }})
}
'Remote &User Mailbox' {
Clear-Host
Write-Host "You selected to convert $($Input) as Remote User Mailbox" -ForegroundColor Yellow
# Set The attributes value for Remote Regular User Mailboxes
$replace = #{
msExchRemoteRecipientType = 4
msExchRecipientDisplayType = -2147483642
msExchRecipientTypeDetails = 2147483648
}
Set-ADUser -Identity $ADUserAttributesValues.Name -Replace $replace -WhatIf
# Check the attributes value
Get-ADUser -identity $Input -Properties $properties |
Select-Object Name,
msExchRemoteRecipientType,
msExchRecipientDisplayType,
msExchRecipientTypeDetails,
(#{Label = 'Email Address'; Expression = {($_.proxyAddresses | Where-Object {($_ -like 'SMTP*') -and ($_ -notlike '*onmicrosoft.com') } | Sort-Object -CaseSensitive -Descending | ForEach-Object {$_.Split(':')[1]}) -join ', ' }})
}
default {
Write-Host 'Goodbye' -ForegroundColor Green
Exit
}
}
So how to combine it to the above?
The code above was created as a rough menu item with lots of repetitions, but it works. I believe it can be optimized by creating functions, but not sure how since the attributes are different.
It is a best practice for Functions to do one thing and one the well.
You then assign the function to your UI design objects.
As for the first part of your post. You cannot do this...
Write-Host "Could not find a user with a sAMAccountName matching '$($env:USERNAME)' !" -ForegroundColor Red | Write-Warning
... it will only return the red text never the warning at all.
Write-Host clears the buffer and thus cannot be used to send results down the
pipeline.
Also, you are literally calling for two warnings for the same code line/string text (one in a red color then the default Write-Warning cmdlet color). So, that's not a thing.
The Write-Warning cmdlet already generates a default color for it, which you cannot change directly. So, just use this...
Write-Warning -Message "Could not find a user with a sAMAccountName matching '$($Username)' !"
... or you have to create your own function to handle colorized text, leveraging stuff like this...
# Colors in the consolehost
$host.UI.RawUI
$host.PrivateData
$host.PrivateData.ConsolePaneTextBackgroundColor
$host.PrivateData.ConsolePaneForegroundColor
# In the ISE
$host.UI.RawUI
[enum]::GetNames([System.ConsoleColor])
$psise.Options.ConsolePaneForegroundColor
# Using .Net
[System.Windows.Media.Colors]
[System.Windows.Media.Colors]::White
[enum]::GetNames([System.ConsoleColor])
[enum]::GetNames([System.ConsoleColor])[0]
$psise.Options
$psise.Options.ConsolePaneForegroundColor
... when using cmldets that don't have color like Write-Output, which is pipeline friendly. You can create your own GUI message boxes/forms to have more control.
Why are you doing this -WhatIf here?
Set-ADUser -Identity $ADUserAttributesValues.Name -Replace $Replace -WhatIf
If this is live code, then this Set-* will never happen and thus makes everything below it is moot, because nothing has changed. Unless what you are posting is your test/debug/OpsCheck code, then this is prudent.
You have this ...
# Check the attributes value
Get-ADUser -identity $UserInput -Properties $MailProperties |
Select-Object Name,
msExchRemoteRecipientType,
msExchRecipientDisplayType,
msExchRecipientTypeDetails,
(#{Label = 'Email Address'; Expression = {
($_.proxyAddresses |
Where-Object {($_ -like 'SMTP*') -and
($_ -notlike '*onmicrosoft.com')
} |
Sort-Object -CaseSensitive -Descending |
ForEach-Object {$_.Split(':')[1]}) -join ', ' }})
}
... shown twice. Just make it a separate function and call it as needed
So, the below is untested, since I am not anywhere near a lab to try this.
Note:
I am using PowerShell's natural line breaks to ensure code readability and not be so long.
There are times when long lines cannot be avoided, but whenever possible, if your code line won't fit on a normal 8.5x11.5 sheet of paper like a normal Word doc or book, then it is too long and a target for refactoring, using natural line breaks, splatting, hash tables, PSCustomerObjects, etc.
Refactored code: Again, I am not in my AD/Exchange lab environment, so I useg Localuser cmdlets for a quick and dirty code test.
(personally I'd put this is a single user form using the PowerShell Help and avoid all the menuing stuff altogether - see this: Poor Man’s GUI or create a custom form using https://poshgui.com.
Add-Type -AssemblyName 'Microsoft.VisualBasic'
<#
Using PowerShell StrictMode Option force code compliance
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/set-strictmode?view=powershell-7
#>
$UserInput = $null
$ADUser = $null
$MailProperties = $null
$ADUserAttributesValues = $null
$MenuCaption = $null
$MenuMessage = $null
$Menu = $null
$MenuChoices = $null
$UserAnswer = $null
$choice = $null
$MenuChoices = $null
$MenuCaption = $null
$MenuMessage = $null
$MenuChoices = $null
$ReplaceValue = $null
Function Get-ADAttributesValue
{
[CmdletBinding(SupportsShouldProcess)]
[Alias('gaav')]
Param
(
)
# Check the attributes value
Get-ADUser -identity $UserInput -Properties $MailProperties |
Select-Object Name,
msExchRemoteRecipientType,
msExchRecipientDisplayType,
msExchRecipientTypeDetails,
(#{Label = 'Email Address'; Expression = {
($PSitem.proxyAddresses |
Where-Object {($PSitem -like 'SMTP*') -and
($PSitem -notlike '*onmicrosoft.com')
} |
Sort-Object -CaseSensitive -Descending |
ForEach-Object {$PSitem.Split(':')[1]}) -join ', ' }})
}
$MailProperties = 'Name,
msExchRemoteRecipientType,
msExchRecipientDisplayType,
msExchRecipientTypeDetails,
proxyAddresses' -split ','
Do
{
Write-Host -Object 'Enter a samAccountName / Alias "First.Lastname", or nothing (Press Enter) to leave; wildcards and a space-separated list are NOT supported.' -ForegroundColor Yellow
$UserInput = [Microsoft.VisualBasic.Interaction]::
InputBox(
'Enter the User AD account to check',
'Name / Alias "First.Lastname"',
$UserInput
)
If ($UserInput)
{
$(ForEach ($Username in $UserInput.Split(' ', [StringSplitOptions]::RemoveEmptyEntries))
{
If ($ADUser = Get-ADuser -Filter { samAccountName -like $UserName } -Properties Name)
{
Write-Verbose -Message "Processing $($ADUser.Name)"
Write-Host "The samAccountName $($UserInput) matching to the AD account '$($ADUser.Name)'" -ForegroundColor Green
}
Else {Write-Warning -Message "Could not find a user with a samAccountName matching '$($UserName)' !"}
})
}
}
Until (-not $UserInput)
If (!(Get-Module "*ActiveDirectory*"))
{
Try { Import-Module ActiveDirectory -ErrorAction Stop }
Catch
{
Write-Warning "Unable to load Active Directory module because $($Error[0])"
Exit
}
}
$UserInput = [Microsoft.VisualBasic.Interaction]::
InputBox(
'Enter the User AD account to check',
'Name / Alias "First.Lastname"',
$UserInput
)
$ADUserAttributesValues = Get-ADUser -identity $Input -Properties $properties |
Select-Object Name,
msExchRemoteRecipientType,
msExchRecipientDisplayType,
msExchRecipientTypeDetails
$MenuCaption = "Hybrid AD User account Exchange attribute modification"
$MenuMessage = "Please select the action to be applied to the user $($UserInput) `n $($ADUserAttributesValues)"
## Format: "Menu Text" = "Help Text"
## "Menu Text" must match the options in the Switch statement below
## "&" marks the character to use as hotkey
$Menu = [ordered]#{
'Remote &Shared Mailbox' = "Convert $($UserInput) as Remote Shared Mailbox"
'Remote &User Mailbox' = "Convert $($UserInput) as Remote User Mailbox"
'&Quit' = 'Leave without changes'
}
$MenuChoices = #()
$Menu.Keys |
ForEach-Object {
$choice = [System.Management.Automation.Host.ChoiceDescription]$PSitem
$choice.HelpMessage = $Menu[$PSitem]
$MenuChoices += $choice
}
$UserAnswer = $host.UI.PromptForChoice(
$MenuCaption ,
$MenuMessage ,
$MenuChoices,
($Menu.Count - 1)
)
Switch ($MenuChoices[$UserAnswer].Label)
{
'Remote &Shared Mailbox' {
Clear-Host
Write-Host "You selected to convert $($UserInput) as Remote Shared Mailbox" -ForegroundColor Yellow
# Set The attributes value for Remote Shared Mailboxes
$ReplaceValue = #{
msExchRemoteRecipientType = 100
msExchRecipientDisplayType = -2147483642
msExchRecipientTypeDetails = 34359738368
}
# Set-ADUser -Identity $ADUserAttributesValues.Name -Replace $ReplaceValue -WhatIf
Get-ADAttributesValue
}
'Remote &User Mailbox' {
Clear-Host
Write-Host "You selected to convert $($UserInput) as Remote User Mailbox" -ForegroundColor Yellow
# Set The attributes value for Remote Regular User Mailboxes
$ReplaceValue = #{
msExchRemoteRecipientType = 4
msExchRecipientDisplayType = -2147483642
msExchRecipientTypeDetails = 2147483648
}
# Set-ADUser -Identity $ADUserAttributesValues.Name -Replace $ReplaceValue -WhatIf
Get-ADAttributesValue
}
default
{
Write-Host 'Goodbye' -ForegroundColor Green
Exit
}
}
Update
As for:
"the script above you've submitted stuck in the Do Until (-not
$UserInput) loop :-) "
As does yours. ;-}
Be sure to pay attention to when to use assignment operators '=' vs comparison operators '-eq'.
Simply, that is because that is what you wrote it to do. I did not address that part as that was not part of your request. Your code specifically will not exit until you pass it an empty string. The input will always be equal.
So, if you are saying it should exit if the name is not found or the like then that is what you have to add to your code. Also, you are using -Like and that needs a wildcard string, otherwise use -eq.
In that 'get request', you should pipe to select-object -Properties or use dot property.
Also, you are instructing the user to only enter one name, but your code is looking for an array. Why?
Lastly in your 'get request', you are only asking for one property, so no others are available, so, there is no need for the Dot property later.
So, I'd refactor the do loop this way...
(Again, there are other ways (like Try/Catch/Finally with or without the Begin/Process/End blocks)
... this is just one.
Add-Type -AssemblyName PresentationCore,PresentationFramework
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName Microsoft.VisualBasic
$MsgText = '
Enter a sAMAccountName / Alias as First.Lastname
Wildcards and a space-separated list are NOT supported.
To quit, enter nothing and click OK.'
$d = [Windows.Forms.MessageBox]::show($MsgText, 'User Instrutions',
[Windows.Forms.MessageBoxButtons]::YesNo,
[Windows.Forms.MessageBoxIcon]::Information)
Do
{
$UserInput = $null
If ($d -eq [Windows.Forms.DialogResult]::Yes)
{
# Show inputbox for user to enter a single username
$UserInput = [Microsoft.VisualBasic.Interaction]::
InputBox(
'Enter the User AD account to check',
'sAMAccountName / Alias "First.Lastname"',
$UserInput
)
}
else
{
Write-Warning -Message 'You decided to exit the requested action'
Break
}
# ForLoop??? --- Why are you checking for an array, when you are only asking for one entry at a time?
If ($UserInput -eq (Get-LocalUser | Where-Object Name -eq $UserInput))
{Write-Host "Processing $UserInput" -ForegroundColor DarkYellow}
Else{Write-Warning -Message "Could not find a user with a Name matching $UserInput ! Try again."}
}
Until (-not $UserInput)
# Results when cancelling the instructions box
<#
WARNING: You decided to exit the requested action
#>
# Results from the inputbox
<#
WARNING: Could not find a user with a Name matching test ! Try again.
WARNING: Could not find a user with a Name matching admin ! Try again.
Processing administrator
WARNING: Could not find a user with a Name matching gues ! Try again.
Processing guest
WARNING: Could not find a user with a Name matching ! Try again.
#>
More code is needed to handle the output of the OK/Cancel button, when and if the input is null or cancel is used. For example the Try/Catch I spoke of earlier.
Again popping GUIs, then sending stuff to the console is a very bad user experience. Stay consistent, pick one or the other. Again, us a single GUI form for this, send user instructions and info to the body of that form.
If you want to combine the code snippets using helper functions, here's an idea for you:
if (!(Get-Module "*ActiveDirectory*")) {
Try { Import-Module ActiveDirectory -ErrorAction Stop }
Catch { Write-Warning "Unable to load Active Directory module because $($Error[0])"; Exit }
}
# function to ask the user which user to update.
# returns either a valid AD including mailbox attributes user or nothing at all
function Prompt-User {
Add-Type -AssemblyName 'Microsoft.VisualBasic'
# enter an endless loop
while ($true) {
Clear-Host
$msg = "Enter the User AD account to check.`r`n`r`nWildcards and a space separated list are NOT supported."
Write-Host $msg -ForegroundColor Yellow
$account = [Microsoft.VisualBasic.Interaction]::InputBox($msg, 'sAMAccountName / Alias "First.Lastname"')
# exit the function if the user entered nothing or whitespace only
if ([string]::IsNullOrWhiteSpace($account)) { return }
$properties = 'DisplayName','msExchRemoteRecipientType','msExchRecipientDisplayType','msExchRecipientTypeDetails'
$ADUser = Get-ADUser -Filter "samAccountName -like '$account'" -Properties $properties -ErrorAction SilentlyContinue
if ($ADUser) {
Write-Host "The sAMAccountName $($account) matches the AD account '$($ADUser.DisplayName)'" -ForegroundColor Green
return $ADUser
}
else {
Write-Warning "Could not find a user with a sAMAccountName matching '$($account)' ! Please try again."
}
}
}
# function to ask the user what action to undertake
function Prompt-Action ([Microsoft.ActiveDirectory.Management.ADUser]$ADUser) {
$menuCaption = "Hybrid AD User account Exchange attribute modification"
$menuMessage = "Please select the action to be applied to the user $($ADUser.Name)"
## Format: "Menu Text" = "Help Text"
## "Menu Text" must match the options in the Switch statement below
## "&" marks the character to use as hotkey
$menu = [ordered]#{
'Remote &Shared Mailbox' = "Convert $($Input) as Remote Shared Mailbox"
'Remote &User Mailbox' = "Convert $($Input) as Remote User Mailbox"
'&Quit' = 'Leave without changes'
}
$menuChoices = $menu.Keys | ForEach-Object {
$choice = [System.Management.Automation.Host.ChoiceDescription]$_
$choice.HelpMessage = $menu[$_]
$choice
}
$answer = $host.UI.PromptForChoice($menuCaption , $menuMessage , $menuChoices, ($menu.Count - 1))
return ($menuChoices[$answer].Label -replace '&') # removing the '&' makes processing later easier
}
# function to display users Mailbox attributes
function Get-UserMailboxDetails ([string]$DistinguishedName) {
$properties = 'msExchRemoteRecipientType','msExchRecipientDisplayType','msExchRecipientTypeDetails','proxyAddresses'
Get-ADUser -Identity $DistinguishedName -Properties $properties -ErrorAction SilentlyContinue |
Select-Object msExchRemoteRecipientType,
msExchRecipientDisplayType,
msExchRecipientTypeDetails,
#{Label = 'Email Address'; Expression = {
($_.proxyAddresses | Where-Object {($_ -like 'SMTP*') -and ($_ -notlike '*onmicrosoft.com') } |
Sort-Object -CaseSensitive -Descending | ForEach-Object {$_.Split(':')[1]}) -join ', ' }}
}
# checks the current user mailbox type
# returns 'Remote User Mailbox', 'Remote Shared Mailbox' or 'Other'
function Check-MailboxAttributes ([Microsoft.ActiveDirectory.Management.ADUser]$ADUser) {
if ($ADUser.msExchRemoteRecipientType -eq 4 -and
$ADUser.msExchRecipientDisplayType -eq -2147483642 -and
$ADUser.msExchRecipientTypeDetails -eq 2147483648) { 'Remote User Mailbox' }
elseif (
$ADUser.msExchRemoteRecipientType -eq 100 -and
$ADUser.msExchRecipientDisplayType -eq -2147483642 -and
$ADUser.msExchRecipientTypeDetails -eq 34359738368) { 'Remote Shared Mailbox' }
else { 'Other' }
}
# this is your main code
do {
$ADUser = Prompt-User
if ($ADUser) {
Clear-Host
# here is where you process the user
$action = Prompt-Action $ADUser
if ($action -like 'Remote*') { # either 'Remote Shared Mailbox' or 'Remote User Mailbox'
# do we need to convert the user mailbox type?
if ((Check-MailboxAttributes $ADUser) -eq $action) {
Write-Host "$($ADUser.DisplayName) is already a $action" -ForegroundColor Yellow
}
else {
Write-Host "You selected to convert $($ADUser.DisplayName) as $action" -ForegroundColor Yellow
if ($action -match 'User') {
# create hashtable for Remote Regular User Mailboxes
$newProperties = #{
msExchRemoteRecipientType = 4
msExchRecipientDisplayType = -2147483642
msExchRecipientTypeDetails = 2147483648
}
}
else {
# create hashtable for Remote Shared Mailboxes
$newProperties = #{
msExchRemoteRecipientType = 100
msExchRecipientDisplayType = -2147483642
msExchRecipientTypeDetails = 34359738368
}
}
$ADUser | Set-ADUser -Replace $newProperties
# reload the user and show the resulting mailbox properties
Get-UserMailboxDetails $ADUser.DistinguishedName
}
}
}
} until (-not $ADUser)
# all done
Write-Host 'Goodbye' -ForegroundColor Green
I agree with postanote that I would create a proper GUI for this rather than using Write-Host stuff in the console
Related
I have a powershell script to remove a user stored under the variable $User which is taken from a user input in the command line. How do I specify multiple users and remove all of them?
Script is below
$User = Read-Host - Prompt 'Enter user name'
Remove-ADUser $User
Write-Host "'$user' account has been removed press any key to close..."
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Agree with #Theo but if you know what you are doing there is a simple solution:
$User = Read-Host - Prompt 'Enter user name'
foreach($u in $User.Split(','))
{
Remove-ADUser $u
Write-Host "'$u' account has been removed"
}
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
All you need to know is delimiter which you have to use. In that case it is ',', so you need to pass logins in pattern: user1,user2
perhaps this helps little with getting a more save result.
it took me a few minutes to write it. maybe it helps.
######################################
# first make sure we know what is happing..
######################################
$name = 'bob'
$AccountToDelete = Get-ADuser -filter {enabled -eq $true} -properties "displayname" | where {$_.displayname -match $name}
######################################
# then go a step further
######################################
$name = 'bob'
$AccountToDelete = Get-ADuser -filter {enabled -eq $true} -properties "displayname" | where {$_.displayname -match $name}
#show results of filter
$AccountToDelete.name
if ($AccountToDelete.count -gt 1)
{
write-warning 'more then one user:'
$AccountToDelete.name
BREAK
}
ELSE
{
'delete {0}' -f $AccountToDelete.name
Remove-ADUser $AccountToDelete -WhatIf
}
######################################
# improvement 1
######################################
$names = 'bob','don'
foreach ($name in $names){
$AccountToDelete = Get-ADuser -filter {enabled -eq $true} -properties "displayname" | where {$_.displayname -match $name}
#show results of filter
$AccountToDelete.name
if ($AccountToDelete.count -gt 1)
{
write-warning 'more then one user:'
$AccountToDelete.name
BREAK
}
ELSE
{
'delete {0}' -f $AccountToDelete.name
Remove-ADUser $AccountToDelete -WhatIf
}
}
######################################
# improvement 2
######################################
#now add names to delete in a notepad textfile, one name per line
<#
you can use this to create a file
PS c:\users\administator> notepad users.txt
#>
#replace the string arrary $names = 'bob','don'
$names = (get-content .\users.txt).split('^t')
$names
'processing {0} names...' -f $names.count
foreach ($name in $names){
$AccountToDelete = Get-ADuser -filter {enabled -eq $true} -properties "displayname" | where {$_.displayname -match $name}
#show results of filter
$AccountToDelete.name
if ($AccountToDelete.count -gt 1)
{
write-warning 'more then one user:'
$AccountToDelete.name
BREAK
}
ELSE
{
'delete {0}' -f $AccountToDelete.name
Remove-ADUser $AccountToDelete -WhatIf
}
}
#finally if the script is showing you the results you need you can remove the -WhatIf
I'm trying to create a loop that tests the connection to specific computers in the Active Directory. The output is #{name=KY-WH-DT01} when I'm looking for KY-WH-DT01. I'm not sure what I'm doing wrong.
As a workaround, I've pulled the list manually and properly inserted it into the variable as hard code.
function testConnection {
$computers = Get-ADComputer -filter 'Name -like "KY-WH*" -or name -like "KY-SR"' | select name
$pass = 0
$fail = 0
foreach ($computer in $computers) {
$testConnection = Test-Path "\\$computer\C$" -ErrorAction Stop
if ($testConnection -eq $true) {
Write-Host $computer -ForegroundColor Green
$pass = $pass + 1
}
else {
Write-Host $computer -ForegroundColor Red -BackgroundColor Black
$fail = $fail + 1
}
}
Write-Host $null
Write-Host "Passed: $pass | Failed: $fail"
}
testConnection
...
This code should output a list of computer names with colors determining whether the connection test passed or failed by turning them either red or green.
You need to drill down a little bit in your variable.
foreach ($computer in $computers.Name) {
This will do it if you only want the name or the computer and no other variable.
You can also change your initial search to include the -ExpandProperty switch and you will not need to dig down into the property.
$computers = Get-ADComputer -filter 'Name -like "KY-WH*" -or name -like "KY-SR"' | select -ExpandProperty name
I'm comparing users between two domains to make sure users that are disabled in one, are disabled in the other, using these two steps:
Domain 1:
Get-ADUser -SearchBase "OU=ou2,OU=ou1,DC=pre,DC=domain1,DC=com" -Filter * -Properties * | Select-Object Name | Export-Csv -encoding "utf8" Users.csv
Domain 2:
$input = import-csv -path "Users.csv"
ForEach ($User in $input) {
$result = get-aduser -SearchBase "OU=ou2,OU=ou1,DC=pre,DC=domain2,DC=com" -Filter "name -eq '$($User.Name)'" | Select-Object Enabled
If ($result -eq $null) { Write-host -ForegroundColor Yellow $User "Name not found. Please do a manual check"
}
elseif ($result -like '*False*')
{
Write-host -ForegroundColor Red "**" $User "** must be disabled!"
}
else {get-aduser -SearchBase "ou=Users,ou=SCS,ou=All,dc=osit,dc=ad" -Filter "name -eq '$($User.Name)'" -Properties * | Select-Object Name, Enabled}
}
This works, but gives me the following output:
Name Enabled
---- -------
Firstname1 Lastname1 True
#{Name=Firstname2 Lastname2} - Name not found. Please do a manual check
How do I remove "#{Name=" and "}"?
I have tried adding -ExtendProperity to $result, and Replace with no luck. I'm probably doing it wrong..
$User is a custom object (type [pscustomobject], as output by Import-Csv), and #{Name=Firstname2 Lastname2} is its stringified representation[1], because Write-Host stringifies its arguments for display.
Access the .Name property instead to get just the name:
Write-host -ForegroundColor Yellow $User.Name "- Name not found. Please do a manual check"
More idiomatically, using a single expandable string (string interpolation inside "..."):
Write-host -ForegroundColor Yellow "$($User.Name) - Name not found. Please do a manual check"
If you want to include the full object representation as it would appear if you printed it directly to the console, you need Out-String, but note that you'll end up with multi-line output:
Write-host -ForegroundColor Yellow "$($User | Out-String) - Name not found. Please do a manual check"
[1] You can verify this as follows: $user = [pscustomobject] #{ Name = 'Firstname1 LastName1' }; "$user". The output is string #{Name=Firstname1 LastName1}.
I have created a PowerShell script to find the computer name from the values in the description. We put the users name in the description and computer name is an asset tag number. If you continue and put the name in a second time it works. If you look for another user you have to do it twice also.
Here is my script:
Import-Module ActiveDirectory
do {
$a = Read-Host "Enter first or last name of user"
$b = "*$a*"
# Validates if the command returns data
$searcher = $(try {
Get-ADComputer -Filter {Description -like $b} -Properties
Name,Description | Select Name,Description
} catch {
$null
})
if ($searcher -ne $null) {
Get-ADComputer -Filter {Description -like $b } -Properties Name,Description |
Select Name,Description
} else {
Write-Host Could not find: $a -ForegroundColor "yellow"
}
# If running in the console, wait for input before closing.
if ($Host.Name -eq "ConsoleHost") {
Write-Host "Press any key to continue..."
$Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyUp") > $null
}
$again = Read-Host 'Would you like to search again? (Y/N)'
} until ($again -eq 'No' -or $again -eq 'n')
Here is a working example using your script with a few changes.
The big change that allowed it to work was piping to Out-Host from using Get-ADComputer
# installs AD module
Import-Module ActiveDirectory
Do {
$a = Read-Host "Enter first or last name of user"
$b = "*$a*"
try {
Get-ADComputer -Filter {Description -like $b} -Properties Name, Description -ErrorVariable MyError |
Select-Object Name,Description |
Out-Host
if ($MyError){
write-host Could not find: $a -foregroundcolor Yellow
}
}
catch {
write-host Could not find: $a -foregroundcolor Red
}
$again = Read-host 'Would you like to search again? (Y/N)'
}
Until (
$again -eq 'No' -or $again -eq 'n'
)
I am trying to input a user using a variable and check active directory to confirm the full name of the user and pause the script before running the next command.
The script is running the pause command before the get-aduser command - see below script
#Enter Username
$username = read-host "Username"
Get-ADUser -Filter "Name -eq '$username'" | Select-Object name, samaccountname
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
#Removes user from groups
Get-ADPrincipalGroupMembership -Identity $username | where {$_.Name -notlike "Domain Users"} |% {Remove-ADPrincipalGroupMembership -Identity $uSername -MemberOf $_ -Confirm:$false}
write-output End
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
In my experience, Get-ADUser and similar commands can take a long time to run, possible up to 20 seconds or so. Rarely, I have found that it makes the code unusable due to some commands running before or after it. If you want to test to see if this is really the case for you, add this line in between every other line in your code:
Read-Host -Prompt "Press Enter to continue"
That way, you can test whether there is a real difference between when you put that line there, and if you don't. If there is actually a difference, you may have to look into start-sleep or wait.
I would do something like this to have the user validate, cause i think that is what you are after, before continuing to revoke the users group membership
Write-Host "`nEnter the UserName: " -NoNewline -ForegroundColor Yellow
$UserName = Read-Host
$UserName = Get-ADUser -Filter "Name -eq '$UserName'" | Select-Object Name, SamAccountName
Write-Host "`nRevoke membership of all groups for user" $UserName.Name "("$UserName.SamAccountName")?`n [Y]es, [N]o : " -ForegroundColor Yellow -NoNewline
$Confirmation = Read-Host
While ("y","yes","n","no" -notcontains $Confirmation) {
Write-Host "`nNot a valid input! Please try again ..." -ForegroundColor Red
Write-Host "`nRevoke membership of all groups for user" $UserName.Name "("$UserName.SamAccountName")?`n [Y]es, [N]o : " -ForegroundColor Yellow -NoNewline
$Confirmation = Read-Host
}
If ($Confirmation -eq "n" -or $Confirmation -eq "no") {
Write-Host "Aborted!" -ForegroundColor Red
Break
}
# Next step here!
# Get-ADPrincipalGroupMembership -Identity $UserName | where {$_.Name -notlike "Domain Users"} |% {Remove-ADPrincipalGroupMembership -Identity $UserName -MemberOf $_ -Confirm:$false}
Just another piece of code, these kind of changes needs some proper logging and error handling, while my code only logs to the console it can still be useful.
It uses confirm in place of 'pause' so the user can choose to continue or stop.
### CmdletBinding
# Alows the use of -Whatif(not used), -Confirm, -Verbose and -Debug.
# Reference: https://technet.microsoft.com/en-us/library/ff677563.aspx
# https://msdn.microsoft.com/en-us/powershell/reference/5.1/microsoft.powershell.core/about/about_functions_cmdletbindingattribute
# https://blogs.technet.microsoft.com/poshchap/2014/10/24/scripting-tips-and-tricks-cmdletbinding/
[CmdletBinding(
SupportsShouldProcess = $true,
ConfirmImpact=’High’
)]
# Script parameters.
Param(
[parameter(HelpMessage = "Command parram, not used.")]$Command = "nothing"
#Run with PowerShell Fix, reference: https://social.technet.microsoft.com/Forums/office/en-US/fe7fb473-7ed6-4397-9c95-120201c34847/problems-with-powershell-30?forum=winserverpowershell
)
#Console clean-up.
Clear-Host
# Set error action to Stop, if something happens and it isnt inside a trap (try/catch) then stop.
$ErrorActionPreference = "Stop"
# Controls the Verbose Output
$VerbosePreference = "Continue" #Optional
#Intial message for User execution, whitespace is for the progressbars.
"
Script: Remove-ADUserGroupMembership.ps1
"
Write-Verbose "Starting main loop."
While ($true){
#White space for in between questions.
Write-Host "
"
#Retrieve username from user input.
Write-Host "Provide the ADUser for ADGroup removal here:"
$Username = read-host "Username"
#Retrieve ADUser object from AD.
Write-Verbose "Querying Active Directory for user $Username"
Try {
$ADUser = Get-ADUser $Username
Write-Verbose "User Found, $($ADUser.Name) "
}
catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
Write-Warning "Could not find user $Username in Active Directory, check spelling and try again."
Continue #this wil reset the while loop
}
Catch {
Write-Warning "Unknown Errror, Could not retrieve user $Username from Active Directory, please try again."
Continue #this wil reset the while loop
}
#Retrieve GroupMembership for user.
Write-Verbose "Querying Active Directory for GroupMembership of User $($ADUser.name), exluding Domain Users"
Try {
$GroupMembership = $ADUser | Get-ADPrincipalGroupMembership | where {$_.Name -notlike "Domain Users"}
Write-Verbose "Found $($GroupMembership.count) GroupMemberships for User $($ADUser.name) (Not inluding Domain Users)"
}
Catch {
Write-Warning "Unknown Errror, Could not retrieve GroupMembership for user $($ADUser.Name) from Active Directory, please try again."
Continue #this wil reset the while loop
}
#Remove GroupMembership for user.
if ($pscmdlet.ShouldProcess("$($ADUser.name)", "Remove-ADPrincipalGroupMembership {$($GroupMembership.count) Groups}")) {
Write-Verbose "Entering GroupMembership removal loop for user $($ADUser.name)"
Foreach ($Group in $GroupMembership) {
Try {
$ADUser | Remove-ADPrincipalGroupMembership -MemberOf $Group -WhatIf -Confirm:$true
Write-Verbose "$Group removed from from user $($ADUser.name)"
}
catch {
Write-Warning "An Error occured, could not remove group $Group from user $($ADUser.Name)"
Continue #this will skip this group.
}
}
}
else {
Write-Warning "Action Remove-ADPrincipalGroupMembership {$($GroupMembers.count) Groups} canceled for $($ADUser.name)"
}
Read-Host "Press Enter to exit."
break #exit from while loop
}