{"id":13151,"date":"2011-07-31T00:01:00","date_gmt":"2011-07-31T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2011\/07\/31\/create-your-own-powershell-rules-for-scriptcop\/"},"modified":"2011-07-31T00:01:00","modified_gmt":"2011-07-31T00:01:00","slug":"create-your-own-powershell-rules-for-scriptcop","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/create-your-own-powershell-rules-for-scriptcop\/","title":{"rendered":"Create Your Own PowerShell Rules for ScriptCop"},"content":{"rendered":"<p><strong>Summary:<\/strong> James Brundage teaches how to create your own Windows PowerShell rules for ScriptCop.\n&nbsp;\nMicrosoft Scripting Guy Ed Wilson here. Today, we have part two (including the conclusion) to the article begun by James Brundage yesterday. Take it away, James!\n&nbsp;<\/p>\n<h2><span style=\"font-size: medium\">Writing your own ScriptCop rules<\/span><\/h2>\n<p>Yesterday, I introduced you to ScriptCop, the tool to help make sure your scripts are following the rules.&nbsp; Today, I&rsquo;ll help you fine-tune the rules ScriptCop follows.&nbsp;\nEvery organization does things its own way. Some people like tabs. Some like spaces. Some like aliases. Others like fully qualified commands. Some domains won&rsquo;t let an unsigned script within 50 feet, and some lay out the welcome mat of &ldquo;Bypass.&rdquo; ScriptCop lets you write simple rules to enforce your organization&rsquo;s best practices.&nbsp;\nTo do this with ScriptCop, you&rsquo;ll need to download it instead of <a href=\"http:\/\/scriptcop.start-automating.com\/Test-Command.aspx\">running it online<\/a>. You can download it from the <a href=\"http:\/\/scriptcop.start-automating.com\/\">Start-Automating ScriptCop site<\/a>. After you <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2011\/04\/02\/scripting-wife-learns-about-unblocking-files-in-powershell.aspx\">unblock the file<\/a>, you can unzip it to DocumentsWindowsPowerShellModules. The scripts themselves are not signed.&nbsp;\nScriptCop rules live in the <b>rules<\/b> directory. ScriptCop rules are special functions or scripts that follow one rule of their own: <b>Test-ScriptCopRule<\/b>. This makes sure the rule has the right parameters so that it can get summarized information about your command without running your command. Remember, these are &ldquo;static&rdquo; analysis tools: we look, but don&rsquo;t touch; therefore your script is <i>not<\/i> modified or harmed in any way.&nbsp;\nThere are a few different things we analyze, and you can write a rule that uses any of them:<\/p>\n<ul>\n<li>The <b>CommandMetaData <\/b>of the command.<\/li>\n<li>The <b>PSModuleInfo <\/b>of the module.<\/li>\n<li>The list of <b>PSScriptTokens <\/b>and text<b> <\/b>in each command.<\/li>\n<li>The <b>MamlCommandHelpInfo<\/b> for a command.&nbsp;<\/li>\n<\/ul>\n<p>The items in bold are <b>typenames<\/b>. Use them or the good old <b>Get-Member<\/b> cmdlet to find out which information is available as you write each rule.&nbsp;\nIn today&rsquo;s blog post, I will help you to write four very quick rules:<\/p>\n<ul>\n<li>A rule to make sure that any parameter named <i>name<\/i> is a string.<\/li>\n<li>A rule to make sure the module is at least version 1.0.<\/li>\n<li>A rule to check that the command doesn&rsquo;t use the Write-Host cmdlet.<\/li>\n<li>A rule to check that the command has author information.&nbsp;<\/li>\n<\/ul>\n<p>A ScriptCop rule is really simplicity itself. It looks at a piece of information and writes out an error.&nbsp;\nThe first rule, to make sure that any parameter named <i>name<\/i> is a string, is simple: We have a copy\/paste header in the rule that sets up the parameters.&nbsp;\nThe <b>$CommandInfo<\/b> variable will have the metadata about the command, including a dictionary for each parameter. We look in that dictionary for <i>name<\/i>, and then check if its parameter type isn&rsquo;t a string. If it&rsquo;s not, we just use the <b>Write-Error<\/b> cmdlet. When you use the <b>Write-Error<\/b> cmdlet within a ScriptCop rule, it marks the rule as having failed. The resulting function is shown here.<\/p>\n<p style=\"padding-left: 30px\">function Test-ParametersNamedNameAreStrings<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; param(<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;TestCommandInfo&#8217;,Mandatory=$true,ValueFromPipeline=$true)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [Management.Automation.CommandInfo]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $CommandInfo<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; )<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; process {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($commandInfo.parameters[&#8216;Name&#8217;] -and<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $commandInfo.parameters[&#8216;Name&#8217;].ParameterType -ne [string]) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Error -Message &#8220;$commandInfo -Name is not a string&#8221;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;\nThe second rule, the one to determine if the module is at least version 1.0, is just as easy to create. The key here is knowing that the <b>$moduleInfo<\/b> variable has a property named <b>version<\/b><i>. <\/i>Other than that, it is pretty straightforward. Here is the complete function:&nbsp;<\/p>\n<p style=\"padding-left: 30px\">function Test-ModuleVersionIsAtLeastOne<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; param(<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;TestModuleInfo&#8217;,Mandatory=$true,ValueFromPipeline=$true)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [Management.Automation.PSModuleInfo]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $ModuleInfo<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; )<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; process {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($ModuleInfo.Version -lt &#8216;1.0&#8217; ) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Error &#8220;$ModuleInfo Version must be 1.0 or greater.&nbsp; It was $($moduleInfo.Version).&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;\nThe check for the <b>Write-Host<\/b> cmdlet is similarly straightforward. Unfortunately, the parameters that this example uses are a little longer. The style of the parameters for a ScriptCop rule is what let&rsquo;s it work. The styles are very picky, so always simply copy and paste the parameters from another ScriptCop rule. To learn more or see all of the possible signatures, run:&nbsp;<\/p>\n<p style=\"padding-left: 30px\">Get-Help about_scriptcop_rules\n&nbsp;\nHere&rsquo;s the <b>Write-Host<\/b>. Don&rsquo;t be daunted by all the parameter code; it was copied and pasted. The meat of the rule is just these few lines:&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$hasWriteHost = $ScriptToken |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Where-Object { $_.Type -eq &#8220;Command&#8221; -and $_.Content -eq &#8220;Write-Host&#8221; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($hasWriteHost) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Error &#8220;$ScriptTokenCommand uses Write-Host.&nbsp; Write-Host makes your scripts unsuable inside other scripts.&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;\nHere&rsquo;s the meat and potatoes:&nbsp;<\/p>\n<p style=\"padding-left: 30px\">Test-DoesNotUseWriteHost<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; #region&nbsp;&nbsp;&nbsp;&nbsp; ScriptTokenValidation Parameter Statement<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; param(<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; &lt;#&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; This parameter will contain the tokens in the script, and will be automatically<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; provided when this command is run within ScriptCop.<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; This parameter should not be used directly, except for testing purposes.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; #&gt;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;TestScriptToken&#8217;,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Mandatory=$true,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ValueFromPipelineByPropertyName=$true)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [Management.Automation.PSToken[]]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $ScriptToken,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; &lt;#&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; This parameter will contain the command that was tokenized, and will be automatically<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; provided when this command is run within ScriptCop.<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; This parameter should not be used directly, except for testing purposes.<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; #&gt;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;TestScriptToken&#8217;,Mandatory=$true,ValueFromPipelineByPropertyName=$true)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [Management.Automation.CommandInfo]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $ScriptTokenCommand,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; &lt;#<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; This parameter contains the raw text of the script, and will be automatically<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; provided when this command is run within ScriptCop<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; This parameter should not be used directly, except for testing purposes.&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; #&gt;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;TestScriptToken&#8217;,Mandatory=$true,ValueFromPipelineByPropertyName=$true)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [string]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $ScriptText<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; )<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; #endregion&nbsp; ScriptTokenValidation Parameter Statement<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; process {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $hasWriteHost = $ScriptToken |<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Where-Object { $_.Type -eq &#8220;Command&#8221; -and $_.Content -eq &#8220;Write-Host&#8221; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($hasWriteHost) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Error &#8220;$ScriptTokenCommand uses Write-Host.&nbsp; Write-Host makes your scripts unsuable inside other scripts.&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;\nThe last rule is the most complex, and by that, I mean it&rsquo;s not really that hard at all. This one line looks at <b>$helpContent<\/b> (which comes preloaded with all of the Help) and tells us if it has an author:&nbsp;<\/p>\n<p style=\"padding-left: 30px\">$hasAuthorInNotes = $helpContent.alertSet | Out-string -Width 1024 | Where-Object { $_ -ilike &#8220;author:*&#8221; }&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;<\/p>\n<p style=\"padding-left: 30px\">function Test-AuthorInNotes<\/p>\n<p style=\"padding-left: 30px\">{<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; param(<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;TestHelpContent&#8217;,ValueFromPipelineByPropertyName=$true)]&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $HelpContent,<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [Parameter(ParameterSetName=&#8217;TestHelpContent&#8217;,Mandatory=$true,ValueFromPipelineByPropertyName=$true)]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; [Management.Automation.CommandInfo]<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; $HelpCommand<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; )<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; process {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (-not $HelpContent) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $hasAuthorInNotes = $helpContent.alertSet | Out-string -Width 1024 | Where-Object { $_ -ilike &#8220;author:*&#8221; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (-not $hasAuthorInNotes) {<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Write-Error &#8220;$HelpCommand does not include an author in the help notes.&#8221;<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"padding-left: 30px\">}<\/p>\n<p style=\"padding-left: 30px\">&nbsp;\nTo use each of these rules, simply create a new file for each rule in the Rules folder inside the ScriptCop module directory. Then, reload ScriptCop by typing<strong> Import-Module ScriptCop &ndash;Force<\/strong>.\n&nbsp;\nTo run ScriptCop locally on or more commands, use:&nbsp;<\/p>\n<p style=\"padding-left: 30px\">Get-Command <span style=\"text-decoration: underline\">$<\/span><i>NameOfCommand<\/i>&nbsp; | Test-Command&nbsp;\nTo run ScriptCop on or more modules, use:&nbsp;<\/p>\n<p style=\"padding-left: 30px\">Get-Module <i>$NameOfModule<\/i> | Test-Command\n&nbsp;To run just one or two rules (like your new rules), run:&nbsp;<\/p>\n<p style=\"padding-left: 30px\">Test-Command &ndash;Rule $NameOfRule&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\nTo exclude rules, use:&nbsp;<\/p>\n<p style=\"padding-left: 30px\">Test-Command -ExcludedRule $NameOfRule\nBy using the rules ScriptCop gives you and writing your own, you can ensure that every script in your company&rsquo;s repository is exactly the way you want it to be. To find out more about the types of rules you can create, type Get-Help about_scriptcop_rules.&nbsp; To download or use the latest version of the tool go, to <a href=\"http:\/\/scriptcop.start-automating.com\/\">http:\/\/scriptcop.start-automating.com\/<\/a>.&nbsp;\nScriptCop is a powerful tool to help your scripts follow the rules. Now you have the power to pick and choose which rules to follow. With great power comes great responsibility&mdash;use it wisely.&nbsp;\n&nbsp;\nJames, thank you for creating ScriptCop, and for taking the time to share with us some examples for its use.&nbsp;\nI invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\">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&nbsp;\n<b>Ed Wilson, Microsoft Scripting Guy<\/b>&nbsp;\n&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: James Brundage teaches how to create your own Windows PowerShell rules for ScriptCop. &nbsp; Microsoft Scripting Guy Ed Wilson here. Today, we have part two (including the conclusion) to the article begun by James Brundage yesterday. Take it away, James! &nbsp; Writing your own ScriptCop rules Yesterday, I introduced you to ScriptCop, the tool [&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,184,3,4,45,77],"class_list":["post-13151","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-guest-blogger","tag-james-brundage","tag-scripting-guy","tag-scripting-techniques","tag-windows-powershell","tag-writing"],"acf":[],"blog_post_summary":"<p>Summary: James Brundage teaches how to create your own Windows PowerShell rules for ScriptCop. &nbsp; Microsoft Scripting Guy Ed Wilson here. Today, we have part two (including the conclusion) to the article begun by James Brundage yesterday. Take it away, James! &nbsp; Writing your own ScriptCop rules Yesterday, I introduced you to ScriptCop, the tool [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/13151","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=13151"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/13151\/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=13151"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=13151"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=13151"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}