<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Joe Talks Microsoft</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/joetalksmicrosoft/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/joetalksmicrosoft/feed.xml</link>
    <description>Learn, Grow, Build</description>
    <lastBuildDate>Wed, 17 Apr 2019 03:23:27 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>(SharePoint) Retrieving User Profile Properties</title>
      <link>https://docs.microsoft.com/archive/blogs/joetalksmicrosoft/sharepoint-retrieving-user-profile-properties</link>
      <pubDate>Mon, 23 Jul 2018 09:59:28 GMT</pubDate>
      <dc:creator><![CDATA[JoeMVP]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/joetalksmicrosoft/?p=875</guid>
      <description><![CDATA[This isn't going to tell you or give you code examples.

This post is to give you the one piece of...]]></description>
      <content:encoded><![CDATA[This isn't going to tell you or give you code examples.

This post is to give you the one piece of information that seems to be lacking in most posts on the subject. The following functions all have this issue.
<ul>
 	<li>The <a href="https://msdn.microsoft.com/library/be001abc-a431-8d48-8e6f-161251aa19ac%28Office.15%29.aspx">getMyProperties</a> method and the <a href="https://msdn.microsoft.com/library/097464db-2630-4c7a-fd0c-0f333680b5a4%28Office.15%29.aspx">getPropertiesFor</a> method return a <a href="https://msdn.microsoft.com/library/0274d97f-b697-f436-2aaf-f5bcf9b70df8%28Office.15%29.aspx">PersonProperties</a> object.</li>
 	<li>The <a href="https://msdn.microsoft.com/library/8674e96f-d320-4a50-1580-9a4568842ee5%28Office.15%29.aspx">getUserProfilePropertiesFor</a> method and the <a href="https://msdn.microsoft.com/library/da048bfa-54c6-8216-e8ef-09bd84f68d8d%28Office.15%29.aspx">getUserProfilePropertyFor</a> method return the values of the user profile properties that you specify.</li>
</ul>
&nbsp;

Review: <a href="https://docs.microsoft.com/en-us/sharepoint/dev/general-development/how-to-retrieve-user-profile-properties-by-using-the-javascript-object-model-in">https://docs.microsoft.com/en-us/sharepoint/dev/general-development/how-to-retrieve-user-profile-properties-by-using-the-javascript-object-model-in </a>

There is an unknown requirement not detailed here or elsewhere.

You must have the "My Site Host" setup and you must have the URL to the "My Site Host" setup in UPS (User Profile Service). Else these functions will not work.

Hope this helps someone not waste time.]]></content:encoded>
    </item>
    <item>
      <title>Exchange Web Service learnings</title>
      <link>https://docs.microsoft.com/archive/blogs/joetalksmicrosoft/exchange-web-service-learnings</link>
      <pubDate>Thu, 12 Jul 2018 09:35:20 GMT</pubDate>
      <dc:creator><![CDATA[JoeMVP]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/joetalksmicrosoft/?p=865</guid>
      <description><![CDATA[Just the other day I worked on a requirement to get emails from exchange.

This sounds simple...]]></description>
      <content:encoded><![CDATA[Just the other day I worked on a requirement to get emails from exchange.

This sounds simple right?

Back in the day I would use IMAP to do this, but starting in 2016 that is disabled by default, review; <a href="https://docs.microsoft.com/en-us/Exchange/plan-and-deploy/deployment-ref/network-ports">https://docs.microsoft.com/en-us/Exchange/plan-and-deploy/deployment-ref/network-ports</a>.

Where I work there is a development environment, fun side story is I was told it was running Exchange 2016, turns out not to be true.

Exchange comes with a web service, you can add a reference to that web service to your project, but it can be difficult to work with.

An API was built, mostly this API abstracts the different versions of Exchange web services, as there are small differences. This allow your program to not have to be changed (service reference) for each version of exchange.

You can download the source code here: <a href="https://github.com/OfficeDev/ews-managed-api">https://github.com/OfficeDev/ews-managed-api</a>

<span style="color: #ff0000"><strong>NOTE:</strong> the nugget package is <strong>3 years old</strong>!! So it doesn't contain any of the recent fixes or add-ins, including enum support of Exchange 2016, best to download the code and reference the compiled assembly.</span>

Lets review getting the object:

string username, password, emailaddress, domainname

emailaddress = "myname@mydomain.com"

username = "myUserName"

domainname = "MyDomain"

password  = "1234567"

<span style="color: #ff0000"><strong>NOTE:</strong></span> you do not need to have the user information above, the code will use the signed account that the code runs under.

ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2013);

//Depending on how AD is setup one of the two options will work.

//service.Credentials = new WebCredentials(emailaddress,password);

For my tests, this version worked:

service.Credentials = new WebCredentials(emailaddress,password,domainname);

//Now it's time to auto discover the URL, note if you have the URL you can cut some mill-seconds from the processing.

//Auto discover the URL via email

service.AutodiscoverUrl(emailaddress); //this attempts to find the web service by using the email address to search the domain of the user.

//OR

service.Url = new Uri("https://urltoexchange/ews/exchange.asmx");

If no errors or exceptions you can now go onto the next phase, getting the items, note, there are three ways shown:

//1: Use a filter to search for items that contain a string
<table class="highlight tab-size js-file-line-container">
<tbody>
<tr>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC3"><span class="pl-k">var</span> <span class="pl-smi">items</span> <span class="pl-k">=</span> <span class="pl-smi">service</span>.<span class="pl-en">FindItems</span>(</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L4"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC4"><span class="pl-c">//Find Mails from Inbox of the given Mailbox</span></td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L5"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC5"><span class="pl-k">new</span> <span class="pl-en">FolderId</span>(<span class="pl-smi">WellKnownFolderName</span>.<span class="pl-smi">Inbox</span>, <span class="pl-k">new</span> <span class="pl-en">Mailbox</span>(emailaddress)),</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L6"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC6"><span class="pl-c">//Filter criterion</span></td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L7"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC7"><span class="pl-k">new</span> <span class="pl-k">SearchFilter</span>.<span class="pl-en">SearchFilterCollection</span>(<span class="pl-smi">LogicalOperator</span>.<span class="pl-smi">And</span>, <span class="pl-k">new</span> <span class="pl-en">SearchFilter</span>[] {</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L8"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC8"><span class="pl-k">new</span> <span class="pl-k">SearchFilter</span>.<span class="pl-en">ContainsSubstring</span>(<span class="pl-smi">ItemSchema</span>.<span class="pl-smi">Subject</span>, "string to search for"),</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L9"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC9"><span class="pl-k">new</span> <span class="pl-k">SearchFilter</span>.<span class="pl-en">IsEqualTo</span>(<span class="pl-smi">EmailMessageSchema</span>.<span class="pl-smi">IsRead</span>, <span class="pl-c1">false</span>)</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L10"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC10">}),</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L11"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC11"><span class="pl-c">//View Size as 15</span></td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L12"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC12"><span class="pl-k">new</span> <span class="pl-en">ItemView</span>(<span class="pl-c1">15</span>));</td>
</tr>
</tbody>
</table>
NOTE: the above will show only 15 items, as well as only show unread emails.

//2: Just show items that have been unread.
<table class="highlight tab-size js-file-line-container">
<tbody>
<tr>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC3"><span class="pl-k">var</span> <span class="pl-smi">items</span> <span class="pl-k">=</span> <span class="pl-smi">service.</span><span class="pl-en">FindItems</span>(</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L4"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC4"><span class="pl-c">//Find Mails from Inbox of the given Mailbox</span></td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L5"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC5"><span class="pl-k">new</span> <span class="pl-en">FolderId</span>(<span class="pl-smi">WellKnownFolderName</span>.<span class="pl-smi">Inbox</span>, <span class="pl-k">new</span> <span class="pl-en">Mailbox</span>(emailaddress)),</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L6"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC6"><span class="pl-c">//Filter criterion</span></td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L7"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC7"><span class="pl-k">new</span> <span class="pl-k">SearchFilter</span>.<span class="pl-en">SearchFilterCollection</span>(<span class="pl-smi">LogicalOperator</span>.<span class="pl-smi">And</span>, <span class="pl-k">new</span> <span class="pl-en">SearchFilter</span>[] {</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L8"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC8"></td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L9"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC9"><span class="pl-k">new</span> <span class="pl-k">SearchFilter</span>.<span class="pl-en">IsEqualTo</span>(<span class="pl-smi">EmailMessageSchema</span>.<span class="pl-smi">IsRead</span>, <span class="pl-c1">false</span>)</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L10"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC10">}),</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L11"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC11"><span class="pl-c">//View Size as 15</span></td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L12"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC12"><span class="pl-k">new</span> <span class="pl-en">ItemView</span>(<span class="pl-c1">15</span>));</td>
</tr>
</tbody>
</table>
Note: just like the previous this only shows unread emails and only 15.

//3: Show all emails (up to view size)

var items = service.FindItems(new FolderId(WellKnownFolderName.Inbox, new Mailbox(emailaddress)),new ItemView(15));

This simply gets all the emails up to 15 limit.

Now to process the emails, the above simply got ID's.
<table class="highlight tab-size js-file-line-container">
<tbody>
<tr>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC3"><span class="pl-k">foreach</span> (<span class="pl-en">EmailMessage</span> <span class="pl-smi">msg</span> <span class="pl-k">in</span> <span class="pl-smi">items</span>)</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L4"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC4">{</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L5"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC5"><span class="pl-c">//Retrieve Additional data for Email</span></td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L6"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC6"><span class="pl-en">EmailMessage</span> <span class="pl-smi">message</span> <span class="pl-k">=</span> <span class="pl-smi">EmailMessage</span>.<span class="pl-en">Bind</span>(<span class="pl-smi">service</span>,</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L7"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC7">(<span class="pl-smi">EmailMessage</span>.<span class="pl-en">Bind</span>(<span class="pl-smi">service</span>, <span class="pl-smi">msg</span>.<span class="pl-smi">Id</span>)).<span class="pl-smi">Id</span>,</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L8"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC8"><span class="pl-k">new</span> <span class="pl-en">PropertySet</span>(<span class="pl-smi">BasePropertySet</span>.<span class="pl-smi">FirstClassProperties</span>,</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L9"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC9"><span class="pl-k">new</span> <span class="pl-en">ExtendedPropertyDefinition</span>(<span class="pl-c1">0x1013</span>, <span class="pl-smi">MapiPropertyType</span>.<span class="pl-smi">Binary</span>)));</td>
</tr>
<tr>
<td class="blob-num js-line-number" id="file-gistfile1-cs-L10"></td>
<td class="blob-code blob-code-inner js-file-line" id="file-gistfile1-cs-LC10">Console.Write(message.Subject);

}</td>
</tr>
</tbody>
</table>
This gets the message for each of the Id's retrieved. The message object includes many properties such as body and to/from as well as subject.

The above helps for you to understand the basics, there are many more examples of using this API via: <a href="https://code.msdn.microsoft.com/exchange/Exchange-2013-101-Code-3c38582c">https://code.msdn.microsoft.com/exchange/Exchange-2013-101-Code-3c38582c </a>]]></content:encoded>
    </item>
    <item>
      <title>PowerShell Settings file (import/export)</title>
      <link>https://docs.microsoft.com/archive/blogs/joetalksmicrosoft/powershell-settings-file-importexport</link>
      <pubDate>Wed, 27 Jun 2018 13:25:59 GMT</pubDate>
      <dc:creator><![CDATA[JoeMVP]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/joetalksmicrosoft/?p=855</guid>
      <description><![CDATA[There are hash tables in PowerShell.

The following is a string representation of a hash table,...]]></description>
      <content:encoded><![CDATA[There are hash tables in PowerShell.

The following is a string representation of a hash table, this can be set in a text file to be loaded.

<span style="color: #00ff00"><em>@{</em></span>

<span style="color: #00ff00"><em>Setting1="test1"</em></span>

<span style="color: #00ff00"><em>Setting2="test2"</em></span>

<span style="color: #00ff00"><em>}</em></span>

When loading the file the following is used:

<span style="color: #00ff00"><em>$settings = Get-Content -path "c:\mypath.settings.txt" -Raw | ConvertFrom-Json</em></span>

This will load the object with all the properties of the hash table, then you can use it as follows:

<em><span style="color: #00ff00">$settings.Setting1</span> </em>

Once you set/update the properties you can then save the $settings hash table back to a file.

<span style="color: #00ff00"><em>$settings | ConvertTo</em></span><span style="color: #00ff00"><em>-Json | Set-Content "c:\mypath.settings.txt" -Encoding Unicode</em></span>

<strong>NOTE:</strong> the encoding is very important, else you will never be able to manually edit the settings file.

If you need to add more properties after the fact (the file that is loaded doesn't have all the properties needed) you can add them either manually before adding, or via PowerShell.

Review the following two examples:

<span class="pln"></span><span style="color: #00ff00"><em><span class="pun">$settings|</span><span class="pln"> </span><span class="typ">Add</span><span class="pun">-</span><span class="typ">Member</span><span class="pln"> </span><span class="pun">-</span><span class="typ">Name</span><span class="pln"> </span><span class="str">'New Property'</span><span class="pln"> </span><span class="pun">-</span><span class="typ">Type</span><span class="pln"> </span><span class="typ">NoteProperty</span><span class="pln"> </span><span class="pun">-</span><span class="typ">Value</span><span class="pln"> </span><span class="lit">23</span><span class="pln"> </span></em></span>

<span style="color: #00ff00"><em><span class="pln"></span><span class="pun">$settings|</span><span class="pln"> </span><span class="typ">Add</span><span class="pun">-</span><span class="typ">Member</span><span class="pln"> </span><span class="pun">-</span><span class="typ">Name</span><span class="pln"> </span><span class="str">'something'</span><span class="pln"> </span><span class="pun">-</span><span class="typ">Type</span><span class="pln"> </span><span class="typ">NoteProperty</span><span class="pln"> </span><span class="pun">-</span><span class="typ">Value</span><span class="pln"> $false</span></em></span>

To remove a property, it's even simpler, review the following example:

<span style="color: #00ff00"><em>$settings.Remove("Name of property")</em></span>

Setting a property is as simple as:

<span style="color: #00ff00"><em>$settings.Setting1 = "Love Live and prosper"</em></span>

The purpose of all of this is simply so that settings can be placed in a single location, a single file and multiple scripts can use it to do the task the script is designed to do.

There are many instances where you have the same settings (URL's, accounts, etc) that are used throughout various components, and it is not feasible to just put everything in one large script.]]></content:encoded>
    </item>
    <item>
      <title>SQL Dialog Box for PowerShell</title>
      <link>https://docs.microsoft.com/archive/blogs/joetalksmicrosoft/sql-dialog-box-for-powershell</link>
      <pubDate>Thu, 24 May 2018 09:53:38 GMT</pubDate>
      <dc:creator><![CDATA[JoeMVP]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/joetalksmicrosoft/?p=845</guid>
      <description><![CDATA[I had a need the other day to build a PowerShell script to automate some tasks for installing a...]]></description>
      <content:encoded><![CDATA[I had a need the other day to build a PowerShell script to automate some tasks for installing a solution I built.

I've always believed that its best to limit how much untested data entry a user has to do. Hence I wanted a dialog box for SQL connections and be able to test the connection.

The following is what I came up with:
<p style="padding-left: 30px"><span style="color: #0000ff">function Show-ConnectionDialog()</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">{</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$arry = "SQL Server database login", "SQL Server", "Username", "Password*", "Database"</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">#create input form</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm = New-Object System.Windows.Forms.Form</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.Text = $arry[0]</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.Size = New-Object System.Drawing.Size(330, 100)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.StartPosition = "CenterScreen"</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">[System.Windows.Forms.Application]::EnableVisualStyles()</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">#handle button click events</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.KeyPreview = $true</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.Add_KeyDown(</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">{</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">if ($_.KeyCode -eq "Enter")</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">{</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.Close()</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">}</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">})</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.Add_KeyDown(</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">{</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">if ($_.KeyCode -eq "Escape")</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">{</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.Close()</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">}</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">})</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">#create OK button</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$okButton = New-Object System.Windows.Forms.Button</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$okButton.Size = New-Object System.Drawing.Size(75, 23)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$okButton.Text = "OK"</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$okButton.Add_Click(</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">{</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.DialogResult = [System.Windows.Forms.DialogResult]::OK</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">})</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.Controls.Add($okButton)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.AcceptButton = $okButton</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">#create Cancel button</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$cancelButton = New-Object System.Windows.Forms.Button</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$cancelButton.Size = New-Object System.Drawing.Size(75,23)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$cancelButton.Text = "Cancel"</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.Controls.Add($cancelButton)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.CancelButton = $cancelButton</span></p>
<p style="padding-left: 30px"></p>
<p style="padding-left: 30px"><span style="color: #0000ff">#create Test button</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$TestButton = New-Object System.Windows.Forms.Button</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$TestButton.Size = New-Object System.Drawing.Size(75, 23)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$TestButton.Text = "Test"</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$TestButton.Add_Click(</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">{</span>
<span style="color: #0000ff"> $server = $txtBoxes[0].Text</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$user = $txtBoxes[1].Text</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$password = $txtBoxes[2].Text</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$database = $txtBoxes[3].Text</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$SqlConnection = New-Object System.Data.SqlClient.SqlConnection</span>
<span style="color: #0000ff"> If ($user -ne "")</span>
<span style="color: #0000ff"> {</span>
<span style="color: #0000ff"> $SqlConnection.ConnectionString = "Server = $server; Database = $database; Integrated Security = false; User ID = $user; Password = $password;"</span>
<span style="color: #0000ff"> }else{</span>
<span style="color: #0000ff"> $SqlConnection.ConnectionString = "Server = $server; Database = $database; Integrated Security = true;"</span>
<span style="color: #0000ff"> }</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">#write-host $SqlConnection.ConnectionString</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">try{</span>
<span style="color: #0000ff"> $SqlConnection.Open()</span>
<span style="color: #0000ff"> [System.Windows.MessageBox]::Show('Connection Successful')</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">}catch{</span>
<span style="color: #0000ff"> [System.Windows.MessageBox]::Show('Connection Failed')</span>
<span style="color: #0000ff"> }</span>
<span style="color: #0000ff"> })</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.Controls.Add($TestButton)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">[System.Collections.Generic.List[System.Windows.Forms.TextBox]] $txtBoxes = New-Object System.Collections.Generic.List[System.Windows.Forms.TextBox]</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$y = -15;</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">for($i=1;$i -lt $arry.Count;$i++)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">{</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$y+=30</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.Height += 30</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">#create label</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$objLabel = New-Object System.Windows.Forms.Label</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$objLabel.Location = New-Object System.Drawing.Size(10, $y)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$objLabel.Size = New-Object System.Drawing.Size(280, 20)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$objLabel.Text = $arry[$i] +":"</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.Controls.Add($objLabel)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$y+=20</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.Height+=20</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">#create TextBox</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$objTextBox = New-Object System.Windows.Forms.TextBox</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$objTextBox.Location = New-Object System.Drawing.Size(10, $y)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$objTextBox.Size = New-Object System.Drawing.Size(290, 20)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.Controls.Add($objTextBox)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$txtBoxes.Add($objTextBox)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$cancelButton.Location = New-Object System.Drawing.Size(110, (35+$y))</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$okButton.Location = New-Object System.Drawing.Size(35, (35+$y))</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$TestButton.Location = New-Object System.Drawing.Size(185, (35+$y))</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">if ($arry[$i].Contains("*"))</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">{</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$objLabel.Text = ($objLabel.Text -replace '\*','')</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$objTextBox.UseSystemPasswordChar = $true</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">}</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">}</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.Topmost = $true</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.MinimizeBox = $false</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.MaximizeBox = $false</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.AutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.SizeGripStyle = [System.Windows.Forms.SizeGripStyle]::Hide</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$inputForm.Add_Shown({$inputForm.Activate(); $txtBoxes[0].Focus()})</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">if ($inputForm.ShowDialog() -ne [System.Windows.Forms.DialogResult]::OK)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">{</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">exit</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">}</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">return ($txtBoxes | Select-Object {$_.Text} -ExpandProperty Text)</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">}</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">Add-Type -AssemblyName "system.windows.forms"</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">#variables for our SQL Server login form</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$login = Show-ConnectionDialog</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$server = $login[0]</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$user = $login[1]</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$password = $login[2]</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$database = $login[3]</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$ConnectionString = "Server = $server; Database = $database; Integrated Security = False; User ID = $user; Password = $password;"</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$ConnectionString = "Server = $server; Database = $database; Integrated Security = True;"</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$SqlConnection = New-Object System.Data.SqlClient.SqlConnection</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$SqlConnection.ConnectionString = $ConnectionString</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">$SqlConnection.Open()</span></p>
<p style="padding-left: 30px"><span style="color: #0000ff">Write-Output $ConnectionString</span></p>]]></content:encoded>
    </item>
    <item>
      <title>AuthenticationManager.GetExternalLoginInfoAsync() returns NULL</title>
      <link>https://docs.microsoft.com/archive/blogs/joetalksmicrosoft/authenticationmanager-getexternallogininfoasync-returns-null</link>
      <pubDate>Thu, 19 Apr 2018 11:30:39 GMT</pubDate>
      <dc:creator><![CDATA[JoeMVP]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/joetalksmicrosoft/?p=835</guid>
      <description><![CDATA[There is a problem when trying to use "GetExternalLoginInfoAsync()".

This is a problem when using...]]></description>
      <content:encoded><![CDATA[There is a problem when trying to use "GetExternalLoginInfoAsync()".

This is a problem when using wsFederation so if you setup federation following these <a href="https://www.cloudidentity.com/blog/2014/02/20/ws-federation-in-microsoft-owin-componentsa-quick-start/">instructions</a>.

Then when trying the following:

<span class="pun">[</span><span class="typ">AllowAnonymous</span><span class="pun">]</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="kwd">async</span><span class="pln"> </span><span class="typ">Task</span><span class="pun">&lt;</span><span class="typ">ActionResult</span><span class="pun">&gt;</span><span class="pln"> </span><span class="typ">ExternalLoginCallback</span><span class="pun">(</span><span class="kwd">string</span><span class="pln"> returnUrl</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span>

<span class="kwd">var</span><span class="pln"> loginInfo </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> </span><span class="typ">AuthenticationManager</span><span class="pun">.</span><span class="typ">GetExternalLoginInfoAsync</span><span class="pun">();</span>

}

The only way you will get the loginInfo is using the following:

<span class="kwd">var</span><span class="pln"> loginInfo </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">await</span><span class="pln"> </span><span class="typ">AuthenticationManager</span><span class="pun"></span>.AuthenticateAsync(AuthenticationTypes.Federation)

There are other examples using externalCookie out in the wilds.

If you debug and look at the AuthenticationManager you will find what authentication type is being used. I have found that if using "wsFederation" then "AuthenticationTypes.Federation" is the key to use to not get NULL above.]]></content:encoded>
    </item>
    <item>
      <title>CRM with CUSTOM STS and ADFS</title>
      <link>https://docs.microsoft.com/archive/blogs/joetalksmicrosoft/crm-with-custom-sts-and-adfs</link>
      <pubDate>Mon, 26 Mar 2018 15:29:21 GMT</pubDate>
      <dc:creator><![CDATA[JoeMVP]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/joetalksmicrosoft/?p=825</guid>
      <description><![CDATA[SO the other day I needed to make the STS (security token service) solution I built work with CRM...]]></description>
      <content:encoded><![CDATA[SO the other day I needed to make the STS (security token service) solution I built work with CRM (customer relations management).

CRM is interesting in that unlike SPS (SharePoint) it does not include an STS its self.

The current documentation indicates that CRM relies on ADFS to be the IdP (identity provider).

There are plenty of articles and blog entries for how to setup Claims with CRM.

This is just to talk about some of the gotchas.

One, if you want to setup CRM to access a custom STS then that STS must provide claims that are required.
<ul>
 	<li>UPN (used email address or if possible a constructed UPN)</li>
 	<li>Name (display name, whatever you want to be visible as the user in CRM)</li>
 	<li>Primary SID (I've used the Serial of the PKI token, but anything works)</li>
</ul>
The following is most important.

When you setup Claims Based Authentication, CRM will want actual users that are in the local AD (local or trusted domain). That's right, the first part of the setup ONLY works with AD. So if you are using a custom STS, then pull the above claims from AD directly.

For Internet Facing Claims Based Authentication, the users are not required to be local.

Keep in mind the way CRM is constructed, you can only setup one IdP. This is why when using ADFS you can setup multiple points, with SharePoint you can add trusted providers and have as many as you want. It still all went though the SPS STS and hence SharePoint can OOB provide a login screen (drop down) for users to pick which provider to use.

So that means the Custom STS needs to be able to tell the difference between a local request, or a internet request. Yes you could do that by reply, as to where the URL that is making the request. Although that means you would have to have each CRM URL in a list.

The other way is to look for the REALM parameter, which looks like "urn:federation:authentication:windows".  Looking for this will indicate the request is for a local AD set of claims.

Keeping the above in mind, will allow CRM to use your custom STS.

Next problem is if you use ADFS with a custom STS. This is needed (unless you add end points, which is only needed if you plan to do plugin development using the SDK).

The primary thing you need to have in order for the STS work with ADFS for CRM is as follows:

The CRM Relaying party in ADFS will error out with the following:  System.IdentityModel.Tokens.SecurityTokenException: The incoming identity did not contain any authentication method claims. It goes on to complain about the authentication type was not included "Password".

This seems to be only an issue for Relaying Parties setup for CRM, not a requirement when setup with regular Claims enabled applications.

To solve this problem, the following claims must be added to the token in the STS.

outputIdentity.AddClaim(new Claim(ClaimTypes.AuthenticationInstant, System.DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffZ")));

outputIdentity.AddClaim(new Claim(ClaimTypes.AuthenticationMethod, "https://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/password"));

You can view all the possible authentication methods here: <a href="https://msdn.microsoft.com/en-us/library/system.identitymodel.tokens.authenticationmethods(v=vs.110).aspx">https://msdn.microsoft.com/en-us/library/system.identitymodel.tokens.authenticationmethods(v=vs.110).aspx</a>

Seems Password is the only one that works, though maybe more investigation is needed.  This will get you past the issue and allow for the custom STS to authenticate and ADFS to pass back to CRM.

Next issue is how do you not show the login page in ADFS asking which IdP to use???

Run the following in PowerShell on the ADFS server:

Set-ADFSRelyingPartyTrust -targetName "display name of relaying party" -claimsprovidername "display name of claims provider trust"

The above will reduce the list and cause (if you only set one) to go directly to the IdP and hence no login screen.

Next time we can talk about making a custom end point that the CRM SDK actually likes. This has been a real PITA so far.

&nbsp;]]></content:encoded>
    </item>
    <item>
      <title>PKI Authentication as a Azure Web App</title>
      <link>https://docs.microsoft.com/archive/blogs/joetalksmicrosoft/pki-authentication-as-a-azure-web-app</link>
      <pubDate>Tue, 06 Feb 2018 16:30:57 GMT</pubDate>
      <dc:creator><![CDATA[JoeMVP]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/joetalksmicrosoft/?p=815</guid>
      <description><![CDATA[Hi everyone,
So recently I had to port a solution for authenticating users which use a PKI token....]]></description>
      <content:encoded><![CDATA[<h3>Hi everyone,</h3>
So recently I had to port a solution for authenticating users which use a PKI token. The solution was onsite and relied heavily on IIS for chain trusts, CRL checks, and more.

In Azure when porting the STS (Security Token Service) there are several elements missing.

Review: https://azure.microsoft.com/en-us/blog/enabling-client-certificate-authentication-for-an-azure-web-app/

This allows any Azure web app service to be client certificate enabled.

What you don't get:
<ul>
 	<li>No chain trust verification, this means that it will not verify the trust chain, you cannot upload CA's into Azure in SaaS.</li>
 	<li>No CRL check, there is no mechanism in place to verify if the client certificate is revoked.</li>
 	<li>No check on expire/valid state.</li>
</ul>
What this means that any certificate can be used that was issued by a CA that is in the Azure Root CA store from a public CA.
<h3>How to solve the Problems</h3>
<strong>Validate the Certificate dates: </strong>

<em>           if (DateTime.Compare(DateTime.Now, cert.NotBefore) &lt; 0 || DateTime.Compare(DateTime.Now, cert.NotAfter) &gt; 0) return false;</em>

<strong>Validate the policy chain:</strong>

<em>           X509Chain ch = new X509Chain();</em>

<em>           ch.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;</em>
<em>            ch.ChainPolicy.RevocationFlag = X509RevocationFlag.ExcludeRoot;</em>
<em>            ch.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(1000);</em>
<em>            ch.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;</em>
<em>            ch.ChainPolicy.VerificationTime = DateTime.Now;</em>

<em>            //build chain</em>
<em>            ch.Build(cert);</em>

<em>            //validate issuer, check the chain trusts</em>
<em>            var certArray = ch.ChainElements.Cast&lt;X509ChainElement&gt;().ToArray();</em>
<em>            if (!Array.TrueForAll(certArray, validatechainelements)) return false;</em>

The function (validatechainelements) is where each CA in the policy chain is verified against a list.  The "Build" method is just used to build the policy but no validation is used, as it will always fail. What it does is builds the chain elements which allow us to validate what's in the chain.

Notice the usage of <a href="https://msdn.microsoft.com/en-us/library/x2kykt9x(v=vs.110).aspx">Array.TrueForAll</a>, this is a very interesting solution, basically this allows each item to be used against a function, like a foreach loop.

<em>        private static bool validatechainelements(X509ChainElement el)</em>
<em>        {</em>
<em>          //Part of the chain is the client cert it's self so we just return true if the client cert matches the cert being passed</em>
<em>            if (el.Certificate.Thumbprint.ToLower().Contains(clientCert.Thumbprint.ToLower())) return true;</em>

<em>            //The getCAThumbprints below pulls a CSV file of trusted thumbprints</em>

<em>            System.Collections.Generic.List&lt;string&gt; thumbprints = GetCAThumbprints();</em>
<em>            if (thumbprints.Contains(el.Certificate.Thumbprint.ToLower())) return true;</em>
<em>            return false;</em>
<em>        }</em>

As you can see, the thumbprints are pulled from another function (not shown) which is just a list of strings of thumbprints of CA's we trust. If the chain element thumbprint matches a trusted CA thumbprint then return true, else false.

Validate CRL:

//check CRL list
X509CertificateParser pr = new X509CertificateParser();
string crlurl = GetCrlDistributionPoints(certArray[1].Certificate)[0];
if (IsCertRevoked(pr.ReadCertificate(cert.RawData),crlurl)) return false;

The above calls two different functions, the first gets the distribution URL, here I  am using the API <a href="https://www.bouncycastle.org/csharp/">BouncyCastle.Crypto </a>to parse the certificate.

We use the second item in the array (1, as is zero based) which is the CA up the chain from the client certificate.

<em>          /// &lt;summary&gt;</em>
<em>        /// Returns an array of CRL distribution points for X509Certificate2 object.</em>
<em>        /// &lt;/summary&gt;</em>
<em>        /// &lt;param name="certificate"&gt;X509Certificate2 object.&lt;/param&gt;</em>
<em>        /// &lt;returns&gt;Array of CRL distribution points.&lt;/returns&gt;</em>
<em>        public static string[] GetCrlDistributionPoints(this X509Certificate2 certificate)</em>
<em>        {</em>
<em>            X509Extension ext = certificate.Extensions.Cast&lt;X509Extension&gt;().FirstOrDefault(</em>
<em>                e =&gt; e.Oid.Value == "2.5.29.31");</em>

<em>            if (ext == null || ext.RawData == null || ext.RawData.Length &lt; 11)</em>
<em>                return null;</em>

<em>            int prev = -2;</em>
<em>            List&lt;string&gt; items = new List&lt;string&gt;();</em>
<em>            while (prev != -1 &amp;&amp; ext.RawData.Length &gt; prev + 1)</em>
<em>            {</em>
<em>                int next = IndexOf(ext.RawData, 0x86, prev == -2 ? 8 : prev + 1);</em>
<em>                if (next == -1)</em>
<em>                {</em>
<em>                    if (prev &gt;= 0)</em>
<em>                    {</em>
<em>                        string item = Encoding.UTF8.GetString(ext.RawData, prev + 2, ext.RawData.Length - (prev + 2));</em>
<em>                        items.Add(item);</em>
<em>                    }</em>

<em>                    break;</em>
<em>                }</em>

<em>                if (prev &gt;= 0 &amp;&amp; next &gt; prev)</em>
<em>                {</em>
<em>                    string item = Encoding.UTF8.GetString(ext.RawData, prev + 2, next - (prev + 2));</em>
<em>                    items.Add(item);</em>
<em>                }</em>

<em>                prev = next;</em>
<em>            }</em>

<em>            return items.ToArray();</em>
<em>        }</em>

<em>        private static int IndexOf(byte[] instance, byte item, int start)</em>
<em>        {</em>
<em>            for (int i = start, l = instance.Length; i &lt; l; i++)</em>
<em>                if (instance[i] == item)</em>
<em>                    return i;</em>

<em>            return -1;</em>
<em>        }</em>

Now that we have the distribution point we can validate the client certificate.

<em>      //reference library BouncyCastle.Crypto</em>
<em>        //https://www.bouncycastle.org/csharp/</em>
<em>        //Load CRL file and access its properties</em>
<em>        private static bool IsCertRevoked(Org.BouncyCastle.X509.X509Certificate cert,string url)</em>
<em>        {</em>

<em>                byte[] buf = GetCacheCRL(url);</em>
<em>                X509CrlParser xx = new X509CrlParser();</em>
<em>                X509Crl ss = xx.ReadCrl(buf);</em>
<em>                var nextupdate = DateTime.Parse(ss.NextUpdate.ToString());</em>
<em>                return ss.IsRevoked(cert);</em>
<em>       </em>
<em>        }</em>

Above the CLR is held in cache, and updated based on the nextupdate property. Although it would not be needed this section is the important part of the "GetCacheCRL".

<em>byte[] data;</em>

<em>                System.Net.WebRequest httpRequest = System.Net.WebRequest.Create(CRLFileURL);</em>
<em>                httpRequest.Timeout = 10000;</em>
<em>               </em>

<em>                System.Net.WebResponse webResponse = httpRequest.GetResponse();</em>

<em>                System.IO.Stream strm  = webResponse.GetResponseStream();</em>

<em>                byte[] buffer = new byte[16 * 1024];</em>
<em>                using (System.IO.MemoryStream ms = new System.IO.MemoryStream())</em>
<em>                {</em>
<em>                    int read;</em>
<em>                    while ((read = strm.Read(buffer, 0, buffer.Length)) &gt; 0)</em>
<em>                    {</em>
<em>                        ms.Write(buffer, 0, read);</em>
<em>                    }</em>
<em>                    data= ms.ToArray();</em>
<em>                }</em>

<em>                strm.Close();</em>
<em>                webResponse.Close();</em>

The last element to get the solution working when dealing with client certificates:

Application Settings as follows:
<ul>
 	<li>WEBSITE_LOAD_USER_PROFILE and set it to 1</li>
 	<li>WEBSITE_LOAD_CERTIFICATES with its value set to the thumbprint of the certificate will make it accessible to your web application.</li>
</ul>
Now that the above is met, then you now have the following validating:
<ol>
 	<li>What you have (the PKI token)</li>
 	<li>What you know (PIN)</li>
 	<li>Is it valid (by date expire)</li>
 	<li>Is it issued by a trusted CA(s)</li>
 	<li>Has it been revoked?</li>
</ol>
Hope this helps others, as working though everything above was a lot of fun.]]></content:encoded>
    </item>
    <item>
      <title>Offline NuGet Packages with VS.NET 2017</title>
      <link>https://docs.microsoft.com/archive/blogs/joetalksmicrosoft/offline-nuget-packages-with-vs-net-2017</link>
      <pubDate>Mon, 14 Aug 2017 14:22:28 GMT</pubDate>
      <dc:creator><![CDATA[JoeMVP]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/joetalksmicrosoft/?p=785</guid>
      <description><![CDATA[So, I work with a customer that has thier development network offline of the general...]]></description>
      <content:encoded><![CDATA[So, I work with a customer that has thier development network offline of the general internet.

One of the development leads built a template for VS.NET (2015) which has all the NuGet packages.

Well, I am running VS.NET 2017, yes I like to run the latest and greatest!

First off, I created a new solution with the template. I then built the solution and had a large number of references to dll's missing.

After a review of the NuGet manager I found that many of the packages said hey were not avaiable in the source. Yet they were listed in the installed section of the manager.

I went about changing things but I did it out of order so here we will do it in order:

First you msut add the location of the new packages (note, the template should probably have installed them correctly but for vairous reasons it didn't):

The following is generic:
<p class="lf-text-block lf-block">To manage package sources:<span class="lf-thread-btn"> </span></p>

<ol class="lf-text-block lf-block">
 	<li>Select the <strong>Settings</strong> icon in the Package Manager UI outlined below or use the <strong>Tools &gt; Options</strong> command and scroll to <strong>NuGet Package Manager</strong>:

<img alt="Package manager UI settings icon" src="https://docs.microsoft.com/en-us/nuget/tools/media/packagesourcesettings.png" /></li>
 	<li>Select the <strong>Package Sources</strong> node:

<img alt="Package Sources options" src="https://docs.microsoft.com/en-us/nuget/tools/media/options.png" /></li>
</ol>
<ol>
 	<li>To add a source, select <strong>+</strong>, edit the name, enter the URL or path in the <strong>Source</strong> control, and select <strong>Update</strong>. The source now appears in the selector drop-down.</li>
 	<li>To change a package source, select it, make edits in the <strong>Name</strong> and <strong>Source</strong> boxes, and select <strong>Update</strong>.</li>
 	<li>To disable a package source, clear the box to the left of the name in the list.</li>
 	<li>To remove a package source, select it and then select the <strong>X</strong> button.</li>
 	<li>Use the up and down arrow buttons to change the priority order of the package sources. Visual Studio searches these sources in the priority order when restoring packages for a project.</li>
</ol>
<strong>What needs to be done is as follows:</strong>
<ol>
 	<li>Uncheck the "nuget.org" as all you get is erors because you can't reach it.</li>
 	<li>Add a new source as the instructions state above. The source would be where the packages are located. In my case I did a search for the template name, then I went one folder back, then clicked on "packages" and all the packages where there. This is the path of the packages that were embeded in the template.</li>
</ol>
Once you have the source then you are ready for the next step (while the solution with the missing assemblies is open):

<strong>Go to: Tools-&gt;NuGet Package Manager-&gt;Package Manager Console</strong>

This opens a console window to run powershell.

Run the following:

<span style="color: #0000ff">Update-Package -reinstall</span>

Now do a rebuild of the solution and all of the references will be resolved.

<strong><span style="color: #ff0000">Note:</span> Once this is done one time on the machine in question, anytime you use the same template again you will not run into this issue.</strong>]]></content:encoded>
    </item>
    <item>
      <title>DO NOT DELETE "Style Resource Readers" Group</title>
      <link>https://docs.microsoft.com/archive/blogs/joetalksmicrosoft/do-not-delete-style-resource-readers-group</link>
      <pubDate>Wed, 28 Jun 2017 20:16:00 GMT</pubDate>
      <dc:creator><![CDATA[JoeMVP]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/joetalksmicrosoft/?p=775</guid>
      <description><![CDATA[So the other day at my customer, the site collection used for training just stopped working, that's...]]></description>
      <content:encoded><![CDATA[So the other day at my customer, the site collection used for training just stopped working, that's what is said.

What stopped working is creating any sub sites, list, libraries.

The error was very vague and unhelpful. The SPS Logs simply showed  IO File not found shortly after trying to activate the "WikipageHomePage" feature.

While users with permissions could not create a site, site collection administrators could. So this is simply a permissions issue.

In researching this issue there were lots of similar cases. Most had to do with lack of permissions to the style/images/master page libraries.

This is when I found that the "Style Resource Readers" which by the way has a description that says "DO NOT DELETE" was missing.

This group normally has the following two accounts:
<ul>
 	<li>Everyone</li>
 	<li>NT AUTHORITY\authenticated users</li>
</ul>
The Group has the following permissions:
<ul>
 	<li>Read</li>
 	<li>Limited access</li>
</ul>
And has the following lists that is used in:
<ul>
 	<li>Site Collection Images</li>
 	<li>Style Library</li>
 	<li>Publishedlinks</li>
 	<li>Quick Deploy Items</li>
 	<li>Master Page Library</li>
</ul>
So even if you re-create the group you may still find that users still can't create sites/lists/libraries.

That is because the hiddentaxonomylist probably has no permissions.

go to site/lists/hiddentaxonomylist

Add "Everyone" to the lists permissions.

This may not fix your problem, but it may help answer some of your problems.

Another possible way to fix the problems of deleting the group is to flip the Publishing feature off then on, both at the site level and site collection level. This should reset/re-create all the required items. Beware, it might cause additional pain in some cases.

References:

<a href="https://support.office.com/en-us/article/default-sharepoint-groups-in-sharepoint-server-50855943-c151-42bf-b6ea-07f2bb366ecc">https://support.office.com/en-us/article/default-sharepoint-groups-in-sharepoint-server-50855943-c151-42bf-b6ea-07f2bb366ecc</a>

<a href="https://sharepoint.stackexchange.com/questions/92862/style-resource-readers-allow-access-to-list-views">https://sharepoint.stackexchange.com/questions/92862/style-resource-readers-allow-access-to-list-views</a>

https://www.brightwork.com/product-blog/user-with-full-control-cannot-create-sub-sites-or-projects]]></content:encoded>
    </item>
    <item>
      <title>SharePoint 2013 Multiple App Domain with Host Named Site Collections Issue</title>
      <link>https://docs.microsoft.com/archive/blogs/joetalksmicrosoft/sharepoint-2013-multiple-app-domain-with-host-named-site-collections-issue</link>
      <pubDate>Fri, 23 Jun 2017 18:53:37 GMT</pubDate>
      <dc:creator><![CDATA[JoeMVP]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/joetalksmicrosoft/?p=735</guid>
      <description><![CDATA[SO, the other day I ran into a problem at a customer:
Summary
My client has setup an environment...]]></description>
      <content:encoded><![CDATA[SO, the other day I ran into a problem at a customer:
<h1>Summary</h1>
My client has setup an environment where they can only utilize sub domains of their primary domain.

For example: command.com

Hence different sites would be sub domains such as:
<ul>
 	<li>release.command.com</li>
 	<li>forward.command.com</li>
</ul>
To save time, maintenance and resources these two web applications are in one central farm. Previously they were in separate farms to secure content from each other.

The client has also utilized HNSC (host named site collections) in order to be cloud ready. During the configuration of app domains for the organization to start building and deploying SharePoint hosted add-ins, the client ran into an issue.

The issue is as follows:

When utilizing HNSC and configuring SharePoint for multiple app domains. The settings for the following app domains are ignored.
<ul>
 	<li>release.command.com</li>
 	<li>forward.command.com</li>
</ul>
A global (via the UI) was setup such as: apps.command.com, and all redirects when utilizing a SharePoint hosted add-in goes to this location.

Problem with this are twofold:
<ul>
 	<li>URL changes to guid.apps.command.com</li>
 	<li>No web site exists at that URL, hence 404.</li>
</ul>
The problem does not exist when not utilizing HNSC, though this would remove the organization from being cloud ready.

This is a discovered issue unique to the configuration the client has, as there are required security boundaries that need to be in place between SharePoint Web Applications. Even if the above configuration is made to work, it means the site hosting the app domain has to have security rights to both web applications. This is a breach in the security design the client needs to maintain between web applications and content.
<h1><strong>Solution Design</strong></h1>
<h2>Summary</h2>
This solution to the issue at hand is to utilize an often used module provided by Microsoft called URL Rewrite. It also involves a careful use of extended sites and IP addresses.

This solution will outline the elements required and the configuration of the solution.

<strong>Note:</strong> <em>there is no code for this solution, this is all configurations.</em>
<h2><strong>DNS Settings</strong></h2>
First step is configuring the DNS, this is a bit different than most documentation. The standard configuration calls for a totally separate domain which isn't possible for the command or desired.

<strong>Note:</strong> <em>these would be load balanced IP's by a load balancer. Each web front end in the farm would require this configuration be applied.</em>

The following Table illustrates the configuration of the DNS for this solution:
<table>
<tbody>
<tr>
<td width="160">Parent Domain</td>
<td width="160">Main Sub Domain</td>
<td width="160">Apps Domain</td>
<td width="160">IP</td>
</tr>
<tr>
<td width="160">Command.com</td>
<td width="160"></td>
<td width="160"></td>
<td width="160">10.0.04</td>
</tr>
<tr>
<td width="160"></td>
<td width="160">Release</td>
<td width="160"></td>
<td width="160">10.0.0.5</td>
</tr>
<tr>
<td width="160"></td>
<td width="160"></td>
<td width="160">Apps</td>
<td width="160">10.0.05</td>
</tr>
<tr>
<td width="160"></td>
<td width="160"></td>
<td width="160">*.apps</td>
<td width="160">10.0.0.6</td>
</tr>
<tr>
<td width="160"></td>
<td width="160">Forward</td>
<td width="160"></td>
<td width="160">10.0.0.5</td>
</tr>
<tr>
<td width="160"></td>
<td width="160"></td>
<td width="160">Apps</td>
<td width="160">10.0.0.5</td>
</tr>
<tr>
<td width="160"></td>
<td width="160"></td>
<td width="160">*.apps</td>
<td width="160">10.0.0.7</td>
</tr>
<tr>
<td width="160"></td>
<td width="160">Apps</td>
<td width="160"></td>
<td width="160">10.0.0.5</td>
</tr>
<tr>
<td width="160"></td>
<td width="160"></td>
<td width="160">*.apps</td>
<td width="160">10.0.0.8</td>
</tr>
</tbody>
</table>
The separate apps domain off the root domain is strictly for redirects and will be discussed later.

As illustrated in the above table, there are four addresses; this is because of the need for SSL certificates that are blank.

<strong>Note:</strong> <em>in Windows Server 2016, the issue of not being able to have "Require Server name Indication" checked for blank host name bindings is resolved. Hence the requirement of multiple IP addresses is null.</em>
<h2>Certificates</h2>
The following is a list of certificates required for the solution:
<table>
<tbody>
<tr>
<td width="319">Used by</td>
<td width="319">Subject Name</td>
</tr>
<tr>
<td width="319">apps.command.com</td>
<td width="319">*.command.com</td>
</tr>
<tr>
<td width="319">*.apps.command.com</td>
<td width="319">*.apps.command.com</td>
</tr>
<tr>
<td width="319">(apps/portal/root/whatever).release.command.com</td>
<td width="319">*.release.command.com</td>
</tr>
<tr>
<td width="319">*.apps.release.command.com</td>
<td width="319">*.apps.release.command.com</td>
</tr>
<tr>
<td width="319">(apps/portal/root/whatever).forward.command.com</td>
<td width="319">*.forward.command.com</td>
</tr>
<tr>
<td width="319">*.apps.forward.command.com</td>
<td width="319">*.apps.forward.command.com</td>
</tr>
</tbody>
</table>
The solution requires the use of wild card certificates; this is not a requirement of this solution but of SharePoint Hosted Add-ins by Microsoft.
<h2>Web Applications</h2>
This solution has the following configuration:

Web Applications:
<ul>
 	<li>Release Home</li>
 	<li>Forward Home</li>
</ul>
Each of the above is extended via Central admin
<ul>
 	<li>Release Apps</li>
 	<li>Forward Apps</li>
</ul>
<strong>Note:</strong> <em>the reason to extend the sites is because of the blank bindings for the *.apps.[site].command.com calls. Hence all such calls will go to the extended site and be covered under that certificate. If extended sites are not used then invalid certificates will be indicated for most sites.</em>

Each site has the following site collection, though what is described is not a limit, just a short list.
<ul>
 	<li>[site].command.com</li>
 	<li>[site].command.com</li>
 	<li>[site].command.com(location of app catalog)</li>
</ul>
<strong>Note:</strong> <em>set the app catalog for each web app pointing to the respective HNSC.</em>

<strong>Note:</strong> <em>also set the app domain URL to:  apps.command.com</em>
<h2>IIS Bindings</h2>
The following are bindings for each website:

<strong>Note:</strong> <em>all are 443, blanks do not have "Require Server name Indication" checked.</em>
<table width="637">
<tbody>
<tr>
<td width="139">Site</td>
<td width="216">Binding</td>
<td width="63">IP</td>
<td width="188">Certificate</td>
</tr>
<tr>
<td width="139">Release Home</td>
<td width="216">Portal.release.command.com</td>
<td width="63">10.0.0.5</td>
<td width="188">*.release.command.com</td>
</tr>
<tr>
<td width="139">Release Home</td>
<td width="216">Dept1.release.command.com</td>
<td width="63">10.0.0.5</td>
<td width="188">*.release.command.com</td>
</tr>
<tr>
<td width="139">Release Home</td>
<td width="216">apps.release.command.com</td>
<td width="63">10.0.0.5</td>
<td width="188">*.release.command.com</td>
</tr>
<tr>
<td width="139">Release Apps</td>
<td width="216">[blank]</td>
<td width="63">10.0.0.6</td>
<td width="188">*.apps.release.command.com</td>
</tr>
<tr>
<td width="139">Forward Home</td>
<td width="216">Portal.forward.command.com</td>
<td width="63">10.0.0.5</td>
<td width="188">*. forward.command.com</td>
</tr>
<tr>
<td width="139">Forward Home</td>
<td width="216">Dept1. forward.command.com</td>
<td width="63">10.0.0.5</td>
<td width="188">*. forward.command.com</td>
</tr>
<tr>
<td width="139">Forward Home</td>
<td width="216">apps. forward.command.com</td>
<td width="63">10.0.0.5</td>
<td width="188">*. forward.command.com</td>
</tr>
<tr>
<td width="139">Forward Apps</td>
<td width="216">[blank]</td>
<td width="63">10.0.0.7</td>
<td width="188">*.apps. forward.command.com</td>
</tr>
<tr>
<td width="139">Redirect Site</td>
<td width="216">Apps.command.com</td>
<td width="63">10.0.0.5</td>
<td width="188">*.command.com</td>
</tr>
<tr>
<td width="139">Redirect Site</td>
<td width="216">[blank]</td>
<td width="63">10.0.0.8</td>
<td width="188">*.apps.command.com</td>
</tr>
</tbody>
</table>
<strong>Note:</strong> <em>due to another issue within the redirect.aspx that builds the URL, all ports have to be 443, this is a SharePoint issue and cannot be changed until a fix is implemented.</em>
<h2>Redirect Site</h2>
A site needs to be created in IIS, it can point to an empty folder, and nothing needs to be set.

Make sure the URL rewrite module is installed, download it if needed: <a href="https://www.iis.net/downloads/microsoft/url-rewrite">https://www.iis.net/downloads/microsoft/url-rewrite</a>

The model would show up when you click on the web site in IIS:

<img width="1001" height="540" class="alignnone size-full wp-image-745" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/06/Rewrite-img1.jpg" />

Two rules need to be created to support the solution discussed.

<strong>NOTE:</strong> <em>this solution could be applied to more than one SharePoint web application, although only 2 are discussed here.</em>

Double click URL Rewrite:

<img width="998" height="485" class="alignnone size-full wp-image-755" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/06/Rewrite-img2.jpg" />

Click Add Rule(s)

<img width="689" height="460" class="alignnone size-full wp-image-765" alt="" src="https://msdnshared.blob.core.windows.net/media/2017/06/Rewrite-img3.jpg" />

Select Blank Rule from the inbound rules section.

Now you must define the actual rewrite rule. In the URL Rewrite Module, a rewrite rule is defined by specifying four required pieces of information:
<ul>
 	<li>Name of the rule.</li>
 	<li>Pattern to use for matching the URL string.</li>
 	<li>Set of conditions.</li>
 	<li>Action to perform if a pattern is matched and whether all conditions checks succeed.</li>
</ul>
<h3>Rule 1:</h3>
<strong>Name</strong>: Release Apps Redirect
<h4>Match URL section</h4>
Requested URL: Matches the Pattern

Using Regular Expressions

Pattern: ^(.*)$

<strong>Note:</strong> <em>the string that comes in as an input is normally "sites/siteofinterest/pages/page1.aspx". Hence nothing to identify it as the URL you want, so here we simply say give us anything that is a string.</em>

<strong>Conditions Section:</strong>

Logical Grouping: Match All
<table>
<tbody>
<tr>
<td width="213">Input</td>
<td width="213">Type</td>
<td width="213">Pattern</td>
</tr>
<tr>
<td width="213">{HTTP_REFERER}</td>
<td width="213">Matches the Pattern</td>
<td width="213">\brelease\b</td>
</tr>
<tr>
<td width="213">{HTTP_POST}</td>
<td width="213">Matches the Pattern</td>
<td width="213">(^app-[0-9a-z]+)</td>
</tr>
</tbody>
</table>
<h4>Action Section</h4>
Action Type: Redirect

Action Properties:Â  <span>https://{C:1}.apps.release.command.com{PATH_INFO}?{QUERY_STRING}</span>

Uncheck Include query
<h3><a></a> Rule 2:</h3>
<strong>Name</strong>: Froward Apps Redirect
<h4>Match URL section</h4>
Requested URL: Matches the Pattern

Using Regular Expressions

Pattern: ^(.*)$

Note: the string that comes in as an input is normally "sites/siteofinterest/pages/page1.aspx". Hence nothing to identify it as the URL you want, so here we simply say give us anything that is a string.

<strong>Conditions Section:</strong>

Logical Grouping: Match All
<table>
<tbody>
<tr>
<td width="213">Input</td>
<td width="213">Type</td>
<td width="213">Pattern</td>
</tr>
<tr>
<td width="213">{HTTP_REFERER}</td>
<td width="213">Matches the Pattern</td>
<td width="213">\bforward\b</td>
</tr>
<tr>
<td width="213">{HTTP_POST}</td>
<td width="213">Matches the Pattern</td>
<td width="213">(^app-[0-9a-z]+)</td>
</tr>
</tbody>
</table>
<h4>Action Section</h4>
Action Type: Redirect

Action Properties:Â  <span>https://{C:1}.apps.forward.command.com{PATH_INFO}?{QUERY_STRING}</span>

Uncheck Include query
<h2><a></a> Review</h2>
At this point, all the rest of the development process for building SharePoint Hosted Add-ins is the same. Deploying SharePoint Hosted Add-ins is the same, this is strictly an infrastructure solution and changes nothing of how users implement or use applications.

One aspect that is not mentioned in this document thus far is authentication; this assumes both the main and extended sites utilize the same authentication methods. If federation is used then the extended sites will also need to utilize the same provider.
<h2><a></a> References</h2>
The following are references for this solution:
<table>
<tbody>
<tr>
<td width="181">Name</td>
<td width="457">URL</td>
</tr>
<tr>
<td width="181">URL Rewrite tips and tricks</td>
<td width="457"><a href="https://ruslany.net/2009/04/10-url-rewriting-tips-and-tricks/">https://ruslany.net/2009/04/10-url-rewriting-tips-and-tricks/</a></td>
</tr>
<tr>
<td width="181">Download URL Rewrite</td>
<td width="457"><a href="https://www.iis.net/downloads/microsoft/url-rewrite">https://www.iis.net/downloads/microsoft/url-rewrite</a></td>
</tr>
<tr>
<td width="181">Creating Outbound rules</td>
<td width="457"><a href="https://docs.microsoft.com/en-us/iis/extensions/url-rewrite-module/creating-outbound-rules-for-url-rewrite-module">https://docs.microsoft.com/en-us/iis/extensions/url-rewrite-module/creating-outbound-rules-for-url-rewrite-module</a></td>
</tr>
<tr>
<td width="181">Using Failed request Tracing to trace Rewrite rules</td>
<td width="457"><a href="https://docs.microsoft.com/en-us/iis/extensions/url-rewrite-module/using-failed-request-tracing-to-trace-rewrite-rules">https://docs.microsoft.com/en-us/iis/extensions/url-rewrite-module/using-failed-request-tracing-to-trace-rewrite-rules</a></td>
</tr>
<tr>
<td width="181">URL Parts Available to URL Rewrite Rules</td>
<td width="457"><a href="https://weblogs.asp.net/owscott/url-parts-available-to-url-rewrite-rules">https://weblogs.asp.net/owscott/url-parts-available-to-url-rewrite-rules</a></td>
</tr>
<tr>
<td width="181">URL Rewrite Configuration Reference</td>
<td width="457"><a href="https://docs.microsoft.com/en-us/iis/extensions/url-rewrite-module/url-rewrite-module-configuration-reference">https://docs.microsoft.com/en-us/iis/extensions/url-rewrite-module/url-rewrite-module-configuration-reference</a></td>
</tr>
<tr>
<td width="181">Getting parts of a URL with Regex</td>
<td width="457"><a href="https://stackoverflow.com/questions/27745/getting-parts-of-a-url-regex">https://stackoverflow.com/questions/27745/getting-parts-of-a-url-regex</a></td>
</tr>
<tr>
<td width="181">Regular Expressions Examples</td>
<td width="457"><a href="https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expressions">https://docs.microsoft.com/en-us/dotnet/standard/base-types/regular-expressions</a></td>
</tr>
<tr>
<td width="181">Developing a Custom Rewrite provider</td>
<td width="457"><a href="https://docs.microsoft.com/en-us/iis/extensions/url-rewrite-module/developing-a-custom-rewrite-provider-for-url-rewrite-module">https://docs.microsoft.com/en-us/iis/extensions/url-rewrite-module/developing-a-custom-rewrite-provider-for-url-rewrite-module</a></td>
</tr>
<tr>
<td width="181">Creating Rewrite Rules</td>
<td width="457"><a href="https://docs.microsoft.com/en-us/iis/extensions/url-rewrite-module/developing-a-custom-rewrite-provider-for-url-rewrite-module">https://docs.microsoft.com/en-us/iis/extensions/url-rewrite-module/developing-a-custom-rewrite-provider-for-url-rewrite-module</a></td>
</tr>
</tbody>
</table>]]></content:encoded>
    </item>
  </channel>
</rss>