{"id":613,"date":"2014-09-29T00:01:00","date_gmt":"2014-09-29T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2014\/09\/29\/powershell-and-active-directory-recycle-bin\/"},"modified":"2014-09-29T00:01:00","modified_gmt":"2014-09-29T00:01:00","slug":"powershell-and-active-directory-recycle-bin","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/powershell-and-active-directory-recycle-bin\/","title":{"rendered":"PowerShell and Active Directory Recycle Bin"},"content":{"rendered":"<p><b>Summary<\/b>: Guest blogger, Alan Kaplan, talks about using Windows PowerShell with the Active Directory Recycle Bin.\nMicrosoft Scripting Guy, Ed Wilson, is here. Today we have a guest blog post written by Alan Kaplan. I met Alan several years ago when the Scripting Wife and Jim Christopher started the Windows PowerShell User Group in Charlotte, North Carolina. Since then, our path continue to cross when I see him at various regional IT group meetings (and at PowerShell Saturday). He is a real Windows PowerShell enthusiast, and he has grown his skills significantly since I first met him. Now he is a guest blogger. Take it away Alan&hellip;\nIn March of 2013, during a presentation to the Carolina IT Professionals User Group in Charlotte, NC, Ed Wilson convinced me that it was time to switch from VBScript to Windows PowerShell. I answered a few questions correctly, and Ed gave me an autographed copy of <i>Windows PowerShell&nbsp;3.0 Step by Step<\/i>. He suggested that I join the Charlotte PowerShell User Group, which he and Teresa (The Scripting Wife) attend as time permits. One and a half years later, I was at the Windows PowerShell meeting where I discussed an earlier version of this script. Ed asked if I would be willing to write a post for the Hey, Scripting Guy! Blog, and after clearing it with my employer&rsquo;s lawyers, I said, &ldquo;Yes.&rdquo;\nThe <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/dd392261%28v=ws.10%29.aspx\" title=\"AD Recycle Bin Technet Article\" target=\"_blank\">Active Directory Recycle Bin<\/a> was introduced in Windows Server 2008 R2. The Recycle Bin must be first be enabled, and the only way to restore a deleted a user account is to use the <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/ee617262.aspx\" title=\"Restore-ADObject Technet\" target=\"_blank\">Restore-ADObject<\/a> cmdlet, with pretty arcane parameters. My goal was to write a script which would:<\/p>\n<ul>\n<li>Provide a GUI front end for <b>Restore-ADObject<\/b><\/li>\n<li>Allow the user to choose one or more user accounts to restore<\/li>\n<li>Permit restoring from all domains within the forest for which an administrator has permissions<\/li>\n<\/ul>\n<p>In the first version of this script, I used well-known GUIDs to refer to the location of the deleted object&rsquo;s container and for the location of the user&#8217;s container, which I chose to be the default location for the restored user object. (For more information about well-known GUIDs, see <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/cc223663.aspx\" target=\"_blank\">6.1.1.4 Well-Known Objects<\/a>.) This turned out to be unnecessary because Windows PowerShell makes getting this easy:<\/p>\n<p style=\"margin-left:30px\">$oDomain = Get-ADDomain<\/p>\n<p style=\"margin-left:30px\">$DeletedObjects = $oDomain.DeletedObjectsContainer<\/p>\n<p style=\"margin-left:30px\">$UsersContainer = $oDomain.UsersContainer\nThese three lines got rid of about 30 lines of code between version 1 and version 2. But how can you get the information for another domain? The simple answer for all Active Directory commands for Windows PowerShell, is to use the <b>-Server<\/b> switch with the <b>DNSDomain<\/b> name instead of a true server name, for example:<\/p>\n<p style=\"margin-left:30px\">$oDomain = Get-ADDomain &ndash;server corp.contoso.com\nUndelete-User.ps1 lets you select the domain to search, and the destination for the restored user object. It defaults to running in test mode, where the changes are not committed to Active Directory. The maximum age of deleted objects is found inside the <b>Configuration<\/b> partition of the root forest domain. I capture that number with this line:<\/p>\n<p style=\"margin-left:30px\">$MaxAge =Get-ADObject -Identity &#8220;CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,$rootforestdomain&#8221;`<\/p>\n<p style=\"margin-left:30px\">&nbsp;-Partition &#8220;CN=Configuration,$rootforestdomain&#8221; -Properties msDS-DeletedObjectLifetime |<\/p>\n<p style=\"margin-left:30px\">&nbsp;select -ExpandProperty msDS-DeletedObjectLifetime\nThe script prompts you to choose how many days to go back in your search with <b>$MaxAge<\/b> as the default. You can choose to restore a single user by specifying the account <b>SamAccountName<\/b>. If you choose another domain, you are prompted to choose the global catalog server closest to you with the following:<\/p>\n<p style=\"margin-left:30px\">&nbsp; $DomainGCs = Get-ADDomainController -server $strDomain `<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; &nbsp;-Filter {(Enabled -eq $true) -and (IsGlobalCatalog -eq $true)}<\/p>\n<p style=\"margin-left:30px\">$strServer = $DomainGCs| select -Property hostname | out-gridview -Title &#8220;Select closest GC&#8221; -PassThru\nWhen using <b>Out-Gridview<\/b>, it is important to remember to add <b>&ndash;PassThru<\/b>. Without it, nothing is returned by the cmdlet.\nThe query has been designed to work as fast as possible:<\/p>\n<p style=\"margin-left:30px\">$users = Get-ADObject -searchbase $searchbase -Server $strServer -resultpagesize 100 `<\/p>\n<p style=\"margin-left:30px\">&nbsp; -Filter {(whenChanged -ge $changedDate) -and (Deleted -eq $true) -and (SamAccountName -like $strUser) } `<\/p>\n<p style=\"margin-left:30px\">&nbsp; -includeDeletedObjects -properties DisplayName, Description, SamAccountName, userprincipalname, DistinguishedName, WhenChanged\nThe default for <b>$strUser<\/b> is the asterisk ( <b>* <\/b>). Because the user name is matched with the&nbsp;<b>like <\/b>operator, you can search for a single <b>SamAccountName<\/b>, or a list of names, such as <b>SamMoor*<\/b>. If you do nothing, the default applies and all accounts that were deleted during the time period are returned.\nInstead of writing a log, I decided to use <b>Start-Transcript<\/b> and <b>Stop-Transcript<\/b>. Because transcription does not work inside the ISE, the script only offers a transcript when you are outside the ISE. The detection works like this:<\/p>\n<p style=\"margin-left:30px\">if ( ($host.Name).Contains(&#8220;ISE&#8221;) -eq $False){<\/p>\n<p style=\"margin-left:30px\">&nbsp; $retval = [System.Windows.Forms.MessageBox]::Show(&#8220;Would you like a transaction log of script activity?&#8221;,`<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; &#8220;Log&#8221;, &#8220;YesNoCancel&rdquo; , &#8220;Question&rdquo; , &#8220;Button2&#8221;)\nThe transcript logfile is sent by default the admin&rsquo;s desktop. Note that I formatted the date inside of the conversion using ToString. Then the InputBox displays the log as the default:<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $strLog = &#8220;$env:UserProfileDesktop&#8221;+$(Get-Date).ToString(&#8220;yyyyMMdd_HHmm&#8221;)+&#8221;_UndeleteUserLog.txt&#8221;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $strLog = [Microsoft.VisualBasic.Interaction]::InputBox(&#8220;Log Path&#8221;, &#8220;Path&#8221;,$strLog)\nThe complete Windows PowerShell script is uploaded on the Script Center Repository: <a href=\"http:\/\/gallery.technet.microsoft.com\/scriptcenter\/PowerShell-Undelete-User-3b2ab443\" target=\"_blank\">PowerShell Undelete User Script<\/a>. The script is extensively commented. Please be a kind, gentle reader. I have only been using Windows PowerShell for a year and a half.\nAnd now, a word from our lawyers: <i>Alan Kaplan is an employee of the U.S. Department of Veteran&rsquo;s Affairs. Any opinion expressed herein are his own, and not necessarily that of the Department.<\/i>\nThank you so much for doing this, Alan&mdash;and for taking the time to share your experience with us today.\nJoin me tomorrow when I will have more way cool Windows PowerShell stuff.\nI invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"http:\/\/blogs.technet.commailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.\n<b>Ed Wilson, Microsoft Scripting Guy<\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Guest blogger, Alan Kaplan, talks about using Windows PowerShell with the Active Directory Recycle Bin. Microsoft Scripting Guy, Ed Wilson, is here. Today we have a guest blog post written by Alan Kaplan. I met Alan several years ago when the Scripting Wife and Jim Christopher started the Windows PowerShell User Group in Charlotte, [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[7,536,3,45],"class_list":["post-613","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-active-directory","tag-recycle-bin","tag-scripting-guy","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Guest blogger, Alan Kaplan, talks about using Windows PowerShell with the Active Directory Recycle Bin. Microsoft Scripting Guy, Ed Wilson, is here. Today we have a guest blog post written by Alan Kaplan. I met Alan several years ago when the Scripting Wife and Jim Christopher started the Windows PowerShell User Group in Charlotte, [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/613","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=613"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/613\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=613"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=613"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=613"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}