tag:blogger.com,1999:blog-22147802438158641562024-03-14T01:17:12.830+01:00[Code]BeaverAnders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.comBlogger61125tag:blogger.com,1999:blog-2214780243815864156.post-77066880365642658552019-08-10T10:11:00.001+02:002019-08-10T10:11:30.319+02:00Azure Function App Managed Service Identity in Visual Studio Code (PowerShell)I started using MSI in Function Apps a few years ago. Back then it was still a nice quality of life addition, but you had to jump through a few hoops to get it working, ex. this was necessary to use the MSI to login to Azure.<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #996633;">$apiVersion</span> = <span style="background-color: #fff0f0;">"2017-09-01"</span>
<span style="color: #996633;">$resourceURI</span> = <span style="background-color: #fff0f0;">"https://management.azure.com/"</span>
<span style="color: #996633;">$tokenAuthURI</span> = <span style="color: #996633;">$env:MSI_ENDPOINT</span> + <span style="background-color: #fff0f0;">"?resource=$resourceURI&api-version=$apiVersion"</span>
<span style="color: #996633;">$tokenResponse</span> = <span style="color: #007020;">Invoke-RestMethod</span> -Method Get -Headers <span style="background-color: #ffaaaa; color: red;">@</span>{<span style="background-color: #fff0f0;">"Secret"</span>=<span style="background-color: #fff0f0;">"$env:MSI_SECRET"</span>} -Uri <span style="color: #996633;">$tokenAuthURI</span>
<span style="color: #996633;">$accessToken</span> = <span style="color: #996633;">$tokenResponse</span>.access_token
Login-AzAccount -Tenant <span style="color: #996633;">$TenantId</span> -AccountId <span style="color: #996633;">$env:WEBSITE_SITE_NAME</span> -AccessToken <span style="color: #996633;">$accessToken</span> -Scope <span style="color: #008800; font-weight: bold;">Process</span>
</pre>
</div>
<br />
After not having done too much with Function Apps for a while I jumped back in and just could not wrap my head around how to get MSI working in a local development environment (I use VS Code). No help to find on the Internet, at least not explicitly stating how to do it. And for a good reason; the trick is to do nothing at all!<br />
<br />
When running locally everything is done in the context of the logged in user, often the developer him/herself.<br />
Above code would also fail (there is no <i>MSI_ENDPOINT </i>and much less an <i>MSI_SECRET</i>). But all the magic happens in profile.ps1 that is auto-created for each function app project. It is already outfitted with below code.<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #888888;"># Authenticate with Azure PowerShell using MSI.</span>
<span style="color: #888888;"># Remove this if you are not planning on using MSI or Azure PowerShell.</span>
<span style="color: #008800; font-weight: bold;">if</span> (<span style="color: #996633;">$env:MSI_SECRET</span> <span style="color: #333333;">-and</span> (<span style="color: #007020;">Get-Module</span> -ListAvailable Az.Accounts)) {
<span style="color: #007020;">Connect-AzAccount</span> -Identity
}
</pre>
</div>
<br />
<br />
If there is an <i>MSI_SECRET </i>in the environment variables (which there should not be in a local development environment), then connect using an MSI, which I imagine doing something similar to the first piece of code I posted.<br />
Another advantage is that you only do the login once (when the function app does a "cold start"). Previously it would be rather dodgy, and to be on the safe side, I would login in each function run, adding significant overhead.<br />
<br />
And when running locally the entire thing is piggybacking off the logged in user. If you want to simulate the access the MSI has when running in Azure, just create a service principal, assign it the same roles as the MSI and login before running your code locally.<br />
<br />
Running PowerShell in Azure Function App has come a long way. With the new <b>Push-OutputBinding</b> it is even easier than before to put the result of a function execution into a queue, table storage, etc.Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com1tag:blogger.com,1999:blog-2214780243815864156.post-92142848695166685872019-05-28T12:43:00.000+02:002019-05-28T12:43:53.267+02:00Monitoring Blocked Requests in Azure Web Application FirewallThe Azure Application Gateway can also function as a Web Application Firewall (WAF), and is a must have in any enterprise environment. In order to audit the firewall events the <b>ApplicationGatewayFirewallLog</b> must be ex. achieved to a storage account or even better, send to log analytics. This can be setup in the <b>Diagnostic settings </b>tab in the WAF.<br />
<br />
The WAF has more than 300 rules it matches each request against, and if the <b>Advanced rule configuration</b> is disabled then all rules enabled and a single match will result in a request being blocked. Each rule is also identified by an Id, and with the amount of rules we will need to map the Id to a descriptive text. Microsoft has this readily available in markdown at <a href="https://raw.githubusercontent.com/MicrosoftDocs/azure-docs/master/articles/application-gateway/application-gateway-crs-rulegroups-rules.md" target="_blank">https://raw.githubusercontent.com/MicrosoftDocs/azure-docs/master/articles/application-gateway/application-gateway-crs-rulegroups-rules.md</a>, which is what they use to generate documentation for the WAF. Currently it is missing a few rule Ids. But we will get back to this part.<br />
<br />
The end goal is to get something like this, presented in a Azure dashboard.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEM3-VcdHrGl_TEGd1gG7FMShRzuqx9sppPZq__MVsnuGir9cUa9G_pNH7VB5iauGWNqlCgrtyK3NovZEMzf0G2DYBWj0Qjw_gqckeseGVDw0seg-2Epf3JAEEsuDCmCgZ0GlzkwXuiZxc/s1600/WAF01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="442" data-original-width="1600" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEM3-VcdHrGl_TEGd1gG7FMShRzuqx9sppPZq__MVsnuGir9cUa9G_pNH7VB5iauGWNqlCgrtyK3NovZEMzf0G2DYBWj0Qjw_gqckeseGVDw0seg-2Epf3JAEEsuDCmCgZ0GlzkwXuiZxc/s640/WAF01.png" width="640" /></a></div>
<br />
Let's look at the query we need to write first.<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #333333;">//</span> variables
let resourceId <span style="color: #333333;">=</span> <span style="color: #aa6600;">"/SUBSCRIPTIONS/<SUBSCRIPTION_ID>/RESOURCEGROUPS/<RESOURCEGROUPNAME>/PROVIDERS/MICROSOFT.NETWORK/APPLICATIONGATEWAYS/<WAF_NAME>"</span>;
let period <span style="color: #333333;">=</span> <span style="color: #0000dd; font-weight: bold;">3</span>d;
<span style="color: #333333;">//</span> <span style="color: #008800; font-weight: bold;">show</span> transactions <span style="color: #008800; font-weight: bold;">with</span> a block action
AzureDiagnostics
<span style="color: #333333;">|</span> <span style="color: #008800; font-weight: bold;">where</span> TimeGenerated <span style="color: #333333;">></span> ago(period)
<span style="color: #333333;">|</span> <span style="color: #008800; font-weight: bold;">where</span> Category <span style="color: #333333;">==</span> <span style="color: #aa6600;">"ApplicationGatewayFirewallLog"</span>
<span style="color: #333333;">|</span> <span style="color: #008800; font-weight: bold;">where</span> ResourceId <span style="color: #333333;">==</span> resourceId
<span style="color: #333333;">|</span> <span style="color: #008800; font-weight: bold;">where</span> action_s <span style="color: #333333;">==</span> <span style="color: #aa6600;">"Matched"</span>
<span style="color: #333333;">|</span> project transactionId_s, ruleId_s, hostname_s, requestUri_s, TimeGenerated
<span style="color: #333333;">|</span> <span style="color: #008800; font-weight: bold;">join</span> (
AzureDiagnostics
<span style="color: #333333;">|</span> <span style="color: #008800; font-weight: bold;">where</span> TimeGenerated <span style="color: #333333;">></span> ago(period)
<span style="color: #333333;">|</span> <span style="color: #008800; font-weight: bold;">where</span> Category <span style="color: #333333;">==</span> <span style="color: #aa6600;">"ApplicationGatewayFirewallLog"</span>
<span style="color: #333333;">|</span> <span style="color: #008800; font-weight: bold;">where</span> ResourceId <span style="color: #333333;">==</span> resourceId
<span style="color: #333333;">|</span> <span style="color: #008800; font-weight: bold;">where</span> action_s <span style="color: #333333;">==</span> <span style="color: #aa6600;">"Blocked"</span>
<span style="color: #333333;">//</span> <span style="color: #008800; font-weight: bold;">using</span> <span style="color: #008800; font-weight: bold;">distinct</span> <span style="color: #008800; font-weight: bold;">as</span> there will always be <span style="color: #0000dd; font-weight: bold;">0</span> <span style="color: #008800; font-weight: bold;">or</span> <span style="color: #0000dd; font-weight: bold;">2</span> <span style="color: #aa6600;">"Blocked"</span> entries <span style="color: #008800; font-weight: bold;">for</span> the transaction <span style="color: #333333;">-</span> resulting <span style="color: #008800; font-weight: bold;">in</span> duplicates <span style="color: #008800; font-weight: bold;">when</span> joining
<span style="color: #333333;">|</span> <span style="color: #008800; font-weight: bold;">distinct</span> transactionId_s
) <span style="color: #008800; font-weight: bold;">on</span> transactionId_s
<span style="color: #333333;">|</span> sort <span style="color: #008800; font-weight: bold;">by</span> TimeGenerated <span style="color: #008800; font-weight: bold;">desc</span>
<span style="color: #333333;">|</span> take <span style="color: #0000dd; font-weight: bold;">50</span>
<span style="color: #333333;">|</span> project <span style="color: #008800; font-weight: bold;">Rule</span> <span style="color: #333333;">=</span> strcat(ruleId_s, <span style="background-color: #fff0f0;">' - '</span>, rule_mapping[ruleId_s]), Uri <span style="color: #333333;">=</span> strcat(hostname_s,requestUri_s),<span style="color: #008800; font-weight: bold;">Timestamp</span> <span style="color: #333333;">=</span> TimeGenerated
</pre>
</div>
<br />
First find your WAF resource Id and paste it in. This query will look at past 3 days of data (and we will eventually take the latest 50 entries). The last part is only relevant in the Log Analytics query editor, but it is best practice to limit data by date early on.<br />
<br />
The <i>left</i> part of the join will get the transactions where the <i>action</i> is <i>Matched</i>. Each request will generate 0 (no rule matches) or more transactions, one for each matching rule, and two more (block actions) if one of the rules are enabled.<br />
Finally only the properties that are needed is projected.<br />
<br />
The <i>right</i> part of the join is very similar, only we look transactions where the <i>action</i> is <i>Blocked.</i> And then we project, using <i>distinct</i>, the property <i>transactionId_s</i>. This is the only property we need for the join, and we use distinct as there will for each transaction be either 0 or 2 entries. If we used project the join would double up each <i>Matched</i> entry.<br />
<br />
The results is then sorted by date and we <i>take</i> the latest 50 entries. In the projection we use a dynamic array to map each rule Id to a descriptive text, which is explained next.<br />
<br />
The following script will generate the code we need to put before the Log Analytics query.<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #996633;">$Markdown</span> = (wget <span style="background-color: #fff0f0;">'https://raw.githubusercontent.com/MicrosoftDocs/azure-docs/master/articles/application-gateway/application-gateway-crs-rulegroups-rules.md'</span>).Content
<span style="color: #008800; font-weight: bold;">function</span> Is-RuleId (<span style="color: #996633;">$Value</span>) {
<span style="color: #008800; font-weight: bold;">return</span> <span style="color: #996633;">$Value</span> <span style="color: #333333;">-match</span> <span style="background-color: #fff0f0;">"^[\d\.]+$"</span>
}
<span style="color: #888888;"># remove each line starting with # and blank lines</span>
cls
<span style="color: #888888;"># $VerbosePreference = 'Continue'</span>
<span style="color: #996633;">$Lines</span> = <span style="color: #996633;">$Markdown</span>.Split(<span style="background-color: #fff0f0;">'|'</span>)
<span style="color: #996633;">$RuleId</span> = <span style="color: #996633;">$null</span>
<span style="color: #996633;">$Rules</span> = <span style="background-color: #ffaaaa; color: red;">@</span>{}
<span style="color: #008800; font-weight: bold;">foreach</span> (<span style="color: #996633;">$line</span> <span style="color: #008800; font-weight: bold;">in</span> <span style="color: #996633;">$Lines</span>) {
<span style="color: #008800; font-weight: bold;">if</span>(<span style="color: #996633;">$RuleId</span>)
{
<span style="color: #888888;"># we just had a rule id, the next must be description</span>
<span style="color: #996633;">$Rules</span>[<span style="color: #996633;">$RuleId</span>] = <span style="color: #996633;">$line</span>
<span style="color: #996633;">$RuleId</span> = <span style="color: #996633;">$null</span>
}
<span style="color: #008800; font-weight: bold;">elseif</span>(Is-RuleId <span style="color: #996633;">$line</span>)
{
<span style="color: #996633;">$RuleId</span> = <span style="color: #003366; font-weight: bold;">[int]</span><span style="color: #996633;">$line</span>
}
}
<span style="color: #888888;"># we can add missing mappings like this</span>
<span style="color: #996633;">$Rules</span>[<span style="background-color: #fff0f0;">'0'</span>] = <span style="background-color: #fff0f0;">'Mandatory rule. Inbound Anomaly Score Exceeded'</span>
<span style="color: #996633;">$Rules</span>[<span style="background-color: #fff0f0;">'941160'</span>] = <span style="background-color: #fff0f0;">'NoScript XSS InjectionChecker: HTML Injection'</span>
<span style="color: #996633;">$Rules</span>[<span style="background-color: #fff0f0;">'942240'</span>] = <span style="background-color: #fff0f0;">'Detects MySQL charset switch and MSSQL DoS attempts'</span>
<span style="color: #996633;">$Rules</span>[<span style="background-color: #fff0f0;">'942180'</span>] = <span style="background-color: #fff0f0;">'Detects basic SQL authentication bypass attempts 1/3'</span>
<span style="color: #996633;">$Rules</span>[<span style="background-color: #fff0f0;">'942390'</span>] = <span style="background-color: #fff0f0;">'SQL Injection Attack'</span>
<span style="color: #888888;">#$Rules['XXX'] = ''</span>
<span style="color: #888888;">#next we format to a Kusto mapping</span>
<span style="color: #888888;">#$Rules.Keys | ForEach-Object {"`t`t`"$_`" : `"$($Rules[$_])`","}</span>
<span style="color: #996633;">$KustoMapping</span> =
<span style="background-color: #fff0f0;">@"</span>
<span style="background-color: #fff0f0;">let rule_mapping = dynamic(</span>
<span style="background-color: #fff0f0;"> {</span>
<span style="background-color: #fff0f0;"> $(</span>
<span style="background-color: #fff0f0;"> $Rules.Keys | ForEach-Object {" `"$_`" : `"$($Rules[$_])`",`n"}</span>
<span style="background-color: #fff0f0;"> )</span>
<span style="background-color: #fff0f0;"> });</span>
<span style="background-color: #fff0f0;">"@</span>
<span style="color: #888888;"># $Rules.Keys.Count</span>
<span style="color: #888888;"># removes the very last , and put the result into clipboard</span>
<span style="color: #996633;">$KustoMapping</span> <span style="color: #333333;">-replace</span> <span style="background-color: #fff0f0;">"(?s)(.*),(.*)"</span>, <span style="background-color: #fff0f0;">''</span> | clip
</pre>
</div>
<br />
After pasting be sure to remove any blank lines in the query. The result can then be pinned to a dashboard of your choice.<br />
<br />
Note that if you have all rules enabled, you can skip the join entirely. Each rule matched will result in the request being blocked. But you may at some point disable some rules (some are very stringent), or perhaps in a future version of WAF it can be customized at what score treshold the request is being blocked; each matched rule will provide a score, the sum of the score is then matched against a treshold I think is "greater than 0" currently. The point of this is that some rules matched is more dangerous than others, and the sum of them all provides a way to determine the risk of not blocking the request.<br />
<br />
The result of the script can be seen below.<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="background-color: #ffaaaa; color: red;">let</span> <span style="background-color: #ffaaaa; color: red;">rule_mapping</span> <span style="background-color: #ffaaaa; color: red;">=</span> <span style="background-color: #ffaaaa; color: red;">dynamic(</span>
{
<span style="color: #007700;">"941320"</span> : <span style="background-color: #fff0f0;">"Possible XSS Attack Detected - HTML Tag Handler"</span>,
<span style="color: #007700;">"960343"</span> : <span style="background-color: #fff0f0;">"Total uploaded files size too large"</span>,
<span style="color: #007700;">"960342"</span> : <span style="background-color: #fff0f0;">"Uploaded file size too large"</span>,
<span style="color: #007700;">"960341"</span> : <span style="background-color: #fff0f0;">"Total arguments size exceeded"</span>,
<span style="color: #007700;">"960335"</span> : <span style="background-color: #fff0f0;">"Too many arguments in request"</span>,
<span style="color: #007700;">"958033"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"973315"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"981253"</span> : <span style="background-color: #fff0f0;">"Detects MySQL and PostgreSQL stored procedure/function injections"</span>,
<span style="color: #007700;">"920340"</span> : <span style="background-color: #fff0f0;">"Request Containing Content but Missing Content-Type header"</span>,
<span style="color: #007700;">"958423"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958422"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958421"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958420"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958419"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958418"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958417"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958416"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958415"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958414"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958413"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958412"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958411"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958410"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958409"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958408"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958407"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958406"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958405"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958404"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"950922"</span> : <span style="background-color: #fff0f0;">"Backdoor access"</span>,
<span style="color: #007700;">"958059"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"950120"</span> : <span style="background-color: #fff0f0;">"Possible Remote File Inclusion (RFI) Attack = Off-Domain Reference/Link"</span>,
<span style="color: #007700;">"950119"</span> : <span style="background-color: #fff0f0;">"Remote File Inclusion Attack"</span>,
<span style="color: #007700;">"950118"</span> : <span style="background-color: #fff0f0;">"Remote File Inclusion Attack"</span>,
<span style="color: #007700;">"950117"</span> : <span style="background-color: #fff0f0;">"Remote File Inclusion Attack"</span>,
<span style="color: #007700;">"950116"</span> : <span style="background-color: #fff0f0;">"Unicode Full/Half Width Abuse Attack Attempt"</span>,
<span style="color: #007700;">"0"</span> : <span style="background-color: #fff0f0;">"Mandatory rule. Inbound Anomaly Score Exceeded"</span>,
<span style="color: #007700;">"950110"</span> : <span style="background-color: #fff0f0;">"Backdoor access"</span>,
<span style="color: #007700;">"950109"</span> : <span style="background-color: #fff0f0;">"Multiple URL Encoding Detected"</span>,
<span style="color: #007700;">"950108"</span> : <span style="background-color: #fff0f0;">"URL Encoding Abuse Attack Attempt"</span>,
<span style="color: #007700;">"950107"</span> : <span style="background-color: #fff0f0;">"URL Encoding Abuse Attack Attempt"</span>,
<span style="color: #007700;">"973305"</span> : <span style="background-color: #fff0f0;">"XSS Attack Detected"</span>,
<span style="color: #007700;">"950103"</span> : <span style="background-color: #fff0f0;">"Path Traversal Attack"</span>,
<span style="color: #007700;">"960209"</span> : <span style="background-color: #fff0f0;">"Argument name too long"</span>,
<span style="color: #007700;">"960208"</span> : <span style="background-color: #fff0f0;">"Argument value too long"</span>,
<span style="color: #007700;">"981270"</span> : <span style="background-color: #fff0f0;">"Finds basic MongoDB SQL injection attempts"</span>,
<span style="color: #007700;">"981320"</span> : <span style="background-color: #fff0f0;">"SQL Injection Attack = Common DB Names Detected"</span>,
<span style="color: #007700;">"981317"</span> : <span style="background-color: #fff0f0;">"SQL SELECT Statement Anomaly Detection Alert"</span>,
<span style="color: #007700;">"981316"</span> : <span style="background-color: #fff0f0;">"Rule 981316"</span>,
<span style="color: #007700;">"981315"</span> : <span style="background-color: #fff0f0;">"Rule 981315"</span>,
<span style="color: #007700;">"981314"</span> : <span style="background-color: #fff0f0;">"Rule 981314"</span>,
<span style="color: #007700;">"981313"</span> : <span style="background-color: #fff0f0;">"Rule 981313"</span>,
<span style="color: #007700;">"981312"</span> : <span style="background-color: #fff0f0;">"Rule 981312"</span>,
<span style="color: #007700;">"981311"</span> : <span style="background-color: #fff0f0;">"Rule 981311"</span>,
<span style="color: #007700;">"981310"</span> : <span style="background-color: #fff0f0;">"Rule 981310"</span>,
<span style="color: #007700;">"981309"</span> : <span style="background-color: #fff0f0;">"Rule 981309"</span>,
<span style="color: #007700;">"981308"</span> : <span style="background-color: #fff0f0;">"Rule 981308"</span>,
<span style="color: #007700;">"981307"</span> : <span style="background-color: #fff0f0;">"Rule 981307"</span>,
<span style="color: #007700;">"981306"</span> : <span style="background-color: #fff0f0;">"Rule 981306"</span>,
<span style="color: #007700;">"981305"</span> : <span style="background-color: #fff0f0;">"Rule 981305"</span>,
<span style="color: #007700;">"981304"</span> : <span style="background-color: #fff0f0;">"Rule 981304"</span>,
<span style="color: #007700;">"981303"</span> : <span style="background-color: #fff0f0;">"Rule 981303"</span>,
<span style="color: #007700;">"981302"</span> : <span style="background-color: #fff0f0;">"Rule 981302"</span>,
<span style="color: #007700;">"981301"</span> : <span style="background-color: #fff0f0;">"Rule 981301"</span>,
<span style="color: #007700;">"981300"</span> : <span style="background-color: #fff0f0;">"Rule 981300"</span>,
<span style="color: #007700;">"932120"</span> : <span style="background-color: #fff0f0;">"Remote Command Execution = Windows PowerShell Command Found"</span>,
<span style="color: #007700;">"933110"</span> : <span style="background-color: #fff0f0;">"PHP Injection Attack = PHP Script File Upload Found"</span>,
<span style="color: #007700;">"981276"</span> : <span style="background-color: #fff0f0;">"Looking for basic sql injection. Common attack string for mysql oracle and others."</span>,
<span style="color: #007700;">"950921"</span> : <span style="background-color: #fff0f0;">"Backdoor access"</span>,
<span style="color: #007700;">"981272"</span> : <span style="background-color: #fff0f0;">"Detects blind sqli tests using sleep() or benchmark()."</span>,
<span style="color: #007700;">"958295"</span> : <span style="background-color: #fff0f0;">"Multiple/Conflicting Connection Header Data Found."</span>,
<span style="color: #007700;">"958291"</span> : <span style="background-color: #fff0f0;">"Range = field exists and begins with 0."</span>,
<span style="color: #007700;">"950019"</span> : <span style="background-color: #fff0f0;">"Email Injection Attack"</span>,
<span style="color: #007700;">"950018"</span> : <span style="background-color: #fff0f0;">"Universal PDF XSS URL Detected."</span>,
<span style="color: #007700;">"960911"</span> : <span style="background-color: #fff0f0;">"Invalid HTTP Request Line"</span>,
<span style="color: #007700;">"981260"</span> : <span style="background-color: #fff0f0;">"SQL Hex Encoding Identified"</span>,
<span style="color: #007700;">"950012"</span> : <span style="background-color: #fff0f0;">"HTTP Request Smuggling Attack."</span>,
<span style="color: #007700;">"950011"</span> : <span style="background-color: #fff0f0;">"SSI injection Attack"</span>,
<span style="color: #007700;">"950010"</span> : <span style="background-color: #fff0f0;">"LDAP Injection Attack"</span>,
<span style="color: #007700;">"950009"</span> : <span style="background-color: #fff0f0;">"Session Fixation Attack"</span>,
<span style="color: #007700;">"950008"</span> : <span style="background-color: #fff0f0;">"Injection of Undocumented ColdFusion Tags"</span>,
<span style="color: #007700;">"950007"</span> : <span style="background-color: #fff0f0;">"Blind SQL Injection Attack"</span>,
<span style="color: #007700;">"950006"</span> : <span style="background-color: #fff0f0;">"System Command Injection"</span>,
<span style="color: #007700;">"950005"</span> : <span style="background-color: #fff0f0;">"Remote File Access Attempt"</span>,
<span style="color: #007700;">"981250"</span> : <span style="background-color: #fff0f0;">"Detects SQL benchmark and sleep injection attempts including conditional queries"</span>,
<span style="color: #007700;">"950003"</span> : <span style="background-color: #fff0f0;">"Session Fixation"</span>,
<span style="color: #007700;">"950002"</span> : <span style="background-color: #fff0f0;">"System Command Access"</span>,
<span style="color: #007700;">"950001"</span> : <span style="background-color: #fff0f0;">"SQL Injection Attack"</span>,
<span style="color: #007700;">"950000"</span> : <span style="background-color: #fff0f0;">"Session Fixation"</span>,
<span style="color: #007700;">"981241"</span> : <span style="background-color: #fff0f0;">"Detects conditional SQL injection attempts"</span>,
<span style="color: #007700;">"981251"</span> : <span style="background-color: #fff0f0;">"Detects MySQL UDF injection and other data/structure manipulation attempts"</span>,
<span style="color: #007700;">"950911"</span> : <span style="background-color: #fff0f0;">"HTTP Response Splitting Attack"</span>,
<span style="color: #007700;">"950910"</span> : <span style="background-color: #fff0f0;">"HTTP Response Splitting Attack"</span>,
<span style="color: #007700;">"950908"</span> : <span style="background-color: #fff0f0;">"SQL Injection Attack."</span>,
<span style="color: #007700;">"981231"</span> : <span style="background-color: #fff0f0;">"SQL Comment Sequence Detected."</span>,
<span style="color: #007700;">"981227"</span> : <span style="background-color: #fff0f0;">"Apache Error = Invalid URI in Request."</span>,
<span style="color: #007700;">"959151"</span> : <span style="background-color: #fff0f0;">"PHP Injection Attack"</span>,
<span style="color: #007700;">"958230"</span> : <span style="background-color: #fff0f0;">"Range = Invalid Last Byte Value."</span>,
<span style="color: #007700;">"933100"</span> : <span style="background-color: #fff0f0;">"PHP Injection Attack = Opening/Closing Tag Found"</span>,
<span style="color: #007700;">"973318"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"942370"</span> : <span style="background-color: #fff0f0;">"Detects classic SQL injection probings 2/2"</span>,
<span style="color: #007700;">"960038"</span> : <span style="background-color: #fff0f0;">"HTTP header is restricted by policy"</span>,
<span style="color: #007700;">"960035"</span> : <span style="background-color: #fff0f0;">"URL file extension is restricted by policy"</span>,
<span style="color: #007700;">"960034"</span> : <span style="background-color: #fff0f0;">"HTTP protocol version is not allowed by policy"</span>,
<span style="color: #007700;">"960032"</span> : <span style="background-color: #fff0f0;">"Method is not allowed by policy"</span>,
<span style="color: #007700;">"958057"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"973313"</span> : <span style="background-color: #fff0f0;">"XSS Attack Detected"</span>,
<span style="color: #007700;">"960010"</span> : <span style="background-color: #fff0f0;">"Request content type is not allowed by policy"</span>,
<span style="color: #007700;">"960024"</span> : <span style="background-color: #fff0f0;">"Meta-Character Anomaly Detection Alert - Repetitive Non-Word Characters"</span>,
<span style="color: #007700;">"960022"</span> : <span style="background-color: #fff0f0;">"Expect Header Not Allowed for HTTP 1.0."</span>,
<span style="color: #007700;">"960021"</span> : <span style="background-color: #fff0f0;">"Request Has an Empty Accept Header"</span>,
<span style="color: #007700;">"960020"</span> : <span style="background-color: #fff0f0;">"Pragma Header requires Cache-Control Header for HTTP/1.1 requests."</span>,
<span style="color: #007700;">"960018"</span> : <span style="background-color: #fff0f0;">"Invalid character in request"</span>,
<span style="color: #007700;">"200004"</span> : <span style="background-color: #fff0f0;">"Possible Multipart Unmatched Boundary."</span>,
<span style="color: #007700;">"960016"</span> : <span style="background-color: #fff0f0;">"Content-Length HTTP header is not numeric."</span>,
<span style="color: #007700;">"960015"</span> : <span style="background-color: #fff0f0;">"Request Missing an Accept Header"</span>,
<span style="color: #007700;">"960012"</span> : <span style="background-color: #fff0f0;">"POST request missing Content-Length Header."</span>,
<span style="color: #007700;">"960011"</span> : <span style="background-color: #fff0f0;">"GET or HEAD Request with Body Content."</span>,
<span style="color: #007700;">"973303"</span> : <span style="background-color: #fff0f0;">"XSS Attack Detected"</span>,
<span style="color: #007700;">"960009"</span> : <span style="background-color: #fff0f0;">"Request Missing a User Agent Header"</span>,
<span style="color: #007700;">"960008"</span> : <span style="background-color: #fff0f0;">"Request Missing a Host Header"</span>,
<span style="color: #007700;">"960007"</span> : <span style="background-color: #fff0f0;">"Empty Host Header"</span>,
<span style="color: #007700;">"960006"</span> : <span style="background-color: #fff0f0;">"Empty User Agent Header"</span>,
<span style="color: #007700;">"981136"</span> : <span style="background-color: #fff0f0;">"Rule 981136"</span>,
<span style="color: #007700;">"981134"</span> : <span style="background-color: #fff0f0;">"Rule 981134"</span>,
<span style="color: #007700;">"981133"</span> : <span style="background-color: #fff0f0;">"Rule 981133"</span>,
<span style="color: #007700;">"960914"</span> : <span style="background-color: #fff0f0;">"Multipart request body failed strict validation"</span>,
<span style="color: #007700;">"960912"</span> : <span style="background-color: #fff0f0;">"Failed to parse request body."</span>,
<span style="color: #007700;">"959073"</span> : <span style="background-color: #fff0f0;">"SQL Injection Attack"</span>,
<span style="color: #007700;">"950801"</span> : <span style="background-color: #fff0f0;">"UTF8 Encoding Abuse Attack Attempt"</span>,
<span style="color: #007700;">"913120"</span> : <span style="background-color: #fff0f0;">"Found request filename/argument associated with security scanner"</span>,
<span style="color: #007700;">"960904"</span> : <span style="background-color: #fff0f0;">"Request Containing Content but Missing Content-Type header"</span>,
<span style="color: #007700;">"960902"</span> : <span style="background-color: #fff0f0;">"Invalid Use of Identity Encoding."</span>,
<span style="color: #007700;">"960901"</span> : <span style="background-color: #fff0f0;">"Invalid character in request"</span>,
<span style="color: #007700;">"913110"</span> : <span style="background-color: #fff0f0;">"Found request header associated with security scanner"</span>,
<span style="color: #007700;">"920460"</span> : <span style="background-color: #fff0f0;">"Abnormal escape characters"</span>,
<span style="color: #007700;">"913102"</span> : <span style="background-color: #fff0f0;">"Found User-Agent associated with web crawler/bot"</span>,
<span style="color: #007700;">"913101"</span> : <span style="background-color: #fff0f0;">"Found User-Agent associated with scripting/generic HTTP client"</span>,
<span style="color: #007700;">"913100"</span> : <span style="background-color: #fff0f0;">"Found User-Agent associated with security scanner"</span>,
<span style="color: #007700;">"920450"</span> : <span style="background-color: #fff0f0;">"HTTP header is restricted by policy (%@{MATCHED_VAR})"</span>,
<span style="color: #007700;">"921120"</span> : <span style="background-color: #fff0f0;">"HTTP Response Splitting Attack"</span>,
<span style="color: #007700;">"920440"</span> : <span style="background-color: #fff0f0;">"URL file extension is restricted by policy"</span>,
<span style="color: #007700;">"920430"</span> : <span style="background-color: #fff0f0;">"HTTP protocol version is not allowed by policy"</span>,
<span style="color: #007700;">"920420"</span> : <span style="background-color: #fff0f0;">"Request content type is not allowed by policy"</span>,
<span style="color: #007700;">"920410"</span> : <span style="background-color: #fff0f0;">"Total uploaded files size too large"</span>,
<span style="color: #007700;">"958002"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"942460"</span> : <span style="background-color: #fff0f0;">"Meta-Character Anomaly Detection Alert - Repetitive Non-Word Characters"</span>,
<span style="color: #007700;">"920400"</span> : <span style="background-color: #fff0f0;">"Uploaded file size too large"</span>,
<span style="color: #007700;">"942450"</span> : <span style="background-color: #fff0f0;">"SQL Hex Encoding Identified"</span>,
<span style="color: #007700;">"920390"</span> : <span style="background-color: #fff0f0;">"Total arguments size exceeded"</span>,
<span style="color: #007700;">"942440"</span> : <span style="background-color: #fff0f0;">"SQL Comment Sequence Detected."</span>,
<span style="color: #007700;">"920380"</span> : <span style="background-color: #fff0f0;">"Too many arguments in request"</span>,
<span style="color: #007700;">"958977"</span> : <span style="background-color: #fff0f0;">"PHP Injection Attack"</span>,
<span style="color: #007700;">"958976"</span> : <span style="background-color: #fff0f0;">"PHP Injection Attack"</span>,
<span style="color: #007700;">"958056"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958054"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"942430"</span> : <span style="background-color: #fff0f0;">"Restricted SQL Character Anomaly Detection (args): # of special characters exceeded (12)"</span>,
<span style="color: #007700;">"958052"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958051"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958049"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958047"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958046"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958045"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"981018"</span> : <span style="background-color: #fff0f0;">"Rule 981018"</span>,
<span style="color: #007700;">"958041"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958040"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958039"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958038"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958037"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958036"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958034"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"942410"</span> : <span style="background-color: #fff0f0;">"SQL Injection Attack"</span>,
<span style="color: #007700;">"958032"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958031"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958030"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"920350"</span> : <span style="background-color: #fff0f0;">"Host header is a numeric IP address"</span>,
<span style="color: #007700;">"958028"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958027"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958026"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958025"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958024"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958023"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958022"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958020"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958019"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958018"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958017"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958016"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958013"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958012"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958011"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958010"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"920330"</span> : <span style="background-color: #fff0f0;">"Empty User Agent Header"</span>,
<span style="color: #007700;">"958008"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958007"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958006"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958005"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958004"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958003"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"942350"</span> : <span style="background-color: #fff0f0;">"Detects MySQL UDF injection and other data/structure manipulation attempts"</span>,
<span style="color: #007700;">"958001"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"958000"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"920320"</span> : <span style="background-color: #fff0f0;">"Missing User Agent Header"</span>,
<span style="color: #007700;">"933180"</span> : <span style="background-color: #fff0f0;">"PHP Injection Attack = Variable Function Call Found"</span>,
<span style="color: #007700;">"920311"</span> : <span style="background-color: #fff0f0;">"Request Has an Empty Accept Header"</span>,
<span style="color: #007700;">"920310"</span> : <span style="background-color: #fff0f0;">"Request Has an Empty Accept Header"</span>,
<span style="color: #007700;">"942360"</span> : <span style="background-color: #fff0f0;">"Detects concatenated basic SQL injection and SQLLFI attempts"</span>,
<span style="color: #007700;">"960017"</span> : <span style="background-color: #fff0f0;">"Host header is a numeric IP address"</span>,
<span style="color: #007700;">"920300"</span> : <span style="background-color: #fff0f0;">"Request Missing an Accept Header"</span>,
<span style="color: #007700;">"933161"</span> : <span style="background-color: #fff0f0;">"PHP Injection Attack = Low-Value PHP Function Call Found"</span>,
<span style="color: #007700;">"933160"</span> : <span style="background-color: #fff0f0;">"PHP Injection Attack = High-Risk PHP Function Call Found"</span>,
<span style="color: #007700;">"920290"</span> : <span style="background-color: #fff0f0;">"Empty Host Header"</span>,
<span style="color: #007700;">"973346"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"933151"</span> : <span style="background-color: #fff0f0;">"PHP Injection Attack = Medium-Risk PHP Function Name Found"</span>,
<span style="color: #007700;">"942340"</span> : <span style="background-color: #fff0f0;">"Detects basic SQL authentication bypass attempts 3/3"</span>,
<span style="color: #007700;">"920280"</span> : <span style="background-color: #fff0f0;">"Request Missing a Host Header"</span>,
<span style="color: #007700;">"920274"</span> : <span style="background-color: #fff0f0;">"Invalid character in request headers (outside of very strict set)"</span>,
<span style="color: #007700;">"920273"</span> : <span style="background-color: #fff0f0;">"Invalid character in request (outside of very strict set)"</span>,
<span style="color: #007700;">"920272"</span> : <span style="background-color: #fff0f0;">"Invalid character in request (outside of printable chars below ascii 127)"</span>,
<span style="color: #007700;">"920271"</span> : <span style="background-color: #fff0f0;">"Invalid character in request (non printable characters)"</span>,
<span style="color: #007700;">"920270"</span> : <span style="background-color: #fff0f0;">"Invalid character in request (null character)"</span>,
<span style="color: #007700;">"933131"</span> : <span style="background-color: #fff0f0;">"PHP Injection Attack = Variables Found"</span>,
<span style="color: #007700;">"933130"</span> : <span style="background-color: #fff0f0;">"PHP Injection Attack = Variables Found"</span>,
<span style="color: #007700;">"942390"</span> : <span style="background-color: #fff0f0;">"SQL Injection Attack"</span>,
<span style="color: #007700;">"921180"</span> : <span style="background-color: #fff0f0;">"HTTP Parameter Pollution (%@{TX.1})"</span>,
<span style="color: #007700;">"920260"</span> : <span style="background-color: #fff0f0;">"Unicode Full/Half Width Abuse Attack Attempt"</span>,
<span style="color: #007700;">"933120"</span> : <span style="background-color: #fff0f0;">"PHP Injection Attack = Configuration Directive Found"</span>,
<span style="color: #007700;">"921170"</span> : <span style="background-color: #fff0f0;">"HTTP Parameter Pollution"</span>,
<span style="color: #007700;">"920250"</span> : <span style="background-color: #fff0f0;">"UTF8 Encoding Abuse Attack Attempt"</span>,
<span style="color: #007700;">"933111"</span> : <span style="background-color: #fff0f0;">"PHP Injection Attack = PHP Script File Upload Found"</span>,
<span style="color: #007700;">"942300"</span> : <span style="background-color: #fff0f0;">"Detects MySQL comments, conditions and ch(a)r injections"</span>,
<span style="color: #007700;">"921160"</span> : <span style="background-color: #fff0f0;">"HTTP Header Injection Attack via payload (CR/LF and header-name detected)"</span>,
<span style="color: #007700;">"920240"</span> : <span style="background-color: #fff0f0;">"URL Encoding Abuse Attack Attempt"</span>,
<span style="color: #007700;">"942290"</span> : <span style="background-color: #fff0f0;">"Finds basic MongoDB SQL injection attempts"</span>,
<span style="color: #007700;">"921151"</span> : <span style="background-color: #fff0f0;">"HTTP Header Injection Attack via payload (CR/LF detected)"</span>,
<span style="color: #007700;">"921150"</span> : <span style="background-color: #fff0f0;">"HTTP Header Injection Attack via payload (CR/LF detected)"</span>,
<span style="color: #007700;">"920230"</span> : <span style="background-color: #fff0f0;">"Multiple URL Encoding Detected"</span>,
<span style="color: #007700;">"932171"</span> : <span style="background-color: #fff0f0;">"Remote Command Execution = Shellshock (CVE-2014-6271)"</span>,
<span style="color: #007700;">"932170"</span> : <span style="background-color: #fff0f0;">"Remote Command Execution = Shellshock (CVE-2014-6271)"</span>,
<span style="color: #007700;">"921140"</span> : <span style="background-color: #fff0f0;">"HTTP Header Injection Attack via headers"</span>,
<span style="color: #007700;">"920220"</span> : <span style="background-color: #fff0f0;">"URL Encoding Abuse Attack Attempt"</span>,
<span style="color: #007700;">"942270"</span> : <span style="background-color: #fff0f0;">"Looking for basic sql injection. Common attack string for mysql oracle and others."</span>,
<span style="color: #007700;">"941350"</span> : <span style="background-color: #fff0f0;">"UTF-7 Encoding IE XSS - Attack Detected."</span>,
<span style="color: #007700;">"921130"</span> : <span style="background-color: #fff0f0;">"HTTP Response Splitting Attack"</span>,
<span style="color: #007700;">"920210"</span> : <span style="background-color: #fff0f0;">"Multiple/Conflicting Connection Header Data Found."</span>,
<span style="color: #007700;">"942260"</span> : <span style="background-color: #fff0f0;">"Detects basic SQL authentication bypass attempts 2/3"</span>,
<span style="color: #007700;">"941340"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"920202"</span> : <span style="background-color: #fff0f0;">"Range = Too many fields for pdf request (6 or more)"</span>,
<span style="color: #007700;">"920201"</span> : <span style="background-color: #fff0f0;">"Range = Too many fields for pdf request (35 or more)"</span>,
<span style="color: #007700;">"920200"</span> : <span style="background-color: #fff0f0;">"Range = Too many fields (6 or more)"</span>,
<span style="color: #007700;">"942251"</span> : <span style="background-color: #fff0f0;">"Detects HAVING injections"</span>,
<span style="color: #007700;">"941330"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"921110"</span> : <span style="background-color: #fff0f0;">"HTTP Request Smuggling Attack"</span>,
<span style="color: #007700;">"920190"</span> : <span style="background-color: #fff0f0;">"Range = Invalid Last Byte Value."</span>,
<span style="color: #007700;">"973345"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"932130"</span> : <span style="background-color: #fff0f0;">"Remote Command Execution = Unix Shell Expression Found"</span>,
<span style="color: #007700;">"921100"</span> : <span style="background-color: #fff0f0;">"HTTP Request Smuggling Attack."</span>,
<span style="color: #007700;">"920180"</span> : <span style="background-color: #fff0f0;">"POST request missing Content-Length Header."</span>,
<span style="color: #007700;">"942230"</span> : <span style="background-color: #fff0f0;">"Detects conditional SQL injection attempts"</span>,
<span style="color: #007700;">"941310"</span> : <span style="background-color: #fff0f0;">"US-ASCII Malformed Encoding XSS Filter - Attack Detected."</span>,
<span style="color: #007700;">"920170"</span> : <span style="background-color: #fff0f0;">"GET or HEAD Request with Body Content."</span>,
<span style="color: #007700;">"990012"</span> : <span style="background-color: #fff0f0;">"Rogue web site crawler"</span>,
<span style="color: #007700;">"941300"</span> : <span style="background-color: #fff0f0;">"XSS using 'object' tag"</span>,
<span style="color: #007700;">"920160"</span> : <span style="background-color: #fff0f0;">"Content-Length HTTP header is not numeric."</span>,
<span style="color: #007700;">"990002"</span> : <span style="background-color: #fff0f0;">"Request Indicates a Security Scanner Scanned the Site"</span>,
<span style="color: #007700;">"941290"</span> : <span style="background-color: #fff0f0;">"XSS using 'applet' tag"</span>,
<span style="color: #007700;">"911100"</span> : <span style="background-color: #fff0f0;">"Method is not allowed by policy"</span>,
<span style="color: #007700;">"943120"</span> : <span style="background-color: #fff0f0;">"Possible Session Fixation Attack = SessionID Parameter Name with No Referrer"</span>,
<span style="color: #007700;">"942200"</span> : <span style="background-color: #fff0f0;">"Detects MySQL comment-/space-obfuscated injections and backtick termination"</span>,
<span style="color: #007700;">"941280"</span> : <span style="background-color: #fff0f0;">"XSS using 'base' tag"</span>,
<span style="color: #007700;">"920370"</span> : <span style="background-color: #fff0f0;">"Argument value too long"</span>,
<span style="color: #007700;">"920140"</span> : <span style="background-color: #fff0f0;">"Multipart request body failed strict validation"</span>,
<span style="color: #007700;">"990902"</span> : <span style="background-color: #fff0f0;">"Request Indicates a Security Scanner Scanned the Site"</span>,
<span style="color: #007700;">"990901"</span> : <span style="background-color: #fff0f0;">"Request Indicates a Security Scanner Scanned the Site"</span>,
<span style="color: #007700;">"943110"</span> : <span style="background-color: #fff0f0;">"Possible Session Fixation Attack = SessionID Parameter Name with Off-Domain Referrer"</span>,
<span style="color: #007700;">"942190"</span> : <span style="background-color: #fff0f0;">"Detects MSSQL code execution and information gathering attempts"</span>,
<span style="color: #007700;">"941270"</span> : <span style="background-color: #fff0f0;">"XSS using 'link' href"</span>,
<span style="color: #007700;">"920130"</span> : <span style="background-color: #fff0f0;">"Failed to parse request body."</span>,
<span style="color: #007700;">"932160"</span> : <span style="background-color: #fff0f0;">"Remote Command Execution = Unix Shell Code Found"</span>,
<span style="color: #007700;">"933150"</span> : <span style="background-color: #fff0f0;">"PHP Injection Attack = High-Risk PHP Function Name Found"</span>,
<span style="color: #007700;">"943100"</span> : <span style="background-color: #fff0f0;">"Possible Session Fixation Attack = Setting Cookie Values in HTML"</span>,
<span style="color: #007700;">"941260"</span> : <span style="background-color: #fff0f0;">"XSS using 'meta' tag"</span>,
<span style="color: #007700;">"942330"</span> : <span style="background-color: #fff0f0;">"Detects classic SQL injection probings 1/2"</span>,
<span style="color: #007700;">"942170"</span> : <span style="background-color: #fff0f0;">"Detects SQL benchmark and sleep injection attempts including conditional queries"</span>,
<span style="color: #007700;">"942160"</span> : <span style="background-color: #fff0f0;">"Detects blind sqli tests using sleep() or benchmark()."</span>,
<span style="color: #007700;">"941240"</span> : <span style="background-color: #fff0f0;">"XSS using 'import' or 'implementation' attribute"</span>,
<span style="color: #007700;">"931130"</span> : <span style="background-color: #fff0f0;">"Possible Remote File Inclusion (RFI) Attack = Off-Domain Reference/Link"</span>,
<span style="color: #007700;">"920100"</span> : <span style="background-color: #fff0f0;">"Invalid HTTP Request Line"</span>,
<span style="color: #007700;">"960915"</span> : <span style="background-color: #fff0f0;">"Multipart parser detected a possible unmatched boundary."</span>,
<span style="color: #007700;">"942150"</span> : <span style="background-color: #fff0f0;">"SQL Injection Attack"</span>,
<span style="color: #007700;">"941230"</span> : <span style="background-color: #fff0f0;">"XSS using 'embed' tag"</span>,
<span style="color: #007700;">"931120"</span> : <span style="background-color: #fff0f0;">"Possible Remote File Inclusion (RFI) Attack = URL Payload Used w/Trailing Question Mark Character (?)"</span>,
<span style="color: #007700;">"942180"</span> : <span style="background-color: #fff0f0;">"Detects basic SQL authentication bypass attempts 1/3"</span>,
<span style="color: #007700;">"942140"</span> : <span style="background-color: #fff0f0;">"SQL Injection Attack = Common DB Names Detected"</span>,
<span style="color: #007700;">"941220"</span> : <span style="background-color: #fff0f0;">"XSS using obfuscated VB Script"</span>,
<span style="color: #007700;">"931110"</span> : <span style="background-color: #fff0f0;">"Possible Remote File Inclusion (RFI) Attack = Common RFI Vulnerable Parameter Name used w/URL Payload"</span>,
<span style="color: #007700;">"958009"</span> : <span style="background-color: #fff0f0;">"Cross-site Scripting (XSS) Attack"</span>,
<span style="color: #007700;">"942130"</span> : <span style="background-color: #fff0f0;">"SQL Injection Attack: SQL Tautology Detected."</span>,
<span style="color: #007700;">"941210"</span> : <span style="background-color: #fff0f0;">"XSS using obfuscated Javascript"</span>,
<span style="color: #007700;">"931100"</span> : <span style="background-color: #fff0f0;">"Possible Remote File Inclusion (RFI) Attack = URL Parameter using IP Address"</span>,
<span style="color: #007700;">"941200"</span> : <span style="background-color: #fff0f0;">"XSS using VML frames"</span>,
<span style="color: #007700;">"942110"</span> : <span style="background-color: #fff0f0;">"SQL Injection Attack: Common Injection Testing Detected"</span>,
<span style="color: #007700;">"941190"</span> : <span style="background-color: #fff0f0;">"XSS using style sheets"</span>,
<span style="color: #007700;">"973348"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"942100"</span> : <span style="background-color: #fff0f0;">"SQL Injection Attack Detected via libinjection"</span>,
<span style="color: #007700;">"941180"</span> : <span style="background-color: #fff0f0;">"Node-Validator Blacklist Keywords"</span>,
<span style="color: #007700;">"920360"</span> : <span style="background-color: #fff0f0;">"Argument name too long"</span>,
<span style="color: #007700;">"973338"</span> : <span style="background-color: #fff0f0;">"XSS Filter - Category 3 = Javascript URI Vector"</span>,
<span style="color: #007700;">"973336"</span> : <span style="background-color: #fff0f0;">"XSS Filter - Category 1 = Script Tag Vector"</span>,
<span style="color: #007700;">"973331"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"973330"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"973329"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"973328"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"973327"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"973326"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"973324"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"930130"</span> : <span style="background-color: #fff0f0;">"Restricted File Access Attempt"</span>,
<span style="color: #007700;">"973321"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"973320"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"942320"</span> : <span style="background-color: #fff0f0;">"Detects MySQL and PostgreSQL stored procedure/function injections"</span>,
<span style="color: #007700;">"973317"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"941150"</span> : <span style="background-color: #fff0f0;">"XSS Filter - Category 5 = Disallowed HTML Attributes"</span>,
<span style="color: #007700;">"973314"</span> : <span style="background-color: #fff0f0;">"XSS Attack Detected"</span>,
<span style="color: #007700;">"930120"</span> : <span style="background-color: #fff0f0;">"OS File Access Attempt"</span>,
<span style="color: #007700;">"941160"</span> : <span style="background-color: #fff0f0;">"NoScript XSS InjectionChecker: HTML Injection"</span>,
<span style="color: #007700;">"973311"</span> : <span style="background-color: #fff0f0;">"XSS Attack Detected"</span>,
<span style="color: #007700;">"973309"</span> : <span style="background-color: #fff0f0;">"XSS Attack Detected"</span>,
<span style="color: #007700;">"973308"</span> : <span style="background-color: #fff0f0;">"XSS Attack Detected"</span>,
<span style="color: #007700;">"973307"</span> : <span style="background-color: #fff0f0;">"XSS Attack Detected"</span>,
<span style="color: #007700;">"973306"</span> : <span style="background-color: #fff0f0;">"XSS Attack Detected"</span>,
<span style="color: #007700;">"941140"</span> : <span style="background-color: #fff0f0;">"XSS Filter - Category 4 = Javascript URI Vector"</span>,
<span style="color: #007700;">"973304"</span> : <span style="background-color: #fff0f0;">"XSS Attack Detected"</span>,
<span style="color: #007700;">"930110"</span> : <span style="background-color: #fff0f0;">"Path Traversal Attack (/../)"</span>,
<span style="color: #007700;">"973302"</span> : <span style="background-color: #fff0f0;">"XSS Attack Detected"</span>,
<span style="color: #007700;">"973301"</span> : <span style="background-color: #fff0f0;">"XSS Attack Detected"</span>,
<span style="color: #007700;">"973300"</span> : <span style="background-color: #fff0f0;">"Possible XSS Attack Detected - HTML Tag Handler"</span>,
<span style="color: #007700;">"942240"</span> : <span style="background-color: #fff0f0;">"Detects MySQL charset switch and MSSQL DoS attempts"</span>,
<span style="color: #007700;">"941130"</span> : <span style="background-color: #fff0f0;">"XSS Filter - Category 3 = Attribute Vector"</span>,
<span style="color: #007700;">"930100"</span> : <span style="background-color: #fff0f0;">"Path Traversal Attack (/../)"</span>,
<span style="color: #007700;">"941110"</span> : <span style="background-color: #fff0f0;">"XSS Filter - Category 1 = Script Tag Vector"</span>,
<span style="color: #007700;">"973323"</span> : <span style="background-color: #fff0f0;">"IE XSS Filters - Attack Detected."</span>,
<span style="color: #007700;">"941100"</span> : <span style="background-color: #fff0f0;">"XSS Attack Detected via libinjection"</span>,
<span style="color: #007700;">"932140"</span> : <span style="background-color: #fff0f0;">"Remote Command Execution = Windows FOR/IF Command Found"</span>
}<span style="background-color: #ffaaaa; color: red;">);</span>
</pre>
</div>
Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-32218232174550416672019-01-06T00:43:00.002+01:002019-01-09T11:54:32.874+01:00Installing PowerShell Modules to Azure Function AppI just realized a really easy way to install PS modules to an Azure Function App. Just run below code in a function.<br />
Make sure that $ModulePath points to a folder path that exists. Look into <a href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-how-to-use-azure-function-app-settings#kudu" target="_blank">KUDU</a> to set this up.<br />
<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #888888;"><#</span>
<span style="color: #888888;">$ModulePath must not already contain the modules or this may fail</span>
<span style="color: #888888;">#></span>
<span style="color: #996633;">$ModulePath</span> = <span style="background-color: #fff0f0;">'D:\home\lib\PSModules'</span>
<span style="color: #996633;">$NuGet</span> = <span style="color: #007020;">Get-PackageProvider</span> -Name NuGet
<span style="color: #008800; font-weight: bold;">if</span>(<span style="color: #996633;">$null</span> <span style="color: #333333;">-eq</span> <span style="color: #996633;">$NuGet</span>)
{
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser
}
<span style="background-color: #fff0f0;">"Saving modules to $ModulePath"</span>
Save-Module AzureRm -Path <span style="color: #996633;">$ModulePath</span> -Force
<span style="background-color: #fff0f0;">"Listing import commands for every module"</span>
<span style="color: #007020;">Get-ChildItem</span> -Path <span style="color: #996633;">$ModulePath</span> -Include <span style="background-color: #fff0f0;">"*.psd1"</span> -Recurse | <span style="color: #008800; font-weight: bold;">ForEach</span>-Object {
<span style="background-color: #fff0f0;">"Import-Module '</span><span style="background-color: #eeeeee;">$(</span><span style="background-color: #ffaaaa; color: red;">$</span><span style="background-color: #eeeeee;">_.FullName)</span><span style="background-color: #fff0f0;">'"</span>
}
</pre>
</div>
<br />
I have created a Github repository containing this and other small function app snippets. Above code will be updated here: <a href="https://github.com/spaelling/AzureFunctionAppSnippets/blob/master/PowerShell/HT_InstallModule.ps1" target="_blank">https://github.com/spaelling/AzureFunctionAppSnippets/blob/master/PowerShell/HT_InstallModule.ps1</a>Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-48815980447060874302018-08-29T22:33:00.000+02:002018-08-29T22:33:04.716+02:00PowerShell in Azure Functions - Lessons LearnedI love writing PowerShell in Azure Functions - it is a mixed blessing not having to worry about a VM (or VMs), but I hope to share a few tips that will result in fewer hairs being torn.<br />
<br />
Forget Write-* except Write-Output. Write-Error will work just like throw (which I would then much prefer to use).<br />
It can be inconvenient that Write-Verbose in (ordinary PowerShell) functions is lost, and you cannot use Write-Output in a function as that would go towards the output of the function, and not the log stream. But there is a trick if you really need that verbose output, you can redirect it (read more on that <a href="https://blogs.technet.microsoft.com/heyscriptingguy/2014/03/30/understanding-streams-redirection-and-write-host-in-powershell/" target="_blank">here</a>). Try running below.<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #888888;"># this will do nothing</span>
<span style="color: #007020;">Write-Verbose</span> <span style="background-color: #fff0f0;">"Verbose"</span>
<span style="color: #888888;"># redirect verbose stream</span>
<span style="color: #007020;">Write-Verbose</span> <span style="background-color: #fff0f0;">"Verbose redirected to success stream"</span> -Verbose 4>&1
<span style="color: #888888;"># verbose output in function</span>
<span style="color: #008800; font-weight: bold;">function</span> function_with_verbose {
[<span style="color: #008800; font-weight: bold;">CmdletBinding</span>()]
<span style="color: #008800; font-weight: bold;">param</span> (
)
<span style="color: #007020;">Write-Verbose</span> <span style="background-color: #fff0f0;">"this is verbose"</span>
<span style="color: #007020;">Write-Verbose</span> <span style="background-color: #fff0f0;">"more verbose"</span>
<span style="color: #888888;"># output result</span>
4
}
<span style="color: #888888;"># redirect verbose stream</span>
<span style="color: #996633;">$result</span> = function_with_verbose -Verbose 4>&1
<span style="color: #888888;"># assuming just a single out</span>
<span style="color: #007020;">Write-Output</span> <span style="background-color: #fff0f0;">"output from function"</span>
(<span style="color: #996633;">$result</span> | <span style="color: #007020;">Select-Object</span> -Last 1)
<span style="color: #007020;">Write-Output</span> <span style="background-color: #fff0f0;">"The verbose stream"</span>
<span style="color: #888888;"># everything but the last</span>
<span style="color: #996633;">$result</span>[0..(<span style="color: #996633;">$result</span>.length-2)]
</pre>
</div>
<br />
All depending this may or may not be worth the trouble. I think that at some point the other streams will be displayed in the logging output.<br />
<br />
There is some documentation on importing modules in a Function App, but what I found the best was to first use Save-Module to download to disk, then in Platform features in the app there is something called Advanced tools (Kudu). Click that and a new tab opens. In the top click Debug Console and select either.<br />
I usually create a new folder (the big + sign) in root, lib, and in that another folder modules. Here you can drag and drop the module folder you just downloaded.<br />
You can zip the module folders before uploading if you like, they are unzipped automatically. Note down the full path to the psd1 file that you will import. When importing in the function app simply<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #007020;">Import-Module</span> <span style="background-color: #fff0f0;">"D:\home\lib\modules\AzureRM.profile\5.3.4\AzureRM.Profile.psd1"</span>
</pre>
</div>
<br />
I I have often seen a -global appended to this command. Not sure why, I have had no luck getting global variables to work. This leads to my next point, when using any Azure PowerShell modules you need to authenticate using ex. Login-AzureRmAccount. Problem with this is that if you have multiple functions running at the same time they will leak into each other, especially something like Select-AzureRmSubscription will mess you up!<br />
<br />
Luckily there is a solution for this (same for Login-AzureRmAccount).<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #996633;">$DefaultProfile</span> = <span style="color: #007020;">Select-AzureRmSubscription</span> -SubscriptionId <span style="color: #996633;">$SubscriptionId</span> -Tenant <span style="color: #996633;">$TenantId</span> -Scope <span style="color: #008800; font-weight: bold;">Process</span>
</pre>
</div>
<br />
The $DefaultProfile is then used in all subsequent calls ex.<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #007020;">Get-AzureRmResource</span> -ResourceType <span style="background-color: #fff0f0;">'Microsoft.DevTestLab/labs/virtualMachines'</span> -ResourceGroupName <span style="color: #996633;">$ResourceGroupName</span> -ExpandProperties -DefaultProfile <span style="color: #996633;">$DefaultProfile</span>
</pre>
</div>
<br />
Now in case a different instance of the same function runs at the same time, it will not interfere. As it is tedious to add this everywhere you can use <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_parameters_default_values?view=powershell-6" target="_blank">$PSDefaultParameterValues</a> and also removes the risk you forgot this somewhere.<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #996633;">$PSDefaultParameterValues</span> = <span style="background-color: #ffaaaa; color: red;">@</span>{<span style="background-color: #fff0f0;">'*:DefaultProfile'</span> = <span style="color: #996633;">$DefaultProfile</span>}
</pre>
</div>
<br />
I use a Managed Service Identity to login to Azure. Under Platform features there is an item "Managed service identity" - click it and select On.<br />
<br />
To run below you need the MSI application Id. You find it in Azure Active Directory under App Registrations (select All apps) and search for your function app name. Copy the application Id. I have added it to Application Settings, and then accessible from $env:<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #996633;">$apiVersion</span> = <span style="background-color: #fff0f0;">"2017-09-01"</span>
<span style="color: #996633;">$resourceURI</span> = <span style="background-color: #fff0f0;">"https://management.azure.com/"</span>
<span style="color: #996633;">$tokenAuthURI</span> = <span style="color: #996633;">$env:MSI_ENDPOINT</span> + <span style="background-color: #fff0f0;">"?resource=$resourceURI&api-version=$apiVersion"</span>
<span style="color: #996633;">$tokenResponse</span> = <span style="color: #007020;">Invoke-RestMethod</span> -Method Get -Headers <span style="background-color: #ffaaaa; color: red;">@</span>{<span style="background-color: #fff0f0;">"Secret"</span>=<span style="background-color: #fff0f0;">"$env:MSI_SECRET"</span>} -Uri <span style="color: #996633;">$tokenAuthURI</span>
<span style="color: #996633;">$accessToken</span> = <span style="color: #996633;">$tokenResponse</span>.access_token
<span style="color: #996633;">$DefaultProfile</span> = Login-AzureRmAccount -Tenant <span style="color: #996633;">$TenantId</span> -AccountId <span style="color: #996633;">$env:ApplicationId</span> -AccessToken <span style="color: #996633;">$accessToken</span> -Scope <span style="color: #008800; font-weight: bold;">Process</span>
</pre>
</div>
<br />
Note that this may fail if the MSI has access to no resources in any subscription. Anyways, it would be rather pointless if it does not.Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-14622199406377421122018-08-29T20:54:00.001+02:002018-08-29T20:54:13.417+02:00Migrate Azure function app from consumption planIf you have your function app running on a consumption plan and ever considered to move it to a different plan then this may have stopped you<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJlRYMbHTD0ZLtbufr2PxKwMzZwGUq_xr8H4-o_u1u6TQwonJlTQGzpdaf1-yjMUTbKCZ2IZSzzB_wonue-p5D2Tc0pHroDuNty25LjwEseiVtPM8LlCuWzCruRsvGTaKJ6JxxAogiuvMJ/s1600/consmplan.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="160" data-original-width="236" height="271" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJlRYMbHTD0ZLtbufr2PxKwMzZwGUq_xr8H4-o_u1u6TQwonJlTQGzpdaf1-yjMUTbKCZ2IZSzzB_wonue-p5D2Tc0pHroDuNty25LjwEseiVtPM8LlCuWzCruRsvGTaKJ6JxxAogiuvMJ/s400/consmplan.PNG" width="400" /></a></div>
<br />
Being greyed out does not mean it is not possible, only that there is not yet any portal support for it. But luckily this is possible using PowerShell. The easiest way to achieve this is likely to start the Azure Cloud Shell (top bar in the portal)<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5anAtKt7-DjEpUzO7RZCwEN1XQTEm-ygXVEBkH7VxflWWtlByrgcRf2wnouFZk8FDmMYaJ3fQjIOZo85Vp4gURZ6D41tJTDKvTyoU-2FWJkwgl-GhAp3Eg_rGVXSVV0ugVwcdHMp_uj3O/s1600/cloudshell.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="61" data-original-width="659" height="36" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5anAtKt7-DjEpUzO7RZCwEN1XQTEm-ygXVEBkH7VxflWWtlByrgcRf2wnouFZk8FDmMYaJ3fQjIOZo85Vp4gURZ6D41tJTDKvTyoU-2FWJkwgl-GhAp3Eg_rGVXSVV0ugVwcdHMp_uj3O/s400/cloudshell.PNG" width="400" /></a></div>
<br />
The shell will pop up in the bottom of the browser. Where it reads Bash, click and select PowerShell.<br />
First we need to select the relevant subscription (you are already logged in). If you only have a single subscription in your tenant, skip this step.<br />
The subscription id is what follows /subscriptions/ in the browsers url, ex.<br />
<br />
<span style="font-size: x-small;">https://portal.azure.com/#@mytenant.onmicrosoft.com/resource/subscriptions/<b>9734191f-63d9-4b3d-880e-8de9a40942f2</b></span><br />
<span style="font-size: x-small;">/resourceGroups/rgfuncapp/providers/Microsoft.Web/sites/funcapp/appServices</span><br />
<br />
Copy this value and enter (paste using mouse right click)<br />
<br />
Select-AzureRmSubscription -SubscriptionId your_subscription_id<br />
<br />
Before continuing make sure you have a new plan (the one you wish to move to) in the same resource group and in the same region.<br />
<br />
To move you need just a single command<br />
<br />
Set-AzureRmWebApp -Name "[function name]" -ResourceGroupName "[resource group]" -AppServicePlan "[new app service plan name]"<br />
<br />
You need to reload the browser before you see the change in the portal.<br />
<br />
Please refer to <a href="https://github.com/Azure/Azure-Functions/issues/155">https://github.com/Azure/Azure-Functions/issues/155</a> for further details. As you may notice this blogpost is simply elaborating on a comment made on the issue, but it took me a while to find that, so hopefully this helps someone while we wait for portal support.<br />
<br />
<br />
<br />Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-54955036028049853442018-08-24T21:11:00.001+02:002018-08-24T21:14:57.570+02:00PowerShell: MS Graph API authentication with Service PrincipalI had to access the MS Graph API from Azure Function App and after wasting some time trying to get it to work with a Managed Service Identity (you can get a token, but cannot assign the MSI any roles, yet), I opted for the good ol' Service Principal (SP).<br />
<br />
There are several blogpost on how to get a token for various Microsoft APIs, and most of the code is very similar, but they are all lacking one essential detail, without it you may get varying results.<br />
<br />
I experienced getting a token that the API claimed was invalid, one that was expired, not getting a token because the SP secret was incorrect (it was not), and maybe just that no overload of the function could be found. Clearly I was doing something wrong.<br />
<br />
For good measure, here is a short guide on getting a working SP. Go to Azure AD,-> App Registrations, and create a new app registration.<br />
The application type is web app/API, you can put anything in the sign-on URL.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhhTYV8dtzDAK6vOeyVr0jgm2Ykpir9EBgERbpvOBkH8Z-bAfnqcq6_AmWeQKytc7hMCS86zkI2Pv4WtK4DwnhzfLV9Pl7QULlC4y-n7WQHq2yJSHywdzYShWG0Zb9YqvxJ8oSsCKzP13r/s1600/newappreg.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="326" data-original-width="542" height="384" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhhTYV8dtzDAK6vOeyVr0jgm2Ykpir9EBgERbpvOBkH8Z-bAfnqcq6_AmWeQKytc7hMCS86zkI2Pv4WtK4DwnhzfLV9Pl7QULlC4y-n7WQHq2yJSHywdzYShWG0Zb9YqvxJ8oSsCKzP13r/s640/newappreg.PNG" width="640" /></a></div>
<br />
This will also create an enterprise application.<br />
<br />
In the settings of the newly registered app click Settings, and under API Access click Required permissions. Add a new permission and select the Microsoft Graph API, and check off the permissions needed.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE3ckn2XQUCmfdGCndMxhjv-l6ETe6FJ_YoZW5Yg5S7qGn5GYDYcI6hEU_mr2wt-ErXb7mDe1O8KL0kzQb79vQuxtOgRaMLq6gJM2UQAt87VxbRS0_vhjc6VgvyEmmrUw1H8hCz8oiLAHA/s1600/newperm.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="920" data-original-width="1488" height="394" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiE3ckn2XQUCmfdGCndMxhjv-l6ETe6FJ_YoZW5Yg5S7qGn5GYDYcI6hEU_mr2wt-ErXb7mDe1O8KL0kzQb79vQuxtOgRaMLq6gJM2UQAt87VxbRS0_vhjc6VgvyEmmrUw1H8hCz8oiLAHA/s640/newperm.PNG" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
When done you need to click Grant Permissions.<br />
<br />
Next click Keys. Fill in a description, select when the key should expire and click Save. The key will be generated and shown. Save this for later.<br />
<br />
Back in your app registration copy the Application ID.<br />
<br />
Next we need a dll file, Microsoft.IdentityModel.Clients.ActiveDirectory.dll - this is the main contribution of this blog. It just happens that there is many different versions of this, and you need the right one to get a working token.<br />
I found that the one in <a href="https://www.powershellgallery.com/packages/AzureRM.profile/5.3.4" target="_blank">AzureRm.Profile 5.3.4</a> works just fine. I would guess versions close to this one is the same. You can get it using Save-Module:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">Save-Module AzureRm.Profile -RequiredVersion 5.3.4 -Path C<span style="background-color: #ffaaaa; color: red;">:</span>\Temp
</pre>
</div>
<br />
Now find Microsoft.IdentityModel.Clients.ActiveDirectory.dll and use Add-Type to load it<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #007020;">Add-Type</span> -Path <span style="background-color: #fff0f0;">"C:\Temp\AzureRM.profile\5.3.4\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"</span>
</pre>
</div>
<br />
Next we need an important piece of information. Run this in a fresh PS-session:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #003366; font-weight: bold;">[appdomain]</span><span style="background-color: #ffaaaa; color: red;">::</span>currentdomain.getassemblies() | <span style="color: #007020;">Where-Object</span> {<span style="color: #996633;">$_</span>.fullname <span style="color: #333333;">-like</span> <span style="background-color: #fff0f0;">"Microsoft.IdentityModel.Clients.ActiveDirectory*"</span>} | <span style="color: #007020;">Select-Object</span> -Property Fullname
</pre>
</div>
<br />
The result is what we need in the following function. We can use it to specify that it is that exact dll-file we are referring to. There could be many of these loaded, and if the wrong one is used we get a non-desirable result.<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #008800; font-weight: bold;">Function</span> <span style="color: #007020;">Get-AADToken</span> {
[<span style="color: #008800; font-weight: bold;">CmdletBinding</span>()]
[OutputType(<span style="color: #003366; font-weight: bold;">[string]</span>)]
<span style="color: #008800; font-weight: bold;">Param</span> (
<span style="color: #003366; font-weight: bold;">[String]</span><span style="color: #996633;">$TenantID</span>,
<span style="color: #003366; font-weight: bold;">[string]</span><span style="color: #996633;">$ServicePrincipalId</span>,
<span style="color: #003366; font-weight: bold;">[securestring]</span><span style="color: #996633;">$ServicePrincipalPwd</span>,
<span style="color: #996633;">$resourceAppIdURI</span> = <span style="background-color: #fff0f0;">'https://graph.microsoft.com/'</span>
)
<span style="color: #008800; font-weight: bold;">Try</span> {
<span style="color: #888888;"># Set Authority to Azure AD Tenant</span>
<span style="color: #996633;">$authority</span> = <span style="background-color: #fff0f0;">'https://login.windows.net/'</span> + <span style="color: #996633;">$TenantId</span>
<span style="color: #996633;">$ClientCred</span> = [Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential, Microsoft.IdentityModel.Clients.ActiveDirectory, Version=2.28.3.860, Culture=neutral, PublicKeyToken=31bf3856ad364e35]<span style="background-color: #ffaaaa; color: red;">::</span>new(<span style="color: #996633;">$ServicePrincipalId</span>, <span style="color: #996633;">$ServicePrincipalPwd</span>)
<span style="color: #996633;">$authContext</span> = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext, Microsoft.IdentityModel.Clients.ActiveDirectory, Version=2.28.3.860, Culture=neutral, PublicKeyToken=31bf3856ad364e35]<span style="background-color: #ffaaaa; color: red;">::</span>new(<span style="color: #996633;">$authority</span>)
<span style="color: #996633;">$authResult</span> = <span style="color: #996633;">$authContext</span>.AcquireTokenAsync(<span style="color: #996633;">$resourceAppIdURI</span>, <span style="color: #996633;">$ClientCred</span>)
<span style="color: #008800; font-weight: bold;">if</span>(<span style="color: #996633;">$authResult</span>.Exception)
{
throw <span style="color: #996633;">$authResult</span>.Exception.InnerException.Message
}
<span style="color: #996633;">$Token</span> = <span style="color: #996633;">$authResult</span>.Result.AccessToken
}
<span style="color: #008800; font-weight: bold;">Catch</span> {
Throw <span style="color: #996633;">$_</span>
}
<span style="color: #996633;">$Token</span>
}
</pre>
</div>
<br />
The function is called as follows<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #888888;"># load this specific Microsoft.IdentityModel.Clients.ActiveDirectory.dll</span>
<span style="color: #007020;">Add-Type</span> -Path <span style="background-color: #fff0f0;">"C:\temp\AzureRM.profile\5.3.4\Microsoft.IdentityModel.Clients.ActiveDirectory.dll"</span>
<span style="color: #888888;"># your Azure AD tenant</span>
<span style="color: #996633;">$TenantId</span> = <span style="background-color: #fff0f0;">'55da3b96-2993-4ef3-ad6f-f0a066401f60'</span>
<span style="color: #888888;"># the application id from the app registration</span>
<span style="color: #996633;">$AppId</span> = <span style="background-color: #fff0f0;">'135fee95-c7c3-48f5-9821-fcaf29fd8a3c'</span>
<span style="color: #888888;"># the key we created - obviously do not store this in cleartext</span>
<span style="color: #996633;">$ServicePrincipalPwd</span> = <span style="background-color: #fff0f0;">'5a1mXQYcZNZADD8h2lSYxzSGHSF0U+chrpk0L5E0Cgw='</span> | <span style="color: #007020;">ConvertTo-SecureString</span> -AsPlainText -Force
<span style="color: #888888;"># get the token</span>
<span style="color: #996633;">$Token</span> = <span style="color: #007020;">Get-AADToken</span> -TenantID <span style="color: #996633;">$TenantId</span> -ServicePrincipalId <span style="color: #996633;">$AppId</span> -ServicePrincipalPwd <span style="color: #996633;">$ServicePrincipalPwd</span>
</pre>
</div>
<br />
Now that we have a token, it is time to put it to work<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #996633;">$Headers</span> = <span style="background-color: #ffaaaa; color: red;">@</span>{
<span style="background-color: #fff0f0;">"Authorization"</span> = <span style="background-color: #fff0f0;">"Bearer $token"</span>
}
<span style="color: #008800; font-weight: bold;">try</span> {
<span style="color: #996633;">$Response</span> = <span style="color: #007020;">Invoke-RestMethod</span> -Uri <span style="background-color: #fff0f0;">'https://graph.microsoft.com/v1.0/users/'</span> -Method Get -UseBasicParsing -Headers <span style="color: #996633;">$Headers</span>
}
<span style="color: #008800; font-weight: bold;">catch</span> {
<span style="color: #996633;">$_</span>
<span style="color: #996633;">$_</span>.Exception.ErrorDetails.Message
}
</pre>
</div>
Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-23508809456117644842018-07-28T11:44:00.001+02:002018-07-28T11:44:54.698+02:00Azure Function App - Frontend and BackendThere are (many) different ways Function Apps can call other function apps. The perhaps most obvious (classic) way is making a web-request, from one function-endpoint to another. I have my "frontends" in Function App functions protected with "App Service Authentication" - one must login with Azure AD to authenticate one self (use the "express" settings to configure this to get it quickly setup).<br />
<br />
Once configured add your users to the <i>Managed application</i> in the "Users and groups" tab.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpzoKQugyzUr9xVqUHfe3QoHSQK9zc4INEZMWsxz4x_BDNBsnCxB8xLSU7UYdrB6C_qvhF72WkE_2lRopls0Ruh10UwFk8ZQsM8YQ2Q4T38nnUUDk4WB9fpgtUfa0LoLxncs-EzZjx6ZeR/s1600/blog1.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="845" data-original-width="1600" height="338" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpzoKQugyzUr9xVqUHfe3QoHSQK9zc4INEZMWsxz4x_BDNBsnCxB8xLSU7UYdrB6C_qvhF72WkE_2lRopls0Ruh10UwFk8ZQsM8YQ2Q4T38nnUUDk4WB9fpgtUfa0LoLxncs-EzZjx6ZeR/s640/blog1.PNG" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
These users will be allowed access to all the functions in your Function App. That seems pretty secure! You can even add Conditional Access to the application for added security.<br />
<br />
Only problem is that if you want to make requests to other functions in the same Function App, then you would also have to authenticate, from the function, and I have so far given up to get this to work.<br />
<br />
So I had to cook up some alternative. What I found was having 2 Function App instances, one is the frontend, and authentication is done using AAD as mentioned before, the backend is not protected by AAD authentication, but you do need a function key to access a given function (ie. no anonymous calls to this endpoint), and we can encrypt the response (also with a key), and both keys will be stored in Azure Key Vault.<br />
<br />
Create 2 function apps and a key vault. In the key vault create a secret called encryptionKey, the value should be 32 characters long (256 bits), and the other is named to match the function and the value being the functions key (found in the <i>Manage </i>tab of a function, named default).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpxOVpCAetcXzVjNgo22j0snYqXUS48r6av_NM_FlvyHKpy1IuYKSNCaa2sQBpHjB-YH1-UwSDr4suyqJW6YjX4t0QD1VjnQXwadXyG-9kvU8R1dupdzA4CAZl8hLvH8lfVx6WqArlsteI/s1600/kv.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="336" data-original-width="1212" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpxOVpCAetcXzVjNgo22j0snYqXUS48r6av_NM_FlvyHKpy1IuYKSNCaa2sQBpHjB-YH1-UwSDr4suyqJW6YjX4t0QD1VjnQXwadXyG-9kvU8R1dupdzA4CAZl8hLvH8lfVx6WqArlsteI/s640/kv.PNG" width="640" /></a></div>
<br />
Next step is to enable <i>Managed service identity</i> on both Function Apps. You can do this under <i>platform features</i>, same place as you find <i>Application settings</i>. Now you need to note down the application id of both function apps, you can find that in the Azure portal under <i>Azure Active Directory->Enterprise Applications</i>. They will be named the same as your function apps.<br />
Add these values to their respective application settings under the name <i>ApplicationId</i>.<br />
<br />
In both Function Apps create a PowerShell Http trigger function.<br />
<br />
<b>Code for the frontend</b><br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #888888;"># get a token for the key vault</span>
<span style="color: #996633;">$apiVersion</span> = <span style="background-color: #fff0f0;">"2017-09-01"</span>
<span style="color: #996633;">$resourceURI</span> = <span style="background-color: #fff0f0;">"https://vault.azure.net"</span>
<span style="color: #996633;">$tokenAuthURI</span> = <span style="color: #996633;">$env:MSI_ENDPOINT</span> + <span style="background-color: #fff0f0;">"?resource=$resourceURI&api-version=$apiVersion"</span>
<span style="color: #996633;">$tokenResponse</span> = <span style="color: #007020;">Invoke-RestMethod</span> -Method Get -Headers <span style="background-color: #ffaaaa; color: red;">@</span>{<span style="background-color: #fff0f0;">"Secret"</span>=<span style="background-color: #fff0f0;">"$env:MSI_SECRET"</span>} -Uri <span style="color: #996633;">$tokenAuthURI</span>
<span style="color: #996633;">$accessToken</span> = <span style="color: #996633;">$tokenResponse</span>.access_token
<span style="color: #888888;"># remember to set these</span>
<span style="color: #008800; font-weight: bold;">if</span>(<span style="color: #333333;">-not</span> <span style="color: #996633;">$accessToken</span>) {throw <span style="background-color: #fff0f0;">"unable to fetch access token"</span>}
<span style="color: #008800; font-weight: bold;">if</span>(<span style="color: #333333;">-not</span> <span style="color: #996633;">$env:ApplicationId</span>) {throw <span style="background-color: #fff0f0;">"application id not set in environmental settings"</span>}
<span style="color: #888888;"># get the function key first</span>
<span style="color: #888888;"># get the base url from the "overview" tab in the key vault</span>
<span style="color: #996633;">$secretName</span> = <span style="background-color: #fff0f0;">'somebackend'</span>
<span style="color: #996633;">$uri</span> = <span style="background-color: #fff0f0;">"https://cbfuncappkv.vault.azure.net/secrets/{0}?api-version=2016-10-01"</span> <span style="color: #333333;">-f</span> <span style="color: #996633;">$secretName</span>
<span style="color: #996633;">$Headers</span> = <span style="background-color: #ffaaaa; color: red;">@</span>{Authorization =<span style="background-color: #fff0f0;">"Bearer $accessToken"</span>}
<span style="color: #996633;">$KeyvaultResponse</span> = (<span style="color: #007020;">Invoke-WebRequest</span> -UseBasicParsing -Uri <span style="color: #996633;">$uri</span> -Method Get -Headers <span style="color: #996633;">$Headers</span>).Content | <span style="color: #007020;">ConvertFrom-Json</span>
<span style="color: #888888;"># get the value of the secret</span>
<span style="color: #996633;">$FunctionKey</span> = <span style="color: #996633;">$KeyvaultResponse</span> | <span style="color: #007020;">Select-Object</span> -ExpandProperty value
<span style="color: #888888;"># now ready to make a request to the backend</span>
<span style="color: #996633;">$uri</span> = <span style="background-color: #fff0f0;">"https://cbfuncappbackend.azurewebsites.net/api/somebackend?code={0}"</span> <span style="color: #333333;">-f</span> <span style="color: #996633;">$FunctionKey</span>
<span style="color: #996633;">$Headers</span> = <span style="background-color: #ffaaaa; color: red;">@</span>{<span style="background-color: #fff0f0;">'content-type'</span> = <span style="background-color: #fff0f0;">"application/x-www-form-urlencoded"</span>}
<span style="color: #888888;"># oh oh, the response we got back is encrypted!</span>
<span style="color: #996633;">$EncryptedOutput</span> = (<span style="color: #007020;">Invoke-WebRequest</span> -UseBasicParsing -Uri <span style="color: #996633;">$uri</span> -Method Get -Headers <span style="color: #996633;">$Headers</span>).Content | <span style="color: #007020;">ConvertFrom-Json</span>
<span style="color: #888888;"># retrieve the encryption key from key vault</span>
<span style="color: #996633;">$secretName</span> = <span style="background-color: #fff0f0;">'encryptionKey'</span>
<span style="color: #888888;"># get the base url from the "overview" tab in the key vault</span>
<span style="color: #996633;">$uri</span> = <span style="background-color: #fff0f0;">"https://cbfuncappkv.vault.azure.net/secrets/{0}?api-version=2016-10-01"</span> <span style="color: #333333;">-f</span> <span style="color: #996633;">$secretName</span>
<span style="color: #996633;">$Headers</span> = <span style="background-color: #ffaaaa; color: red;">@</span>{Authorization =<span style="background-color: #fff0f0;">"Bearer $accessToken"</span>}
<span style="color: #996633;">$KeyvaultResponse</span> = (<span style="color: #007020;">Invoke-WebRequest</span> -UseBasicParsing -Uri <span style="color: #996633;">$uri</span> -Method Get -Headers <span style="color: #996633;">$Headers</span>).Content | <span style="color: #007020;">ConvertFrom-Json</span>
<span style="color: #888888;"># get the value of the secret</span>
<span style="color: #996633;">$encryptionKey</span> = <span style="color: #996633;">$KeyvaultResponse</span> | <span style="color: #007020;">Select-Object</span> -ExpandProperty value
<span style="color: #996633;">$Key</span> = (<span style="color: #003366; font-weight: bold;">[system.Text.Encoding]</span><span style="background-color: #ffaaaa; color: red;">::</span>UTF8).GetBytes(<span style="color: #996633;">$encryptionKey</span>)
<span style="color: #888888;"># decrypt the secure string</span>
<span style="color: #996633;">$DecryptedSecureString</span> = <span style="color: #996633;">$EncryptedOutput</span> | <span style="color: #007020;">ConvertTo-SecureString</span> -Key <span style="color: #996633;">$Key</span>
<span style="color: #888888;"># copies the content of the secure string into unmanaged memory</span>
<span style="color: #996633;">$ptr</span> = <span style="color: #003366; font-weight: bold;">[System.Runtime.InteropServices.marshal]</span><span style="background-color: #ffaaaa; color: red;">::</span>SecureStringToBSTR(<span style="color: #996633;">$DecryptedSecureString</span>)
<span style="color: #888888;"># convert to a string</span>
<span style="color: #996633;">$DecryptedOutput</span> = <span style="color: #003366; font-weight: bold;">[System.Runtime.InteropServices.marshal]</span><span style="background-color: #ffaaaa; color: red;">::</span>PtrToStringAuto(<span style="color: #996633;">$ptr</span>)
<span style="color: #888888;"># html part - </span>
<span style="color: #996633;">$html</span> = <span style="background-color: #fff0f0;">@"</span>
<span style="background-color: #fff0f0;"><head><style>$style</style></head></span>
<span style="background-color: #fff0f0;"><title>Hello PS Backend</title></span>
<span style="background-color: #fff0f0;"><h1>Hello PS Backend</h1></span>
<span style="background-color: #fff0f0;"><h5>Time is $(Get-Date)</h2></span>
<span style="background-color: #fff0f0;">$DecryptedOutput</span>
<span style="background-color: #fff0f0;">"@</span>
<span style="color: #888888;"># output as a webpage</span>
<span style="background-color: #ffaaaa; color: red;">@</span>{
headers = <span style="background-color: #ffaaaa; color: red;">@</span>{ <span style="background-color: #fff0f0;">"content-type"</span> = <span style="background-color: #fff0f0;">"text/html"</span>}
body = <span style="color: #996633;">$html</span>
} | <span style="color: #007020;">ConvertTo-Json</span> | <span style="color: #007020;">Out-File</span> -Encoding Ascii -FilePath <span style="color: #996633;">$res</span>
</pre>
</div>
<br />
<b>And for the backend</b><br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #888888;"># get a token for the key vault</span>
<span style="color: #996633;">$apiVersion</span> = <span style="background-color: #fff0f0;">"2017-09-01"</span>
<span style="color: #996633;">$resourceURI</span> = <span style="background-color: #fff0f0;">"https://vault.azure.net"</span>
<span style="color: #996633;">$tokenAuthURI</span> = <span style="color: #996633;">$env:MSI_ENDPOINT</span> + <span style="background-color: #fff0f0;">"?resource=$resourceURI&api-version=$apiVersion"</span>
<span style="color: #996633;">$tokenResponse</span> = <span style="color: #007020;">Invoke-RestMethod</span> -Method Get -Headers <span style="background-color: #ffaaaa; color: red;">@</span>{<span style="background-color: #fff0f0;">"Secret"</span>=<span style="background-color: #fff0f0;">"$env:MSI_SECRET"</span>} -Uri <span style="color: #996633;">$tokenAuthURI</span>
<span style="color: #996633;">$accessToken</span> = <span style="color: #996633;">$tokenResponse</span>.access_token
<span style="color: #888888;"># remember to set these</span>
<span style="color: #008800; font-weight: bold;">if</span>(<span style="color: #333333;">-not</span> <span style="color: #996633;">$accessToken</span>) {throw <span style="background-color: #fff0f0;">"unable to fetch access token"</span>}
<span style="color: #008800; font-weight: bold;">if</span>(<span style="color: #333333;">-not</span> <span style="color: #996633;">$env:ApplicationId</span>) {throw <span style="background-color: #fff0f0;">"application id not set in environmental settings"</span>}
<span style="color: #888888;"># retrieve the encryption key from key vault</span>
<span style="color: #996633;">$secretName</span> = <span style="background-color: #fff0f0;">'encryptionKey'</span>
<span style="color: #888888;"># get the base url from the "overview" tab in the key vault</span>
<span style="color: #996633;">$uri</span> = <span style="background-color: #fff0f0;">"https://cbfuncappkv.vault.azure.net/secrets/{0}?api-version=2016-10-01"</span> <span style="color: #333333;">-f</span> <span style="color: #996633;">$secretName</span>
<span style="color: #996633;">$Headers</span> = <span style="background-color: #ffaaaa; color: red;">@</span>{Authorization =<span style="background-color: #fff0f0;">"Bearer $accessToken"</span>}
<span style="color: #996633;">$KeyvaultResponse</span> = (<span style="color: #007020;">Invoke-WebRequest</span> -UseBasicParsing -Uri <span style="color: #996633;">$uri</span> -Method Get -Headers <span style="color: #996633;">$Headers</span>).Content | <span style="color: #007020;">ConvertFrom-Json</span>
<span style="color: #888888;"># get the value of the secret</span>
<span style="color: #996633;">$encryptionKey</span> = <span style="color: #996633;">$KeyvaultResponse</span> | <span style="color: #007020;">Select-Object</span> -ExpandProperty value
<span style="color: #888888;"># secure and encrypt the below output</span>
<span style="color: #996633;">$Output</span> = <span style="background-color: #fff0f0;">"Hello from the backend"</span>
<span style="color: #888888;"># convert our encryption key to byte array, if string is 32 characters, we get 8*32=256 bit encryption</span>
<span style="color: #996633;">$Key</span> = (<span style="color: #003366; font-weight: bold;">[system.Text.Encoding]</span><span style="background-color: #ffaaaa; color: red;">::</span>UTF8).GetBytes(<span style="color: #996633;">$encryptionKey</span>)
<span style="color: #888888;"># convert to secure string, then to en encrypted string (the string must be secure before it can be encrypted)</span>
<span style="color: #996633;">$EncryptedOutput</span> = <span style="color: #996633;">$Output</span> | <span style="color: #007020;">ConvertTo-SecureString</span> -AsPlainText -Force | <span style="color: #007020;">ConvertFrom-SecureString</span> -key <span style="color: #996633;">$key</span>
<span style="color: #888888;"># write encrypted output</span>
<span style="color: #007020;">Out-File</span> -Encoding Ascii -FilePath <span style="color: #996633;">$res</span> -inputObject <span style="color: #996633;">$EncryptedOutput</span>
</pre>
</div>
<br />
Lastly we need to grant access to the secrets in the key vault, <i>Get</i> operation on secrets is sufficient.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhP5tr7vm2thV9-u-9WNY_H9x4q-mIBpPUO6Az-xLKwb-pO6Z-ocYjf2goe0c93BTU5gtZxi3RAcgAwLIlaQ0MJSk6CaqSxTAZVqL0BsW4wAxG3PQSNjocvKMeZSnlvjOZYjxzkCyty8NP/s1600/kvaccess.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="494" data-original-width="557" height="566" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhP5tr7vm2thV9-u-9WNY_H9x4q-mIBpPUO6Az-xLKwb-pO6Z-ocYjf2goe0c93BTU5gtZxi3RAcgAwLIlaQ0MJSk6CaqSxTAZVqL0BsW4wAxG3PQSNjocvKMeZSnlvjOZYjxzkCyty8NP/s640/kvaccess.PNG" width="640" /></a></div>
<br />
<br />
Optionally enable AAD authentication on the frontend Function App before running the example, and in that case remember to add your own user!<br />
<br />
For added security you could add a timed trigger function that resets the keys in the key vault at regular intervals. To make sure matching encryption keys are used (in both ends), you could provide the version of the encryption key as part of the response.<br />
I also think that you can use service endpoints on the key vault so that only these functions are able to retrieve the key in the first place.<br />
<br />
The result should look like this<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaA7b0hu5k7fNj1stTMM6YMzT-XgIZ3NJZbGkLjgYCMnf6EwtA5pjjPoQ-04dF8aITChloHDT2QipCf5IOEcLFMdes2UQxZFDM87DRCrdgHc1hHMHEjapu-X_itVXf30hKMLj3bGEs9ygP/s1600/res.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="189" data-original-width="693" height="174" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaA7b0hu5k7fNj1stTMM6YMzT-XgIZ3NJZbGkLjgYCMnf6EwtA5pjjPoQ-04dF8aITChloHDT2QipCf5IOEcLFMdes2UQxZFDM87DRCrdgHc1hHMHEjapu-X_itVXf30hKMLj3bGEs9ygP/s640/res.PNG" width="640" /></a></div>
<br />Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-77259623309818228182018-07-25T21:10:00.000+02:002018-07-25T21:10:16.734+02:00Simple website in Azure Function App written in PowerShellJust as the title says, in this post I will show how to write a simple website using Azure Function App in the still "experimental language" PowerShell. You can skip ahead and view there result <a href="https://funcapppswebsite.azurewebsites.net/api/PSWebsite" target="_blank">here</a>.<br />
<br />
Doug Finke already <a href="https://dfinke.github.io/powershell/2018/04/24/PowerShell-Serving-an-HTML-Page-from-Azure-Functions.html" target="_blank">showed</a> how to do this, but in his example you need to write the HTML code yourself. Being the lazy programmer I am, I wanted to use <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/convertto-html?view=powershell-6" target="_blank">ConvertTo-Html</a>.<br />
<br />
I am assuming you are familiar with rolling a Function App. Go ahead and create a HTTP trigger function and language set to PowerShell.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifkcU_QMzIzkGNqDeerRPcJX7BeqpjDvyu9Mkzf0pxppP_6ugkjX9lu-ZyR6tl0XWz1tcJw3dL649yy7WLcxHuzcNtoI3xja5QRdB4B4sG4ZGlJ6w5cDPy5UMgQC2GtwCsvMqOd2uM9KtJ/s1600/funcappps.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="383" data-original-width="1600" height="152" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifkcU_QMzIzkGNqDeerRPcJX7BeqpjDvyu9Mkzf0pxppP_6ugkjX9lu-ZyR6tl0XWz1tcJw3dL649yy7WLcxHuzcNtoI3xja5QRdB4B4sG4ZGlJ6w5cDPy5UMgQC2GtwCsvMqOd2uM9KtJ/s640/funcappps.PNG" width="640" /></a></div>
<br />
Name it however you like and leave other settings to default.<br />
<br />
The real magic happens with the discovery of the -Fragment switch to ConvertTo-Html. It will provide you only with the body, meaning you can combine multiple fragments, and that is exactly what is needed to output HTML in a Function App.<br />
<br />
The code part is pretty basic. We have some CSS, some semi-static HTML, and then using ConvertTo-Html to output available PS-modules.<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #888888;"># inline CSS - stole this somewhere, sorry dude, can't remember</span>
<span style="color: #996633;">$style</span> = <span style="background-color: #fff0f0;">@"</span>
<span style="background-color: #fff0f0;">h1, h2, h3, h4, h5, th { text-align: center; }</span>
<span style="background-color: #fff0f0;">table { margin: auto; font-family: Segoe UI; box-shadow: 10px 10px 5px #888; border: thin ridge grey; }</span>
<span style="background-color: #fff0f0;">th { background: #0046c3; color: #fff; max-width: 400px; padding: 5px 10px; }</span>
<span style="background-color: #fff0f0;">td { font-size: 11px; padding: 5px 20px; color: #000; }</span>
<span style="background-color: #fff0f0;">tr { background: #b8d1f3; }</span>
<span style="background-color: #fff0f0;">tr:nth-child(even) { background: #dae5f4; }</span>
<span style="background-color: #fff0f0;">tr:nth-child(odd) { background: #b8d1f3; }</span>
<span style="background-color: #fff0f0;">"@</span>
<span style="color: #888888;"># html part - show Azure modules and non-Azure modules available in Function Apps</span>
<span style="color: #996633;">$html</span> = <span style="background-color: #fff0f0;">@"</span>
<span style="background-color: #fff0f0;"><head><style>$style</style></head></span>
<span style="background-color: #fff0f0;"><title>Hello PS Website</title></span>
<span style="background-color: #fff0f0;"><h1>Hello PS Website</h1></span>
<span style="background-color: #fff0f0;"><h5>Time is $(Get-Date)</h2></span>
<span style="background-color: #fff0f0;"><h2>Azure modules</h2></span>
<span style="background-color: #fff0f0;">$(get-module -ListAvailable | where-object {$_.name -like "*azure*"} | ConvertTo-Html -Fragment -property Name, version)</span>
<span style="background-color: #fff0f0;"><h2>Other modules</h2></span>
<span style="background-color: #fff0f0;">$(get-module -ListAvailable | where-object {$_.name -notlike "*azure*"} | ConvertTo-Html -Fragment -property Name, version)</span>
<span style="background-color: #fff0f0;">"@</span>
<span style="color: #888888;"># thank you Doug!</span>
<span style="background-color: #ffaaaa; color: red;">@</span>{
headers = <span style="background-color: #ffaaaa; color: red;">@</span>{ <span style="background-color: #fff0f0;">"content-type"</span> = <span style="background-color: #fff0f0;">"text/html"</span>}
body = <span style="color: #996633;">$html</span>
} | <span style="color: #007020;">ConvertTo-Json</span> | <span style="color: #007020;">Out-File</span> -Encoding Ascii -FilePath <span style="color: #996633;">$res</span>
</pre>
</div>
<br />
The output will look something like this<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTEUk_ZCk4PSgWLPm25ysHCQGCMcQXjVkfii9wOg43AUaezvufkcRiBYgdEKnWa3BrmKccRy5B5XSGMq02fTXyCnVDfHjC2wTWBJGRaxT5b__W5u3UrgulB__20c8aSVnPqpEQKZIwtJe_/s1600/websiteout.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="905" data-original-width="469" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTEUk_ZCk4PSgWLPm25ysHCQGCMcQXjVkfii9wOg43AUaezvufkcRiBYgdEKnWa3BrmKccRy5B5XSGMq02fTXyCnVDfHjC2wTWBJGRaxT5b__W5u3UrgulB__20c8aSVnPqpEQKZIwtJe_/s640/websiteout.PNG" width="330" /></a></div>
<br />Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-26474260363717168982018-06-14T21:10:00.000+02:002018-06-14T21:12:09.897+02:00Set Expiry Date on DevTest Lab VMsA colleague of mine recently handed me the script from <a href="https://github.com/Azure-Samples/virtual-machines-powershell-auto-expired/">https://github.com/Azure-Samples/virtual-machines-powershell-auto-expired/</a> and asked for my help with what he thought was a permission issue.<br />
The script is 2 years old, so things have changed quite a bit since, but it is just not very clever as it fetches all resources in an entire subscription (something this colleague did not have permission to do), which by all means is a bad idea.<br />
<br />
I made some improvements and wanted to share. Below is simply run and you are prompted to select a lab, then one or more VMs in that lab, and finally in how many days the VMs should expire.<br />
<br />
Note that I will not be updating below with fixes so grab it from Technet <a href="http://noteuploadedyet/" target="_blank">here</a>.<br />
<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #888888;"># you can remove the TenantId if you have just a single tenant</span>
<span style="color: #996633;">$TenantId</span> = <span style="background-color: #fff0f0;">''</span>
<span style="color: #007020;">Select-AzureRmSubscription</span> -TenantId <span style="color: #996633;">$TenantId</span> -SubscriptionId <span style="background-color: #fff0f0;">''</span> | <span style="color: #007020;">Out-Null</span>
<span style="color: #008800; font-weight: bold;">Function</span> <span style="color: #007020;">Set-AzureVirtualMachineExpiredDate</span>
{
[<span style="color: #008800; font-weight: bold;">CmdletBinding</span>()]
<span style="color: #008800; font-weight: bold;">Param</span>
(
[<span style="color: #008800; font-weight: bold;">Parameter</span>(<span style="color: #008800; font-weight: bold;">Mandatory</span>=<span style="color: #996633;">$true</span>, <span style="color: #008800; font-weight: bold;">ValueFromPipeline</span>, <span style="color: #008800; font-weight: bold;">Position</span>=1)][ValidateNotNull()]<span style="color: #003366; font-weight: bold;">[String]</span><span style="color: #996633;">$VMName</span>,
[<span style="color: #008800; font-weight: bold;">Parameter</span>(<span style="color: #008800; font-weight: bold;">Mandatory</span>=<span style="color: #996633;">$true</span>)][ValidateNotNull()]<span style="color: #003366; font-weight: bold;">[String]</span><span style="color: #996633;">$LabName</span>,
[<span style="color: #008800; font-weight: bold;">Parameter</span>(<span style="color: #008800; font-weight: bold;">Mandatory</span>=<span style="color: #996633;">$true</span>)][ValidateNotNull()]<span style="color: #003366; font-weight: bold;">[String]</span><span style="color: #996633;">$LabResourceGroupName</span>,
[<span style="color: #008800; font-weight: bold;">Parameter</span>(<span style="color: #008800; font-weight: bold;">Mandatory</span>=<span style="color: #996633;">$true</span>)][ValidateNotNull()]<span style="color: #003366; font-weight: bold;">[DateTime]</span><span style="color: #996633;">$ExpiredUTCDate</span>
)
<span style="color: #008800; font-weight: bold;">Begin</span>{
<span style="color: #996633;">$Jobs</span> = <span style="background-color: #ffaaaa; color: red;">@</span>()
}
<span style="color: #008800; font-weight: bold;">Process</span>
{
<span style="color: #008800; font-weight: bold;">try</span> {
<span style="color: #888888;"># get vm info </span>
<span style="color: #996633;">$targetVMInfo</span> = <span style="color: #007020;">Get-AzureRmResource</span> -ResourceName <span style="background-color: #fff0f0;">"$LabName/$VMName"</span> -ResourceGroupName <span style="color: #996633;">$LabResourceGroupName</span> `
-ResourceType <span style="background-color: #fff0f0;">'Microsoft.DevTestLab/labs/virtualMachines'</span> -ExpandProperties
}
<span style="color: #008800; font-weight: bold;">catch</span> {
Throw <span style="background-color: #fff0f0;">"$VMName not found in $LabName, error was:`n$_"</span>
}
<span style="color: #888888;"># get vm properties </span>
<span style="color: #996633;">$vmProperties</span> = <span style="color: #996633;">$targetVMInfo</span>.Properties
<span style="color: #888888;"># set expired date</span>
<span style="color: #996633;">$vmProperties</span> | <span style="color: #007020;">Add-Member</span> -MemberType NoteProperty -Name expirationDate -Value <span style="color: #996633;">$ExpiredUTCDate</span> -Force
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">"Setting expiry date to $ExpiredUTCDate on $LabName/$VMName..."</span>
<span style="color: #996633;">$Jobs</span> += <span style="color: #007020;">Set-AzureRmResource</span> -ResourceId <span style="color: #996633;">$targetVMInfo</span>.ResourceId -Properties <span style="color: #996633;">$vmProperties</span> -Force `
-ErrorAction Stop -AsJob
} <span style="color: #888888;"># end of process</span>
<span style="color: #008800; font-weight: bold;">End</span>
{
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">"Waiting for jobs to complete..."</span>
<span style="color: #996633;">$Jobs</span> | <span style="color: #007020;">Wait-Job</span> | <span style="color: #007020;">Receive-Job</span> | <span style="color: #008800; font-weight: bold;">ForEach</span>-Object {
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">"Expiry date on </span><span style="background-color: #eeeeee;">$(</span><span style="background-color: #ffaaaa; color: red;">$</span><span style="background-color: #eeeeee;">_.Name)</span><span style="background-color: #fff0f0;"> set to </span><span style="background-color: #eeeeee;">$(</span><span style="background-color: #ffaaaa; color: red;">$</span><span style="background-color: #eeeeee;">_.Properties.expirationDate)</span><span style="background-color: #fff0f0;">"</span>
}
}
}
<span style="color: #996633;">$Lab</span> = <span style="color: #007020;">Get-AzureRmResource</span> -ResourceType <span style="background-color: #fff0f0;">'Microsoft.DevTestLab/labs'</span> | <span style="color: #007020;">Out-GridView</span> -Title <span style="background-color: #fff0f0;">"Select DevTest Lab"</span> -PassThru
<span style="color: #996633;">$LabName</span> = <span style="color: #996633;">$Lab</span> | <span style="color: #007020;">Select-Object</span> -ExpandProperty Name
<span style="color: #996633;">$VM</span> = <span style="color: #007020;">Get-AzureRmResource</span> -ResourceName <span style="background-color: #fff0f0;">"$LabName/*"</span> -ResourceType <span style="background-color: #fff0f0;">'Microsoft.DevTestLab/labs/virtualMachines'</span> | `
<span style="color: #007020;">Out-GridView</span> -Title <span style="background-color: #fff0f0;">"Select VM"</span> -PassThru
<span style="color: #996633;">$AddDays</span> = 1..14 | <span style="color: #007020;">Out-GridView</span> -Title <span style="background-color: #fff0f0;">"Expire in days..."</span> -PassThru
<span style="color: #996633;">$VM</span> | <span style="color: #008800; font-weight: bold;">ForEach</span>-Object {(<span style="background-color: #fff0f0;">"</span><span style="background-color: #eeeeee;">$(</span><span style="background-color: #ffaaaa; color: red;">$</span><span style="background-color: #eeeeee;">_.Name)</span><span style="background-color: #fff0f0;">"</span>.Split(<span style="background-color: #fff0f0;">'/'</span>) | <span style="color: #007020;">Select-Object</span> -Last 1)} | <span style="color: #007020;">Set-AzureVirtualMachineExpiredDate</span> `
-LabName <span style="color: #996633;">$LabName</span> `
-LabResourceGroupName <span style="color: #996633;">$Lab</span>.ResourceGroupName `
-ExpiredUTCDate (<span style="color: #007020;">Get-Date</span>).AddDays(<span style="color: #996633;">$AddDays</span>)
</pre>
</div>
Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-61702114284909778942018-06-14T20:55:00.000+02:002018-06-14T20:55:09.437+02:00Out-GridView with Selected PropertiesA script is worth a thousand words, right?<br />
<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #888888;"><#</span>
<span style="color: #888888;"> I use Out-GridView (alias: ogv) alot for interactively selecting objects. Also sometimes I need some extra information not </span>
<span style="color: #888888;"> described in the object itself.</span>
<span style="color: #888888;"> Let's say we need to enumerate files in c:\temp for a list of computers. After collecting all the files we wish to use ogv </span>
<span style="color: #888888;"> for displaying some of the properties like the name of the file, the size in kb and the computer on which the file is found. </span>
<span style="color: #888888;"> We will pretend (because this is an example anyone can run) that the file object is missing the last part, hence we add it </span>
<span style="color: #888888;"> to the object using Add-Member</span>
<span style="color: #888888;"> another usecase is simply that ogv will not show the properties we wish to see. Using Select-Object will create a new object</span>
<span style="color: #888888;"> and if we use the -Passthru parameter to ogv it will not be the original object we get. In below example we convert the size</span>
<span style="color: #888888;"> of each file to kb, which also uses Select-Object and a calculated property to do the conversion</span>
<span style="color: #888888;">#></span>
<span style="color: #996633;">$ComputerNames</span> = <span style="background-color: #ffaaaa; color: red;">@</span>(<span style="color: #996633;">$env:COMPUTERNAME</span>)
<span style="color: #996633;">$FilesInTemp</span> = <span style="background-color: #ffaaaa; color: red;">@</span>()
<span style="color: #008800; font-weight: bold;">foreach</span>(<span style="color: #996633;">$ComputerName</span> <span style="color: #008800; font-weight: bold;">in</span> <span style="color: #996633;">$ComputerNames</span>)
{
<span style="color: #996633;">$FilesInTemp</span> += <span style="color: #007020;">Invoke-Command</span> -ScriptBlock {
<span style="color: #007020;">Get-ChildItem</span> -Path c<span style="background-color: #ffaaaa; color: red;">:</span>\temp <span style="color: #333333;">-File</span>
} -ComputerName <span style="color: #996633;">$ComputerName</span> | `
<span style="color: #007020;">Add-Member</span> -Name MachineName -Value <span style="color: #996633;">$ComputerName</span> -MemberType NoteProperty -PassThru
}
<span style="color: #888888;"># Now we have a list of files that we can select from, but below fails</span>
<span style="color: #996633;">$FilesInTemp</span> | <span style="color: #007020;">Select-Object</span> -Property Name, <span style="background-color: #ffaaaa; color: red;">@</span>{ Name = <span style="background-color: #fff0f0;">'SizeInKb'</span><span style="background-color: #ffaaaa; color: red;">;</span> Expression = { <span style="color: #996633;">$_</span>.Length/1KB }}, MachineName | `
<span style="color: #007020;">Out-GridView</span> -Title <span style="background-color: #fff0f0;">"Select files to delete (example 1)"</span> -PassThru | `
<span style="color: #007020;">Remove-Item</span> -WhatIf
<span style="color: #888888;"><#</span>
<span style="color: #888888;"> the problem is that Select-Object creates a new object with just the selected properties. Why Select-Object? Try running </span>
<span style="color: #888888;"> the line below</span>
<span style="color: #888888;">#></span>
<span style="color: #007020;">Get-ChildItem</span> -Path c<span style="background-color: #ffaaaa; color: red;">:</span>\temp <span style="color: #333333;">-File</span> | <span style="color: #007020;">Out-GridView</span>
<span style="color: #888888;"><# </span>
<span style="color: #888888;"> we did get some decent properties, but it is showing the same we would get from a Format-Table, ie. the default properties</span>
<span style="color: #888888;"> if we want something different we use Select-Object, but as mentioned we get an entirely new object (with just the properties</span>
<span style="color: #888888;"> selected) , which is why Remove-Item fails</span>
<span style="color: #888888;"> A solution which can be applied in probably every case is found below</span>
<span style="color: #888888;"> The only difference is that we add the object to itself as a member and then later "extract" it before the pipe to Remove-Item</span>
<span style="color: #888888;">#></span>
<span style="color: #996633;">$FilesInTemp</span> = <span style="background-color: #ffaaaa; color: red;">@</span>()
<span style="color: #008800; font-weight: bold;">foreach</span>(<span style="color: #996633;">$ComputerName</span> <span style="color: #008800; font-weight: bold;">in</span> <span style="color: #996633;">$ComputerNames</span>)
{
<span style="color: #996633;">$FilesInTemp</span> += <span style="color: #007020;">Invoke-Command</span> -ScriptBlock {
<span style="color: #007020;">Get-ChildItem</span> -Path c<span style="background-color: #ffaaaa; color: red;">:</span>\temp <span style="color: #333333;">-File</span>
} -ComputerName <span style="color: #996633;">$ComputerName</span> | `
<span style="color: #007020;">Add-Member</span> -Name MachineName -Value <span style="color: #996633;">$ComputerName</span> -MemberType NoteProperty -PassThru | `
<span style="color: #008800; font-weight: bold;">ForEach</span>-Object {<span style="color: #996633;">$_</span> | <span style="color: #007020;">Add-Member</span> -Name _self -Value <span style="color: #996633;">$_</span> -MemberType NoteProperty -PassThru}
}
<span style="color: #996633;">$FilesInTemp</span> | <span style="color: #007020;">Select-Object</span> -Property Name, <span style="background-color: #ffaaaa; color: red;">@</span>{ Name = <span style="background-color: #fff0f0;">'SizeInKb'</span><span style="background-color: #ffaaaa; color: red;">;</span> Expression = { <span style="color: #996633;">$_</span>.Length/1KB }}, MachineName, _self | `
<span style="color: #007020;">Out-GridView</span> -Title <span style="background-color: #fff0f0;">"Select files to delete (example 2)"</span> -PassThru | `
<span style="color: #007020;">Select-Object</span> -ExpandProperty _self | `
<span style="color: #007020;">Remove-Item</span> -WhatIf
<span style="color: #888888;"><#</span>
<span style="color: #888888;"> We can make this even easier with some helper functions. Below I have used _self as the property name. Some may recognize</span>
<span style="color: #888888;"> the name as used in Python, and equivalent of "this" in C#</span>
<span style="color: #888888;">#></span>
<span style="color: #008800; font-weight: bold;">Function</span> <span style="color: #007020;">Add-Self</span>
{
<span style="color: #008800; font-weight: bold;">process</span>
{
<span style="color: #008800; font-weight: bold;">ForEach</span>-Object {<span style="color: #996633;">$_</span> | <span style="color: #007020;">Add-Member</span> -Name <span style="background-color: #fff0f0;">'_self'</span> -Value <span style="color: #996633;">$_</span> -MemberType NoteProperty -PassThru}
}
}
<span style="color: #008800; font-weight: bold;">Function</span> <span style="color: #007020;">Get-Self</span>
{
<span style="color: #008800; font-weight: bold;">process</span>
{
<span style="color: #996633;">$_</span> | <span style="color: #007020;">Select-Object</span> -ExpandProperty <span style="background-color: #fff0f0;">'_self'</span>
}
}
<span style="color: #996633;">$FilesInTemp</span> = <span style="background-color: #ffaaaa; color: red;">@</span>()
<span style="color: #008800; font-weight: bold;">foreach</span>(<span style="color: #996633;">$ComputerName</span> <span style="color: #008800; font-weight: bold;">in</span> <span style="color: #996633;">$ComputerNames</span>)
{
<span style="color: #996633;">$FilesInTemp</span> += <span style="color: #007020;">Invoke-Command</span> -ScriptBlock {
<span style="color: #007020;">Get-ChildItem</span> -Path c<span style="background-color: #ffaaaa; color: red;">:</span>\temp <span style="color: #333333;">-File</span>
} -ComputerName <span style="color: #996633;">$ComputerName</span> | `
<span style="color: #007020;">Add-Member</span> -Name MachineName -Value <span style="color: #996633;">$ComputerName</span> -MemberType NoteProperty -PassThru | `
<span style="color: #007020;">Add-Self</span>
}
<span style="color: #996633;">$FilesInTemp</span> | <span style="color: #007020;">Select-Object</span> -Property Name, <span style="background-color: #ffaaaa; color: red;">@</span>{ Name = <span style="background-color: #fff0f0;">'SizeInKb'</span><span style="background-color: #ffaaaa; color: red;">;</span> Expression = { <span style="color: #996633;">$_</span>.Length/1KB }}, MachineName, _self | `
<span style="color: #007020;">Out-GridView</span> -Title <span style="background-color: #fff0f0;">"Select files to delete (example 3)"</span> -PassThru | `
<span style="color: #007020;">Get-Self</span> | `
<span style="color: #007020;">Remove-Item</span> -WhatIf
</pre>
</div>
Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com1tag:blogger.com,1999:blog-2214780243815864156.post-21323152312565314492017-07-13T20:26:00.000+02:002017-07-13T20:26:12.057+02:00[PowerShell] List of Azure public IPsI needed a list of public IPs in Azure that was attached to a virtual network interface. PowerShell to the rescue.<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">(<span style="color: #007020;">Get-AzureRmPublicIpAddress</span> | <span style="color: #007020;">Where-Object</span> {<span style="color: #996633;">$_</span>.PublicIpAllocationMethod <span style="color: #333333;">-eq</span> <span style="background-color: #fff0f0;">'Static'</span> <span style="color: #333333;">-and</span> <span style="color: #996633;">$_</span>.IpC
onfiguration.Id <span style="color: #333333;">-like</span> <span style="background-color: #fff0f0;">'*Microsoft.Network/networkinterfaces*'</span>} | <span style="color: #007020;">Select-Object</span> -ExpandProperty IpAddress | <span style="color: #008800; font-weight: bold;">ForEach</span>-Objec
t {<span style="background-color: #fff0f0;">"$_,"</span>}) -join <span style="background-color: #fff0f0;">''</span> | clip
</pre>
</div>
Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com1tag:blogger.com,1999:blog-2214780243815864156.post-59285694633392687732016-10-19T10:49:00.002+02:002016-10-19T10:49:46.512+02:00CustomScriptExtension in ARM Templates and Shared Access Signature (SAS) TokensI had some trouble with a custom script extension where the script required a SAS token to download some software. The token was simply truncated after the first '&'.<br />
<br />
After some digging I thought I had to put the SAS token into quotes, and when looking into <i>C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.8\RuntimeSettings\0.settings</i> I found that it was a sensible solution. I could also copy the "<i>commandToExecute</i>" and run it and get the expected result. In the variables section I added a:<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"> <span style="background-color: #fff0f0;">"variables"</span><span style="background-color: #ffaaaa; color: red;">:</span> {
<span style="color: #007700;">"singlequote"</span>: <span style="background-color: #fff0f0;">"'"</span>,
</pre>
</div>
<br />
And then put single quotes around the <i>parameters('SASToken')</i>. But no dice. The token was still getting truncated, this time with a 'in front...<br />
<br />
So I decided to get rid of the '&', at least temporarily. base64 encoding to the rescue. And Luckily there is an ARM template function for <a href="https://azure.microsoft.com/en-us/documentation/articles/resource-group-template-functions/#base64" target="_blank">just that</a>. In the script I then added:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #996633;">$SASToken</span> = <span style="color: #003366; font-weight: bold;">[System.Text.Encoding]</span><span style="background-color: #ffaaaa; color: red;">::</span>UTF8.GetString(<span style="color: #003366; font-weight: bold;">[System.Convert]</span><span style="background-color: #ffaaaa; color: red;">::</span>FromBase64String(<span style="color: #996633;">$SASToken</span>))</pre>
</div>
<br />
Problem solved!<br />
<br />
Seems to me that there is something odd in how the custom script extension calls PowerShell in this particular instance.Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com1tag:blogger.com,1999:blog-2214780243815864156.post-61363973496498629452016-10-05T16:42:00.001+02:002016-10-05T16:42:26.289+02:00Begin..Process..End and Error HandlingI had to wrap my mind around error handling and the <i>begin..process..end</i> function in PowerShell. It becomes really fun when I start throwing different <i>ErrorActions</i> after it!<br />
<br />
This will be mostly some PowerShell snippets and their result. So without further ado, lets dive into some code!<br />
<br />
This is a really simple function:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #008800; font-weight: bold;">function</span> myfunc
{
[<span style="color: #008800; font-weight: bold;">cmdletbinding</span>()]
<span style="color: #008800; font-weight: bold;">param</span>()
<span style="color: #008800; font-weight: bold;">begin</span>
{
<span style="color: #888888;"># some init code that throws an error</span>
<span style="color: #008800; font-weight: bold;">try</span>
{
throw <span style="background-color: #fff0f0;">'some error'</span>
<span style="color: #888888;"># code never reaches here</span>
<span style="color: #007020;">Write-Output</span> <span style="background-color: #fff0f0;">'begin block'</span>
}
<span style="color: #008800; font-weight: bold;">catch</span> <span style="color: #003366; font-weight: bold;">[System.Exception]</span>
{
<span style="color: #007020;">Write-Error</span> <span style="background-color: #fff0f0;">'begin block'</span>
}
}
<span style="color: #008800; font-weight: bold;">process</span>
{
<span style="color: #007020;">Write-Output</span> <span style="background-color: #fff0f0;">'process block'</span>
}
<span style="color: #008800; font-weight: bold;">end</span>
{
<span style="color: #007020;">Write-Output</span> <span style="background-color: #fff0f0;">'end block'</span>
}
}
<span style="color: #007020;">Clear-Host</span>
<span style="color: #996633;">$VerbosePreference</span> = <span style="background-color: #fff0f0;">"Continue"</span>
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">"-ErrorAction SilentlyContinue: the Write-Error in the begin block is suppressed"</span> `
-ForegroundColor Cyan
myfunc -ErrorAction SilentlyContinue
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">"-ErrorAction Continue: displays the Write-Error in the begin block,</span>
<span style="background-color: #fff0f0;">but the process and end block is executed"</span> `
-ForegroundColor Cyan
myfunc -ErrorAction <span style="color: #008800; font-weight: bold;">Continue</span>
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">"-ErrorAction Stop: displays the Write-Error in the begin block. </span>
<span style="background-color: #fff0f0;">The Write-Error in the begin block becomes a terminating error. </span>
<span style="background-color: #fff0f0;">The process and end block is not executed"</span> `
-ForegroundColor Cyan
myfunc -ErrorAction Stop
</pre>
</div>
<br />
The output is:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggzsdOg2cU56ZNkdDVEcZogpkkzWs6OzzUxSiGDQnxuzgxQQiyRNAUDlSDEepIbM6MXmg0ot2FFE_fiaA0lX0-NN-rLhUZ_rFkZxQkd4yDIYggEPD3nGFBJHDv9-KWt1Lx9XCxM9tE-Er2/s1600/output1.PNG" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggzsdOg2cU56ZNkdDVEcZogpkkzWs6OzzUxSiGDQnxuzgxQQiyRNAUDlSDEepIbM6MXmg0ot2FFE_fiaA0lX0-NN-rLhUZ_rFkZxQkd4yDIYggEPD3nGFBJHDv9-KWt1Lx9XCxM9tE-Er2/s1600/output1.PNG" /></a><br />
<br />
<br />
We see that for both ErrorActions <i>Continue/SilentlyContinue</i> that the process block is executed. When we use <i>Stop</i> then Write-Error becomes a terminating error and the pipeline is stopped.<br />
<br />
Let us not dwell on that and move onto a function with some actual input:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #888888;"># with input</span>
<span style="color: #008800; font-weight: bold;">function</span> myfunc
{
[<span style="color: #008800; font-weight: bold;">cmdletbinding</span>()]
<span style="color: #008800; font-weight: bold;">param</span>(
[<span style="color: #008800; font-weight: bold;">Parameter</span>(
<span style="color: #008800; font-weight: bold;">Position</span>=0,
<span style="color: #008800; font-weight: bold;">Mandatory</span>=<span style="color: #996633;">$true</span>,
<span style="color: #008800; font-weight: bold;">ValueFromPipeline</span>=<span style="color: #996633;">$true</span>,
<span style="color: #008800; font-weight: bold;">ValueFromPipelineByPropertyName</span>=<span style="color: #996633;">$true</span>)
]
<span style="color: #996633;">$x</span>
)
<span style="color: #008800; font-weight: bold;">begin</span>
{
<span style="color: #888888;"># No errors in the begin block this time</span>
<span style="color: #007020;">Write-Output</span> <span style="background-color: #fff0f0;">'begin block'</span>
}
<span style="color: #008800; font-weight: bold;">process</span>
{
<span style="color: #008800; font-weight: bold;">if</span>(<span style="color: #996633;">$x</span> <span style="color: #333333;">-gt</span> 2)
{
<span style="color: #007020;">Write-Error</span> <span style="background-color: #fff0f0;">"$x is too big to handle!"</span>
}
<span style="color: #888888;"># echo input</span>
<span style="color: #007020;">Write-Output</span> <span style="color: #996633;">$x</span>
}
<span style="color: #008800; font-weight: bold;">end</span>
{
<span style="color: #007020;">Write-Output</span> <span style="background-color: #fff0f0;">'end block'</span>
}
}
<span style="color: #007020;">Clear-Host</span>
<span style="color: #996633;">$VerbosePreference</span> = <span style="background-color: #fff0f0;">"Continue"</span>
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">"-ErrorAction SilentlyContinue: the Write-Error in the process block is suppressed"</span> `
-ForegroundColor Cyan
<span style="background-color: #ffaaaa; color: red;">@</span>(1,2,3) | myfunc -ErrorAction SilentlyContinue
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">"-ErrorAction Continue: The Write-Error in the process block is displayed,</span>
<span style="background-color: #fff0f0;">but `$x is still echoed"</span> `
-ForegroundColor Cyan
<span style="background-color: #ffaaaa; color: red;">@</span>(1,2,3) | myfunc -ErrorAction <span style="color: #008800; font-weight: bold;">Continue</span>
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">"-ErrorAction Stop: The Write-Error in the process block becomes a terminating error, </span>
<span style="background-color: #fff0f0;">`$x > 2 is NOT echoed"</span> `
-ForegroundColor Cyan
<span style="background-color: #ffaaaa; color: red;">@</span>(1,2,3) | myfunc -ErrorAction Stop
</pre>
</div>
<br />
The output is:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5xi7qxDyNvJATL4wrr92xkaLh6oifUbzxHGiKm6-mwvm_MF5OJG8DBwt9OfLrQ0-Te0yM0e_2EiNMcQVVhbr5285tPFsKXDV6J_Z5pdmdJu1J6R054XgpLtvArNgJVY0sPL59walzZKjl/s1600/output2.PNG" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5xi7qxDyNvJATL4wrr92xkaLh6oifUbzxHGiKm6-mwvm_MF5OJG8DBwt9OfLrQ0-Te0yM0e_2EiNMcQVVhbr5285tPFsKXDV6J_Z5pdmdJu1J6R054XgpLtvArNgJVY0sPL59walzZKjl/s1600/output2.PNG" /></a><br />
<br />
Now we see that something uninteded is happening for both ErrorActions <i>Continue/SilentlyContinue</i>. 3 is echoed still. With <i>Stop</i> the story is as before, Write-Error becomes a terminating error and 3 is not echoed.<br />
<br />
Now we basically just add a return statement:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #888888;"># with input</span>
<span style="color: #008800; font-weight: bold;">function</span> myfunc
{
[<span style="color: #008800; font-weight: bold;">cmdletbinding</span>()]
<span style="color: #008800; font-weight: bold;">param</span>(
[<span style="color: #008800; font-weight: bold;">Parameter</span>(
<span style="color: #008800; font-weight: bold;">Position</span>=0,
<span style="color: #008800; font-weight: bold;">Mandatory</span>=<span style="color: #996633;">$true</span>,
<span style="color: #008800; font-weight: bold;">ValueFromPipeline</span>=<span style="color: #996633;">$true</span>,
<span style="color: #008800; font-weight: bold;">ValueFromPipelineByPropertyName</span>=<span style="color: #996633;">$true</span>)
]
<span style="color: #996633;">$x</span>
)
<span style="color: #008800; font-weight: bold;">begin</span>
{
<span style="color: #888888;"># No errors in the begin block this time</span>
<span style="color: #007020;">Write-Output</span> <span style="background-color: #fff0f0;">'begin block'</span>
}
<span style="color: #008800; font-weight: bold;">process</span>
{
<span style="color: #008800; font-weight: bold;">if</span>(<span style="color: #996633;">$x</span> <span style="color: #333333;">-gt</span> 2)
{
<span style="color: #007020;">Write-Error</span> <span style="background-color: #fff0f0;">"$x is too big to handle!"</span>
<span style="color: #888888;"># continue on the pipeline. NOTE: continue does NOT continue but rather shuts down the pipeline completely</span>
<span style="color: #008800; font-weight: bold;">return</span>
}
<span style="color: #888888;"># echo input</span>
<span style="color: #007020;">Write-Output</span> <span style="color: #996633;">$x</span>
}
<span style="color: #008800; font-weight: bold;">end</span>
{
<span style="color: #007020;">Write-Output</span> <span style="background-color: #fff0f0;">'end block'</span>
}
}
<span style="color: #007020;">Clear-Host</span>
<span style="color: #996633;">$VerbosePreference</span> = <span style="background-color: #fff0f0;">"Continue"</span>
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">"-ErrorAction SilentlyContinue: the Write-Error in the process block is suppressed</span>
<span style="background-color: #fff0f0;">(for both 3 and 4), and `$x > 2 is not echoed"</span> `
-ForegroundColor Cyan
<span style="background-color: #ffaaaa; color: red;">@</span>(1,2,3,4) | myfunc -ErrorAction SilentlyContinue
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">"-ErrorAction Continue: The Write-Error in the process block is displayed</span>
<span style="background-color: #fff0f0;">(twice, for both 3 and 4). `$x > 2 is not echoed"</span> `
-ForegroundColor Cyan
<span style="background-color: #ffaaaa; color: red;">@</span>(1,2,3,4) | myfunc -ErrorAction <span style="color: #008800; font-weight: bold;">Continue</span>
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">'The script keeps running'</span> `
-ForegroundColor Cyan
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">"-ErrorAction Stop: The Write-Error in the process block becomes a terminating error,</span>
<span style="background-color: #fff0f0;">'3' is NOT echoed. return is not exectuted hence the pipeline stops"</span> `
-ForegroundColor Cyan
<span style="background-color: #ffaaaa; color: red;">@</span>(1,2,3,4) | myfunc -ErrorAction Stop
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">'this is not reached'</span> `
-ForegroundColor Cyan
</pre>
</div>
<br />
The output is:<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUQC7JmnQnYuG3g_vZyHWsi7X-3479rzUuyhd4XGbXPUD6qRSKl5mmK1RYyjtvjf-NSdwvQPGPih3SHUJFp5bgfXDoY-aih5361uhrV5E7PjDahjq_nlYv9bXWICh3DI5Z6q2suBeCaX7x/s1600/output3.PNG" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUQC7JmnQnYuG3g_vZyHWsi7X-3479rzUuyhd4XGbXPUD6qRSKl5mmK1RYyjtvjf-NSdwvQPGPih3SHUJFp5bgfXDoY-aih5361uhrV5E7PjDahjq_nlYv9bXWICh3DI5Z6q2suBeCaX7x/s1600/output3.PNG" /></a><br />
<br />
We see that in all 3 cases that x greater than 2 is not echoed. Now ErrorAction Stop makes sense. We indicate that if the function fails for any input we do not wish to continue the script.<br />
<br />
And we can add some error handling:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #888888;"># with input</span>
<span style="color: #008800; font-weight: bold;">function</span> myfunc
{
[<span style="color: #008800; font-weight: bold;">cmdletbinding</span>()]
<span style="color: #008800; font-weight: bold;">param</span>(
[<span style="color: #008800; font-weight: bold;">Parameter</span>(
<span style="color: #008800; font-weight: bold;">Position</span>=0,
<span style="color: #008800; font-weight: bold;">Mandatory</span>=<span style="color: #996633;">$true</span>,
<span style="color: #008800; font-weight: bold;">ValueFromPipeline</span>=<span style="color: #996633;">$true</span>,
<span style="color: #008800; font-weight: bold;">ValueFromPipelineByPropertyName</span>=<span style="color: #996633;">$true</span>)
]
<span style="color: #996633;">$x</span>
)
<span style="color: #008800; font-weight: bold;">begin</span>
{
<span style="color: #888888;"># No errors in the begin block this time</span>
<span style="color: #007020;">Write-Output</span> <span style="background-color: #fff0f0;">'begin block'</span>
}
<span style="color: #008800; font-weight: bold;">process</span>
{
<span style="color: #008800; font-weight: bold;">try</span>
{
<span style="color: #008800; font-weight: bold;">if</span>(<span style="color: #996633;">$x</span> <span style="color: #333333;">-gt</span> 2)
{
<span style="color: #888888;"># this puts the error into the $Error variable</span>
throw <span style="background-color: #fff0f0;">"$x is too big to handle!"</span>
}
<span style="color: #888888;"># echo input</span>
<span style="color: #007020;">Write-Output</span> <span style="color: #996633;">$x</span>
}
<span style="color: #008800; font-weight: bold;">catch</span> <span style="color: #003366; font-weight: bold;">[System.Exception]</span>
{
<span style="color: #007020;">Write-Error</span> <span style="color: #996633;">$Error</span>[0].Exception
<span style="color: #007020;">Write-Verbose</span> <span style="background-color: #fff0f0;">"continue on the pipeline '$x'"</span>
<span style="color: #008800; font-weight: bold;">return</span>
}
<span style="color: #007020;">Write-Verbose</span> <span style="background-color: #fff0f0;">"continue on the pipeline '$x'"</span>
}
<span style="color: #008800; font-weight: bold;">end</span>
{
<span style="color: #007020;">Write-Output</span> <span style="background-color: #fff0f0;">'end block'</span>
}
}
<span style="color: #007020;">Clear-Host</span>
<span style="color: #996633;">$VerbosePreference</span> = <span style="background-color: #fff0f0;">"Continue"</span>
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">"-ErrorAction SilentlyContinue: the Write-Error in the process block is suppressed </span>
<span style="background-color: #fff0f0;">(for both 3 and 4), and `$x is not echoed"</span> `
-ForegroundColor Cyan
<span style="background-color: #ffaaaa; color: red;">@</span>(1,2,3,4) | myfunc -ErrorAction SilentlyContinue
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">"-ErrorAction Continue: The Write-Error in the process block is displayed </span>
<span style="background-color: #fff0f0;">(twice, for both 3 and 4).`$x is not echoed"</span> `
-ForegroundColor Cyan
<span style="background-color: #ffaaaa; color: red;">@</span>(1,2,3,4) | myfunc -ErrorAction <span style="color: #008800; font-weight: bold;">Continue</span>
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">'The script keeps running'</span> `
-ForegroundColor Cyan
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">"-ErrorAction Stop: The Write-Error in the process block becomes a terminating error, </span>
<span style="background-color: #fff0f0;">'3' is NOT echoed. return is not exectuted and the pipeline stops"</span> `
-ForegroundColor Cyan
<span style="background-color: #ffaaaa; color: red;">@</span>(1,2,3,4) | myfunc -ErrorAction Stop
<span style="color: #007020;">Write-Host</span> <span style="background-color: #fff0f0;">'this is not reached'</span> `
-ForegroundColor Cyan
</pre>
</div>
<br />
The output is:<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTzqdtr3V8M9zxYDZVZ0JjBDYmLVFis_0f1ohtKUVHk8yyOtzYEdWFV0LtmibZloMcHzUELP1ya9t1xhVQjt5NOp69NwkNsYKPUqjj-VOKMIoC2kf-iarTp150hKn20ZC_dUGBaSTvHe_H/s1600/output4.PNG" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTzqdtr3V8M9zxYDZVZ0JjBDYmLVFis_0f1ohtKUVHk8yyOtzYEdWFV0LtmibZloMcHzUELP1ya9t1xhVQjt5NOp69NwkNsYKPUqjj-VOKMIoC2kf-iarTp150hKn20ZC_dUGBaSTvHe_H/s1600/output4.PNG" /></a><br />
<br />
I hope this helps understanding how some of the <i>begin..process..end</i> function works with regards to errors and error handling. I know I will be returning to this from time and again :D<br />
<br />
<br />Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-59310336577991718022016-10-04T12:00:00.000+02:002016-10-04T12:00:11.037+02:00ARM Template Tip: NamesNaming resources in ARM templates can be quite lengthy. This is an example of naming a network interface:<br />
<br />
<div style="background-attachment: initial; background-clip: initial; background-image: initial; background-origin: initial; background-position: initial; background-repeat: initial; background-size: initial; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0px;"><span style="background-color: #fff0f0;">"name"</span><span style="background-color: #ffaaaa; color: red;">:</span><span style="background-color: white;"> </span><span style="background-color: #fff0f0;">"[concat(parameters('vmNamePrefix'), '-', padLeft(copyIndex(1), 2, '0'), variables('nicPostfix'), '-', padLeft(copyIndex(1), 2, '0'))]"</span><span style="background-color: #ffaaaa; color: red;">,</span><span style="background-color: white;">
</span></pre>
</div>
<br />
And we have to reference this at a later point for the virtual machine resource. If we then change the name, we will have to remember to change this reference also.<br />
<br />
What we can do is to define the name in the variables section like this:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"> <span style="background-color: #fff0f0;">"nic"</span><span style="background-color: #ffaaaa; color: red;">:</span> {
<span style="color: #007700;">"name"</span>: <span style="background-color: #fff0f0;">"[concat(parameters('vmNamePrefix'), '-', padLeft('{0}', 4, '0'), variables('nicPostfix'), '-', padLeft('{0}', 4, '0'))]"</span>
}
</pre>
</div>
<br />
(I like to group variables). And then reference this variable in the resource like:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="background-color: #fff0f0;">"name"</span><span style="background-color: #ffaaaa; color: red;">:</span> <span style="background-color: #fff0f0;">"[replace(variables('nic').name, '{0}', string(copyIndex(1)))]"</span><span style="background-color: #ffaaaa; color: red;">,</span>
</pre>
</div>
<br />
What I have done is to make <b>{0}</b> a placeholder and then replace it with the result from <b>copyIndex()</b>. We now have a central location to change the name if needed with no need to update any resources.<br />
<br />
Would be cool if we had a template function for formatting:<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="background-color: #fff0f0;">"name"</span><span style="background-color: #ffaaaa; color: red;">:</span> <span style="background-color: #fff0f0;">"[format(variables('nic').name, copyIndex(1), '-nic')]"</span>
</pre>
</div>
<br />
It would take the string as input and then a variable number of additional arguments. Ex.<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="background-color: #fff0f0;">"nic"</span><span style="background-color: #ffaaaa; color: red;">:</span> {
<span style="color: #007700;">"name"</span>: <span style="background-color: #fff0f0;">"concat(parameters('vmNamePrefix'), '0{0}', '{1}')]"</span>
}
</pre>
</div>
<br />
would become(<b>{0}</b> is replaced with the result from <b>copyIndex(1)</b> and <b>{1}</b> replaced with <b>-nic</b>):<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="background-color: #fff0f0;">"VM01-nic"</span>
</pre>
</div>
<br />
And it could be made more advanced, perhaps leaning on the good ol' <i>sprintf.</i>Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-47933324587666632402016-09-29T12:26:00.002+02:002016-09-29T12:26:30.774+02:00Logging webhooks using Azure Functions and OMS Log AnalyticsWe recently discussed webhooks internally at <a href="http://www.lumagate.com/" target="_blank">work</a> and the question popped on how to maintain and log the activity. Webhooks normally have a limited timespan (could be years though), and they should generally be kept a secret even if they are accompanied by a token that authorizes the caller.<br />
<br />
What better way to log the webhook calls than using <a href="https://www.microsoft.com/en/server-cloud/solutions/log-analytics.aspx" target="_blank">OMS Log Analytics</a>? Once the data is logged there you have a plethora of options on what to do with it. Go ask my colleague <a href="https://cloudadministrator.wordpress.com/" target="_blank">Stanislav</a>.<br />
<br />
I also wanted to try out the fairly new <a href="https://azure.microsoft.com/en-us/documentation/articles/functions-overview/" target="_blank">Azure Functions</a>, which acts as a relay to <a href="https://azure.microsoft.com/en-us/documentation/articles/log-analytics-data-collector-api/" target="_blank">Log Analytics Data Collector API</a>. The webhook itself comes from an Azure Automation runbook.<br />
<br />
I documented the entire solution on Github, and you can find the repository <a href="https://github.com/spaelling/azure-functions-webhook-logger" target="_blank">here</a> - it takes you from A to Z on how to setup the various moving parts in Azure. I hope you can find some inspiration on how to manage your webhooks.Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-38026949366012211092016-09-26T13:21:00.000+02:002016-09-26T13:21:36.589+02:00Hello Azure Functions - Integrating with GithubI had a hard time finding out how to integrate a Github Repository into Azure functions, or rather what files and the structure to put in the repository so that Azure Functions would pick them up. A very basic setup follows.<br />
<br />
This assumes an understanding of Github and Azure Functions. There are plenty of resources out there explaining that better than I can.<br />
<h3>
Github</h3>
<div>
Create a fresh repository and create a file, <i>host.json</i>, in the root:</div>
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">{
<span style="color: #007700;">"functions"</span> : [
<span style="background-color: #fff0f0;">"HelloAzureFunctions"</span>
],
<span style="color: #007700;">"id"</span>: <span style="background-color: #fff0f0;">"ed5d78e575e14f0481c899532d41f5c0"</span>
}
</pre>
</div>
<div>
<br />
Now create a folder called <span style="background-color: #fff0f0;"><i>HelloAzureFunctions</i>. Inside that create a file, <i>function.json</i>:</span><br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">{
<span style="color: #007700;">"bindings"</span>: [
{
<span style="color: #007700;">"type"</span>: <span style="background-color: #fff0f0;">"httpTrigger"</span>,
<span style="color: #007700;">"name"</span>: <span style="background-color: #fff0f0;">"req"</span>,
<span style="color: #007700;">"direction"</span>: <span style="background-color: #fff0f0;">"in"</span>,
<span style="color: #007700;">"methods"</span>: [ <span style="background-color: #fff0f0;">"get"</span> ]
},
{
<span style="color: #007700;">"type"</span>: <span style="background-color: #fff0f0;">"http"</span>,
<span style="color: #007700;">"name"</span>: <span style="background-color: #fff0f0;">"res"</span>,
<span style="color: #007700;">"direction"</span>: <span style="background-color: #fff0f0;">"out"</span>
}
]
}
</pre>
</div>
<span style="background-color: #fff0f0;"><br /></span>
<span style="background-color: #fff0f0;">And in this case we will use PowerShell; we need a file called <i>run.ps1:</i></span><br />
<!-- HTML generated using hilite.me -->
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #996633;">$requestBody</span> = <span style="color: #007020;">Get-Content</span> <span style="color: #996633;">$req</span> -Raw | <span style="color: #007020;">ConvertFrom-Json</span>
<span style="color: #996633;">$name</span> = <span style="color: #996633;">$requestBody</span>.name
<span style="color: #008800; font-weight: bold;">if</span> (<span style="color: #996633;">$req_query_name</span>)
{
<span style="color: #996633;">$name</span> = <span style="color: #996633;">$req_query_name</span>
}
<span style="color: #007020;">Out-File</span> -Encoding Ascii -FilePath <span style="color: #996633;">$res</span> -inputObject <span style="background-color: #fff0f0;">"Hello, $name"</span>
</pre>
</div>
<span style="background-color: #fff0f0;">That is it! Commit to Github and go to your Azure Function app and integrate with the repository. The <i>HelloAzureFunctions</i> should appear as a function after a short while.</span><br />
<span style="background-color: #fff0f0;"><br /></span>
<span style="background-color: #fff0f0;">You can fork my repository if you like, <a href="https://github.com/spaelling/hello-azure-functions">https://github.com/spaelling/hello-azure-functions</a>. There is also a a <a href="https://github.com/spaelling/hello-azure-functions/blob/master/test.ps1" target="_blank">PowerShell script there that can be used for testing</a> (you can just paste in the webhook URI in a browser if you rather like that).</span><br />
<span style="background-color: #fff0f0;"><br /></span>
<span style="background-color: #fff0f0;">Also keep your webhooks a secret. In the aforementioned script I show how to get the webhook URI from an Azure Key Vault.</span><br />
<span style="background-color: #fff0f0;"><br /></span>
<span style="background-color: #fff0f0;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9ENt70hPDnyZFTgdERPTGBZyhwqVrNgPtSCBi7EI9yWbbpVZeJHmBa6zZFL1hTEA6F856VpqhJ_fy-vOI84f4nym4OPsflYz86dmGqdwfFMzberKTQAHsYMUrPB3CjXrU2IpD0w43_BhW/s1600/1.PNG" imageanchor="1"><img border="0" height="344" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9ENt70hPDnyZFTgdERPTGBZyhwqVrNgPtSCBi7EI9yWbbpVZeJHmBa6zZFL1hTEA6F856VpqhJ_fy-vOI84f4nym4OPsflYz86dmGqdwfFMzberKTQAHsYMUrPB3CjXrU2IpD0w43_BhW/s640/1.PNG" width="640" /></a></span></div>
Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-81533307019287270712016-09-16T15:03:00.000+02:002016-09-16T15:03:58.544+02:00Analyzing your bank statement using PowerBII wanted to figure out what we were spending our money on, but our bank is lacking behind when it comes to finance insight, so what better way than to use <a href="https://app.powerbi.com/" target="_blank">PowerBI</a>?<br />
<br />
First you need to export your bank statements into CSV. We have multiple accounts, so I just looked into the account that we use for everyday shopping (food, etc.). I had some trouble importing into PowerBI, so I imported the CSV data into Excel where you then have to select (all) the data and make it into a table (ctrl+t) before you can import it into PowerBI.<br />
<br />
I had to sanitize the data; removing transfers from one account to another and purchases that should have been made on another account. If you spot something later simply remove the row in excel and import the file again.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8Ndi4CMxVzEqW0kq-eaYTILey_GFabdHdnbvNUmRrTU6fiRULwC839wH0BoqDlj1L1-K0jvQQYhiD97x0livqIuk0PRmQsKTDX9Qpe_CniB0DvsUAegCmx79A2lLCYDTBVb-NAhb01OC7/s1600/1.png" imageanchor="1"><img border="0" height="528" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8Ndi4CMxVzEqW0kq-eaYTILey_GFabdHdnbvNUmRrTU6fiRULwC839wH0BoqDlj1L1-K0jvQQYhiD97x0livqIuk0PRmQsKTDX9Qpe_CniB0DvsUAegCmx79A2lLCYDTBVb-NAhb01OC7/s640/1.png" width="640" /></a><br />
<br />
You are now ready to create some reports based on the bank statement data. It should look something like this (if there is only a single row in the <i>fields</i> box it means that PowerBI was unable to make sense of the data):<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizUNqaHge1vnTlOMDURD0mJUgTtkZNM2SO8DkKEfIGllGN-_oqVnGJEnjmL9ftf4-3DFikedbWknJSER125zp04m8S367HOHAMR1sMgAUFSCqUBzZ9hucHH7yt4f3Nv1SWr3d5e1hwrLMD/s1600/2.png" imageanchor="1"><img border="0" height="376" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizUNqaHge1vnTlOMDURD0mJUgTtkZNM2SO8DkKEfIGllGN-_oqVnGJEnjmL9ftf4-3DFikedbWknJSER125zp04m8S367HOHAMR1sMgAUFSCqUBzZ9hucHH7yt4f3Nv1SWr3d5e1hwrLMD/s640/2.png" width="640" /></a><br />
<br />
Now check the box next to the Σ and then one of the other options and then click the pie-chart icon. My bank statement comes with category and sub-category for each entry. If you have some sort of categorisation, and checked that, then you will see something like this (without redactions):<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-sujFIadHmld33OIk7RKW4tY95UsWUK9o-bfZBd4ImRbPhv6NWzeM22S-aIybO0gLWh0rWSkGNb65B5OoS6zPQ4ztJ9ZpFUGl7bNEipcpcb6Uy_MsmHZFqWqkblt23BqrESu7fTUqUjtK/s1600/3.png" imageanchor="1"><img border="0" height="612" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-sujFIadHmld33OIk7RKW4tY95UsWUK9o-bfZBd4ImRbPhv6NWzeM22S-aIybO0gLWh0rWSkGNb65B5OoS6zPQ4ztJ9ZpFUGl7bNEipcpcb6Uy_MsmHZFqWqkblt23BqrESu7fTUqUjtK/s640/3.png" width="640" /></a><br />
Wow! Ok, you could do that in Excel also (I would spend hours how to figure this out in Excel though). It simply shows the distribution of purchases into each category. The big one is grocery-shopping, which is the primary purpose for this account.<br />
<br />
Now comes the magic. Deselect the graph and then again click on the Σ, and whatever translates into an entry description and select the table icon. That is more or less just what we have in Excel, right?<br />
<br />
Select one of the categories in the piechart and see what happens.<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjljKZzgaUx18Lv1Y5V58IEFDf13clv5fLXGfAYnsK5Tu4Khxm-mPxasB-v7irCyBEY0273C5xNUxdsuvx6EPpGZ8_XTiMtAPI2Ml8yjjKdF4ZRoWZM78fSS6gBL1ExFwo9u2sJa50diQxW/s1600/4.png" imageanchor="1"><img border="0" height="372" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjljKZzgaUx18Lv1Y5V58IEFDf13clv5fLXGfAYnsK5Tu4Khxm-mPxasB-v7irCyBEY0273C5xNUxdsuvx6EPpGZ8_XTiMtAPI2Ml8yjjKdF4ZRoWZM78fSS6gBL1ExFwo9u2sJa50diQxW/s640/4.png" width="640" /></a><br />
<br />
It now shows only the entries (summary of the amount) in the table that are related to the category that you selected. This is just the tip of the iceberg. PowerBI can do much more than that!<br />
<br />
Finally you can figure out what your wife is spending all your money on ;)Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com1tag:blogger.com,1999:blog-2214780243815864156.post-85377563531745823942016-09-16T15:01:00.001+02:002016-09-16T15:01:34.836+02:00How Awesome is Docker?Fully configured Ubuntu server up and running in minutes? On Windows? Impossible you say? It is not!<br />
<br />
Start by installing <a href="https://www.docker.com/" target="_blank">Docker</a>. We will try to run the following Python code in the Docker container.<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;"><span style="color: #008800; font-weight: bold;">try</span>:
<span style="color: #008800; font-weight: bold;">from</span> <span style="color: #0e84b5; font-weight: bold;">slackclient</span> <span style="color: #008800; font-weight: bold;">import</span> SlackClient
<span style="color: #888888;">#import os # we need this for the next line</span>
<span style="color: #888888;"># print the environment variable we exported in Dockerfile</span>
<span style="color: #008800; font-weight: bold;">print</span>(os<span style="color: #333333;">.</span>environ<span style="color: #333333;">.</span>get(<span style="background-color: #fff0f0;">'SOME_TOKEN'</span>))
<span style="color: #008800; font-weight: bold;">except</span> <span style="color: red; font-weight: bold;">Exception</span> <span style="color: #008800; font-weight: bold;">as</span> e:
<span style="color: #008800; font-weight: bold;">print</span>(<span style="background-color: #fff0f0;">"Error when importing 'SlackClient'"</span>)
<span style="color: #008800; font-weight: bold;">print</span>(<span style="color: #007020;">repr</span>(e))
<span style="color: #008800; font-weight: bold;">else</span>:
<span style="color: #008800; font-weight: bold;">print</span>(<span style="background-color: #fff0f0;">"Succes!!!'"</span>)
<span style="color: #008800; font-weight: bold;">finally</span>:
<span style="color: #008800; font-weight: bold;">pass</span>
</pre>
</div>
<br />
Copy this snippet to a file and name it <i>somecode.py</i>. Create a file called <i>Dockerfile</i> and paste the following into it.<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">FROM ubuntu:latest
<span style="color: #888888;"># update apt-get then install python3.5 and pip3</span>
RUN apt-get -y update <span style="color: #333333;">&&</span> apt-get install -y python3.5 <span style="color: #333333;">&&</span> apt-get install -y python3-pip
<span style="color: #888888;"># update pip3</span>
RUN pip3 install --upgrade pip
<span style="color: #888888;"># install python modulesslackclient</span>
RUN pip3 install <span style="color: #996633;">slackclient</span><span style="color: #333333;">==</span>1.0.0
<span style="color: #888888;"># copy source files</span>
COPY somecode.py /src/somecode.py
<span style="color: #888888;"># export some tokens</span>
ENV <span style="color: #996633;">SOME_TOKEN</span><span style="color: #333333;">=</span><span style="background-color: #fff0f0;">'this is a token'</span>
<span style="color: #888888;"># run the bot</span>
CMD <span style="color: #333333;">[</span><span style="background-color: #fff0f0;">"python3.5"</span>, <span style="background-color: #fff0f0;">"/src/somecode.py"</span><span style="color: #333333;">]</span>
</pre>
</div>
<br />
Then run these few lines of PowerShell.<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<pre style="line-height: 125%; margin: 0;">cd <span style="color: #996633;">$PSScriptRoot</span>
<span style="color: #888888;"># build the image (based on 'Dockerfile' in this folder) - ignore the security warning</span>
docker build -t codebeaver/dockerisawesome --force-rm .
<span style="color: #888888;"># run a container using the image we just created, --rm means we remove the container after it exists</span>
docker run --rm codebeaver/dockerisawesome
</pre>
</div>
<br />
It may take some time to download the Ubuntu base image (ca. 500mb).<br />
<br />
I intentionally put in an error. We did not import the <i>os</i> library in the Python code. Uncomment <i>import os</i> and run the PowerShell code again. That was it. You can easily install additional Python libraries by editing the Dockerfile.<br />
<br />
You can run the container in Azure and there are various services for running Docker containers for you.Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-75876976456312431452016-08-28T22:12:00.001+02:002016-08-28T22:12:25.946+02:00How to Slack the Most in LifeWhat have you been doing this weekend? I have been slacking (don't tell my boss). Most people in IT have heard of <a href="https://slack.com/" target="_blank">Slack</a> by now. If not, you should check it out.'<br />
<br />
Slack is normally used for team collaboration, but I wanted to see if I could "<i>be less busy</i>" in my privatelife too. Just too many apps I cycle between to check if something is new; Feedly, YouTube, Facebook, LinkedIn, Huffingtong Post, Twitter to name a few.<br />
<br />
For a private user you will most likely stay in the free tier. This means that you are limited to 10 apps/integrations. But as you will learn, you can get a long way with just a few apps.<br />
<br />
So what I have done so far is create some channels.<br />
<br />
<ul>
<li>general (default, can't leave it)</li>
<li>message</li>
<li>news</li>
<li>reminders</li>
<li>social</li>
<li>todo</li>
<li>youtube</li>
</ul>
<div>
The idea is to put stuff like email and other direct messages into the message channel. News is for, well news. Reminders is linked to my gmail calendar where I keep my private appointments and appointments for work that goes outside normal work hours. Social is for stuff like Facebook and Twitter. Todo is meant for keeping a todo-list, and finally the youtube channel is for my YouTube channel subscriptions.</div>
<div>
<br /></div>
<div>
Each channel will highligh if there is something new in them, and since I am the sole member on the team, I know it is an app or bot that has updated the channel.</div>
<div>
<br /></div>
<div>
So far I have been able to link my Gmail to the <i>message </i>channel using <a href="http://ifttt.com/">ifttt.com</a>. This is one way, meaning I get notified of new email and can see the body of the email. I also would like my iMessages in there, but I have yet to find an integration that can do this. There are other integrations that I have not tried yet (I just dont use alot of messaging apps):</div>
<div>
<ul>
<li><a href="https://www.skype.com/en/features/slack/" target="_blank">Skype</a></li>
<li>Facebook Messenger (LinkedIn messaging, etc.)</li>
</ul>
<div>
Facebook Messenger is not something I use alot, but sometimes. There is an integration through <a href="https://smooch.io/integrations/messenger/" target="_blank">Smooch</a>, but that seems to be limited to Facebook Pages (makes sense in a business perspective). <a href="http://zapier.com/" target="_blank">Zapier</a> (has loads of advanced Slack integrations) and looks also to target Pages.</div>
</div>
<div>
It basically allows you to get messages send to a Facebook Page into Slack. And reply from Slack also.</div>
<div>
<br /></div>
<div>
For the <i>news</i> channel I have lots of RSS feeds. That is simply using an RSS app (there are 3 or so of those) and then adding configurations for each feed.</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigUanAo3BM6Th7ssoZWhxAGF88QLk1KW_4pWfX_PBrpbcQasr63pnyrKMZ8XlqFn1aZV6GCGEp6chRybhhYO6IbeKiMt_qrfwqrGV93Lk4qxmCLYH8kUmRTyfJ9Z93hrpSpWbkBVITKq9G/s1600/news.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="332" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigUanAo3BM6Th7ssoZWhxAGF88QLk1KW_4pWfX_PBrpbcQasr63pnyrKMZ8XlqFn1aZV6GCGEp6chRybhhYO6IbeKiMt_qrfwqrGV93Lk4qxmCLYH8kUmRTyfJ9Z93hrpSpWbkBVITKq9G/s640/news.PNG" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Some science news from Huffington Post</td></tr>
</tbody></table>
<div>
Now a little trick that I learned; There was no RSS feed for <i>I fucking love science</i>, so I resorted to <a href="https://twitrss.me/" target="_blank">https://twitrss.me/</a> as all (?) new articles are tweeted from the <i>iflscience</i> user. RSS feeds can sometimes be the answer when wanting to integrate something with Slack.</div>
<div>
I have also connected <a href="http://www.slalert.com/" target="_blank">slalert</a> to the news channel. That is a more generic way to get news. I have yet to get something from Slalert, but then I have not put alot of keywords in it yet. It simply connects to a Slack webhook and whenever it finds something on the keywords you have given Slalert it will post it to the channel the webook is connected to. </div>
<div>
<br /></div>
<div>
The <i>reminders</i> channel is currently linked to my Gmail calendar (there is an app called Google Calendar - doesn't get much easier than that). You can choose which of your calendars to sync to Slack and then how far in advance to remind you of an event.</div>
<div>
You can also just tell the slackbot to remind you or someone else (or even a channel) of something.</div>
<div>
<table cellpadding="0" cellspacing="0" class="tr-caption-container" style="float: left; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg44zKhaAApVgzvGKNQJeEf07H_xJ79K4I_DxG4fJPYfkC3o45cdN2V-sLjAPaJyO6cUD0OBBlm8BgW5VvzwA7wHtDLxRTpvwXI3GiqAMhwjio4kbYzpyEfzChA6K8YOtEIfREkWKkNLJud/s1600/reminder.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="56" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg44zKhaAApVgzvGKNQJeEf07H_xJ79K4I_DxG4fJPYfkC3o45cdN2V-sLjAPaJyO6cUD0OBBlm8BgW5VvzwA7wHtDLxRTpvwXI3GiqAMhwjio4kbYzpyEfzChA6K8YOtEIfREkWKkNLJud/s640/reminder.PNG" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Gotta keep hydrated</td></tr>
</tbody></table>
</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjO1JaQaua9EzZBBAXsAItukOGBtXFfct8tMVTk0PnTpnPRnctCmSIUilOF-bPNU5Fg19GirpV9i1G8lRKuQA2N9WLYBm65HvDZ0e9Ez6o2HLPqASdaIyJcoFQbySdCqK1h0rG1itv7sFZw/s1600/reminder2.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="64" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjO1JaQaua9EzZBBAXsAItukOGBtXFfct8tMVTk0PnTpnPRnctCmSIUilOF-bPNU5Fg19GirpV9i1G8lRKuQA2N9WLYBm65HvDZ0e9Ez6o2HLPqASdaIyJcoFQbySdCqK1h0rG1itv7sFZw/s640/reminder2.PNG" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Resisting temptation to remind me again in 15 minutes :D</td></tr>
</tbody></table>
<div>
The social channel was supposed to cover me in Facebook, Twitter, and other social networks. It would be nice to be able to get the Facebook <i>news feed</i> in Slack, but Facebook closed down that part of the API in 2015. I guess it allowed people to get the essence of Facebook on a different platform than Facebook, removing their main source of income (advertisements).</div>
<div>
I guess the story will be much the same, now or in the future, for other social networks, so I will not even try.</div>
<div>
<br /></div>
<div>
The <i>todo</i> channel was to function as a todo-list. I can use something like Slack <i>posts</i> (a file of sorts) and star them. Or pin them to the todo channel. It is possible to pin all kinds of items.</div>
<div>
<br /></div>
<div>
The <i>youtube</i> channel is for my YouTube subscriptions. I can get the feed of each subscribed YouTube channel and add them to my RSS app. I could not find a way to programatically add all of the subscribed channels, so abit of manual work was needed. And when I subscribe to a new channel I will have to add it here. I guess it is a matter of time before someone writes a YouTube app for Slack.</div>
<div>
<br /></div>
<div>
So have I really <i>slackified</i> my life? Not quite. But it is a step in the right direction. Slack is still pretty new, and as apps are developed specifically for some of the things I have had to hack abit to get working, things will get better and more feature-rich.</div>
<div>
The focus is obviously on features that can be used by actual teams (and not the solo-slacker). And still, limited by what APIs are offered by the provider in the other end. They will not be willing to just fully let go and have users leave their platform entirely.</div>
Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-29724138232913696052016-06-09T15:49:00.001+02:002016-06-09T15:49:36.009+02:00Filtering on NULL in SMLetsNo time since I wrote about Service Manager. Something that comes back to haunt me from time to time is filtering on NULL. I always forget how, so now I will document it, once and for all!<br />
<br />
A script speaks a thousand words:<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<table><tbody>
<tr><td><pre style="line-height: 125%; margin: 0;">1
2
3
4
5
6
7</pre>
</td><td><pre style="line-height: 125%; margin: 0;"><span style="color: #996633;">$IRClass</span> = <span style="color: #007020;">Get-SCSMClass</span> system.workitem.incident$
<span style="color: #888888;"># get all IRs where the classification is not set</span>
<span style="color: #007020;">Get-SCSMObject</span> -Class <span style="color: #996633;">$IRClass</span> -Filter <span style="background-color: #fff0f0;">"Classification -ISNULL"</span>
<span style="color: #888888;"># if we need to filter on a property from a class extension, specify that exact class</span>
<span style="color: #996633;">$MyClassExt</span> = <span style="color: #007020;">Get-SCSMClass</span> incident.extension$
<span style="color: #007020;">Get-SCSMObject</span> -Class <span style="color: #996633;">$MClassExt</span> -Filter <span style="background-color: #fff0f0;">"CustomerProperty -ISNULL"</span>
</pre>
</td></tr>
</tbody></table>
</div>
Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-34880184222148676972016-05-24T15:02:00.003+02:002016-05-24T15:02:50.656+02:00Script for deploying Nano Server (TP5)There are plenty of scripts around that helps deploying Nano server. But there seems to be issues between the various TPs, I had trouble with a script that worked for TP4 but not at all for TP5.<br />
<br />
So I ended up creating my own. It should just run in one go, but I suggest you take a few lines at at time to sort out any issues.<br />
<br />
The script as follows, and found on <a href="https://gallery.technet.microsoft.com/Scripted-Nano-server-439ec923" target="_blank">Technet gallery</a>.<br />
<br />
<!-- HTML generated using hilite.me --><br />
<div style="background: #ffffff; border-width: 0.1em 0.1em 0.1em 0.8em; border: solid gray; overflow: auto; padding: 0.2em 0.6em; width: auto;">
<table><tbody>
<tr><td><pre style="line-height: 125%; margin: 0;"> 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98</pre>
</td><td><pre style="line-height: 125%; margin: 0;"><span style="color: #888888;"># note this is written for Server 2016 TP5 - it probably doesn't work on other TPs</span>
<span style="color: #888888;"># create this folder and copy the NanoServerImageGenerator from the 2016 media</span>
cd D<span style="background-color: #ffaaaa; color: red;">:</span>\NanoServer
<span style="color: #007020;">Import-Module</span> .\NanoServerImageGenerator\NanoServerImageGenerator.psm1
<span style="color: #996633;">$BasePath</span> = <span style="background-color: #fff0f0;">"D:\NanoServer"</span>
<span style="color: #996633;">$TargetPath</span> = <span style="background-color: #fff0f0;">"$BasePath\Nano01.vhd"</span>
<span style="color: #996633;">$ComputerName</span> = <span style="background-color: #fff0f0;">"Nano01"</span>
<span style="color: #996633;">$Password</span> = <span style="color: #007020;">ConvertTo-SecureString</span> -AsPlainText -String <span style="background-color: #fff0f0;">"Password"</span> -Force
<span style="color: #996633;">$IPAddress</span> = <span style="background-color: #fff0f0;">"192.168.0.42"</span>
<span style="color: #996633;">$GatewayAddress</span> = <span style="background-color: #fff0f0;">"192.168.0.1"</span>
<span style="color: #996633;">$DNSAddresses</span> = (<span style="background-color: #fff0f0;">'192.168.0.21'</span>,<span style="background-color: #fff0f0;">'8.8.8.8'</span>)
<span style="color: #996633;">$Ipv4SubnetMask</span> = <span style="background-color: #fff0f0;">"255.255.255.0"</span>
<span style="color: #996633;">$Domain</span> = <span style="background-color: #fff0f0;">'my.domain'</span>
<span style="color: #996633;">$Parameters</span> = <span style="background-color: #ffaaaa; color: red;">@</span>{
DeploymentType = <span style="background-color: #fff0f0;">'Guest'</span>
Edition = <span style="background-color: #fff0f0;">'Datacenter'</span>
MediaPath = <span style="background-color: #fff0f0;">"E:\"</span>
BasePath = <span style="color: #996633;">$BasePath</span>
TargetPath = <span style="color: #996633;">$TargetPath</span>
ComputerName = <span style="color: #996633;">$ComputerName</span>
AdministratorPassword = <span style="color: #996633;">$Password</span>
Ipv4Address = <span style="color: #996633;">$IPAddress</span>
Ipv4SubnetMask = <span style="color: #996633;">$Ipv4SubnetMask</span>
Ipv4Gateway = <span style="color: #996633;">$GatewayAddress</span>
Ipv4Dns = <span style="color: #996633;">$DNSAddresses</span>
InterfaceNameOrIndex = <span style="background-color: #fff0f0;">"Ethernet"</span>
}
<span style="color: #007020;">New-NanoServerImage</span> <span style="color: #996633;">@Parameters</span> -ErrorAction Stop
<span style="color: #888888;"># credentials for the nano server</span>
<span style="color: #996633;">$User</span> = <span style="background-color: #fff0f0;">"$IPAddress\Administrator"</span>
<span style="color: #996633;">$Credential</span> = <span style="color: #007020;">New-Object</span> -TypeName System.Management.Automation.PSCredential -ArgumentList <span style="color: #996633;">$User</span>, <span style="color: #996633;">$Password</span>
<span style="color: #888888;"># add it to trusted hosts</span>
<span style="color: #007020;">Set-Item</span> WSMan<span style="background-color: #ffaaaa; color: red;">:</span>\localhost\Client\TrustedHosts -Value <span style="color: #996633;">$IPAddress</span> -Force -Concatenate
<span style="color: #888888;"># size of the vhd - WOW!</span>
<span style="color: #003366; font-weight: bold;">[int]</span>((<span style="color: #007020;">Get-ChildItem</span> -Path <span style="color: #996633;">$TargetPath</span>).Length / 1MB)
<span style="color: #888888;"># can only install IIS and SCVMM offline</span>
Find-NanoServerPackage *iis* | Install-NanoServerPackage -Culture <span style="background-color: #fff0f0;">'en-us'</span> -ToVhd <span style="color: #996633;">$TargetPath</span> -Verbose
Find-NanoServerPackage *scvmm* | Install-NanoServerPackage -Culture <span style="background-color: #fff0f0;">'en-us'</span> -ToVhd <span style="color: #996633;">$TargetPath</span> -Verbose
<span style="color: #888888;"># create a new VM</span>
<span style="color: #996633;">$VMName</span> = <span style="background-color: #fff0f0;">"Nano01"</span>
<span style="color: #007020;">New-VM</span> -Name <span style="color: #996633;">$VMName</span> -MemoryStartupBytes 512MB -SwitchName MGMT -VHDPath <span style="color: #996633;">$TargetPath</span> -Generation 1 -Verbose
<span style="color: #888888;"># and start it</span>
<span style="color: #007020;">Start-VM</span> -Name <span style="color: #996633;">$VMName</span>
<span style="color: #888888;"># we wait - first boot can be "slow" :D</span>
<span style="color: #007020;">Write-Verbose</span> <span style="background-color: #fff0f0;">"waiting abit for VM to boot for the first time..."</span>
<span style="color: #007020;">Start-Sleep</span> -Seconds 20
<span style="color: #888888;"># need to run this with administrative priviliges in the domain</span>
djoin.exe /provision /domain <span style="color: #996633;">$Domain</span> /machine <span style="color: #996633;">$ComputerName</span> /savefile .\<span style="background-color: #fff0f0;">"$ComputerName.txt"</span>
<span style="color: #888888;"># create session object</span>
<span style="color: #996633;">$Session</span> = <span style="color: #007020;">New-PSSession</span> -ComputerName <span style="color: #996633;">$IPAddress</span> -Credential <span style="color: #996633;">$Credential</span>
<span style="color: #888888;"># copy domain join blob file to nano server</span>
<span style="color: #007020;">Copy-Item</span> -ToSession <span style="color: #996633;">$Session</span> -Path .\<span style="background-color: #fff0f0;">"$ComputerName.txt"</span> -Destination <span style="background-color: #fff0f0;">"c:\"</span>
<span style="color: #888888;"># enter the session</span>
<span style="color: #007020;">Enter-PSSession</span> -Session <span style="color: #996633;">$Session</span>
<span style="color: #888888;"># domain join nano server</span>
djoin /requestodj /loadfile c<span style="background-color: #ffaaaa; color: red;">:</span>\<span style="color: #996633;">$env:COMPUTERNAME</span>.txt /windowspath c<span style="background-color: #ffaaaa; color: red;">:</span>\windows /localos
<span style="color: #888888;"># and do a restart</span>
<span style="color: #007020;">Restart-Computer</span>
<span style="color: #888888;"># wait for restart</span>
<span style="color: #888888;"># need to create a new session after it restarts - and we will use domain credentials</span>
<span style="color: #996633;">$User</span> = <span style="background-color: #fff0f0;">"$Domain\Administrator"</span>
<span style="color: #996633;">$Password</span> = <span style="color: #007020;">ConvertTo-SecureString</span> <span style="background-color: #fff0f0;">"domainadminpassword"</span> -AsPlainText -Force
<span style="color: #996633;">$Credential</span> = <span style="color: #007020;">New-Object</span> -TypeName System.Management.Automation.PSCredential -ArgumentList <span style="color: #996633;">$User</span>, <span style="color: #996633;">$Password</span>
<span style="color: #996633;">$Session</span> = <span style="color: #007020;">New-PSSession</span> -ComputerName <span style="color: #996633;">$IPAddress</span> -Credential <span style="color: #996633;">$Credential</span>
<span style="color: #888888;"># enter the session</span>
<span style="color: #007020;">Enter-PSSession</span> -Session <span style="color: #996633;">$Session</span>
<span style="color: #888888;"># install the nano server package provider</span>
Install-PackageProvider NanoServerPackage
<span style="color: #007020;">Import-PackageProvider</span> NanoServerPackage
Find-NanoServerPackage
</pre>
</td></tr>
</tbody></table>
</div>
Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-43684718733738849212016-05-02T22:41:00.000+02:002016-05-02T22:41:02.589+02:00Automated module installation to Azure Automation<span style="font-family: 'Segoe UI', Verdana, Arial; font-size: 13.008px;">Ever needed to install a bunch of modules to an Azure Automation Account? This script can do it for you. Only thing you need to do is point it at the ARM-template that deploys the module. These templates are easy to obtain by following the description in the script-file. A simple loop can install multiple modules (example on how in the file).</span><br />
<span style="font-family: 'Segoe UI', Verdana, Arial; font-size: 13.008px;"><br /></span>
<span style="font-family: 'Segoe UI', Verdana, Arial; font-size: 13.008px;"><a href="https://gallery.technet.microsoft.com/Automated-module-9806ed11" target="_blank">Get it while it's hot</a>.</span>Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-59912434572287807652016-04-26T14:22:00.000+02:002016-04-26T14:22:46.534+02:00Generate ARM template parameter fileI find it tedious to create parameter files for my ARM templates. With the script I just wrote you don't have to.<br />
<br />
Simply point at the ARM template that you need a parameter file for, and the function will spit out the json that matches. Pipe it to Out-File or "clip" to put it in your clipboard and paste it into Visual Studio or other ARM template editor of choice.<br />
<br />
Go grab it <a href="https://gallery.technet.microsoft.com/Generate-ARM-template-c7e974b3" target="_blank">here</a> - remember to rate if you like it :DAnders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0tag:blogger.com,1999:blog-2214780243815864156.post-31257366748149412672015-10-21T12:25:00.003+02:002015-10-21T12:25:53.099+02:00Pinging with Powershell - Get a list of online serversJust about every IT guy or girl on the planet has used <i>ping</i> countless of times. With Powershell those days are over: Meet <a href="https://technet.microsoft.com/en-us/library/hh849808.aspx" target="_blank">Test-Connection</a>. It works just as you would expect and similar to ping, the advantage being that you (by default) get an object of type "System.Management.ManagementObject#root\cimv2\Win32_PingStatus" back.<br />
<br />
I wont go into details on how to use Test-Connection, there are plenty of ressources doing just that. But I recently discover a very cool way to use Test-Connection against a large number of servers. And as they say, A script is worth a thousand words:<br />
<br />
<pre style="background: #ffffff; color: black;"><span style="color: maroon; font-weight: bold;">function</span> <span style="color: #005fd2;">Check-Online</span>
<span style="color: #808030;">{</span>
<span style="color: maroon; font-weight: bold;">param</span><span style="color: #808030;">(</span>
<span style="color: #797997;">$ComputerName</span>
<span style="color: #808030;">)</span>
<span style="color: maroon; font-weight: bold;">Begin</span>
<span style="color: #808030;">{</span>
<span style="color: dimgrey;"># put live servers into this list</span>
<span style="color: #797997;">$</span><span style="color: #007997;">Script:</span><span style="color: #797997;">serverlist</span> <span style="color: #44aadd;">=</span> <span style="color: #808030;">@(</span><span style="color: #808030;">)</span>
<span style="color: #808030;">}</span>
<span style="color: maroon; font-weight: bold;">Process</span>
<span style="color: #808030;">{</span>
<span style="color: maroon; font-weight: bold;">If</span> <span style="color: #808030;">(</span><span style="color: #666616;">Test-Connection</span> <span style="color: #074726;">-Count</span> <span style="color: #008c00;">1</span> <span style="color: #074726;">-ComputerName</span> <span style="color: #007997;">$_</span> <span style="color: #074726;">-TimeToLive</span> <span style="color: #008c00;">5</span> <span style="color: #074726;">-asJob</span> <span style="color: #bb7977; font-weight: bold;">|</span>
<span style="color: #666616;">Wait-Job</span> <span style="color: #bb7977; font-weight: bold;">|</span>
<span style="color: #666616;">Receive-Job</span> <span style="color: #bb7977; font-weight: bold;">|</span>
<span style="color: #005fd2;">?</span> <span style="color: #808030;">{</span> <span style="color: #007997;">$_</span><span style="color: #44aadd;">.</span><span style="color: #005fd2;">StatusCode</span> <span style="color: #44aadd;">-eq</span> <span style="color: #008c00;">0</span> <span style="color: #808030;">}</span> <span style="color: #808030;">)</span>
<span style="color: #808030;">{</span>
<span style="color: #797997;">$</span><span style="color: #007997;">Script:</span><span style="color: #797997;">serverlist</span> <span style="color: #44aadd;">+=</span> <span style="color: #007997;">$_</span>
<span style="color: #808030;">}</span>
<span style="color: #808030;">}</span>
<span style="color: maroon; font-weight: bold;">End</span>
<span style="color: #808030;">{</span>
<span style="color: maroon; font-weight: bold;">return</span> <span style="color: #797997;">$</span><span style="color: #007997;">Script:</span><span style="color: #797997;">serverlist</span>
<span style="color: #808030;">}</span>
<span style="color: #808030;">}</span>
<span style="color: dimgrey;"># get computer names from hyper-v</span>
<span style="color: #797997;">$ComputerNames</span> <span style="color: #44aadd;">=</span> <span style="color: #005fd2;">Get-VM</span> <span style="color: #bb7977; font-weight: bold;">|</span> <span style="color: #666616;">Select-Object</span> <span style="color: #074726;">-ExpandProperty</span> <span style="color: #0000e6;">Name</span>
<span style="color: dimgrey;"># check which are online</span>
<span style="color: #797997;">$ComputerNames</span> <span style="color: #bb7977; font-weight: bold;">|</span> <span style="color: #005fd2;">Check-Online</span>
</pre>
<br />
Simply feed the Check-Online function a list of computernames (IPs should work just as well) and it will return a list of online servers within seconds.<br />
<br />
I think credit for the original code goes to my <a href="http://www.lumagate.com/" target="_blank">Lumagate</a> colleague <a href="http://www.xipher.dk/WordPress/" target="_blank">Claus Nielsen</a> who just happens to be a Powershell MVP.<br />
<br />
Download script from <a href="https://gallery.technet.microsoft.com/Get-a-list-of-online-ea97e7f7" target="_blank">Technet gallery</a>.Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com1tag:blogger.com,1999:blog-2214780243815864156.post-81821477308147319762015-10-16T11:20:00.000+02:002015-10-16T11:25:42.991+02:00Reattaching Drives on VMs Using Powershell (General access denied on vhdx)I recently had some storage issues in the company lab which meant that after a lengthy CHKDSK that permission on all VHDX files was lost. The solution was luckily simple: Reattach each drive and the permission on the file was restored. Problem was then that there was more than 50 drives all in all. Solution then became, as it often is, do it with Powershell.<br />
<br />
The script as follows (<a href="https://gallery.technet.microsoft.com/Reattaching-Drives-on-VMs-3b038909" target="_blank">Download from Technet</a>), formatted using <a href="https://tohtml.com/powershell/" target="_blank">https://tohtml.com/powershell/</a>:<br />
<br />
<pre style="background: #ffffff; color: black;"><span style="color: #797997;">$States</span> <span style="color: #44aadd;">=</span> <span style="color: #808030;">(</span> <span style="color: #bb7977;">[Microsoft.HyperV.PowerShell.VMState]</span><span style="color: #44aadd;">::</span><span style="color: #005fd2;">Off</span><span style="color: #44aadd;">,</span>
<span style="color: #bb7977;">[Microsoft.HyperV.PowerShell.VMState]</span><span style="color: #44aadd;">::</span><span style="color: #005fd2;">OffCritical</span>
<span style="color: #808030;">)</span>
<span style="color: #797997;">$VMs</span> <span style="color: #44aadd;">=</span> <span style="color: #005fd2;">Get-VM</span> <span style="color: #bb7977; font-weight: bold;">|</span> <span style="color: #005fd2;">?</span> <span style="color: #808030;">{</span><span style="color: #007997;">$_</span><span style="color: #44aadd;">.</span><span style="color: #005fd2;">State</span> <span style="color: #44aadd;">-in</span> <span style="color: #797997;">$States</span><span style="color: #808030;">}</span>
<span style="color: #797997;">$DriveCount</span> <span style="color: #44aadd;">=</span> <span style="color: #808030;">(</span><span style="color: #797997;">$VMs</span> <span style="color: #bb7977; font-weight: bold;">|</span> <span style="color: #005fd2;">Get-VMHardDiskDrive</span><span style="color: #808030;">)</span><span style="color: #44aadd;">.</span><span style="color: #005fd2;">Count</span>
<span style="color: #797997;">$Counter</span> <span style="color: #44aadd;">=</span> <span style="color: #008c00;">0</span>
<span style="color: maroon; font-weight: bold;">foreach</span><span style="color: #808030;">(</span><span style="color: #797997;">$VM</span> <span style="color: maroon; font-weight: bold;">in</span> <span style="color: #797997;">$VMs</span><span style="color: #808030;">)</span>
<span style="color: #808030;">{</span>
<span style="color: #797997;">$Drives</span> <span style="color: #44aadd;">=</span> <span style="color: #797997;">$VM</span> <span style="color: #bb7977; font-weight: bold;">|</span> <span style="color: #005fd2;">Get-VMHardDiskDrive</span>
<span style="color: maroon; font-weight: bold;">foreach</span><span style="color: #808030;">(</span><span style="color: #797997;">$Drive</span> <span style="color: maroon; font-weight: bold;">in</span> <span style="color: #797997;">$Drives</span><span style="color: #808030;">)</span>
<span style="color: #808030;">{</span>
<span style="color: #797997;">$Counter</span> <span style="color: #44aadd;">+=</span> <span style="color: #008c00;">1</span>
<span style="color: dimgrey;"># Some of these values (Path at least) disappear from the $Drive object when we remove it from the machine</span>
<span style="color: #797997;">$ControllerNumber</span> <span style="color: #44aadd;">=</span> <span style="color: #797997;">$Drive</span><span style="color: #44aadd;">.</span><span style="color: #005fd2;">ControllerNumber</span>
<span style="color: #797997;">$ControllerLocation</span> <span style="color: #44aadd;">=</span> <span style="color: #797997;">$Drive</span><span style="color: #44aadd;">.</span><span style="color: #005fd2;">ControllerLocation</span>
<span style="color: #797997;">$Path</span> <span style="color: #44aadd;">=</span> <span style="color: #797997;">$Drive</span><span style="color: #44aadd;">.</span><span style="color: #005fd2;">Path</span>
<span style="color: #797997;">$SupportPersistentReservations</span> <span style="color: #44aadd;">=</span> <span style="color: #797997;">$Drive</span><span style="color: #44aadd;">.</span><span style="color: #005fd2;">SupportPersistentReservations</span>
<span style="color: #797997;">$ControllerType</span> <span style="color: #44aadd;">=</span> <span style="color: #797997;">$Drive</span><span style="color: #44aadd;">.</span><span style="color: #005fd2;">ControllerType</span>
<span style="color: #666616;">Write-Progress</span> <span style="color: #074726;">-Activity</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">Reattaching drives</span><span style="color: maroon;">"</span> <span style="color: #0f69ff;">`</span>
<span style="color: #074726;">-Status</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">Removing </span><span style="color: #797997;">$Path</span><span style="color: #0000e6;"> from </span><span style="color: #808030;">$(</span><span style="color: #797997;">$VM</span><span style="color: #44aadd;">.</span><span style="color: #005fd2;">Name</span><span style="color: #808030;">)</span><span style="color: maroon;">"</span> <span style="color: #0f69ff;">`</span>
<span style="color: #074726;">-PercentComplete</span> <span style="color: #808030;">(</span><span style="color: #008c00;">100</span><span style="color: #44aadd;">*</span><span style="color: #797997;">$Counter</span><span style="color: #44aadd;">/</span><span style="color: #797997;">$DriveCount</span><span style="color: #808030;">)</span>
<span style="color: #005fd2;">Remove-VMHardDiskDrive</span> <span style="color: #074726;">-VMHardDiskDrive</span> <span style="color: #797997;">$Drive</span>
<span style="color: maroon; font-weight: bold;">if</span><span style="color: #808030;">(</span><span style="color: #797997;">$SupportPersistentReservations</span><span style="color: #808030;">)</span>
<span style="color: #808030;">{</span>
<span style="color: dimgrey;"># Shared vhdx</span>
<span style="color: #005fd2;">Add-VMHardDiskDrive</span> <span style="color: #074726;">-VM</span> <span style="color: #797997;">$VM</span> <span style="color: #0f69ff;">`</span>
<span style="color: #074726;">-ControllerNumber</span> <span style="color: #797997;">$ControllerNumber</span> <span style="color: #0f69ff;">`</span>
<span style="color: #074726;">-ControllerLocation</span> <span style="color: #797997;">$ControllerLocation</span> <span style="color: #0f69ff;">`</span>
<span style="color: #074726;">-Path</span> <span style="color: #797997;">$Path</span> <span style="color: #0f69ff;">`</span>
<span style="color: #074726;">-ControllerType</span> <span style="color: #797997;">$ControllerType</span> <span style="color: #0f69ff;">`</span>
<span style="color: #074726;">-SupportPersistentReservations</span>
<span style="color: #808030;">}</span>
<span style="color: maroon; font-weight: bold;">else</span>
<span style="color: #808030;">{</span>
<span style="color: #005fd2;">Add-VMHardDiskDrive</span> <span style="color: #074726;">-VM</span> <span style="color: #797997;">$VM</span> <span style="color: #0f69ff;">`</span>
<span style="color: #074726;">-ControllerNumber</span> <span style="color: #797997;">$ControllerNumber</span> <span style="color: #0f69ff;">`</span>
<span style="color: #074726;">-ControllerLocation</span> <span style="color: #797997;">$ControllerLocation</span> <span style="color: #0f69ff;">`</span>
<span style="color: #074726;">-Path</span> <span style="color: #797997;">$Path</span> <span style="color: #0f69ff;">`</span>
<span style="color: #074726;">-ControllerType</span> <span style="color: #797997;">$ControllerType</span>
<span style="color: #808030;">}</span>
<span style="color: #666616;">Write-Progress</span> <span style="color: #074726;">-Activity</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">Reattaching drives</span><span style="color: maroon;">"</span> <span style="color: #0f69ff;">`</span>
<span style="color: #074726;">-Status</span> <span style="color: maroon;">"</span><span style="color: #0000e6;">Reattached </span><span style="color: #797997;">$Path</span><span style="color: #0000e6;"> to </span><span style="color: #808030;">$(</span><span style="color: #797997;">$VM</span><span style="color: #44aadd;">.</span><span style="color: #005fd2;">Name</span><span style="color: #808030;">)</span><span style="color: maroon;">"</span> <span style="color: #0f69ff;">`</span>
<span style="color: #074726;">-PercentComplete</span> <span style="color: #808030;">(</span><span style="color: #008c00;">100</span><span style="color: #44aadd;">*</span><span style="color: #797997;">$Counter</span><span style="color: #44aadd;">/</span><span style="color: #797997;">$DriveCount</span><span style="color: #808030;">)</span>
<span style="color: #808030;">}</span>
<span style="color: #808030;">}</span>
</pre>
Anders Spællinghttp://www.blogger.com/profile/01490099464550485079noreply@blogger.com0