{"id":5912,"date":"2017-05-21T20:23:49","date_gmt":"2017-05-22T03:23:49","guid":{"rendered":"http:\/\/tommymaynard.com\/?p=5912"},"modified":"2022-06-09T20:31:02","modified_gmt":"2022-06-10T03:31:02","slug":"powershell-code-and-aws-cloudformation-userdata-2017","status":"publish","type":"post","link":"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/","title":{"rendered":"PowerShell Code and AWS CloudFormation UserData"},"content":{"rendered":"<p><strong>Note<\/strong>: <em>This post was written well over a month ago, but was never posted, due to some issues I was seeing in AWS GovCloud. It works 100% of the time now, in both GovCloud and non-GovCloud AWS. That said, if you&#8217;re using <code>Read-S3Object<\/code> in GovCloud, you&#8217;re going to need to include the <strong>Region<\/strong> parameter name and value.<br \/>\n<\/em><\/p>\n<p>As I spend more and more time with AWS, I end up back at PowerShell. If I haven&#8217;t said it yet, thank you Amazon Web Services, for writing us a PowerShell module.<\/p>\n<p>In the last month, or two, I&#8217;ve been getting into the CloudFormation template business. I love the whole UserData option we have&#8212;injecting PowerShell code into an EC2 instance during its initialization, and love, that while we can do it in the AWS Management Console, we can do it with CloudFormation (CFN) too. In the last few months, I&#8217;ve decided to do things a bit differently. Instead of dropping large amounts of PowerShell code inside my UserData property in the CFN template, I decided to use <code>Read-S3Object<\/code> to copy PowerShell modules to EC2 instances, and then just issue calls to the functions in the remainder of the CFN UserData. In one instance, I went from 200+ lines of PowerShell in the CFN template to just a few.<\/p>\n<p>To test, I needed to verify if I could get a module folder and file into the proper place on the instance <em>and<\/em> be able to use the module&#8217;s function(s) immediately, without any need to end one PowerShell session and start a new one. I suspected this would work just fine, but it needed to be seen.<\/p>\n<p>Here&#8217;s how the testing went: On my Desktop, I have a folder called <code>MyModule<\/code>. Inside the folder, I have a file called <code>MyModule.psm1<\/code>. If you haven&#8217;t seen it before, this file extension indicates the file is a PowerShell module file. The contents of the file, are as follows:<\/p>\n<pre class=\"brush: powershell; title: ; notranslate\" title=\"\">\r\nFunction Get-A {\r\n    'A'\r\n}\r\n\r\nFunction Get-B {\r\n    'B'\r\n}\r\n\r\nFunction Get-C {\r\n    'C'\r\n}\r\n<\/pre>\n<p>The file contents indicate that the module contains three functions: <code>Get-A<\/code>, <code>Get-B<\/code>, and <code>Get-C<\/code>. In the next example, we can see that the Desktop folder isn&#8217;t a place where a module file and folder can exist, where we can expect that the modules will be automatically loaded into the PowerShell session. PowerShell isn&#8217;t aware of this module on its own, as can be seen below.<\/p>\n<pre class=\"brush: powershell; title: ; notranslate\" title=\"\">\r\nGet-A\r\n<\/pre>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nGet-A : The term 'Get-A' is not recognized as the name of a cmdlet, function, script file, or operable program. Check\r\nthe spelling of the name, or if a path was included, verify that the path is correct and try again.\r\nAt line:1 char:1\r\n+ Get-A\r\n+ ~~~~~\r\n    + CategoryInfo          : ObjectNotFound: (Get-A:String) &#x5B;], CommandNotFoundException\r\n    + FullyQualifiedErrorId : CommandNotFoundException\r\n<\/pre>\n<pre class=\"brush: powershell; title: ; notranslate\" title=\"\">\r\nGet-B\r\n<\/pre>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nGet-B : The term 'Get-B' is not recognized as the name of a cmdlet, function, script file, or operable program. Check\r\nthe spelling of the name, or if a path was included, verify that the path is correct and try again.\r\nAt line:1 char:1\r\n+ Get-B\r\n+ ~~~~~\r\n    + CategoryInfo          : ObjectNotFound: (Get-B:String) &#x5B;], CommandNotFoundException\r\n    + FullyQualifiedErrorId : CommandNotFoundException\r\n<\/pre>\n<pre class=\"brush: powershell; title: ; notranslate\" title=\"\">\r\nGet-C\r\n<\/pre>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nGet-C : The term 'Get-C' is not recognized as the name of a cmdlet, function, script file, or operable program. Check\r\nthe spelling of the name, or if a path was included, verify that the path is correct and try again.\r\nAt line:1 char:1\r\n+ Get-C\r\n+ ~~~~~\r\n    + CategoryInfo          : ObjectNotFound: (Get-C:String) &#x5B;], CommandNotFoundException\r\n    + FullyQualifiedErrorId : CommandNotFoundException\r\n<\/pre>\n<p>While I could tell PowerShell to look on my desktop, what I wanted to do is have my CFN template copy the module folder out of S3 and place it on the instances, in a preferred and proper location: <code>C:\\Program Files\\WindowsPowerShell\\Modules<\/code>. This <em>is<\/em> a location that PowerShell checks for modules automatically, and loads them the moment a contained function, or cmdlet, from the module, is requested. My example uses a different path, but PowerShell will check here automatically, as well. As a part of this testing, we&#8217;re pretending that the movement from my desktop is close enough to the movement from S3 to an EC2 instance. I&#8217;ll obviously test this more <em>with<\/em> AWS.<\/p>\n<pre class=\"brush: powershell; title: ; notranslate\" title=\"\">\r\nMove-Item -Path .\\Desktop\\MyModule\\ -Destination C:\\Users\\tommymaynard\\Documents\\WindowsPowerShell\\Modules\\\r\nGet-A\r\nA\r\nPS &gt; Get-B\r\nB\r\nGet-C\r\nC\r\n<\/pre>\n<p>Without the need to open a new PowerShell session, I absolutely could use the functions in my module, the moment the module was moved from the Desktop into a folder PowerShell looks at by default. Speaking of those locations, you can view them by returning the value in the <code>$env:PSModulePath<\/code> environmental variable. Use <code>$env:PSModulePath -split ';'<\/code> to make it easier to read.<\/p>\n<p>Well, it looks like I was right. I can simply drop those module folders on the EC2 instance, into <code>C:\\Program Files\\WindowsPowerShell\\Modules<\/code>, just before they&#8217;re used with no need for anything more than the current PowerShell session that&#8217;s moving them into place.<\/p>\n<p><strong>Update<\/strong>: After this on-my-own-computer test, I took it to AWS. It works, and now it&#8217;s the only way I use my CFN template UserData. I write my function(s), house them in a PowerShell module(s), copy them to S3, and finally use my CFN UserData to copy them to the EC2 instance. When that&#8217;s complete, I can call the contained function(s) without any hesitation, or additional work. It wasn&#8217;t necessary, but I added sleep commands between the function invocations. Here&#8217;s a quick, modified example you might find in the UserData of one of my CloudFormation templates.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n      UserData:\r\n        Fn::Base64:\r\n          !Sub |\r\n          &lt;powershell&gt;\r\n            # Download PowerShell Modules from S3.\r\n            $Params = @{\r\n              BucketName = 'windows'\r\n              Keyprefix = 'WindowsPowerShell\/Modules\/ProjectVII\/'\r\n              Folder = &quot;$env:ProgramFiles\\WindowsPowerShell\\Modules&quot;\r\n            }\r\n            Read-S3Object @Params | Out-Null\r\n\r\n            # Invoke function(s).\r\n            Set-TimeZone -Verbose -Log\r\n            Start-Sleep -Seconds 15\r\n\r\n            Add-EncryptionType -Verbose -Log\r\n            Start-Sleep -Seconds 15\r\n\r\n            Install-ProjectVII -Verbose -Log\r\n            Start-Sleep -Seconds 15\r\n            &lt;\/powershell&gt;\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Note: This post was written well over a month ago, but was never posted, due to some issues I was seeing in AWS GovCloud. It works 100% of the time now, in both GovCloud and non-GovCloud AWS. That said, if you&#8217;re using Read-S3Object in GovCloud, you&#8217;re going to need to include the Region parameter name [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":true,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[2],"tags":[467,388,464,466,465],"class_list":["post-5912","post","type-post","status-publish","format-standard","hentry","category-quick-learn","tag-envpsmodulepath","tag-aws","tag-cloudformation","tag-module","tag-userdata"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.2 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>PowerShell Code and AWS CloudFormation UserData - tommymaynard.com<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"PowerShell Code and AWS CloudFormation UserData - tommymaynard.com\" \/>\n<meta property=\"og:description\" content=\"Note: This post was written well over a month ago, but was never posted, due to some issues I was seeing in AWS GovCloud. It works 100% of the time now, in both GovCloud and non-GovCloud AWS. That said, if you&#8217;re using Read-S3Object in GovCloud, you&#8217;re going to need to include the Region parameter name [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/\" \/>\n<meta property=\"og:site_name\" content=\"tommymaynard.com\" \/>\n<meta property=\"article:published_time\" content=\"2017-05-22T03:23:49+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-06-10T03:31:02+00:00\" \/>\n<meta name=\"author\" content=\"Tommy Maynard\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Tommy Maynard\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/\"},\"author\":{\"name\":\"Tommy Maynard\",\"@id\":\"https:\/\/tommymaynard.com\/#\/schema\/person\/c74505a44d3d93dc7f82a08502d81ff8\"},\"headline\":\"PowerShell Code and AWS CloudFormation UserData\",\"datePublished\":\"2017-05-22T03:23:49+00:00\",\"dateModified\":\"2022-06-10T03:31:02+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/\"},\"wordCount\":960,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/tommymaynard.com\/#\/schema\/person\/c74505a44d3d93dc7f82a08502d81ff8\"},\"keywords\":[\"$env:PSModulePath\",\"AWS\",\"CloudFormation\",\"Module\",\"UserData\"],\"articleSection\":[\"Quick Learn\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/\",\"url\":\"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/\",\"name\":\"PowerShell Code and AWS CloudFormation UserData - tommymaynard.com\",\"isPartOf\":{\"@id\":\"https:\/\/tommymaynard.com\/#website\"},\"datePublished\":\"2017-05-22T03:23:49+00:00\",\"dateModified\":\"2022-06-10T03:31:02+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/tommymaynard.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"PowerShell Code and AWS CloudFormation UserData\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/tommymaynard.com\/#website\",\"url\":\"https:\/\/tommymaynard.com\/\",\"name\":\"tommymaynard.com\",\"description\":\"A PowerShell Resource. Since June 2014.\",\"publisher\":{\"@id\":\"https:\/\/tommymaynard.com\/#\/schema\/person\/c74505a44d3d93dc7f82a08502d81ff8\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/tommymaynard.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Person\",\"Organization\"],\"@id\":\"https:\/\/tommymaynard.com\/#\/schema\/person\/c74505a44d3d93dc7f82a08502d81ff8\",\"name\":\"Tommy Maynard\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/secure.gravatar.com\/avatar\/ff5e885ce365c2b895df63b9b0d98a7f1a1d1421def6bfb59336a95460fc2329?s=96&d=mm&r=g\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/ff5e885ce365c2b895df63b9b0d98a7f1a1d1421def6bfb59336a95460fc2329?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/ff5e885ce365c2b895df63b9b0d98a7f1a1d1421def6bfb59336a95460fc2329?s=96&d=mm&r=g\",\"caption\":\"Tommy Maynard\"},\"logo\":{\"@id\":\"https:\/\/secure.gravatar.com\/avatar\/ff5e885ce365c2b895df63b9b0d98a7f1a1d1421def6bfb59336a95460fc2329?s=96&d=mm&r=g\"},\"description\":\"Windows PowerShell enthusiast with over 15 years of Information Technology experience.\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"PowerShell Code and AWS CloudFormation UserData - tommymaynard.com","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/","og_locale":"en_US","og_type":"article","og_title":"PowerShell Code and AWS CloudFormation UserData - tommymaynard.com","og_description":"Note: This post was written well over a month ago, but was never posted, due to some issues I was seeing in AWS GovCloud. It works 100% of the time now, in both GovCloud and non-GovCloud AWS. That said, if you&#8217;re using Read-S3Object in GovCloud, you&#8217;re going to need to include the Region parameter name [&hellip;]","og_url":"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/","og_site_name":"tommymaynard.com","article_published_time":"2017-05-22T03:23:49+00:00","article_modified_time":"2022-06-10T03:31:02+00:00","author":"Tommy Maynard","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Tommy Maynard","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/#article","isPartOf":{"@id":"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/"},"author":{"name":"Tommy Maynard","@id":"https:\/\/tommymaynard.com\/#\/schema\/person\/c74505a44d3d93dc7f82a08502d81ff8"},"headline":"PowerShell Code and AWS CloudFormation UserData","datePublished":"2017-05-22T03:23:49+00:00","dateModified":"2022-06-10T03:31:02+00:00","mainEntityOfPage":{"@id":"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/"},"wordCount":960,"commentCount":0,"publisher":{"@id":"https:\/\/tommymaynard.com\/#\/schema\/person\/c74505a44d3d93dc7f82a08502d81ff8"},"keywords":["$env:PSModulePath","AWS","CloudFormation","Module","UserData"],"articleSection":["Quick Learn"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/","url":"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/","name":"PowerShell Code and AWS CloudFormation UserData - tommymaynard.com","isPartOf":{"@id":"https:\/\/tommymaynard.com\/#website"},"datePublished":"2017-05-22T03:23:49+00:00","dateModified":"2022-06-10T03:31:02+00:00","breadcrumb":{"@id":"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/tommymaynard.com\/powershell-code-and-aws-cloudformation-userdata-2017\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/tommymaynard.com\/"},{"@type":"ListItem","position":2,"name":"PowerShell Code and AWS CloudFormation UserData"}]},{"@type":"WebSite","@id":"https:\/\/tommymaynard.com\/#website","url":"https:\/\/tommymaynard.com\/","name":"tommymaynard.com","description":"A PowerShell Resource. Since June 2014.","publisher":{"@id":"https:\/\/tommymaynard.com\/#\/schema\/person\/c74505a44d3d93dc7f82a08502d81ff8"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/tommymaynard.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Person","Organization"],"@id":"https:\/\/tommymaynard.com\/#\/schema\/person\/c74505a44d3d93dc7f82a08502d81ff8","name":"Tommy Maynard","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/ff5e885ce365c2b895df63b9b0d98a7f1a1d1421def6bfb59336a95460fc2329?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/ff5e885ce365c2b895df63b9b0d98a7f1a1d1421def6bfb59336a95460fc2329?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/ff5e885ce365c2b895df63b9b0d98a7f1a1d1421def6bfb59336a95460fc2329?s=96&d=mm&r=g","caption":"Tommy Maynard"},"logo":{"@id":"https:\/\/secure.gravatar.com\/avatar\/ff5e885ce365c2b895df63b9b0d98a7f1a1d1421def6bfb59336a95460fc2329?s=96&d=mm&r=g"},"description":"Windows PowerShell enthusiast with over 15 years of Information Technology experience."}]}},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p8mce3-1xm","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/tommymaynard.com\/wp-json\/wp\/v2\/posts\/5912","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tommymaynard.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tommymaynard.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tommymaynard.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tommymaynard.com\/wp-json\/wp\/v2\/comments?post=5912"}],"version-history":[{"count":3,"href":"https:\/\/tommymaynard.com\/wp-json\/wp\/v2\/posts\/5912\/revisions"}],"predecessor-version":[{"id":12930,"href":"https:\/\/tommymaynard.com\/wp-json\/wp\/v2\/posts\/5912\/revisions\/12930"}],"wp:attachment":[{"href":"https:\/\/tommymaynard.com\/wp-json\/wp\/v2\/media?parent=5912"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tommymaynard.com\/wp-json\/wp\/v2\/categories?post=5912"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tommymaynard.com\/wp-json\/wp\/v2\/tags?post=5912"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}