<

LAPS to KeePass

Published on
3,782 Points
382 Views
4 Endorsements
Last Modified:
Shaun Vermaak
My name is Shaun Vermaak and I have always been fascinated with technology and how we use it to enhance our lives and business.
The "Local Administrator Password Solution" (LAPS) provides a centralized storage of secrets/passwords in Active Directory (AD). On the other hand, KeePass is an open source password manager. This Powershell script generates a KeePass XML file from a LAPS enabled Active Directory, ready for import.

Introduction


"Local Administrator Password Solution" (LAPS) is a great way to automatically set Active Directory computer local administrator passwords, each to a random value, and leverage Active Directory to store this password.


Even though LAPS provides a password viewer it cannot be used offline and some of my clients already adopted solutions such as KeePass to store their other system passwords.


It was this that motivate the creation of this LAPS to KeePass XML script


The Script (LAPSToKeePass.ps1)


Please see the comments within the script for an explanation of what it is doing. The script is a bit more verbose than necessary but it might be more understandable this way.


# Get all computer with LAPS passwords
$computers = Get-ADComputer -Filter {ms-Mcs-AdmPwd -like "*"} -Properties ms-Mcs-AdmPwd,operatingsystem | select @{Label="Name";Expression={$_.name}}, @{Label="OS";Expression={$_.operatingsystem}}, @{Label="Distinguished name";Expression={$_.'distinguishedname'}},@{Label="Password";Expression={$_.'ms-Mcs-AdmPwd'}}

# Date used for all date values
$date = Get-Date -UFormat '+%Y-%m-%dT%H:%M:%S.000Z';

# Creates XML document object
[xml]$doc = New-Object System.Xml.XmlDocument

# Add XML declaration 
$dec = $Doc.CreateXmlDeclaration("1.0","UTF-8",$null);
$doc.AppendChild($dec) | Out-Null;

# The actual root mode of the XML document. Called KeePassFile
# KeePassFile
$keePassFile = $doc.CreateNode("element","KeePassFile",$null);
$doc.AppendChild($keePassFile) | Out-Null;

# A node called root but is actually a child of the actual root node KeePassFile
# KeePassFile > Root
$root = $doc.CreateNode("element","Root",$null);
$keePassFile.AppendChild($root) | Out-Null;

# The main group in KeePass called Database
# KeePassFile > Root > Group
$group = $doc.CreateNode("element","Group",$null);
$root.AppendChild($group) | Out-Null;

# KeePassFile > Root > Group > UUID
$node = $doc.CreateNode("element","UUID",$null);
$node.InnerText = "dwtCYiH+ZEmmEzshjUqaWA==";
$group.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Name
$node = $doc.CreateNode("element","Name",$null);
$node.InnerText = "Database";
$group.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Notes
$node = $doc.CreateNode("element","Notes",$null);
$node.InnerText = "";
$group.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > IconID
$node = $doc.CreateNode("element","IconID",$null);
$node.InnerText = "49";
$group.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Times
$times = $doc.CreateNode("element","Times",$null);
$times.InnerText = "";
$group.AppendChild($times) | Out-Null;

# KeePassFile > Root > Group > Times > CreationTime
$node = $doc.CreateNode("element","CreationTime",$null);
$node.InnerText = $date;
$times.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Times > LastModificationTime
$node = $doc.CreateNode("element","LastModificationTime",$null);
$node.InnerText = $date;
$times.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Times > LastAccessTime
$node = $doc.CreateNode("element","LastAccessTime",$null);
$node.InnerText = $date;
$times.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Times > ExpiryTime
$node = $doc.CreateNode("element","ExpiryTime",$null);
$node.InnerText = $date;
$times.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Times > Expires
$node = $doc.CreateNode("element","Expires",$null);
$node.InnerText = "False";
$times.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Times > UsageCount
$node = $doc.CreateNode("element","UsageCount",$null);
$node.InnerText = "0";
$times.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Times > LocationChanged
$node = $doc.CreateNode("element","LocationChanged",$null);
$node.InnerText = $date;
$times.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > IsExpanded
$node = $doc.CreateNode("element","IsExpanded",$null);
$node.InnerText = "True";
$group.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > DefaultAutoTypeSequence
$node = $doc.CreateNode("element","DefaultAutoTypeSequence",$null);
$node.InnerText = "";
$group.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > EnableAutoType
$node = $doc.CreateNode("element","EnableAutoType",$null);
$node.InnerText = "null";
$group.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > EnableSearching
$node = $doc.CreateNode("element","EnableSearching",$null);
$node.InnerText = "null";
$group.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > LastTopVisibleEntry
$node = $doc.CreateNode("element","LastTopVisibleEntry",$null);
$node.InnerText = "AAAAAAAAAAAAAAAAAAAAAA==";
$group.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group
$subGroup = $doc.CreateNode("element","Group",$null);
$subGroup.InnerText = "";
$group.AppendChild($subGroup) | Out-Null;

