{"id":11661,"date":"2011-12-27T00:01:00","date_gmt":"2011-12-27T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2011\/12\/27\/use-powershell-to-work-with-the-mdt-customsettings-ini-file\/"},"modified":"2011-12-27T00:01:00","modified_gmt":"2011-12-27T00:01:00","slug":"use-powershell-to-work-with-the-mdt-customsettings-ini-file","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/use-powershell-to-work-with-the-mdt-customsettings-ini-file\/","title":{"rendered":"Use PowerShell to Work with the MDT CustomSettings.ini File"},"content":{"rendered":"<p><b>Summary<\/b>: Microsoft PowerShell MVP, Sean Kearney, shows how to use Windows PowerShell to work with the MDT CustomSettings.ini file.\nMicrosoft Scripting Guy, Ed Wilson, is here. This week, Windows PowerShell MVP, <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/tags\/windows+powershell\/guest+blogger\/sean+kearney\/\" target=\"_blank\">Sean Kearney<\/a>, is our guest blogger, and he is writing about Microsoft Development Kit Update&nbsp;1 (MDT).\nAll throughout history, there have been great teams: Abbott and Costello, Sears and Roebuck, hotdogs and ketchup. Today, we continue with another great team: MDT and Windows PowerShell.\n<a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2011\/12\/26\/use-powershell-to-create-deployment-shares-for-mdt.aspx\" target=\"_blank\">Yesterday<\/a>, we showed you how to improve the stock cmdlet for making a new Deployment Share in MDT. We found that it was like some of the greatest symphonies&mdash;a little unfinished, but we polished it up. But I mentioned that there was another feature that is not echoed in our cmdlet or script. When you create a new Deployment Share, MDT asks you three questions&hellip;three questions that cause the greatest of philosophers to debate into the night the answer to life, the universe, and&#8230;everything.\n(Pssst, it&rsquo;s 42, and it works in Base 13.)\nNow that I have angered several philosophers, and possibly put them out of work, let us step back to MDT&hellip;\nMDT prompts us with the following key questions about the base ability of this share:<\/p>\n<ul>\n<li>Ask if an image should be captured (selected by default)<\/li>\n<li>Ask user to set the local Administrator Password (not selected by default)<\/li>\n<li>Ask user for a product key (not selected by default)<\/li>\n<\/ul>\n<p>When you are finished with the standard wizard, these are converted to three settings in a file called CustomSettings.ini, which is located under the <b>Control<\/b> subfolder in your newly created Deployment Share. By default this file will look like this (depending on the options you have selected):<\/p>\n<p style=\"padding-left: 30px\">[Settings] <br \/> Priority=Default <br \/> Properties=MyCustomProperty<\/p>\n<p style=\"padding-left: 30px\">[Default] <br \/> OSInstall=Y <br \/> SkipAppsOnUpgrade=YES <br \/> SkipCapture=YES <br \/> SkipAdminPassword=YES <br \/> SkipProductKey=YES\nWe will be dealing with the last three lines and their settings. Fortunately, the actual variable matches exactly what it is doing. The answer is obvious too. In the GUI, all you have to know is if you didn&rsquo;t select it, the answer for that option will be No; otherwise, it will be Yes.\nThat sounds like a very &ldquo;Boolean&rdquo; answer to me, so we could actually add some simple parameters to ask for a $True\/$False as part of the script. When we build it, we&rsquo;ll put in three Boolean parameters like this:<\/p>\n<p style=\"padding-left: 30px\">[Parameter (Mandatory=$false)] <br \/> [Boolean] $PromptPassword, <br \/> [Parameter (Mandatory=$false)] <br \/> [Boolean] $PromptKey, <br \/> [Parameter (Mandatory=$false)] <br \/> [Boolean] $CaptureImage\nThe next task, of course, is&hellip;well&hellip;how do we edit it? Because CustomSettings.ini is a text file, we can get away with building a simple &ldquo;search and destroy.&rdquo; This could be accomplished probably far better with regular expressions (Oh, Dr. ReGeX ! Wherefore art thou!), but we&rsquo;ll try something simpler. Let us get the data, store it away in a Windows PowerShell variable, and pass that through <b>Select-String<\/b>.\nWhy, you wonder? We may have to edit three lines or no lines. We need the data in memory so we can work with it as many times as we need it. So because we&rsquo;re going to build on our original script, we&rsquo;ll use the following script:<\/p>\n<p style=\"padding-left: 30px\">$CustomSettingsINI=(GET-CONTENT &ldquo;$FolderControlCustomSettings.ini&rdquo;)\nIf you did not pick up&hellip;that&rsquo;s a little trick that you can do with variables in Windows PowerShell. If you are assigning a value, if it&rsquo;s within double quotation marks, and it contains a Windows PowerShell variable (for example, <b>$Folder<\/b>), Windows PowerShell will expand it to its actual value when assigning it.\nNow we can use <b>Select-String<\/b> on one of the lines to find its location in the file as shown here:<\/p>\n<p style=\"padding-left: 30px\">$CustomSettingsINI | SELECT-STRING &ndash;pattern &ldquo;SkipProductKey&rdquo;\nIf you have played with <b>Select-String<\/b>, you know that one of the properties returned with that object is <strong>LineNumber<\/strong>, which is the row where it found your data within an array. So I can easily access the value of the variable <b>$CustomSettingsINI<\/b> by referencing the results of the first search.<\/p>\n<p style=\"padding-left: 30px\">$CustomSettingsINI[($CustomSettingsINI | SELECT-STRING &ndash;pattern &ldquo;SkipProductKey&rdquo;)]\nBut this would fail because an array always starts counting at 0, but the row count starts at 1. If <b>Select-String<\/b> finds your data, you must bump back the count by 1.<\/p>\n<p style=\"padding-left: 30px\">$CustomSettingsINI[(($CustomSettingsINI | SELECT-STRING &ndash;pattern &ldquo;SkipProductKey&rdquo;)-1)]\nWe&rsquo;ve found the row with the value. How do we edit it? The choice is up to you. You can get really fancy or just do something dead simple. (Remember, you can always go back to your script and improve how you did it later.)\nI am going to get a little bit fancy. Because the parameter is going to be a Boolean $True\/$False only, we can build a tiny array of &ldquo;Yes&rdquo; and &ldquo;No&rdquo; for the values, and have it flip that value simply dependent on the Boolean value. To convert a Boolean $True\/$False to a value like one or zero we simply do this:<\/p>\n<p style=\"padding-left: 30px\">[int]$SomeBooleanValue\nNow for my array, I was not kidding&hellip;it is a tiny two-member array with the values of &ldquo;Yes&rdquo; or &ldquo;No&rdquo; to edit into the value depending on our parameter:<\/p>\n<p style=\"padding-left: 30px\">$YESNO=(&#8220;NO&#8221;,&#8221;YES&#8221;)\nYou will notice that it is backwards. In our case, if the box is selected ($True), we want to make sure the answer to the value is &ldquo;Yes.&rdquo; So a Boolean $False (0) will yield &ldquo;No&rdquo; and a Boolean $True (1) will yield &ldquo;Yes.&rdquo; I do that with this little bit of trickery:<\/p>\n<p style=\"padding-left: 30px\">$CustomSettingsANSWER=$YESNO[([int]$SkipProductKey)]\nNow the rest the answer is up to you. I will be honest. I cheated. I sat down and wrote an &ldquo;If&rdquo; statement for the three Boolean tests. That feels like a cheat to me because I am repeating a procedure. But then again, as I have said before, this may well get me my answer in the short term. I can always go back and improve on the script with the time that I have saved by not sitting in the GUI all day.\nSo let us pull the pieces together and turn all of this into a new advanced function called <b>NEW-MDTDeploymentShare<\/b>.<\/p>\n<p style=\"padding-left: 30px\"><b>function global:NEW-MDTDeploymentShare()<\/b><\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">[CmdletBinding()] <br \/> param( <br \/> [Parameter (Mandatory=$true)] <br \/> [String] $Folder, <br \/> [Parameter (Mandatory=$true)] <br \/> [String] $Description, <br \/> [Parameter (Mandatory=$true)] <br \/> [String] $Share, <br \/> [Parameter (Mandatory=$false)] <br \/> [Boolean] $PromptPassword, <br \/> [Parameter (Mandatory=$false)] <br \/> [Boolean] $PromptKey, <br \/> [Parameter (Mandatory=$false)] <br \/> [Boolean] $CaptureImage <br \/> )<\/p>\n<p style=\"padding-left: 30px\">Process <br \/> { <br \/> Add-PSSnapIn Microsoft.BDD.PSSnapIn<\/p>\n<p style=\"padding-left: 30px\">$ListOfShares=GET-MDTPersistentDrive <br \/> Do { <br \/> $DSNAME=&#8221;DS&#8221;+((GET-RANDOM 999999999).tostring().trim()) <br \/> } Until ( ! ( $ListOfShares | Select-string -Pattern $DSNAME))<\/p>\n<p style=\"padding-left: 30px\"># Get NETBIOS name of computer<br \/> $ComputerName=$ENV:Computername<\/p>\n<p style=\"padding-left: 30px\"># Create Folder for Deployment Share<br \/> new-item -type Directory -path $Location<\/p>\n<p style=\"padding-left: 30px\"># Create Network Share for Deployment Share<br \/> $UNC=&rdquo;<b><\/b>$Computername$Sharename&#8221;<br \/> ([wmiclass]&#8221;Win32_share&#8221;).Create($Location,$Sharename,0)<\/p>\n<p style=\"padding-left: 30px\"># Create Deployment Point within MDT<br \/> new-PSDrive -Name $DSNAME -PSProvider &#8220;MDTProvider&#8221; &ndash;Root $Location &ndash;Description $Description &ndash;NetworkPath $UNC -Verbose | add-MDTPersistentDrive &ndash;Verbose<\/p>\n<p style=\"padding-left: 30px\"># Based upon Supplied parameters, Customize the Custom.ini<br \/> # Contained with the specific Deployment point<br \/> $YESNO=(&#8220;NO&#8221;,&#8221;YES&#8221;) <br \/> $CustomINI=(GET-CONTENT &#8220;$LocationControlCustomSettings.INI&#8221;) <br \/> IF ($PromptPassword) <br \/> { <br \/> $AnswerValue=$YESNO[([int] $PromptPassword)] <br \/> $CustomINI[(($CustomINI | SELECT-STRING -Pattern &#8220;SkipAdminPassword&#8221;).LineNumber)-1]=&#8221;SkipAdminPassword=$AnswerValue&#8221; <br \/> } <br \/> IF ($PromptKey) <br \/> { <br \/> $AnswerValue=$YESNO[([int] $PromptKey)] <br \/> $CustomINI[(($CustomINI | SELECT-STRING -Pattern &#8220;SkipProductKey&#8221;).LineNumber)-1]=&#8221;SkipProductKey=$AnswerValue&#8221; <br \/> } <br \/> IF ($CaptureImage) <br \/> { <br \/> $AnswerValue=$YESNO[([int] $CaptureImage)] <br \/> $CustomINI[(($CustomINI | SELECT-STRING -Pattern &#8220;SkipCapture&#8221;).LineNumber)-1]=&#8221;SkipCapture=$AnswerValue&#8221; <br \/> } <br \/> $CustomINI | SET-CONTENT -path &#8220;$LocationControlCustomSettings.ini&#8221; <br \/> } <br \/> }\nThere are obviously many ways that we can build on and improve on this&mdash;error checking, adding some Help, perhaps even rewriting how I prompted for the settings in CustomSettings.ini.\nBut what this does give us is a way to fully automate the creation of Deployment Shares. It shows us that we can also improve existing tools that are provided to us if they do not meet our needs.\nSo with this added to my profile or a module, I can now do something as simple as this:<\/p>\n<p style=\"padding-left: 30px\">NEW-MDTDeploymentShare &ndash;Folder &lsquo;C:MyShare&rsquo; &ndash;Description &lsquo;Another MDT Share&rsquo; &ndash;Share &lsquo;ThisShare&rsquo;\nMDT will do all the work, or I can have a CSV file with configurations for a lab environment and simply execute the script to rebuild it as I need.\nTomorrow we will dive into the real power of MDT&mdash;bringing in data to make that image useful, and utilizing Windows PowerShell to repeat those tasks easily.\n~Sean\nThanks, Sean!\nJoin us tomorrow for Part 3.\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>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Microsoft PowerShell MVP, Sean Kearney, shows how to use Windows PowerShell to work with the MDT CustomSettings.ini file. Microsoft Scripting Guy, Ed Wilson, is here. This week, Windows PowerShell MVP, Sean Kearney, is our guest blogger, and he is writing about Microsoft Development Kit Update&nbsp;1 (MDT). All throughout history, there have been great teams: [&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":[56,316,3,154,130,45],"class_list":["post-11661","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-guest-blogger","tag-mdt","tag-scripting-guy","tag-sean-kearney","tag-servers","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Microsoft PowerShell MVP, Sean Kearney, shows how to use Windows PowerShell to work with the MDT CustomSettings.ini file. Microsoft Scripting Guy, Ed Wilson, is here. This week, Windows PowerShell MVP, Sean Kearney, is our guest blogger, and he is writing about Microsoft Development Kit Update&nbsp;1 (MDT). All throughout history, there have been great teams: [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/11661","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=11661"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/11661\/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=11661"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=11661"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=11661"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}