{"id":593,"date":"2020-09-02T19:58:00","date_gmt":"2020-09-02T19:58:00","guid":{"rendered":"http:\/\/draith.azurewebsites.net\/?p=593"},"modified":"2021-01-19T20:02:33","modified_gmt":"2021-01-19T20:02:33","slug":"use-a-config-file-with-powershell","status":"publish","type":"post","link":"https:\/\/draith.com\/?p=593","title":{"rendered":"Use a Config File with PowerShell"},"content":{"rendered":"\n<p>How many times has this occurred?<\/p>\n\n\n\n<p><br> <em>&#8220;Hey App Onwer &#8211; all of these PowerShell automation scripts you had our team create started failing this weekend.  Did something change?&#8221;<br> &#8220;Oh yeah Awesome Automation Team.  We flipped over to a new database cluster on Saturday, along with a new web front end.  Our API endpoints all changed as well.  Didn&#8217;t anyone tell you?  How long will it take to update your code?&#8221;<\/em><br> Looking at dozens of scripts you personally didn&#8217;t create: &#8220;Ummmmm&#8221;<\/p>\n\n\n\n<p>This scenario happens all of the time, and there are probably a hundred different ways to deal with it.  I personally use a config file, and for very specific reasons.  If you are coding in PowerShell 7+ (and you should be), then making your code cross-platform compatiable should be at the top of your priority list.  In the past, I would store things like database connection strings or API endpoints in the Windows registry, and my scripts would just reference that reg key.  This worked great &#8211; I didn&#8217;t have the same property listed in a dozen different scripts, and I only had to update the property in once place.  Once I started coding with cross-plat in mind, I obviously had to change my thinking.  This is where a simple JSON config file comes in handy.  A config file can be placed anywhere, and the structured JSON format makes creating, editing, and reading easy to do.  Using a config file allows me to take my whole code and move it from one system to another regardless of platform.<\/p>\n\n\n\n<p>Here is what a sample config file might look like:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;application\/json&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JSON&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;json&quot;}\">{\n    &quot;Environments&quot;: [\n        {\n            &quot;Name&quot;: &quot;Prod&quot;,\n            &quot;CMDBConnectionString&quot;: &quot;Data Source=cmdbdatabaseserverAG;MultiSubnetFailover=True;Initial Catalog=CMDB;Integrated Security=SSPI&quot;,\n            &quot;LoggingDBConnectionString&quot;: &quot;Data Source=LoggingAG;MultiSubnetFailover=True;Initial Catalog=Logs;Integrated Security=SSPI&quot;,\n            &quot;ServiceNowRestAPI&quot;:&quot;https:\/\/prodservicenowURL:4433\/rest&quot;,\n            &quot;Servers&quot;: [\n                {\n                    &quot;ServerName&quot;: &quot;prodserver1&quot;,\n                    &quot;ModulesDirectory&quot;: &quot;e:\\\\tasks\\\\pwsh\\\\modules&quot;,\n                    &quot;LogsDirectory&quot;: &quot;e:\\\\tasks\\\\pwsh\\\\logs\\\\&quot;\n                },\n                {\n                    &quot;ServerName&quot;: &quot;prodserver2&quot;,\n                    &quot;ModulesDirectory&quot;: &quot;e:\\\\tasks\\\\pwsh\\\\modules&quot;,\n                    &quot;LogsDirectory&quot;: &quot;e:\\\\tasks\\\\pwsh\\\\logs\\\\&quot;\n                }\n            ]\n        },\n        {\n            &quot;Name&quot;: &quot;Dev&quot;,\n            &quot;CMDBConnectionString&quot;: &quot;Data Source=testcmdbdatabaseserverAG;MultiSubnetFailover=True;Initial Catalog=CMDB;Integrated Security=SSPI&quot;,\n            &quot;LoggingDBConnectionString&quot;: &quot;Data Source=testLoggingAG;MultiSubnetFailover=True;Initial Catalog=Logs;Integrated Security=SSPI&quot;,\n            &quot;ServiceNowRestAPI&quot;:&quot;https:\/\/testservicenowURL:4433\/rest&quot;,\n            &quot;Servers&quot;: [\n                {\n                    &quot;ServerName&quot;: &quot;testserver1&quot;,\n                    &quot;ModulesDirectory&quot;: &quot;e:\\\\tasks\\\\pwsh\\\\modules&quot;,\n                    &quot;LogsDirectory&quot;: &quot;e:\\\\tasks\\\\pwsh\\\\logs\\\\&quot;\n                },\n                {\n                    &quot;ServerName&quot;: &quot;testserver2&quot;,\n                    &quot;ModulesDirectory&quot;: &quot;e:\\\\tasks\\\\pwsh\\\\modules&quot;,\n                    &quot;LogsDirectory&quot;: &quot;e:\\\\tasks\\\\pwsh\\\\logs\\\\&quot;\n                }\n            ]            \n        }\n    ]\n}<\/pre><\/div>\n\n\n\n<p>And here is what the code that parses it looks like in each script:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;powershell&quot;,&quot;mime&quot;:&quot;application\/x-powershell&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;PowerShell&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;powershell&quot;}\">$config = get-content $PSScriptRoot\\..\\config.json|convertfrom-json\n$ModulesDir = $config.Environments.Servers|where-object {$_.ServerName -eq [Environment]::MachineName}|select-object -expandProperty ModulesDirectory\n$LogsDir = $config.Environments.Servers|where-object {$_.ServerName -eq [Environment]::MachineName}|select-object -expandProperty LogsDirectory\n$DBConnectionString = $config.Environments|where-object {[Environment]::MachineName -in $_.Servers.ServerName}|select-object -expandProperty CMDBConnectionString\n<\/pre><\/div>\n\n\n\n<p>Note that in this particular setup, I use the server name of the server actually running the script to determine if the environment is production or not.  That is completely optional &#8211; your config file might be as simple as a couple of lines &#8211; something like this:<\/p>\n\n\n\n<div class=\"wp-block-codemirror-blocks-code-block code-block\"><pre class=\"CodeMirror\" data-setting=\"{&quot;showPanel&quot;:true,&quot;languageLabel&quot;:&quot;language&quot;,&quot;fullScreenButton&quot;:true,&quot;copyButton&quot;:true,&quot;mode&quot;:&quot;javascript&quot;,&quot;mime&quot;:&quot;application\/json&quot;,&quot;theme&quot;:&quot;material&quot;,&quot;lineNumbers&quot;:false,&quot;styleActiveLine&quot;:false,&quot;lineWrapping&quot;:false,&quot;readOnly&quot;:true,&quot;fileName&quot;:&quot;&quot;,&quot;language&quot;:&quot;JSON&quot;,&quot;maxHeight&quot;:&quot;400px&quot;,&quot;modeName&quot;:&quot;json&quot;}\">{\n    &quot;CMDBConnectionString&quot;: &quot;Data Source=cmdbdatabaseserverAG;MultiSubnetFailover=True;Initial Catalog=CMDB;Integrated Security=SSPI&quot;,\n    &quot;LoggingDBConnectionString&quot;: &quot;Data Source=LoggingAG;MultiSubnetFailover=True;Initial Catalog=Logs;Integrated Security=SSPI&quot;,\n    &quot;ServiceNowRestAPI&quot;:&quot;https:\/\/prodservicenowURL:4433\/rest&quot;\n}\n<\/pre><\/div>\n\n\n\n<p>When you use a simple file like this, you don&#8217;t need to parse the machine name &#8211; a simple &#8220;$ModulesDir = $config |select-object -expandProperty ModulesDirectory&#8221; would work great!  <\/p>\n\n\n\n<p>Now you can simply update the connection string or any other property in one location and start your testing.  No need to &#8220;Find in all Files&#8221; or search and replace across all your scripts.  An added plus is that using a config file vs something like a registry key is that the code is cross-platform compatible.  I&#8217;ve used JSON here due to a personal preference, but you could use any type of flat file you wanted, including a simple text file.  If you use a central command and control tool &#8211; something like SMA, then those properties are already stored locally, but this method would be handy for those that don&#8217;t have capability or simply want to prepare their scripts for running on K8s or Docker.<\/p>\n\n\n\n<p>Let me know what you think about this &#8211; Do you like this approach or do you have another method you like better?  Hit me up on Twitter &#8211; @donnie_taylor<\/p>\n","protected":false},"excerpt":{"rendered":"<p>How many times has this occurred? &#8220;Hey App Onwer &#8211; all of these PowerShell automation scripts you had our team create started failing this weekend. Did something change?&#8221; &#8220;Oh yeah Awesome Automation Team. We flipped over to a new database cluster on Saturday, along with a new web front end. Our API endpoints all changed &hellip; <a href=\"https:\/\/draith.com\/?p=593\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Use a Config File with PowerShell&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[16],"class_list":["post-593","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-powershell"],"_links":{"self":[{"href":"https:\/\/draith.com\/index.php?rest_route=\/wp\/v2\/posts\/593","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/draith.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/draith.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/draith.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/draith.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=593"}],"version-history":[{"count":4,"href":"https:\/\/draith.com\/index.php?rest_route=\/wp\/v2\/posts\/593\/revisions"}],"predecessor-version":[{"id":597,"href":"https:\/\/draith.com\/index.php?rest_route=\/wp\/v2\/posts\/593\/revisions\/597"}],"wp:attachment":[{"href":"https:\/\/draith.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=593"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/draith.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=593"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/draith.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=593"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}