# KeePassFile > Root > Group > Group > UUID
$node = $doc.CreateNode("element","UUID",$null);
$node.InnerText = "SAKp8Sn4wU6wQ5hTV1AG0A==";
$subGroup.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Name
$node = $doc.CreateNode("element","Name",$null);
$node.InnerText = "LAPS";
$subGroup.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Notes
$node = $doc.CreateNode("element","Notes",$null);
$node.InnerText = "";
$subGroup.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > IconID
$node = $doc.CreateNode("element","IconID",$null);
$node.InnerText = "18";
$subGroup.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Times
$subGroupTimes = $doc.CreateNode("element","Times",$null);
$subGroupTimes.InnerText = "";
$subGroup.AppendChild($subGroupTimes) | Out-Null;

# KeePassFile > Root > Group > Group > Times > CreationTime
$node = $doc.CreateNode("element","CreationTime",$null);
$node.InnerText = $date;
$subGroupTimes.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Times > LastModificationTime
$node = $doc.CreateNode("element","LastModificationTime",$null);
$node.InnerText = $date;
$subGroupTimes.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Times > LastAccessTime
$node = $doc.CreateNode("element","LastAccessTime",$null);
$node.InnerText = $date;
$subGroupTimes.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Times > ExpiryTime
$node = $doc.CreateNode("element","ExpiryTime",$null);
$node.InnerText = $date;
$subGroupTimes.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Times > Expires
$node = $doc.CreateNode("element","Expires",$null);
$node.InnerText = "False";
$subGroupTimes.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Times > UsageCount
$node = $doc.CreateNode("element","UsageCount",$null);
$node.InnerText = "0";
$subGroupTimes.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Times > LocationChanged
$node = $doc.CreateNode("element","LocationChanged",$null);
$node.InnerText = $date;
$subGroupTimes.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > IsExpanded
$node = $doc.CreateNode("element","IsExpanded",$null);
$node.InnerText = "True";
$subGroup.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > DefaultAutoTypeSequence
$node = $doc.CreateNode("element","DefaultAutoTypeSequence",$null);
$node.InnerText = "";
$subGroup.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > EnableAutoType
$node = $doc.CreateNode("element","EnableAutoType",$null);
$node.InnerText = "null";
$subGroup.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > EnableSearching
$node = $doc.CreateNode("element","EnableSearching",$null);
$node.InnerText = "null";
$subGroup.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > LastTopVisibleEntry
$node = $doc.CreateNode("element","LastTopVisibleEntry",$null);
$node.InnerText = "AAAAAAAAAAAAAAAAAAAAAA==";
$subGroup.AppendChild($node) | Out-Null;

