Update-UserMFA.ps1
<#PSScriptInfo .VERSION 0.5 .GUID 499fcb19-e02b-4f3e-80b8-1013c2fce966 .AUTHOR jmcarthur@roundrocktexas.gov .COMPANYNAME .COPYRIGHT .TAGS .LICENSEURI .PROJECTURI .ICONURI .EXTERNALMODULEDEPENDENCIES .REQUIREDSCRIPTS .EXTERNALSCRIPTDEPENDENCIES .RELEASENOTES Fixed issue with input box grabbing focus. .PRIVATEDATA #> #Requires -Module AzureAD <# .DESCRIPTION Script utilizing WPF forms to update MFA status of specified user. .PARAMETER test Switch that enables generic testing of error handling while updating MFA settings for a given user. This flag will simulate an error by throwing a simple exception. The $testing flag is then set to false, and the Update-MFA function is called recursively with the user as a parameter to allow retrying the operation. #> Param ( [Parameter(HelpMessage="Flag to test failures while updating MFA")] [switch]$test ) $ErrorActionPreference = "SilentlyContinue" $WarningPreference = "SilentlyContinue" Add-Type -AssemblyName PresentationFramework Add-Type �assemblyName PresentationCore Add-Type �assemblyName WindowsBase Function _Invoke-InputBox { [cmdletbinding(DefaultParameterSetName="plain")] [OutputType([system.string],ParameterSetName='plain')] [OutputType([system.security.securestring],ParameterSetName='secure')] Param( [Parameter(ParameterSetName="secure")] [Parameter(HelpMessage = "Enter the title for the input box. No more than 25 characters.", ParameterSetName="plain")] [ValidateNotNullorEmpty()] [ValidateScript({$_.length -le 25})] [string]$Title = "Update MFA Status", [Parameter(ParameterSetName="secure")] [Parameter(HelpMessage = "Enter a prompt. No more than 50 characters.",ParameterSetName="plain")] [ValidateNotNullorEmpty()] [ValidateScript({$_.length -le 50})] [string]$Prompt = "Target user's email address:", [Parameter(HelpMessage = "Use to mask the entry and return a secure string.", ParameterSetName="secure")] [switch]$AsSecureString ) if ($PSEdition -eq 'Core') { Write-Warning "Sorry. This command will not run on PowerShell Core." #bail out Return } Write-Verbose "In _Invoke-InputBox" #remove the variable because it might get cached in the ISE or VS Code Remove-Variable -Name myInput -Scope script -ErrorAction SilentlyContinue $form = New-Object System.Windows.Window $stack = New-object System.Windows.Controls.StackPanel #define what it looks like $form.Title = $title $form.Height = 150 $form.Width = 350 $label = New-Object System.Windows.Controls.Label $label.Content = " $Prompt" $label.HorizontalAlignment = "left" $stack.AddChild($label) if ($AsSecureString) { $inputbox = New-Object System.Windows.Controls.PasswordBox } else { $inputbox = New-Object System.Windows.Controls.TextBox } $inputbox.Width = 300 $inputbox.HorizontalAlignment = "center" $stack.AddChild($inputbox) $space = new-object System.Windows.Controls.Label $space.Height = 10 $stack.AddChild($space) $btn = New-Object System.Windows.Controls.Button $btn.Content = "_OK" $btn.Width = 65 $btn.HorizontalAlignment = "center" $btn.VerticalAlignment = "bottom" #add an event handler $btn.Add_click( { if ($AsSecureString) { $script:myInput = $inputbox.SecurePassword } else { $script:myInput = $inputbox.text } $form.Close() }) $btn.IsDefault = $true $stack.AddChild($btn) $space2 = new-object System.Windows.Controls.Label $space2.Height = 10 $stack.AddChild($space2) $btn2 = New-Object System.Windows.Controls.Button $btn2.Content = "_Cancel" $btn2.Width = 65 $btn2.HorizontalAlignment = "center" $btn2.VerticalAlignment = "bottom" #add an event handler $btn2.Add_click( { $form.Close() }) $stack.AddChild($btn2) #add the stack to the form $form.AddChild($stack) #show the form $inputbox.Focus() | Out-Null $form.WindowStartupLocation = [System.Windows.WindowStartupLocation]::CenterScreen $form.ShowDialog() | out-null Write-Verbose ("myInput: {0}" -f $script:myInput) #write the result from the input box back to the pipeline $script:myInput } Function _Invoke-SelectionBox { Param( [Parameter(Mandatory=$true)]$user ) if ($PSEdition -eq 'Core') { Write-Warning "Sorry. This command will not run on PowerShell Core." #bail out Return } #remove the variable because it might get cached in the ISE or VS Code Remove-Variable -Name myInput -Scope script -ErrorAction SilentlyContinue #check that $user has necessary properties to continue if (-not ($user.DisplayName)) { Write-Warning "The provided user does not have the required properties." Return } elseif (-not $user.StrongAuthenticationRequirements) { # this is a new user who's MFA has never been configured $name = $user.DisplayName.ToString() $str = $($name + "'s MFA has not been enabled.") $notEnabled = $true } else { $name = $user.DisplayName.ToString() $state = $user.StrongAuthenticationRequirements.State.ToString() $str = $($name + "'s MFA status is " + $state) } $form = New-Object System.Windows.Window $stack = New-object System.Windows.Controls.StackPanel #define what it looks like $form.Title = "Update MFA Status" $form.Height = if ($notEnabled) {175} else {150} $form.Width = 350 $label = New-Object System.Windows.Controls.Label $label.Content = " $str" $label.HorizontalAlignment = "left" $stack.AddChild($label) # build options and outputs based on MFA state if ($state -eq "Enforced") { $opt1 = "Enabled" $opt2 = "Disabled" } elseif ($state -eq "Disabled") { $opt1 = "Enabled" $opt2 = "Enforced" } elseif ($notEnabled) { $opt1 = "Enabled" $opt2 = "Enforced" $opt3 = "Cancel" } else { $opt1 = "Enforced" $opt2 = "Disabled" } #region BUTTON1 $space = new-object System.Windows.Controls.Label $space.Height = 10 $stack.AddChild($space) $btn = New-Object System.Windows.Controls.Button $btn.Content = $opt1 $btn.Width = 65 $btn.HorizontalAlignment = "center" $btn.VerticalAlignment = "bottom" #add an event handler $btn.Add_click( { $script:myInput = $opt1 $form.Close() }) $stack.AddChild($btn) #endregion #region BUTTON2 $space2 = new-object System.Windows.Controls.Label $space2.Height = 10 $stack.AddChild($space2) $btn2 = New-Object System.Windows.Controls.Button $btn2.Content = $opt2 $btn2.Width = 65 $btn2.HorizontalAlignment = "center" $btn2.VerticalAlignment = "bottom" #add an event handler $btn2.Add_click( { $script:myInput = $opt2 $form.Close() }) $stack.AddChild($btn2) #endregion #region BUTTON3 if ($notEnabled) { $space3 = new-object System.Windows.Controls.Label $space3.Height = 10 $stack.AddChild($space3) $btn3 = New-Object System.Windows.Controls.Button $btn3.Content = $opt3 $btn3.Width = 65 $btn3.HorizontalAlignment = "center" $btn3.VerticalAlignment = "bottom" #add an event handler $btn3.Add_click( { if (-not $notEnabled) { $script:myInput = $opt3 } $form.Close() }) $stack.AddChild($btn3) } #endregion #add the stack to the form $form.AddChild($stack) #show the form #$inputbox.Focus() | Out-Null $form.WindowStartupLocation = [System.Windows.WindowStartupLocation]::CenterScreen $form.ShowDialog() | out-null #write the result from the input box back to the pipeline $script:myInput } Function _Check-MsolSession { Get-MsolDomain return $(if ($?) { $true } else { $false }) } Function _Update-MFA { Param ( [Parameter(Mandatory=$false)]$user ) Write-Verbose ("Testing? {0}" -f [bool]$script:testing) Write-Verbose ("User passed? {0}" -f [bool]$PSBoundParameters.ContainsKey('user')) $user = if ($PSBoundParameters.ContainsKey('user')) {$user} else {Write-Verbose "Calling _Invoke-InputBox" ;_Invoke-InputBox} #$user = Invoke-InputBox if ($user -eq $null) { Write-Verbose "User cancelled" return } if (-not (_Check-MsolSession)) { Connect-MsolService } try { $ms_user = Get-MsolUser -UserPrincipalName $user -EA Stop } catch { $retry = [System.Windows.MessageBox]::Show($_.Exception.Message,'Error fetching user','Ok','Error') Write-Verbose $retry # recursively call this function to start over without prompting if ($retry -eq 'Ok') {_Update-MFA} } $name = $ms_user.DisplayName $update = _Invoke-SelectionBox $ms_user Write-Verbose "Update: $update" if ($update) { Write-Verbose ("Setting MFA to {0} for {1}" -f $update,$name) if ($update -ne "Disabled") { $auth = New-Object -TypeName Microsoft.Online.Administration.StrongAuthenticationRequirement $auth.RelyingParty = "*" #$auth.RememberDevicesNotIssuedBefore = (Get-Date) $auth.State = $update } else # 'Disabled { $auth = @() } try { if ($testing) { throw "Test catching update MFA errors" $script:testing = $false } else { Set-MsolUser -UserPrincipalName $user -StrongAuthenticationRequirements $auth } } catch { $msgBoxAction = [System.Windows.MessageBox]::Show($_.Exception.Message,'Error updating MFA - Retry?','YesNo','Error') if ($msgBoxAction -eq 'Yes') { _Update-MFA -user $user } } } } Function Run { [bool]$continue = $true while ($continue) { _Update-MFA $continueQuery = [System.Windows.MessageBox]::Show('Process another user?','Operation Successful','YesNo','Question') if ($continueQuery -eq 'No') { $continue = $false } } } #region execute $script:testing = if ($PSBoundParameters.ContainsKey('test')) {$true} else {$false} Run #endregion |