This is a PowerShell XAML GUI tool that on startup fetches all deleted objects from Active Directory and presents them in a list. From the list one or more objects can be selected and restored by a click on a button.
Theres to parts to the tool. First is the powershell (.ps1) file containing the logic and second there’s a .xaml file containing the xaml code for the GUI.
ADLostAndFoundRestoreTool.ps1
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
#Start-Process 'https://learn.microsoft.com/en-us/powershell/module/activedirectory/restore-adobject?view=windowsserver2022-ps' if ( $host.name -eq "Windows PowerShell ISE Host" ) { $xamlfilename = $psISE.CurrentFile.FullPath.Replace(".ps1",".xaml") } else { $xamlfilename = $MyInvocation.MyCommand.Path.Replace(".ps1",".xaml") } $scriptpath = $xamlfilename -replace "$($xamlfilename -split '\\' | Select-Object -Last 1)",'' #Import-Module ActiveDirectory # Load a WPF GUI from a XAML file build with Visual Studio Add-Type -AssemblyName presentationframework, presentationcore #region FORM INITIALIZE # NOTE: Either load from a XAML file or paste the XAML file content in a "Here String" $inputXML = Get-Content -Path $xamlfilename #$inputXML = @" #"@ $inputXMLClean = $inputXML -replace 'mc:Ignorable="d"','' -replace "x:N",'N' -replace 'x:Class=".*?"','' -replace 'd:DesignHeight="\d*?"','' -replace 'd:DesignWidth="\d*?"','' [xml]$xaml = $inputXMLClean $reader = New-Object System.Xml.XmlNodeReader $xaml $tempform = [Windows.Markup.XamlReader]::Load($reader) $namedNodes = $xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'Name')]]") $namedNodes | ForEach-Object{Set-Variable -Name ($_.Name) -Value $tempform.FindName($_.Name) -Scope Global} #endregion $UsrStr = "$($env:UserDomain)\$($env:UserName)".ToLower() $ComputerDNSName = "$($env:ComputerName).$($env:USERDNSDOMAIN)".ToLower() $tempform.Title = "[$($UsrStr)] [$($ComputerDNSName)] Active Directory Lost and Found Restoration Powershell GUI Tool" $ObjectList_DGrd.RowBackground = "#88FFFFFF" $ObjectList_DGrd.AlternatingRowBackground = "#66AAAAAA" $ObjectList_DGrd.HeadersVisibility = "Column" $ObjectList_DGrd.OpacityMask = "#AAFFFFFF" $ObjectListFontSize_Tbx.Text = $ObjectList_DGrd.FontSize $fltrstr = '' #$deletedObjects = Get-ADObject -LDAPFilter "(&(msDS-LastKnownRDN=*)(name=*$($fltrstr)*))".Replace('**','*') -IncludeDeletedObjects -Properties * $ObjectList_DGrd.ItemsSource = @(Get-ADObject -LDAPFilter "(&(msDS-LastKnownRDN=*)(name=*$($fltrstr)*))".Replace('**','*') -IncludeDeletedObjects -Properties * | Select-Object sAMAccountName,DisplayName,Description,msDS-LastKnownRDN,LastKnownParent,ObjectClass,lastLogoff,lastLogon,lastLogonTimestamp,localPolicyFlags,logonCount,accountExpires,badPasswordTime,badPwdCount,CN,Name,DistinguishedName,codePage,countryCode,Created,Modified,Deleted,dNSHostName,instanceType,isCriticalSystemObject,isDeleted,ObjectGUID,objectSid,primaryGroupID,ProtectedFromAccidentalDeletion,pwdLastSet,userAccountControl) #$ObjectList_DGrd.ItemsSource | Export-Excel -Path "$($scriptpath)excel.xlsx" -TableName 'TABLE' -NoNumberConversion '*' -Show $OutExcel_Btn.Add_Click({ $ObjectList_DGrd.ItemsSource | Export-Excel -Path "$($scriptpath)DeletedADObject-$(Get-Date -Format 'yyyyMMdd-HHmm').xlsx" -TableName 'TABLE' -NoNumberConversion '*' -Show }) $OutGridView_Btn.Add_Click({ $ObjectList_DGrd.ItemsSource | Out-GridView }) function FontSizeNumUp{ if ($ObjectListFontSize_Tbx.Text -eq ""){ $ObjectListFontSize_Tbx.Text = "1" $ObjectListFontSizeNumDown_Btn.IsEnabled = $true } else{ $cpint = [int]$ObjectListFontSize_Tbx.Text $cpint = $cpint + 1 $ObjectListFontSize_Tbx.Text = [string]$cpint } $ObjectList_DGrd.FontSize = $ObjectListFontSize_Tbx.Text } $ObjectListFontSizeNumUp_Btn.Add_Click({ FontSizeNumUp }) function FontSizeNumDown{ if ( $ObjectListFontSize_Tbx.Text -ne "" ) { if ( [int]$ObjectListFontSize_Tbx.Text -gt 0 ){ $cpint = [int]$ObjectListFontSize_Tbx.Text $cpint = $cpint - 1 if ( $cpint -eq 0 ){ $Capacity_Tbx.Text = "" $ObjectListFontSizeNumDown_Btn.IsEnabled = $false } else { $ObjectListFontSize_Tbx.Text = [string]$cpint } } } $ObjectList_DGrd.FontSize = $ObjectListFontSize_Tbx.Text } $ObjectListFontSizeNumDown_Btn.Add_Click({ FontSizeNumDown }) $ObjectRestoreSelected_Btn.Add_Click({ Restore-ADObject -Identity ($ObjectList_DGrd.SelectedItem.ObjectGUID | Select-Object -ExpandProperty Guid) $restoredObject = Get-ADObject -LDAPFilter "(name=$($ObjectList_DGrd.SelectedItem.'msDS-LastKnownRDN'))" $Result = [System.Windows.MessageBox]::Show("`r`nID:`r`n$($restoredObject.Name)`r`n`r`nObjectClass:`r`n$($restoredObject.ObjectClass)`r`n`r`nDN:`r`n$($restoredObject.DistinguishedName)",'Restored object','OK','Information') }) $ObjectRestoreMultiple_Btn.Add_Click({ [array] $selectedItems = $ObjectList_DGrd.ItemsSource | Out-GridView -PassThru $restoredObjects = New-Object Collections.Generic.List[PSCustomObject] foreach( $objectItem in $selectedItems ) {#<# Restore-ADObject -Identity ($objectItem.ObjectGUID | Select-Object -ExpandProperty Guid) $restoredObjects.Add( (Get-ADObject -LDAPFilter "(name=$($objectItem.'msDS-LastKnownRDN'))") ) #> } $restoredObjects | Out-GridView -Title "Restored Objects" }) #region LOAD THE FORM <# =========================================================================== LOAD THE FORM Older way >>>>> $wpf.MyFormName.ShowDialog() | Out-Null >>>>> generates crash if run multiple times Newer way >>>>> avoiding crashes after a couple of launches in PowerShell... USing method from https://gist.github.com/altrive/6227237 to avoid crashing Powershell after we re-run the script after some inactivity time or if we run it several times consecutively... OLD: $tempform.ShowDialog() | Out-Null =========================================================================== #> $async = $tempform.Dispatcher.InvokeAsync({ $tempform.ShowDialog() | Out-Null }) $async.Wait() | Out-Null #endregion LOAD THE FORM |
ADLostAndFoundRestoreTool.xaml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
<Window x:Class="ADFindAndLostRestoreTool.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:ADFindAndLostRestoreTool" mc:Ignorable="d" Title="Active Directory Found and Lost Restoration Tool" Height="600" Width="1024" MinWidth="1024" MinHeight="600" Background="#FFB5C8D4"> <Grid Margin="0"> <Grid.ColumnDefinitions> <ColumnDefinition Width="10"/> <ColumnDefinition Width="49"/> <ColumnDefinition Width="130"/> <ColumnDefinition Width="130"/> <ColumnDefinition/> <ColumnDefinition Width="200"/> <ColumnDefinition Width="10"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="10"/> <RowDefinition Height="32"/> <RowDefinition Height="6"/> <RowDefinition/> <RowDefinition Height="6"/> <RowDefinition Height="34"/> <RowDefinition Height="15"/> </Grid.RowDefinitions> <Grid.Background> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#19F0F0F0" Offset="0"/> <GradientStop Color="#19E5E5E5" Offset="1"/> </LinearGradientBrush> </Grid.Background> <DataGrid x:Name="ObjectList_DGrd" Grid.Column="1" Margin="0" Grid.Row="3" Grid.ColumnSpan="5"/> <Label x:Name="label" Content="Active Directory Found and Lost Restoration Tool" Grid.Column="1" Margin="0" Grid.Row="1" FontSize="18" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontWeight="Bold" FontStyle="Italic" Background="#19115BB4" Grid.ColumnSpan="5" Foreground="#FF115BB4"/> <Button x:Name="ObjectRestoreSelected_Btn" Content="Restore Selected Object" Margin="0" Padding="4,0" RenderTransformOrigin="2.867,0.8" Grid.Row="5" Grid.ColumnSpan="1" Grid.Column="5" FontWeight="Bold" FontStyle="Italic" FontSize="14"> <Button.Background> <LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0"> <GradientStop Color="#79F0F0F0"/> <GradientStop Color="#33E5E5E5" Offset="0.5"/> </LinearGradientBrush> </Button.Background> <Button.Resources> <Style TargetType="Border"> <Setter Property="CornerRadius" Value="15,0,15,0"/> </Style> </Button.Resources> </Button> <Button x:Name="OutGridView_Btn" Content="Out-Gridview" Margin="5" Padding="16,0" RenderTransformOrigin="2.867,0.8" Grid.Column="2" Grid.Row="5" Grid.ColumnSpan="1"> <Button.Resources> <Style TargetType="{x:Type Border}"> <Setter Property="CornerRadius" Value="10,0,10,0"/> </Style> </Button.Resources> <Button.Background> <LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0"> <GradientStop Color="#79F0F0F0"/> <GradientStop Color="#19E5E5E5" Offset="0.5"/> </LinearGradientBrush> </Button.Background> </Button> <Button x:Name="OutExcel_Btn" Content="Export to Excel" Margin="5" Padding="16,0" RenderTransformOrigin="2.867,0.8" Grid.Column="3" Grid.Row="5"> <Button.Resources> <Style TargetType="{x:Type Border}"> <Setter Property="CornerRadius" Value="10,0,10,0"/> </Style> </Button.Resources> <Button.Background> <LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0"> <GradientStop Color="#79F0F0F0"/> <GradientStop Color="#19E5E5E5" Offset="0.5"/> </LinearGradientBrush> </Button.Background> </Button> <TextBox x:Name="ObjectListFontSize_Tbx" VerticalContentAlignment="Center" HorizontalAlignment="Left" Height="30" VerticalAlignment="Center" Width="30" MaxLines="1" FontSize="14" ToolTip="Storlek på typsnitt i objektlista" IsEnabled="False" HorizontalContentAlignment="Center" FontFamily="Courier New" Grid.Column="1" Grid.Row="5"/> <Button x:Name="ObjectListFontSizeNumUp_Btn" Content="+" HorizontalAlignment="Left" Margin="30,2,0,0" VerticalAlignment="Top" Width="15" Height="15" Padding="0" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" FontFamily="Courier New" Grid.Column="1" Grid.Row="5"/> <Button x:Name="ObjectListFontSizeNumDown_Btn" Content="-" HorizontalAlignment="Left" Margin="30,17,0,0" VerticalAlignment="Top" Width="15" Height="15" Padding="0" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" FontFamily="Courier New" Grid.Column="1" Grid.Row="5"/> <Button x:Name="ObjectRestoreMultiple_Btn" Content="Restore Muliple Object" Margin="0,0,4,0" Padding="16,0" RenderTransformOrigin="2.867,0.8" Grid.Row="5" Grid.ColumnSpan="1" Grid.Column="4" FontWeight="Bold" FontStyle="Italic" FontSize="14" HorizontalAlignment="Right" ToolTip="This will use Out-Gridview -Passthru to facilitate filtering and multiple selection"> <Button.Resources> <Style TargetType="{x:Type Border}"> <Setter Property="CornerRadius" Value="15,0,15,0"/> </Style> </Button.Resources> <Button.Background> <LinearGradientBrush EndPoint="0.5,1" MappingMode="RelativeToBoundingBox" StartPoint="0.5,0"> <GradientStop Color="#79F0F0F0"/> <GradientStop Color="#33E5E5E5" Offset="0.5"/> </LinearGradientBrush> </Button.Background> </Button> </Grid> </Window> |