foreach ($computer in $computers)
{
# KeePassFile > Root > Group > Group > Entry
$entry = $doc.CreateNode("element","Entry",$null);
$entry.InnerText = "";
$subGroup.AppendChild($entry) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > UUID
$md5 = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider;
$utf8 = new-object -TypeName System.Text.UTF8Encoding;
$hash = [System.BitConverter]::ToString($md5.ComputeHash($utf8.GetBytes($computer.Name)));
$guid = [guid]$hash.Replace("-","");
$bytes = $guid.ToByteArray()
$uuid = [convert]::ToBase64String($bytes)

$node = $doc.CreateNode("element","UUID",$null);
$node.InnerText = $uuid;
$entry.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > IconID
$node = $doc.CreateNode("element","IconID",$null);
$node.InnerText = "0";
$entry.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > ForegroundColor
$node = $doc.CreateNode("element","ForegroundColor",$null);
$node.InnerText = "";
$entry.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > BackgroundColor
$node = $doc.CreateNode("element","BackgroundColor",$null);
$node.InnerText = "";
$entry.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > OverrideURL
$node = $doc.CreateNode("element","OverrideURL",$null);
$node.InnerText = "";
$entry.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > Tags
$node = $doc.CreateNode("element","Tags",$null);
$node.InnerText = "";
$entry.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > Times
$entryTimes = $doc.CreateNode("element","Times",$null);
$entryTimes.InnerText = "";
$entry.AppendChild($entryTimes) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > Times > CreationTime
$node = $doc.CreateNode("element","CreationTime",$null);
$node.InnerText = $date;
$entryTimes.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > Times > LastModificationTime
$node = $doc.CreateNode("element","LastModificationTime",$null);
$node.InnerText = $date;
$entryTimes.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > Times > LastAccessTime
$node = $doc.CreateNode("element","LastAccessTime",$null);
$node.InnerText = $date;
$entryTimes.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > Times > ExpiryTime
$node = $doc.CreateNode("element","ExpiryTime",$null);
$node.InnerText = $date;
$entryTimes.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > Times > Expires
$node = $doc.CreateNode("element","Expires",$null);
$node.InnerText = "False";
$entryTimes.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > Times > UsageCount
$node = $doc.CreateNode("element","UsageCount",$null);
$node.InnerText = "0";
$entryTimes.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > Times > LocationChanged
$node = $doc.CreateNode("element","LocationChanged",$null);
$node.InnerText = $date;
$entryTimes.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > String
$string1 = $doc.CreateNode("element","String",$null);
$string1.InnerText = "";
$entry.AppendChild($string1) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > String > Key
$node = $doc.CreateNode("element","Key",$null);
$node.InnerText = "Notes";
$string1.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > String > Value
$node = $doc.CreateNode("element","Value",$null);
$node.InnerText = $computer."Distinguished name";
$string1.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > String
$string2 = $doc.CreateNode("element","String",$null);
$string2.InnerText = "";
$entry.AppendChild($string2) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > String > Key
$node = $doc.CreateNode("element","Key",$null);
$node.InnerText = "Password";
$string2.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > String > Password
$node = $doc.CreateNode("element","Value",$null);
$node.SetAttribute("ProtectInMemory","True");
$node.InnerText = $computer.Password;
$string2.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > String
$string3 = $doc.CreateNode("element","String",$null);
$string3.InnerText = "";
$entry.AppendChild($string3) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > String > Key
$node = $doc.CreateNode("element","Key",$null);
$node.InnerText = "Title";
$string3.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > String > Value
$node = $doc.CreateNode("element","Value",$null);
$node.InnerText = $computer.Name;
$string3.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > String
$string4 = $doc.CreateNode("element","String",$null);
$string4.InnerText = "";
$entry.AppendChild($string4) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > String > Key
$node = $doc.CreateNode("element","Key",$null);
$node.InnerText = "URL";
$string4.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > String > Value
$node = $doc.CreateNode("element","Value",$null);
$node.InnerText = "";
$string4.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > String
$string5 = $doc.CreateNode("element","String",$null);
$string5.InnerText = "";
$entry.AppendChild($string5) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > String > Key
$node = $doc.CreateNode("element","Key",$null);
$node.InnerText = "UserName";
$string5.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > String > Value
$node = $doc.CreateNode("element","Value",$null);
$node.InnerText = "";
$string5.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > AutoType
$autoType = $doc.CreateNode("element","AutoType",$null);
$autoType.InnerText = "";
$entry.AppendChild($autoType) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > AutoType > Enabled
$node = $doc.CreateNode("element","Enabled",$null);
$node.InnerText = "True";
$autoType.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > AutoType > DataTransferObfuscation
$node = $doc.CreateNode("element","DataTransferObfuscation",$null);
$node.InnerText = "0";
$autoType.AppendChild($node) | Out-Null;

# KeePassFile > Root > Group > Group > Entry > History
$history = $doc.CreateNode("element","History",$null);
$history.InnerText = "";
$entry.AppendChild($history) | Out-Null;
}

# Display XML
$doc.InnerXml;


Execution


To execute this script, run the following from Powershell, where .\LAPSExport.xml is the desired name and path of the outputted XML file


.\LAPSToKeePass.ps1 | Out-File -Encoding UTF8 -FilePath .\LAPSExport.xml


Importing into KeePass


To import into KeePass, you can do it directly via the GUI with the import option or you can automate the process via KPScript1.


1 Information on KPScript can be found here https://keepass.info/help/v2_dev/scr_sc_index.html (steps are outside the scope of this article)



Conclusion


There you have it. Essentially every time you run this script it will generate an output that can be saved as XML and imported into KeePass.


Please ensure that you filter out any accounts that you do not want to be exported.


This process was tested with the following

  • KeePass v2.41
  • Active Directory 2016 (although it should work perfectly fine on 2019)
  • LAPS v6.2


I hope you found this tutorial useful. You are encouraged to ask questions, report any bugs or make any other comments about it below.

 

Note: If you need further "Support" about this topic, please consider using the Ask a Question feature of Experts Exchange. I monitor questions asked and would be pleased to provide any additional support required in questions asked in this manner, along with other EE experts...  

 

Please do not forget to press the "Thumbs Up" button if you think this article was helpful and valuable for EE members.


It also provides me with positive feedback. Thank you!


Arrow pointing to the Thumbs Up icon

4
Ask questions about what you read
If you have a question about something within an article, you can receive help directly from the article author. Experts Exchange article authors are available to answer questions and further the discussion.
Get 7 days free