创建Powershell菜单项并在菜单项中调用函数
Creating Powershell menu Item and calling functions in the menu item
我正在尝试将以下代码片段合并到现有代码中以更新用户 AD 属性。
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)
上面的代码片段用于验证针对 Active Directory 用户帐户的用户输入,以查看 AD 帐户是否有效。
更新AD属性
# 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
这是主菜单项代码,但没有输入检查验证部分:
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
}
}
那么如何结合到上面呢?
上面的代码是作为一个粗略的菜单项创建的,有很多重复,但它确实有效。我相信它可以通过创建函数来优化,但不确定如何,因为属性不同。
Functions 做一件事,做好一件事情是最佳实践。
然后您将该功能分配给您的 UI 设计对象。
至于你的第一部分post。你不能这样做...
Write-Host "Could not find a user with a sAMAccountName matching '$($env:USERNAME)' !" -ForegroundColor Red | Write-Warning
...它只会 return 红色文本根本不会出现警告。
Write-Host 清除缓冲区,因此不能用于将结果发送到
管道。
此外,您实际上是在为同一代码 line/string 文本调用两个警告(一个为红色,然后为默认的 Write-Warning cmdlet 颜色)。所以,这不是问题。
Write-Warning cmdlet 已经为其生成默认颜色,您无法直接更改。所以,就用这个...
Write-Warning -Message "Could not find a user with a sAMAccountName matching '$($Username)' !"
...或者您必须创建自己的函数来处理彩色文本,利用这样的东西...
# 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
...当使用像 Write-Output 这样没有颜色的 cmldets 时,它是管道友好的。您可以创建自己的 GUI 消息 boxes/forms 以获得更多控制权。
你为什么要这样做 -WhatIf 在这里?
Set-ADUser -Identity $ADUserAttributesValues.Name -Replace $Replace -WhatIf
如果这是实时代码,那么这个 Set-* 将永远不会发生,因此它下面的所有内容都没有实际意义,因为什么都没有改变。除非你 posting 是你的 test/debug/OpsCheck 代码,否则这是谨慎的。
你有这个...
# 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 ', ' }})
}
...显示两次。把它做成一个单独的函数,需要的时候调用就可以了
因此,以下内容未经测试,因为我不在实验室附近,无法尝试此操作。
注:
我正在使用 PowerShell 的自然换行符来确保代码的可读性并且不会太长。
有时无法避免长行,但只要有可能,如果您的代码行无法像普通的 Word 文档或书籍一样放在普通的 8.5x11.5 sheet 纸上,那么它太长并且是重构的目标,使用自然换行符、splatting、哈希表、PSCustomerObjects 等
重构代码:同样,我不在我的 AD/Exchange 实验室环境中,所以我使用了 Localuser cmdlets 进行快速而肮脏的代码测试。
(我个人认为这是一个使用 PowerShell 帮助的单一用户表单,并完全避免所有菜单内容 - 参见: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://docs.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
}
}
更新
至于:
"the script above you've submitted stuck in the Do Until (-not
$UserInput) loop :-) "
你的也是。 ;-}
一定要注意何时使用赋值运算符“=”与比较运算符“-eq”。
很简单,那是因为这就是您编写它的目的。我没有解决那部分,因为那不是您请求的一部分。你的代码在你传递一个空字符串之前不会退出。输入将始终相等。
所以,如果您说如果找不到名称或类似的名称就应该退出,那么这就是您必须添加到代码中的内容。此外,您正在使用 -Like 并且需要通配符字符串,否则使用 -eq.
在 'get request' 中,您应该通过管道传输到 select-object -Properties 或使用点 属性.
此外,您指示用户只输入一个名称,但您的代码正在寻找一个数组。为什么?
最后在你的'get request'中,你只要求一个属性,所以没有其他可用的,所以,以后不需要点属性。
所以,我将以这种方式重构 do 循环...
(同样,还有其他方法(如 Try/Catch/Finally 有或没有 Begin/Process/End 块)
...这只是一个。
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.
#>
需要更多代码来处理 OK/Cancel 按钮的输出,当输入为空或使用取消时。比如我之前说的Try/Catch
再次弹出 GUIs,然后将内容发送到控制台是非常糟糕的用户体验。保持一致,选择一个或另一个。同样,我们为此使用了一个 GUI 表单,将用户说明和信息发送到该表单的主体。
如果您想使用辅助函数组合代码片段,这里有一个适合您的想法:
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
我同意 postanote 我会为此创建一个合适的 GUI 而不是在控制台中使用 Write-Host 东西
我正在尝试将以下代码片段合并到现有代码中以更新用户 AD 属性。
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)
上面的代码片段用于验证针对 Active Directory 用户帐户的用户输入,以查看 AD 帐户是否有效。
更新AD属性# 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
这是主菜单项代码,但没有输入检查验证部分:
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
}
}
那么如何结合到上面呢?
上面的代码是作为一个粗略的菜单项创建的,有很多重复,但它确实有效。我相信它可以通过创建函数来优化,但不确定如何,因为属性不同。
Functions 做一件事,做好一件事情是最佳实践。 然后您将该功能分配给您的 UI 设计对象。
至于你的第一部分post。你不能这样做...
Write-Host "Could not find a user with a sAMAccountName matching '$($env:USERNAME)' !" -ForegroundColor Red | Write-Warning
...它只会 return 红色文本根本不会出现警告。
Write-Host 清除缓冲区,因此不能用于将结果发送到 管道。
此外,您实际上是在为同一代码 line/string 文本调用两个警告(一个为红色,然后为默认的 Write-Warning cmdlet 颜色)。所以,这不是问题。
Write-Warning cmdlet 已经为其生成默认颜色,您无法直接更改。所以,就用这个...
Write-Warning -Message "Could not find a user with a sAMAccountName matching '$($Username)' !"
...或者您必须创建自己的函数来处理彩色文本,利用这样的东西...
# 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
...当使用像 Write-Output 这样没有颜色的 cmldets 时,它是管道友好的。您可以创建自己的 GUI 消息 boxes/forms 以获得更多控制权。
你为什么要这样做 -WhatIf 在这里?
Set-ADUser -Identity $ADUserAttributesValues.Name -Replace $Replace -WhatIf
如果这是实时代码,那么这个 Set-* 将永远不会发生,因此它下面的所有内容都没有实际意义,因为什么都没有改变。除非你 posting 是你的 test/debug/OpsCheck 代码,否则这是谨慎的。
你有这个...
# 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 ', ' }})
}
...显示两次。把它做成一个单独的函数,需要的时候调用就可以了
因此,以下内容未经测试,因为我不在实验室附近,无法尝试此操作。
注:
我正在使用 PowerShell 的自然换行符来确保代码的可读性并且不会太长。
有时无法避免长行,但只要有可能,如果您的代码行无法像普通的 Word 文档或书籍一样放在普通的 8.5x11.5 sheet 纸上,那么它太长并且是重构的目标,使用自然换行符、splatting、哈希表、PSCustomerObjects 等
重构代码:同样,我不在我的 AD/Exchange 实验室环境中,所以我使用了 Localuser cmdlets 进行快速而肮脏的代码测试。
(我个人认为这是一个使用 PowerShell 帮助的单一用户表单,并完全避免所有菜单内容 - 参见: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://docs.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
}
}
更新
至于:
"the script above you've submitted stuck in the Do Until (-not $UserInput) loop :-) "
你的也是。 ;-}
一定要注意何时使用赋值运算符“=”与比较运算符“-eq”。
很简单,那是因为这就是您编写它的目的。我没有解决那部分,因为那不是您请求的一部分。你的代码在你传递一个空字符串之前不会退出。输入将始终相等。
所以,如果您说如果找不到名称或类似的名称就应该退出,那么这就是您必须添加到代码中的内容。此外,您正在使用 -Like 并且需要通配符字符串,否则使用 -eq.
在 'get request' 中,您应该通过管道传输到 select-object -Properties 或使用点 属性.
此外,您指示用户只输入一个名称,但您的代码正在寻找一个数组。为什么?
最后在你的'get request'中,你只要求一个属性,所以没有其他可用的,所以,以后不需要点属性。
所以,我将以这种方式重构 do 循环...
(同样,还有其他方法(如 Try/Catch/Finally 有或没有 Begin/Process/End 块)
...这只是一个。
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.
#>
需要更多代码来处理 OK/Cancel 按钮的输出,当输入为空或使用取消时。比如我之前说的Try/Catch
再次弹出 GUIs,然后将内容发送到控制台是非常糟糕的用户体验。保持一致,选择一个或另一个。同样,我们为此使用了一个 GUI 表单,将用户说明和信息发送到该表单的主体。
如果您想使用辅助函数组合代码片段,这里有一个适合您的想法:
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
我同意 postanote 我会为此创建一个合适的 GUI 而不是在控制台中使用 Write-Host 东西