<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:blogger='http://schemas.google.com/blogger/2008' xmlns:georss='http://www.georss.org/georss' xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3846425018755283358</id><updated>2026-02-06T06:38:02.551-06:00</updated><category term="Troubleshooting"/><category term="PowerShell"/><category term="Self Learning"/><category term="Backup/Recovery"/><category term="Configuration"/><category term="Monitoring"/><category term="Maintenance"/><category term="Performance Tuning"/><category term="Security"/><category term="SSIS"/><category term="Azure"/><category term="SSRS"/><category term="Hardware"/><category term="Replication"/><category term="SSMS"/><category term="Script"/><category term="Visual Studio"/><category term="Github"/><category term="Licensing"/><category term="Linux"/><category term="SQL Agent"/><category term="Sharepoint"/><category term="sqlcmd"/><title type='text'>Travis Gan</title><subtitle type='html'>Technical blog on SQL Server, BI Stack, Azure and other technologies</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.travisgan.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default?redirect=false'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default?start-index=26&amp;max-results=25&amp;redirect=false'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>88</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-5312578162629481995</id><published>2025-04-07T07:00:00.001-05:00</published><updated>2025-04-07T07:00:00.112-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="PowerShell"/><category scheme="http://www.blogger.com/atom/ns#" term="Sharepoint"/><title type='text'>PowerShell for SharePoint</title><content type='html'>SharePoint has been introduced since a long ago and has evolved over time. This post attempts to gather different methods of interacting SharePoint with PowerShell.

&lt;h2&gt;SharePoint Management Shell&lt;/h2&gt;
There are two SharePoint Mangement Shell PowerShell modules. One for &lt;a href=&quot;https://learn.microsoft.com/en-us/powershell/sharepoint/sharepoint-online/introduction-sharepoint-online-management-shell&quot; target=&quot;_blank&quot;&gt;SharePoint online&lt;/a&gt; and another one for &lt;a href=&quot;https://learn.microsoft.com/en-us/powershell/sharepoint/sharepoint-server/sharepoint-server-cmdlets&quot; target=&quot;_blank&quot;&gt;SharePoint Server&lt;/a&gt;.

&lt;h3&gt;SharePoint Online&lt;/h3&gt;
This is a Windows PowerShell module to manage SharePoint Settings for organization or site collection level. Useful for SharePoint administrator and site admin. The module is Microsoft.Online.SharePoint.PowerShell and the command naming convention starts with SPO as its noun, as example

&lt;pre class=&quot;brush:ps&quot;&gt;Install-Module -Name Microsoft.Online.SharePoint.PowerShell -Scope CurrentUser
Connect-SPOService -Url https://contoso-admin.sharepoint.com -Credential admin@contoso.com
&lt;/pre&gt;

List of SharePoint Online PowerShell commands and its &lt;a href=&quot;https://learn.microsoft.com/en-us/powershell/module/sharepoint-online/?view=sharepoint-ps&quot; target=&quot;_blank&quot;&gt;documentation&lt;/a&gt;

&lt;h3&gt;SharePoint Server&lt;/h3&gt;The PowerShell cmdlets comes along with SharePoint Server installation. Useful for SharePoint administrator. To use the SharePoint cmdlets (provided as snap-in, not module) in Windows PowerShell, register the snap-in manually, Add-PSSnapin Microsoft.SharePoint.PowerShell. If it is executed from SharePoint Managemetn Shell, it is automatically registered. Command naming convention usually starts with SP as its noun, as example

&lt;pre class=&quot;brush:ps&quot;&gt;Add-PSSnapin Microsoft.SharePoint.PowerShell
Get-SPSite
&lt;/pre&gt;

List of SharePoint Server PowerShell commands and its &lt;a href=&quot;https://learn.microsoft.com/en-us/powershell/module/sharepoint-server/?view=sharepoint-server-ps&quot; target=&quot;_blank&quot;&gt;documentation&lt;/a&gt;

&lt;h2&gt;PnP PowerShell&lt;/h2&gt;
This is a &lt;a href=&quot;https://learn.microsoft.com/en-us/powershell/sharepoint/sharepoint-pnp/sharepoint-pnp-cmdlets&quot; target=&quot;_blank&quot;&gt;PowerShell module&lt;/a&gt; with more than 600 commands to interact with SharepPoint Online, Microsoft Teams, Microsoft Entra ID, and etc. It is an open source and community provided library under Microsoft 365 platform community initiative. This module is useful for various team including SharePoint admnistrator, developer, power user and etc. The module is PnP.PowerShell and the command naming convention starts with PnP, as example 

&lt;pre class=&quot;brush:ps&quot;&gt;Install-Module PnP.PowerShell -Scope CurrentUser
Connect-PnPOnline
&lt;/pre&gt;
List of PnP PowerShell commands and its &lt;a href=&quot;https://pnp.github.io/powershell/cmdlets/index.html&quot; target=&quot;_blank&quot;&gt;documentation&lt;/a&gt;

&lt;h2&gt;Microsoft Graph PowerShell&lt;/h2&gt;
This is a &lt;a href=&quot;https://learn.microsoft.com/en-us/powershell/microsoftgraph/overview?view=graph-powershell-1.0&quot; target=&quot;_blank&quot;&gt;PowerShell module&lt;/a&gt; (recommended PowerShell core, can be used in Windows PowerShell) that acting as API wrapper for Microsoft Graph APIs. Microsoft Graph API cover various aspects from Azure Entra ID, to other Microsoft services like SharePoint, Exchange, Outlook and etc. This module is useful for SharePoint admin, developer and power user. The main module is Microsoft.Graph and for SharePoint specific submodule is Microsoft.Graph.Sites. The command naming convention starts with Mg, as example

&lt;pre class=&quot;brush:ps&quot;&gt;Install-Module Microsoft.Graph -Scope CurrentUser 
Connect-MgGraph
&lt;/pre&gt;

List of SharePoint command in Microsoft Graph and its &lt;a href=&quot;https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.sites/?view=graph-powershell-1.0&quot; target=&quot;_blank&quot;&gt;documentation&lt;/a&gt;

&lt;h2&gt;PowerShell with CSOM / API&lt;/h2&gt;SharePoint .NET client-side object model (&lt;a href=&quot;https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/complete-basic-operations-using-sharepoint-client-library-code&quot; target=&quot;_blank&quot;&gt;CSOM&lt;/a&gt;) and REST API could be used in PowerShell as .NET object or wrapped as PowerShell command&lt;br /&gt;&lt;br /&gt;
The CSOM are available to download via &lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=42038&quot; target=&quot;_blank&quot;&gt;SharePoint Online Client Component SDK&lt;/a&gt; or &lt;a href=&quot;https://www.nuget.org/packages/Microsoft.SharePointOnline.CSOM&quot; target=&quot;_blank&quot;&gt;SharePoint .NET nuget package&lt;/a&gt;.
&lt;br /&gt;&lt;br /&gt;
as example to add/use CSOM (after the CSOM dll been downloaded and location the identified)
&lt;pre class=&quot;brush:ps&quot;&gt;Add-Type -Path &quot;C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.dll&quot;
Add-Type -Path &quot;C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\16\ISAPI\Microsoft.SharePoint.Client.Runtime.dll&quot;

$Context = New-Object Microsoft.SharePoint.Client.ClientContext($WebUrl) 
&lt;/pre&gt;

SharePoint REST API V1 (&lt;a href=&quot;https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/get-to-know-the-sharepoint-rest-service?tabs=csom&quot; target=&quot;_blank&quot;&gt;documentation&lt;/a&gt;)&lt;br /&gt;
SharePoint REST API V2 (&lt;a href=&quot;https://learn.microsoft.com/en-us/sharepoint/dev/apis/sharepoint-rest-graph&quot; target=&quot;_blank&quot;&gt;documentation&lt;/a&gt;)&lt;br /&gt;
SharePoint Sites Microsoft Graph REST API (&lt;a href=&quot;https://learn.microsoft.com/en-us/graph/api/resources/sharepoint?view=graph-rest-1.0&quot; target=&quot;_blank&quot;&gt;documentation&lt;/a&gt;)&lt;br /&gt;
</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/5312578162629481995/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2025/04/powershell-for-sharepoint.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/5312578162629481995'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/5312578162629481995'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2025/04/powershell-for-sharepoint.html' title='PowerShell for SharePoint'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-1852317416927573807</id><published>2022-11-23T23:40:00.041-06:00</published><updated>2023-01-10T23:59:02.613-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Self Learning"/><title type='text'>Microsoft Certified</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_wJD1CstaQ_9F70LSCAeOdKpodgF_L7dmRwzBbVLpQDgjwrTChKUta9FhVKIv2B-NPd5mjhBH3uLx7LJKUNpQ6ly-mz5QbNHGwR4VL_d1NiwZBcwonUc2Qfoypg0bs0KpN6jqq_RpCZL1Uq9AXgc89d8TWiL2Di1mxSm1VVVjn4voxIetWX2cDSgb1w/s367/mscer.png&quot; style=&quot;clear: left; display: block; float: left; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;137&quot; data-original-width=&quot;367&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_wJD1CstaQ_9F70LSCAeOdKpodgF_L7dmRwzBbVLpQDgjwrTChKUta9FhVKIv2B-NPd5mjhBH3uLx7LJKUNpQ6ly-mz5QbNHGwR4VL_d1NiwZBcwonUc2Qfoypg0bs0KpN6jqq_RpCZL1Uq9AXgc89d8TWiL2Di1mxSm1VVVjn4voxIetWX2cDSgb1w/s320/mscer.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

It has been a while since I last pursued a &lt;a href=&quot;https://www.travisgan.com/2015/12/microsoft-recertification.html&quot;&gt;Microsoft certification&lt;/a&gt;. 
&lt;br /&gt;&lt;br /&gt;
After a few years working as a developer, I focused primarily on SQL Server data platform and business intelligence. I learned from work experiences, Microsoft articles as well as various blogs from the awesome SQL Server communities. To test my level of knowledge and exposure in this field, I also took the challenge of certification for Microsoft Certified Solution Expert for Data Platform and Business Intelligence.
&lt;br /&gt;&lt;br /&gt;
As my role at work continue to evolve, I have been wearing multiple hats and at time switch hat. I found myself heavily involved in Azure AD, Okta, Azure cloud resources, Azure Devops, Github, App / API Gateway, etc.. in addition to SQL Server administration. 
&lt;br /&gt;&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;So again, I thought it might be a good idea to take up the challenge to the certification in these areas to gauge my knowledge and learn something new that I might not have exposed to previously. At the same time, also reap the benefit of increasing credibility to existing or new employers. Win win.
&lt;br /&gt;&lt;br /&gt;
For those who have not taken Microsoft certification for a while, Microsoft has make some changes on their websites and certification path. Microsoft now also provided free online study material in addition to instructor-led class. You can find the newest information at this &lt;a href=&quot;https://learn.microsoft.com/en-us/certifications/&quot; target=&quot;_blank&quot;&gt;Microsoft Learn certification site&lt;/a&gt;. In addition to this, at the moment, Microsoft also offers &lt;a href=&quot;https://esi.microsoft.com/&quot; target=&quot;_blank&quot;&gt;Enterprise Skills Initiative&lt;/a&gt;&amp;nbsp;to organization for their employees to learn and take the certification examination for free.
&lt;br /&gt;&lt;br /&gt;
I reviewed different Microsoft certifications and here are 3 that I felt that I have the experience or/and interested in.
&lt;br /&gt;&lt;br /&gt;
&lt;b&gt;Microsoft Azure Administrator&lt;/b&gt; (&lt;a href=&quot;https://learn.microsoft.com/en-us/certifications/azure-administrator/&quot;&gt;more details&lt;/a&gt;) &lt;br /&gt; 
Manage Azure identities and governance&lt;br /&gt;
Implement and manage storage&lt;br /&gt;
Deploy and manage Azure compute resources&lt;br /&gt;
Configure and manage virtual networking&lt;br /&gt;
Monitor and maintain Azure resources
&lt;br /&gt;&lt;br /&gt;

&lt;b&gt;Azure Solutions Architect Expert&lt;/b&gt; (&lt;a href=&quot;https://learn.microsoft.com/en-us/certifications/azure-solutions-architect/&quot;&gt;more details&lt;/a&gt;) &lt;br /&gt;
Design identity, governance, and monitoring solutions&lt;br /&gt;
Design data storage solutions&lt;br /&gt;
Design business continuity solutions&lt;br /&gt;
Design infrastructure solutions
&lt;br /&gt;&lt;br /&gt;

&lt;b&gt;DevOps Engineer Expert&lt;/b&gt; (&lt;a href=&quot;https://learn.microsoft.com/en-us/certifications/devops-engineer/&quot;&gt;more details&lt;/a&gt;) &lt;br /&gt; 
Develop an instrumentation strategy&lt;br /&gt;
Develop a Site Reliability Engineering (SRE) strategy&lt;br /&gt;
Develop a security and compliance plan&lt;br /&gt;
Manage source control&lt;br /&gt;
Facilitate communication and collaboration&lt;br /&gt;
Define and implement continuous integration&lt;br /&gt;
Define and implement a continuous delivery and release management strategy
&lt;br /&gt;&lt;br /&gt;

After few months of study and exploration, I am glad that in addition to the MSCE for Data Platform and Business Intelligence, I am now certified as Azure Administrator Associate, Azure Solutions Architect Expert and DevOps Engineer Expert.
&lt;br /&gt;&lt;br /&gt;
Happy holiday!</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/1852317416927573807/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2022/11/microsoft-certified.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/1852317416927573807'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/1852317416927573807'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2022/11/microsoft-certified.html' title='Microsoft Certified'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_wJD1CstaQ_9F70LSCAeOdKpodgF_L7dmRwzBbVLpQDgjwrTChKUta9FhVKIv2B-NPd5mjhBH3uLx7LJKUNpQ6ly-mz5QbNHGwR4VL_d1NiwZBcwonUc2Qfoypg0bs0KpN6jqq_RpCZL1Uq9AXgc89d8TWiL2Di1mxSm1VVVjn4voxIetWX2cDSgb1w/s72-c/mscer.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-5778135876911825064</id><published>2022-11-02T07:30:00.020-05:00</published><updated>2022-11-02T23:40:08.562-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Azure"/><category scheme="http://www.blogger.com/atom/ns#" term="PowerShell"/><title type='text'>Access Office 365 Exchange Online Mailbox with Client Credential Flow</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgY4u6bDrWYRgvIDY0NHB1U7fOSbiFsxmU1DqWnBjHU2WT9vgHvQtHvpp6ZIM6Rj3Mq5TOwmsgs4TbY61znomd51bMn0-P3h_oGRCgd-MlNJJjyeuCZdaTlDEHeKe437LiQtDW0k_WUvlAr79E-z5cbAuiddxljc-7JDykJ1oq0h5XfXdcOtzFNrS9HsA/s290/mail-oauth0.jpg&quot; style=&quot;clear: left; display: block; float: left; padding: 0em 15px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;174&quot; data-original-width=&quot;290&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgY4u6bDrWYRgvIDY0NHB1U7fOSbiFsxmU1DqWnBjHU2WT9vgHvQtHvpp6ZIM6Rj3Mq5TOwmsgs4TbY61znomd51bMn0-P3h_oGRCgd-MlNJJjyeuCZdaTlDEHeKe437LiQtDW0k_WUvlAr79E-z5cbAuiddxljc-7JDykJ1oq0h5XfXdcOtzFNrS9HsA/s320/mail-oauth0.jpg&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
As of this writing, Microsoft has disabled the basic authentication to Exchange Online, the alternate more secure method to authenticate and access Exchange Online is via &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow&quot; target=&quot;_blank&quot;&gt;Client Credential flow&lt;/a&gt; using OAuth token. OAuth (OIDC) Client Credential flow is typically used for background (eg. windows services or daemon) process without interaction with user.
&lt;br /&gt;&lt;br /&gt;
Microsoft provides different APIs to access Exchange Online. To authenticate against these APIs, we need OAuth access token with appropriate permissions when invoking these APIs. And this type of access token is obtained from Azure AD. 
&lt;br /&gt;&lt;br /&gt;
&lt;span&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/span&gt;
A little background, in order to access Microsoft resource eg. Azure, Exchange Online, SharePoint via Microsoft APIs, the entry point via an identity (or service principal). In this case, it would be by creating an Azure AD app registration. 
&lt;br /&gt;&lt;br /&gt;
In Azure AD, an API is granted either with delegated permission or application permission. When using Client Credential flow in Azure AD, the access token is acquired as application identity with only the application permission (delegated permission is not used since there is no user). The scope used during access token request is with *./default scope, for example https://graph.microsoft.com/.default or https://outlook.office365.com/.default depending on which API you are using.
&lt;br /&gt;&lt;br /&gt;To configure the access restriction of the application, we will need the Exchange Online PowerShell module (ExchangeOnlineManagement). It is the Microsoft Exchange Online administrative interface to Microsoft Exchange Online. To install the PowerShell module,
&lt;pre class=&quot;brush:ps&quot;&gt;Install-Module -Name ExchangeOnlineManagement -Scope CurrentUser&lt;/pre&gt;
Connect to the organization Microsoft Exchange Online, enter the credential in the interactive prompt
&lt;pre class=&quot;brush:ps&quot;&gt;Connect-ExchangeOnline&lt;/pre&gt;
Depending if your account has the appropriate role in the organization eg. global admin, exchange admin, etc. it appears to subsequently downloads dynamic module to user path like this, C:\Users\abc\AppData\Local\Temp\tmpEXO_xyzabc10.a12\ where the module seems to contain similar cmdlets like the ones in PowerShell module. 
&lt;br /&gt;&lt;br /&gt;
To locate the dynamic module, you can lookup some cmdlet eg. 
&lt;pre class=&quot;brush:ps&quot;&gt;Get-Command Get-ServicePrincipal&lt;/pre&gt;
With all the tools and some background covered, here are the two examples of using OAuth to access Office 365 Exchange Online mailbox. One using Legacy Office 365 Exchange Online API to access and read mails of a mailbox via IMAP/POP, and another using Graph API to send mail as the user. Please be aware these are not the only ways, but simply an example. Microsoft provide several other ways to achieve to read and send mails with OAuth.
&lt;br /&gt;&lt;br /&gt;
In order to access the APIs, we need a valid access token with appropriate permission (scope) from Azure AD. First we need to create a application service principal via app registration.
&lt;br /&gt;&lt;br /&gt;
Login to Azure portal, go to Azure Active Directory. Select app registration and select new registration.
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9UKjFVDbppup0IERkjr2-yLiUyzg4bqMiJuw53PBYaX2ZOfhDj4qO54F3KjLKcAKh2avx-jbt2pkr7cvU58_KQkqNlm6B7Gi2ZzHccIcd3rwssJf_4ylbHVEbjkcXdd1nzvEe14zwegGyZ6xnR4rtmqbjLgOfyV6q1Ji0t25HmKC9G7d0A5OedZteKw/s1600/mail-oauth1.png&quot; style=&quot;display: block; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;293&quot; data-original-width=&quot;919&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9UKjFVDbppup0IERkjr2-yLiUyzg4bqMiJuw53PBYaX2ZOfhDj4qO54F3KjLKcAKh2avx-jbt2pkr7cvU58_KQkqNlm6B7Gi2ZzHccIcd3rwssJf_4ylbHVEbjkcXdd1nzvEe14zwegGyZ6xnR4rtmqbjLgOfyV6q1Ji0t25HmKC9G7d0A5OedZteKw/s1600/mail-oauth1.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

Since the application will be using client credential flow, the default option without any redirect URI is sufficient.
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0mS1tPh45Gm4o6vzcJT32pu9A3cS_je2nm7vM7R9DCr23go-26XGplwEku2ffB4yaugATfGWj98BqSNdZO65EgFzNX7_nIBXinMan8Vt0dwVABY2Kp2NOMYcIUfoksga_w5j9YnbvH3D2qBkfOsf94Tr53-QnSt7As73Y-fTpLNxkmQ1NvUo0RdSXJw/s1600/mail-oauth2.png&quot; style=&quot;display: block; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;664&quot; data-original-width=&quot;763&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0mS1tPh45Gm4o6vzcJT32pu9A3cS_je2nm7vM7R9DCr23go-26XGplwEku2ffB4yaugATfGWj98BqSNdZO65EgFzNX7_nIBXinMan8Vt0dwVABY2Kp2NOMYcIUfoksga_w5j9YnbvH3D2qBkfOsf94Tr53-QnSt7As73Y-fTpLNxkmQ1NvUo0RdSXJw/s1600/mail-oauth2.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

Next we will need to create a Client Secret. Select certificates &amp;amp; secrets on the left menu and click on Client secrets and create a new client secret. Keep the Client Secret for later use.
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlmfxTTvDrV7huKGl_GoyLF6o-GyLS2fgWNpJAuws14bhstWb-0MnxhETYUyIuo0M4fq_DIVGpeJvv6uFzJs5-n_TgMQB3mGCCvnePj4rfhydEs82noYNtaUMHX8P2A-Bf-21A0y0QxgU8wKbuhq4tuDRMr4uszO6TWAP9N9LIDA0ggObrltv1zpGh2A/s830/mail-oauth4.png&quot; style=&quot;display: block; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;642&quot; data-original-width=&quot;830&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlmfxTTvDrV7huKGl_GoyLF6o-GyLS2fgWNpJAuws14bhstWb-0MnxhETYUyIuo0M4fq_DIVGpeJvv6uFzJs5-n_TgMQB3mGCCvnePj4rfhydEs82noYNtaUMHX8P2A-Bf-21A0y0QxgU8wKbuhq4tuDRMr4uszO6TWAP9N9LIDA0ggObrltv1zpGh2A/s320/mail-oauth4.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

Next we need to grant API permission to the application. Select API Permissions on the left menu, and add a permission. 
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirMx2p1k3JppaL4Th2axKWYKNY0MRW_t_EA40rxHmjnHwG6094gIgKw6gg0TVtjWqHzAI5Us6_3XDQ3Gesp_Qrt_bt6cwc-MCm2J6-oqqOpg9gR8PmcL-sUvi3Ya1LUq1p5HtM3xspn5oPfusvyCHb4Jwgyn-NuiphHdiuljO1lZTd5V3LHYVIxOrR5Q/s861/mail-oauth5.png&quot; style=&quot;display: block; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;676&quot; data-original-width=&quot;861&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEirMx2p1k3JppaL4Th2axKWYKNY0MRW_t_EA40rxHmjnHwG6094gIgKw6gg0TVtjWqHzAI5Us6_3XDQ3Gesp_Qrt_bt6cwc-MCm2J6-oqqOpg9gR8PmcL-sUvi3Ya1LUq1p5HtM3xspn5oPfusvyCHb4Jwgyn-NuiphHdiuljO1lZTd5V3LHYVIxOrR5Q/s320/mail-oauth5.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

For Graph API, select Microsoft Graph, click on Application Permissions.
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4P4rUfGTa7Mhj7QJiNqYX6hUNWIP-yb4eMLVQehSUPMfEkEeOLRGfW0d0w4uVqkkX7dG9nQ8ja3n2cCxIB9mZcj_LWcQmxy5_6YlWmEuyFORwtn2gghXDI9vcOB_hBHgo4Aui6FLUcBr9TQSSApvTW3X3YRjIqJKy5e3VX_GYweOMXVmtGOH7aKGHjw/s1248/mail-oauth6.png&quot; style=&quot;display: block; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;396&quot; data-original-width=&quot;1248&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4P4rUfGTa7Mhj7QJiNqYX6hUNWIP-yb4eMLVQehSUPMfEkEeOLRGfW0d0w4uVqkkX7dG9nQ8ja3n2cCxIB9mZcj_LWcQmxy5_6YlWmEuyFORwtn2gghXDI9vcOB_hBHgo4Aui6FLUcBr9TQSSApvTW3X3YRjIqJKy5e3VX_GYweOMXVmtGOH7aKGHjw/s320/mail-oauth6.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

Search for mail, and check Mail.Send. Notice that the permission will allow the application to send mail as any user. We will restrict its access via application access policy described later.
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBIvRfByCTh06muzceXokw46TH6Ft1HulGpMTwnMEjtG6hhpdY7Kq7z2rTF9R1_-5scRdA_IH8BE2cNOY00mz3zOCVyj6fo_52pWh1wzMaTHMj0y11bEttO7CCZs0SMum9QtOL4watNDQWLpfHC1BbLDz41apWa1d97fXwQan9G2_ZgdBHxiZLTMSDKA/s1138/mail-oauth7.png&quot; style=&quot;display: block; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;719&quot; data-original-width=&quot;1138&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBIvRfByCTh06muzceXokw46TH6Ft1HulGpMTwnMEjtG6hhpdY7Kq7z2rTF9R1_-5scRdA_IH8BE2cNOY00mz3zOCVyj6fo_52pWh1wzMaTHMj0y11bEttO7CCZs0SMum9QtOL4watNDQWLpfHC1BbLDz41apWa1d97fXwQan9G2_ZgdBHxiZLTMSDKA/s320/mail-oauth7.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

For Office 365 Exchange Online API, select APIs my organization uses tab, search for office and select Office 365 Exchange Online. click on Application Permissions. 
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgm3N0Tw2vZRmwFZOKQiF5r9Q4C2dqNArXfbgjEOKdynAgYq3pDK-NN-48iqgIV-u3LCO-yQ20MbXLOcuJxzKBdZY09ZH3jxoYRjXr4Fn1ieh0DtynrmqWBS-8mlMw72jf5inlHSaceNBJGA7eVjtzp3V9_fD6QEEuLkISsaN6WgdwP6_wJTe28ZyE4g/s617/mail-oauth8.png&quot; style=&quot;display: block; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;513&quot; data-original-width=&quot;617&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgm3N0Tw2vZRmwFZOKQiF5r9Q4C2dqNArXfbgjEOKdynAgYq3pDK-NN-48iqgIV-u3LCO-yQ20MbXLOcuJxzKBdZY09ZH3jxoYRjXr4Fn1ieh0DtynrmqWBS-8mlMw72jf5inlHSaceNBJGA7eVjtzp3V9_fD6QEEuLkISsaN6WgdwP6_wJTe28ZyE4g/s320/mail-oauth8.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

Search for and check IMAP.AccessAsApp (if you need to access via POP, check POP.AccessAsApp). 
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQHrUHQHc1D6Rrtp5n7K4QZsof9GU4fy9uElknZ_2dr_Bi6kWS_nfl7IBxKaoGzSev8yPmWRcuvhAcFXO-_twVQFGsJxllK7sR_HOpp0GzCh9-QM3N_b44fCWrF82bfx4hLdX4-pFwxtXnpUdfSEZ8IpPaxlfUtBQU9YazewWd173bZY_biaOgl7RC8w/s1232/mail-oauth9.png&quot; style=&quot;display: block; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;735&quot; data-original-width=&quot;1232&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQHrUHQHc1D6Rrtp5n7K4QZsof9GU4fy9uElknZ_2dr_Bi6kWS_nfl7IBxKaoGzSev8yPmWRcuvhAcFXO-_twVQFGsJxllK7sR_HOpp0GzCh9-QM3N_b44fCWrF82bfx4hLdX4-pFwxtXnpUdfSEZ8IpPaxlfUtBQU9YazewWd173bZY_biaOgl7RC8w/s320/mail-oauth9.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

Grant admin consent to the permissions added.
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgF44MZBMy0KNfK6Z7euJtrcxwZUZJlkFHydlhruqRtleCpS0NZ_U1rL9yWOmZA1WPCCB7sxrfpyrEiYaZwgU811Rouc-EkO1IOksNkrK8uqHAqOmTxUIJ59wjfx8QnkYONIZ5Lpi5Gy7E9XQAM5VaPEqVtUPq5LyKuzmNYaxjwPNTN4Oo1_T1BGG5TkA/s1851/mail-oauth10.png&quot; style=&quot;display: block; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;718&quot; data-original-width=&quot;1851&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgF44MZBMy0KNfK6Z7euJtrcxwZUZJlkFHydlhruqRtleCpS0NZ_U1rL9yWOmZA1WPCCB7sxrfpyrEiYaZwgU811Rouc-EkO1IOksNkrK8uqHAqOmTxUIJ59wjfx8QnkYONIZ5Lpi5Gy7E9XQAM5VaPEqVtUPq5LyKuzmNYaxjwPNTN4Oo1_T1BGG5TkA/s320/mail-oauth10.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

We need to get the ApplicationID (ClientID) and ObjectID of the application service principal for later use. Go to Enterprise Applications on the left menu
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvxg5yYV6zH33YBYxibq8JbgkkoNSZrEuzuiRjgAzyMnY1q23rOu5qBipl0Mdspi3RF_DGEqu233wLAaCm8DoLUVsE8MrkYqdNzGctjJk7dhrmVr_GmVnoB85t6LIG12wIc8bG9inzIEVV_AvAnftkO_uUZwLb_FjyRvI4_hrD-b_WkTC8IFtH1LdRow/s436/mail-oauth11.png&quot; style=&quot;display: block; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;436&quot; data-original-width=&quot;341&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvxg5yYV6zH33YBYxibq8JbgkkoNSZrEuzuiRjgAzyMnY1q23rOu5qBipl0Mdspi3RF_DGEqu233wLAaCm8DoLUVsE8MrkYqdNzGctjJk7dhrmVr_GmVnoB85t6LIG12wIc8bG9inzIEVV_AvAnftkO_uUZwLb_FjyRvI4_hrD-b_WkTC8IFtH1LdRow/s320/mail-oauth11.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

Search for the application and note down the ObjectID and ApplicationID.
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipGYcdFx2i_qsVJS5lquV2ZFLRuf0B5W5wCGGzKF_BClHJZ76MQxymuO2ibCISzavllQCVd3cqO-lm71W07Ro7bO1P_fgB7thgvvzmb1O4e2QOZTfCRR2BEXM_gbsPLCDZyk4CilmLNVpY-1axeXO8woT2-7Mu22d8QYZUeXA5zNMHrCMTA4C2yG0oQw/s1425/mail-oauth12.png&quot; style=&quot;display: block; padding: 1em 0px; text-align: center;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;484&quot; data-original-width=&quot;1425&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipGYcdFx2i_qsVJS5lquV2ZFLRuf0B5W5wCGGzKF_BClHJZ76MQxymuO2ibCISzavllQCVd3cqO-lm71W07Ro7bO1P_fgB7thgvvzmb1O4e2QOZTfCRR2BEXM_gbsPLCDZyk4CilmLNVpY-1axeXO8woT2-7Mu22d8QYZUeXA5zNMHrCMTA4C2yG0oQw/s320/mail-oauth12.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

The app registration is now completed. If needed, you can verify if the access token could be obtained. It is important to know ahead which API you are trying to invoke so you can obtain an access token with correct audience (aud claim in the JWT). For Graph API, use https://graph.microsoft.com/.default as the scope; And for Office 365 Exchange Online API, use https://outlook.office365.com/.default as the scope. Here is an example acquiring an access token for Graph API usage.&lt;br /&gt;
&lt;pre class=&quot;brush:ps&quot;&gt;$tenantid = &#39;your tenant id&#39;
$clientid = &#39;your app client id&#39;
$clientsecret = &#39;your app client secret&#39;

$token = Invoke-RestMethod -Method Post -ContentType &#39;application/x-www-form-urlencoded&#39; -Uri &quot;https://login.microsoftonline.com/$tenantid/oauth2/v2.0/token&quot; -Body @{
    client_id = $clientid
    client_secret = $clientsecret
    scope = &#39;https://graph.microsoft.com/.default&#39;
    grant_type = &#39;client_credentials&#39;
}

$token.access_token
&lt;/pre&gt;

You can copy/paste to view the decoded access token (JWT) &lt;a href=&quot;https://jwt.ms&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. See the below example of the aud claim and scope with Mail.Send role (permission) configured earlier.
&lt;br/&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiVAQ5CK5O0ZwElmRAuwZjLOqFOK-NM_32cX0n57WtFJySlB4W8pSd2fLDcSbogiu-jpo60mfuBO_cVl6Gnf3VMBSE4ywKP0b1GkCTp0xITKQvsW04N9AEIXXoXKMLygtsqpcQWJzXgCvmHs6hS3_JY4qWa1h1-3CrVLBzk6s1r06EZ-0X8mL-57IGRA/s976/mail-oauth13.png&quot; style=&quot;display: block; padding: 1em 0; text-align: center; &quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;320&quot; data-original-height=&quot;830&quot; data-original-width=&quot;976&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiVAQ5CK5O0ZwElmRAuwZjLOqFOK-NM_32cX0n57WtFJySlB4W8pSd2fLDcSbogiu-jpo60mfuBO_cVl6Gnf3VMBSE4ywKP0b1GkCTp0xITKQvsW04N9AEIXXoXKMLygtsqpcQWJzXgCvmHs6hS3_JY4qWa1h1-3CrVLBzk6s1r06EZ-0X8mL-57IGRA/s320/mail-oauth13.png&quot;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;b&gt;Legacy Office 365 Exchange Online APIs&lt;/b&gt;&lt;br /&gt;
In the API permission, we granted IMAP.AccessAsApp or POP.AccessAsApp access to the application service principal. This allows the application to access IMAP or POP. However, the application can&#39;t access any mailbox yet. And to allow it to access specific mailbox, we first need to add the service principal in exchange online and then grant that service principal permission (delegation) to the mailbox.
&lt;br /&gt;&lt;br /&gt;
Create an exchange service principal. Use the AppId and ObjectId from the Azure AD application registration.
&lt;br /&gt;
&lt;pre class=&quot;brush:ps&quot;&gt;New-ServicePrincipal -AppId c33aa7**-****-****-****-******ffca0d -ServiceId c21b49**-****-****-****-******f33f47 -Organization &#39;tenant id&#39; -DisplayName mailtest
&lt;/pre&gt;
To grant permission for service principal to a mailbox (add delegation),
&lt;pre class=&quot;brush:ps&quot;&gt;Add-MailboxPermission -Identity &quot;usera@emaildomain.com&quot; -User c21b49**-****-****-****-******f33f47 -AccessRights FullAccess
&lt;/pre&gt;
To check the existing permission (delegation) on a mailbox,
&lt;pre class=&quot;brush:ps&quot;&gt;Get-MailboxPermission -Identity &quot;usera@emaildomain.com&quot;
&lt;/pre&gt;
More information to configure for IMAP/POP can be found &lt;a href=&quot;https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth#register-service-principals-in-exchange&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.
&lt;br /&gt;&lt;br /&gt;
To verify the IMAP access with the application, you can use this &lt;a href=&quot;https://github.com/DanijelkMSFT/ThisandThat/blob/main/Get-IMAPAccessToken.ps1&quot; target=&quot;_blank&quot;&gt;PowerShell script&lt;/a&gt; written by one of the Microsoft employee for verification. 
&lt;br /&gt;
&lt;pre class=&quot;brush:ps&quot;&gt;
$tenantid = &#39;tenant id&#39;
$clientid = &#39;c21b49**-****-****-****-******f33f47&#39;
$clientsecret = &#39;***************************&#39;
$maibox = &#39;usera@emaildomain.com&#39;

.\Get-IMAPAccessToken.ps1 -tenantID $tenantid -clientId $cliendid -clientsecret $clientsecret -targetMailbox $mailbox
&lt;/pre&gt;
&lt;br/&gt;
&lt;b&gt;Graph API&lt;/b&gt;&lt;br /&gt;
For Graph API, understand that granting API Application Permission usually allow it access to all tenant resources. For example, Mail.Send permission allows it to send mail as any user; Mail.Read permission allows it to read mail in all mailboxes. To restrict it to specific mailbox / security group, creates an Application Access Policy.
&lt;br /&gt;&lt;br /&gt;
Note, as of this writing, if there is no application access policy existed, the get application access policy cmdlet returns an error.
&lt;pre class=&quot;brush:ps&quot;&gt;Get-ApplicationAccessPolicy&lt;/pre&gt;
&lt;span style=&quot;color: red;&quot;&gt;Write-ErrorMessage : Ex6F9304|Microsoft.Exchange.Configuration.Tasks.ManagementObjectNotFoundException|The operation
couldn&#39;t be performed because object &#39;OU=abcefg.onmicrosoft.com,OU=Microsoft Exchange Hosted
Organizations,DC=ABCDE123456,DC=PROD,DC=OUTLOOK,DC=COM\*&#39; couldn&#39;t be found on
&#39;AABBCC112233000.ABCDE123456.PROD.OUTLOOK.COM&#39;.
At C:\Users\abc\AppData\Local\Temp\tmpEXO_xyzabc10.a12\tmpEXO_xyzabc10.a12.psm1:1113 char:13
+             Write-ErrorMessage $ErrorObject $IsFromBatchingRequest
+             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Get-ApplicationAccessPolicy], ManagementObjectNotFoundException
    + FullyQualifiedErrorId : [Server=ABCDEFGHIJ123,RequestId=abcd-efgh-3e96-e79a-123456789012,TimeStamp=Sat, 31 S
   ep 2022 25:02:47 GMT],Write-ErrorMessage&lt;/span&gt;  
&lt;br /&gt;
&lt;br/&gt;
To add application access policy to restrict application access to certain mailbox or security group, use the AppId during the Azure AD application registration. The PolicyScopeGroupId is the mailbox or security group that can be only access by the app.
&lt;pre class=&quot;brush:ps&quot;&gt;New-ApplicationAccessPolicy -AccessRight RestrictAccess -AppId c33aa7**-****-****-****-******ffca0d -PolicyScopeGroupId usera@emaildomain.com -Description &quot;restrict mailtest access&quot;&lt;/pre&gt;

To test the policy against the restricted account or/and other account, the AccessCheckResult returns if the effective access is denied or granted.
&lt;pre class=&quot;brush:ps&quot;&gt;Test-ApplicationAccessPolicy -AppId c33aa7**-****-****-****-******ffca0d -Identity usera@emaildomain.com
&lt;/pre&gt;
From the experience, even thought the test command responded the testing result of the policy, in practical the policy could take more than 1 hour to be effective. From personal experience, it takes about an hour.
&lt;br /&gt;&lt;br /&gt;
To list all existing application access policy,
&lt;pre class=&quot;brush:ps&quot;&gt;Get-ApplicationAccessPolicy
&lt;/pre&gt;
Please note that the Application Access Policy only applicable to certain Microsoft Graph application permission. As of the time of writing,
&lt;br /&gt;&lt;br /&gt;
Mail.Read&lt;br /&gt;
Mail.ReadBasic&lt;br /&gt;
Mail.ReadBasic.All&lt;br /&gt;
Mail.ReadWrite&lt;br /&gt;
Mail.Send&lt;br /&gt;
MailboxSettings.Read&lt;br /&gt;
MailboxSettings.ReadWrite&lt;br /&gt;
Calendars.Read&lt;br /&gt;
Calendars.ReadWrite&lt;br /&gt;
Contacts.Read&lt;br /&gt;
Contacts.ReadWrite&lt;br /&gt;
&lt;br /&gt;Exchange Web Services permission scope: full_access_as_app.&lt;br /&gt;&lt;br /&gt;
More information can be found &lt;a href=&quot;https://learn.microsoft.com/en-us/graph/auth-limit-mailbox-access&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/5778135876911825064/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2022/11/access-office-365-exchange-online.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/5778135876911825064'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/5778135876911825064'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2022/11/access-office-365-exchange-online.html' title='Access Office 365 Exchange Online Mailbox with Client Credential Flow'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgY4u6bDrWYRgvIDY0NHB1U7fOSbiFsxmU1DqWnBjHU2WT9vgHvQtHvpp6ZIM6Rj3Mq5TOwmsgs4TbY61znomd51bMn0-P3h_oGRCgd-MlNJJjyeuCZdaTlDEHeKe437LiQtDW0k_WUvlAr79E-z5cbAuiddxljc-7JDykJ1oq0h5XfXdcOtzFNrS9HsA/s72-c/mail-oauth0.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-6432416055922322913</id><published>2022-08-17T07:00:00.007-05:00</published><updated>2023-01-12T22:29:07.631-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Github"/><category scheme="http://www.blogger.com/atom/ns#" term="PowerShell"/><title type='text'>GitHub Create Private Fork of a Public Repository</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmPyVgHciGcaKCXtOsgMRJfdkrGF1cMfWVfU0MFBV8EVsVCghHbY6QtoSPkA03Jr5GBuWQGrazUC3d9pMxwYUDwIQrd7H_MV1RCaT9DMv6hph1j_rTfPLqZwxaNNj7NucQeGP6Lr9MGU1guGd8CY0Bn802vDuvy66OHpCNKonxPLCPlv84YPypoW0kFw/s1600/clone.jpg&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; data-original-height=&quot;224&quot; data-original-width=&quot;224&quot; height=&quot;100&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmPyVgHciGcaKCXtOsgMRJfdkrGF1cMfWVfU0MFBV8EVsVCghHbY6QtoSPkA03Jr5GBuWQGrazUC3d9pMxwYUDwIQrd7H_MV1RCaT9DMv6hph1j_rTfPLqZwxaNNj7NucQeGP6Lr9MGU1guGd8CY0Bn802vDuvy66OHpCNKonxPLCPlv84YPypoW0kFw/w200-h200/clone.jpg&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

When making a fork of a GitHub public repository, the fork repository is set up with public visibility. However, under certain circumstances, it may be desirable to make the fork repository with private or internal (in an organization) visibility. 
&lt;br /&gt;&lt;br /&gt;
Here is one way to indirectly &#39;forking&#39; a repository with with private or internal visibility.  
&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;
&lt;span&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/span&gt;
First creates a new repo (private-repo.git) in Github through the ui or cli.
&lt;br /&gt;&lt;br /&gt;
Then, clone a public repo to local. &lt;br /&gt;
--bare option is use to make a copy all branches and tags without mapping them to the remotes origin
&lt;br /&gt;&lt;br /&gt;
Then, push the local to the private repo&lt;br /&gt;
--mirror option is use to push all refs
&lt;br /&gt;&lt;br /&gt;
Remove the local copy of public repo .git directory.

&lt;pre class=&quot;brush:ps&quot;&gt;git clone --bare https://github.com/original_user/public-repo.git

cd public-repo.git
git push --mirror https://github.com/your_user/private-repo.git

cd ..
rm -rf public-repo.git
&lt;/pre&gt;

To perform any changes, &lt;br /&gt;&lt;br /&gt;
First clone the private copy to local.&lt;br /&gt;
Make changes and push the changes up to private repo.

&lt;pre class=&quot;brush:ps&quot;&gt;git clone https://github.com/your_user/private-repo.git
cd private-repo

git commit
git push origin master
&lt;/pre&gt;

For any subsequent changes on the private repo, pull it down to local

&lt;pre class=&quot;brush:ps&quot;&gt;git pull origin # or git pull origin branch

# or using fetch/merge

git fetch origin
git merge origin/master # or intended remote branch
&lt;/pre&gt;

To update from public repo to private repo. &lt;br /&gt;
First add the public repo to remote. Now there should be two remotes.&lt;br /&gt;
One for origin pointing to the private repo (your_user), and one as public pointing to public repo (original_user)
&lt;br /&gt;&lt;br /&gt;
Then pull the latest codes from public to local&lt;br /&gt;
Then push to the private repo
&lt;br /&gt;&lt;br /&gt;
To also update all the tags from the public repo to private repo&lt;br /&gt;
fetch the public repo tags&lt;br /&gt;
push the tags to private repo

&lt;pre class=&quot;brush:ps&quot;&gt;cd private-repo
git remote add public https://github.com/original_user/public-repo.git

# check list of remote
git remove -v

# Creates a merge commit
git pull public master 

git push origin master

git fetch public --tags
git push origin --tags

# if there is a need to remove the local tag to let public tag take precendent
git tag -d &amp;lt;tag&amp;gt;

# if there is a need to remove the remote tag
git push origin --delete &amp;lt;tag&amp;gt;
&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/6432416055922322913/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2022/08/github-create-private-fork-of-public.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/6432416055922322913'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/6432416055922322913'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2022/08/github-create-private-fork-of-public.html' title='GitHub Create Private Fork of a Public Repository'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmPyVgHciGcaKCXtOsgMRJfdkrGF1cMfWVfU0MFBV8EVsVCghHbY6QtoSPkA03Jr5GBuWQGrazUC3d9pMxwYUDwIQrd7H_MV1RCaT9DMv6hph1j_rTfPLqZwxaNNj7NucQeGP6Lr9MGU1guGd8CY0Bn802vDuvy66OHpCNKonxPLCPlv84YPypoW0kFw/s72-w200-h200-c/clone.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-305053587123761006</id><published>2021-04-08T23:22:00.010-05:00</published><updated>2021-04-09T12:56:12.586-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Azure"/><category scheme="http://www.blogger.com/atom/ns#" term="PowerShell"/><title type='text'>Create Sharepoint Site with Azure Function using PowerShell</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUqbWYhEWpvidKE_tnDqoXWLmIZH9GrX4qRrPchnHj564uDVeKDUv7jEyaADNRBt6yDH6w6BBMeVigKwwtaBx_ZrRAVyQU6eZZ8JcaQN5kfwUIdaTuspWgqyYKzm93IJ0m7QtRE03OHeXe/&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;634&quot; data-original-width=&quot;695&quot; height=&quot;183&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUqbWYhEWpvidKE_tnDqoXWLmIZH9GrX4qRrPchnHj564uDVeKDUv7jEyaADNRBt6yDH6w6BBMeVigKwwtaBx_ZrRAVyQU6eZZ8JcaQN5kfwUIdaTuspWgqyYKzm93IJ0m7QtRE03OHeXe/w200-h183/image.png&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Business Case&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Create SharePoint site based on user requirement without SharePoint Admin manually performing the task.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Design&lt;/b&gt;&lt;br /&gt;&lt;div&gt;A SharePoint List is created and shared with designated users to enter the desired SharePoint site criteria. Using an Azure function with PnP PowerShell module (compatible with PowerShell core in Azure function) to periodically check if any new entry in the SharePoint List and create SharePoint site based on the user entered criteria.&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Create a SharePoint List&lt;/b&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;Create a new SharePoint List. In this example, it is named as NewSiteList.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ1iiRfaH2HRmNJiDGz8k0GSBstz53dahE7wW6vzr5ORoSK9bGQwmxNJTi70KYgB33ATWYaZda1RCicPzFYzvXhTP_4lohh8X6Yc2rZr15rcWv9PP3-tqrYJ8KZpTDNuHud47CDviTbblq/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;555&quot; data-original-width=&quot;380&quot; height=&quot;240&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ1iiRfaH2HRmNJiDGz8k0GSBstz53dahE7wW6vzr5ORoSK9bGQwmxNJTi70KYgB33ATWYaZda1RCicPzFYzvXhTP_4lohh8X6Yc2rZr15rcWv9PP3-tqrYJ8KZpTDNuHud47CDviTbblq/&quot; width=&quot;164&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Create necessary fields/columns to capture new SharePoint site criteria.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0nKWQLVPDBxhYLmNMbFmeLq8n0zc-1czeFGclpnhgEDBhwZLUUfv1PACsCWzkTnIMxXcHysykX7GW8HLcVGr3Ewj-F6KXSQs_NhNEjEty1n1QUzj9L7cYpfRiWngENwKw2q1FEW4GcvuX/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;314&quot; data-original-width=&quot;1656&quot; height=&quot;122&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0nKWQLVPDBxhYLmNMbFmeLq8n0zc-1czeFGclpnhgEDBhwZLUUfv1PACsCWzkTnIMxXcHysykX7GW8HLcVGr3Ewj-F6KXSQs_NhNEjEty1n1QUzj9L7cYpfRiWngENwKw2q1FEW4GcvuX/w640-h122/image.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Create an account to access SharePoint List&lt;/b&gt;&lt;/div&gt;Create an account in Azure AD which would be used by the Azure function to access the SharePoint List. Similar account is also used to create SharePoint site.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;b&gt;Grant account permission&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Grant the account read (and write access in needed to update the list) access to the list.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5C27OatrsKiXsl4Zzzz0EKjXQhXriH4ac3s9h3Br0RDomXFqf496SCGb7pdOvrUrcOke3LLA4JxkxIYAHD9KGoqjZpD5OjrZf27yHJlTnT-DJPf-yIVdgqFleyrdMCrSHSPcE63ZTp4cR/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;505&quot; data-original-width=&quot;517&quot; height=&quot;240&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5C27OatrsKiXsl4Zzzz0EKjXQhXriH4ac3s9h3Br0RDomXFqf496SCGb7pdOvrUrcOke3LLA4JxkxIYAHD9KGoqjZpD5OjrZf27yHJlTnT-DJPf-yIVdgqFleyrdMCrSHSPcE63ZTp4cR/&quot; width=&quot;246&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Check the setting where user can create site or set site creation permission accordingly.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXAWS2UAp2Rc7_Ap9prXyTW05nMgg_IH0gtGkFYL9rKnHV0sWxCRr9QzbDTO28D4sXuWZHfeMYtSDEw0mXSRa5IMBorWdUUuE2d90QIIFj_fUyGGBgxTWqVwcSVVjs72KdK7N30Zn97UPP/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;493&quot; data-original-width=&quot;1603&quot; height=&quot;196&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXAWS2UAp2Rc7_Ap9prXyTW05nMgg_IH0gtGkFYL9rKnHV0sWxCRr9QzbDTO28D4sXuWZHfeMYtSDEw0mXSRa5IMBorWdUUuE2d90QIIFj_fUyGGBgxTWqVwcSVVjs72KdK7N30Zn97UPP/w640-h196/image.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;Create Azure function&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Select code and PowerShell Core as runtime stack with PowerShell 7.0&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgo8UF0rrD8gB2MIeSz_pDcGEUjVw-jL4SNhETbDilkCe-DwknYREQzuQIfVyCxsp6cvUeAx6v_nQeIgEfjbYV8om8Yahkuexu3r49SMAaaZh8SIGEuO3bXfPWfC9-DmgGUnOoxbMgPTwnJ/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;703&quot; data-original-width=&quot;1097&quot; height=&quot;410&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgo8UF0rrD8gB2MIeSz_pDcGEUjVw-jL4SNhETbDilkCe-DwknYREQzuQIfVyCxsp6cvUeAx6v_nQeIgEfjbYV8om8Yahkuexu3r49SMAaaZh8SIGEuO3bXfPWfC9-DmgGUnOoxbMgPTwnJ/w640-h410/image.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Store user credential in Azure Key Vault&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Azure Key Vault is used to store the credential safely. In order for Azure function to access the Secret in the Key vault, an identity is created for Azure function, and the identity is used for granting access permission in Key vault.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Under identity, on System assigned tab, create system managed identity by selecting On for the status. Copy the Object ID for later use.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhw-b5w-nmY0wUX2Kc0DDkaSZuXzpJ7P3Xe5XqxarNn-m0klOMKlTbriudRf0w-90h8j1sZ7ojcsMiSRJYdVcNoAE3t8_iH08zpL-XpmiFQsOfvl62ffVTMi_0j0jl3iozHGnpxkOLMO39g/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;704&quot; data-original-width=&quot;1057&quot; height=&quot;426&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhw-b5w-nmY0wUX2Kc0DDkaSZuXzpJ7P3Xe5XqxarNn-m0klOMKlTbriudRf0w-90h8j1sZ7ojcsMiSRJYdVcNoAE3t8_iH08zpL-XpmiFQsOfvl62ffVTMi_0j0jl3iozHGnpxkOLMO39g/w640-h426/image.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Create Key Vault.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-H-EBvdrnwTwUi8PfoP4u7xK9e07B-2ZMZUsPMybmn-6VSHuY46f6exsc7RnDyE0sOEZbktpJs552HAKpdxXAVy2u3B6yzJEw7WrUGc3yrPyse8UMOEGRhpuYE5dCNq8I7fFYwCbwgpUD/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;495&quot; data-original-width=&quot;762&quot; height=&quot;416&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-H-EBvdrnwTwUi8PfoP4u7xK9e07B-2ZMZUsPMybmn-6VSHuY46f6exsc7RnDyE0sOEZbktpJs552HAKpdxXAVy2u3B6yzJEw7WrUGc3yrPyse8UMOEGRhpuYE5dCNq8I7fFYwCbwgpUD/w640-h416/image.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Create a Secret each for user and password&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvfrSNzqYYOw9wutyMgrzX3wWmXXS9uN_PwVr6uutV85ii8lFIUtTIT81rqiQ2FJC2cfNbZsWmf-M0kzBOXpObSbX17FyiaVLwHkSGK3w4jbLrEsdFmSXZn1VuFvEY5dyNqtOiuPReA2-A/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;542&quot; data-original-width=&quot;916&quot; height=&quot;378&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvfrSNzqYYOw9wutyMgrzX3wWmXXS9uN_PwVr6uutV85ii8lFIUtTIT81rqiQ2FJC2cfNbZsWmf-M0kzBOXpObSbX17FyiaVLwHkSGK3w4jbLrEsdFmSXZn1VuFvEY5dyNqtOiuPReA2-A/w640-h378/image.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Copy the secret identifier for user and password&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXvjW5Bo-UkOV4FtBvhl7cbya7DgXyA-YUCaQNHfKaJdRwgqZ-YZHW89z-WHhONqvty6Qdo_tiXjiKqhVJJSGuhPsXHiK3ojc_Y0zVcHM45EyoIjafmW6_Qbib64bdEu7Zf4ajG32VMoSO/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;573&quot; data-original-width=&quot;676&quot; height=&quot;543&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhXvjW5Bo-UkOV4FtBvhl7cbya7DgXyA-YUCaQNHfKaJdRwgqZ-YZHW89z-WHhONqvty6Qdo_tiXjiKqhVJJSGuhPsXHiK3ojc_Y0zVcHM45EyoIjafmW6_Qbib64bdEu7Zf4ajG32VMoSO/w640-h543/image.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Grant Get access to Secret for the Azure function system managed identity created earlier&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0KpxuZEuxiuyy1SD2VNCAkobiVG55zAnvRBkER5doTIinpA48-J3LUpJEFmgChjUMuwzEYz4Fe8eCtzMqim0o7wDTGZf_aZePEzCWfKiWhOyt3Q-0Gwxd6bLS7ir8CSEdlEXcjwyhgQly/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;835&quot; data-original-width=&quot;552&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0KpxuZEuxiuyy1SD2VNCAkobiVG55zAnvRBkER5doTIinpA48-J3LUpJEFmgChjUMuwzEYz4Fe8eCtzMqim0o7wDTGZf_aZePEzCWfKiWhOyt3Q-0Gwxd6bLS7ir8CSEdlEXcjwyhgQly/w265-h400/image.png&quot; width=&quot;265&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;b&gt;Create new application settings each for the user and password in the Azure function.&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Back to Azure function, under Configuration, create new application setting with a name and the secret identifier created previously.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The format of the value is&amp;nbsp;&lt;/div&gt;&lt;div&gt;@Microsoft.KeyVault(SecretUri=&amp;lt;secret identifier&amp;gt;)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;More information in this article,&lt;/div&gt;&lt;div&gt;&lt;div&gt;https://docs.microsoft.com/en-us/azure/app-service/app-service-key-vault-references#reference-syntax&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIxDl-Ft6BxXlJ1RvADkfNSx7nudOT6D_GVxWJPiueq7_tCYgtffGxIi8dqoW8D3OSIOiZhUW7Zv7iqhP8p5DrNoYZgpyjkSyRraLsYG8umFQzknmkPV4LZsxIf9E3RdmCv9b4Jwz91Yxx/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;399&quot; data-original-width=&quot;1766&quot; height=&quot;144&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIxDl-Ft6BxXlJ1RvADkfNSx7nudOT6D_GVxWJPiueq7_tCYgtffGxIi8dqoW8D3OSIOiZhUW7Zv7iqhP8p5DrNoYZgpyjkSyRraLsYG8umFQzknmkPV4LZsxIf9E3RdmCv9b4Jwz91Yxx/w640-h144/image.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Configure PnP PowerShell Shell permission&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Admin permission is required to grant permission to PnP PowerShell shell to execute Microsoft Graph API on behalf of the user.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To grant permission, execute the following command locally with an Azure AD admin account (with Application Admin role), it would prompt to grant admin permission the first time.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Connect-PnPOnline -Url https://test.sharepoint.com/sites/test -Interactive&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Admin consent can be also granted through Azure AD enterprise application.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEji72Kx9wpck8fkSXfzuheCSliqb470trG_U1OTXxDCZP-68iX0Mu4t3w7oyBDqxfVz8HfjG6v42alMP09gYsEaJINi9x4DZydlQ1yNQZ8nto6foNG8Tnxjz8AW7nBpH8g9Wkw1E1sCWnK7/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;677&quot; data-original-width=&quot;1172&quot; height=&quot;370&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEji72Kx9wpck8fkSXfzuheCSliqb470trG_U1OTXxDCZP-68iX0Mu4t3w7oyBDqxfVz8HfjG6v42alMP09gYsEaJINi9x4DZydlQ1yNQZ8nto6foNG8Tnxjz8AW7nBpH8g9Wkw1E1sCWnK7/w640-h370/image.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Setup PnP PowerShell Module in Azure function&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Option 1, use dependency requirement specified in the requirement.psd1 file&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDTga_SYFdl5AQ_deD2FQ5AUethLdnKBuCE5f8B0qWmxtX1HXxVRaan7NTETwfXQLw8yA5rreu4uHpWumoZDSmdk3JD6EjZQATVpVZwuGoAUit1COpOclf7_k5QwQpZERATJ7tGzC9Ncfl/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;561&quot; data-original-width=&quot;1538&quot; height=&quot;234&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhDTga_SYFdl5AQ_deD2FQ5AUethLdnKBuCE5f8B0qWmxtX1HXxVRaan7NTETwfXQLw8yA5rreu4uHpWumoZDSmdk3JD6EjZQATVpVZwuGoAUit1COpOclf7_k5QwQpZERATJ7tGzC9Ncfl/w640-h234/image.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;Option 2, via Kudu, create a Modules folder under site/wwwroot. Copy (drag and drop) the PnP.PowerShell module (downloaded to PC) to the Modules folder.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnKIW8DmRZt8jqdQPy1lU-5NoRVgEypM6Yec1SqCyGlRvdI9Cu-YbiiC3toexj2AP1g8q1_IlyTj1bxVP1sYFLmXpY5TTUUV-1zPKP2_bLYu9FgfNllRHWajUFpAkEtXg0gscHg6FGu_LO/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;300&quot; data-original-width=&quot;728&quot; height=&quot;264&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnKIW8DmRZt8jqdQPy1lU-5NoRVgEypM6Yec1SqCyGlRvdI9Cu-YbiiC3toexj2AP1g8q1_IlyTj1bxVP1sYFLmXpY5TTUUV-1zPKP2_bLYu9FgfNllRHWajUFpAkEtXg0gscHg6FGu_LO/w640-h264/image.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvl_2dZUnOLwaC3t4psldH7K0UgKYf_IBZc6hogVmVzgkyvHmTgGdFmvRopCcLYAJPDPnhu1Ogj5CDfUr5et9aWGtO19uGDQNFT93fazfuuPR78YXPfHq1aaKHS8qKIReGuPfPdPqMi_GA/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;850&quot; data-original-width=&quot;820&quot; height=&quot;640&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvl_2dZUnOLwaC3t4psldH7K0UgKYf_IBZc6hogVmVzgkyvHmTgGdFmvRopCcLYAJPDPnhu1Ogj5CDfUr5et9aWGtO19uGDQNFT93fazfuuPR78YXPfHq1aaKHS8qKIReGuPfPdPqMi_GA/w619-h640/image.png&quot; width=&quot;619&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Azure function code&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Add new function. Select Time Trigger to periodically trigger every 5 minutes.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjngMBnL-voPjHJ2TbziugVJk4cuuynbTNpJNcKivpi_GcY9h1lmBExcCOSQS-q_BXwk5xuluLiwON3KH_HzJkTGPAZ2yBHflvpBt6n7lJ-K5-_cVaYt-iw1QFnH1xpTnlsA3J-Qb8M118o/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;646&quot; data-original-width=&quot;1677&quot; height=&quot;246&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjngMBnL-voPjHJ2TbziugVJk4cuuynbTNpJNcKivpi_GcY9h1lmBExcCOSQS-q_BXwk5xuluLiwON3KH_HzJkTGPAZ2yBHflvpBt6n7lJ-K5-_cVaYt-iw1QFnH1xpTnlsA3J-Qb8M118o/w640-h246/image.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Azure function code&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNCK_uqVyJl2gJZlX2jZmW99Lx6f2EVRvv1xn_NdPgnDMf-2_Po0hajit4T-6xwqmHYt99uk5pgikbXCPSKvZ6Iur9AhnqeW6CcNOT81j9ofMYorEoK1squCU_Iy36Hj0TobOr4CL0dr1I/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;691&quot; data-original-width=&quot;1336&quot; height=&quot;332&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNCK_uqVyJl2gJZlX2jZmW99Lx6f2EVRvv1xn_NdPgnDMf-2_Po0hajit4T-6xwqmHYt99uk5pgikbXCPSKvZ6Iur9AhnqeW6CcNOT81j9ofMYorEoK1squCU_Iy36Hj0TobOr4CL0dr1I/w640-h332/image.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;##If using the option 2, import the module with the path&lt;/div&gt;&lt;div&gt;Import-Module -Name C:\home\site\wwwroot\Modules\PnP.PowerShell&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;##Retrieve user credential from application setting where the source is from the secrets stored in key vault.&lt;/div&gt;&lt;div&gt;$user = $env:spfunctionuser&lt;/div&gt;&lt;div&gt;$pw = $env:spfunctionpw | ConvertTo-SecureString -AsPlainText&lt;/div&gt;&lt;div&gt;$cred = New-Object System.Management.Automation.PSCredential($user, $pw)&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;##Connect to SharePoint site where the SharePoint list is hosted&amp;nbsp;&lt;/div&gt;&lt;div&gt;Connect-PnPOnline -Url https://test.sharepoint.com/sites/test -Credentials $cred -ErrorAction Stop&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;##Only get the entries where SiteCreated is &#39;No&#39; using CAML filter query&lt;/div&gt;&lt;div&gt;$sites = Get-PnPListItem -List lists/newsitelist -Query &quot;&amp;lt;View&amp;gt;&amp;lt;Query&amp;gt;&amp;lt;Where&amp;gt;&amp;lt;Eq&amp;gt;&amp;lt;FieldRef Name=&#39;SiteCreated&#39;/&amp;gt;&amp;lt;Value Type=&#39;Integer&#39;&amp;gt;0&amp;lt;/Value&amp;gt;&amp;lt;/Eq&amp;gt;&amp;lt;/Where&amp;gt;&amp;lt;/Query&amp;gt;&amp;lt;/View&amp;gt;&quot;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;##Loop through the entries and create the site based on the criteria provided, and update the SiteCreated field of the entry.&lt;/div&gt;&lt;div&gt;if ($sites) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; $sites | ForEach-Object {&amp;nbsp;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; New-PnPSite -Title $_.FieldValues.Title `&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; -Type $_.FieldValues.SiteType `&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; -Url $(&quot;https://test.sharepoint.com/sites/$($_.FieldValues.Title)&quot;) `&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; -ShareByEmailEnabled:$_.FieldValues.ShareByEmailEnabled `&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; -SiteDesign $_.FieldValues.SiteDesign `&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; -Owner $_.FieldValues.SiteOwner.Email&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if ($?) {&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Write-Output &quot;site created&quot;&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Set-PnPListItem -List lists/newsitelist -Identity $_.Id -Values @{&quot;SiteCreated&quot; = 1}&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;&amp;nbsp; &amp;nbsp; }&lt;/div&gt;&lt;div&gt;}&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Once the Azure function schedule trigger the execution, new SharePoint Site is created accordingly. Success!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDk-ga1fTNAXG8MaiYQeVnsksy1a3OTbbEebRHEPef4O6D0hiHmVuLIr3NjslnGfnWqobRPqq_aUcqfXn-UxanuvxCvW5Xj3ah96cF5_V0goq8TTofrFDZbmtGXoFn0UPLjlLINQIM8Qpq/&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img alt=&quot;&quot; data-original-height=&quot;419&quot; data-original-width=&quot;1191&quot; height=&quot;226&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDk-ga1fTNAXG8MaiYQeVnsksy1a3OTbbEebRHEPef4O6D0hiHmVuLIr3NjslnGfnWqobRPqq_aUcqfXn-UxanuvxCvW5Xj3ah96cF5_V0goq8TTofrFDZbmtGXoFn0UPLjlLINQIM8Qpq/w640-h226/image.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;Instead of executing Azure function with a schedule, potential improvement is to leverage SharePoint webhooks to notify, for example via Azure queue to invoke Azure function when there is new entry in the SharePoint List.&lt;/div&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/305053587123761006/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2021/04/create-sharepoint-site-with-azure.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/305053587123761006'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/305053587123761006'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2021/04/create-sharepoint-site-with-azure.html' title='Create Sharepoint Site with Azure Function using PowerShell'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUqbWYhEWpvidKE_tnDqoXWLmIZH9GrX4qRrPchnHj564uDVeKDUv7jEyaADNRBt6yDH6w6BBMeVigKwwtaBx_ZrRAVyQU6eZZ8JcaQN5kfwUIdaTuspWgqyYKzm93IJ0m7QtRE03OHeXe/s72-w200-h183-c/image.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-7507163608548443699</id><published>2019-04-25T07:00:00.000-05:00</published><updated>2019-05-07T21:47:36.012-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Troubleshooting"/><title type='text'>Network Traffic Capture Without Installing Any Software</title><content type='html'>&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMMUY0gx4K4ET8GQJdAr9eVfTHlIIbXzMIpNBEUp14Fdql7WZioJEofycZsDBz6ujxK2HzjbljaKcUokHfo7bhQLzMHpZo8bRajCK2pYjxp_IPMCYxP0dCq-3T3wsL0_QyEjzV9_Q7-XEF/s1600/netmon.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;173&quot; data-original-width=&quot;589&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMMUY0gx4K4ET8GQJdAr9eVfTHlIIbXzMIpNBEUp14Fdql7WZioJEofycZsDBz6ujxK2HzjbljaKcUokHfo7bhQLzMHpZo8bRajCK2pYjxp_IPMCYxP0dCq-3T3wsL0_QyEjzV9_Q7-XEF/s1600/netmon.png&quot; /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
From time to time, there is a need to capture network traffic for troubleshooting on server. Some network tool like Wireshark is pretty popular for network capturing. However, these tool often need additional installation on the server, and depending on your security team rules, it may take days to be installed on the server.&lt;br /&gt;
&lt;br /&gt;
There is an alternate way to capture network traffic on Windows OS without additional software installation. Using Netsh trace. Most of you may familiar with Netsh for different type of common purpose like firewall, http listener, network interface info, etc. Netsh could be also used to collect network trace.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;Open the elevated command prompt / powershell,&lt;br /&gt;
&lt;br /&gt;
netsh trace -?&lt;br /&gt;
&lt;br /&gt;
It shows the list of parameter purpose, examples and other useful information on netsh trace&lt;br /&gt;
&lt;br /&gt;
To simply start a trace,&lt;br /&gt;
netsh trace start capture=yes tracefile=c:\nettrace-example.etl&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVzR4KPaaqS4XErfSU-MwquKOde6SBrxK7hPxgyPYZJXoiPwT1Hnr0bRi0WSeNcfclAOnmp5riyTU74jLtJ7rN3mvcxwDZjcOhIGjwlGxe6QgmcrC-fyBloHWITYhBtB243Yf80lVOoJoM/s1600/netsh-trace-start.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;175&quot; data-original-width=&quot;569&quot; height=&quot;122&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVzR4KPaaqS4XErfSU-MwquKOde6SBrxK7hPxgyPYZJXoiPwT1Hnr0bRi0WSeNcfclAOnmp5riyTU74jLtJ7rN3mvcxwDZjcOhIGjwlGxe6QgmcrC-fyBloHWITYhBtB243Yf80lVOoJoM/s400/netsh-trace-start.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
To stop the trace,&lt;br /&gt;
netsh trace stop&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2NSONPnW5oUApRwlMvADpS4eTC4JkY5519JHcYTufc8waK7EWoA9Bn6_Ub0uYQjX-vJ5XSAQxUl6nlWRoL8nE7kRkZ4oYtPe_IOjoqodysNBqR8tw_l8CMrcESjK3XWrSGMjPn88Gst4K/s1600/netsh-trace-stop.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;121&quot; data-original-width=&quot;766&quot; height=&quot;100&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2NSONPnW5oUApRwlMvADpS4eTC4JkY5519JHcYTufc8waK7EWoA9Bn6_Ub0uYQjX-vJ5XSAQxUl6nlWRoL8nE7kRkZ4oYtPe_IOjoqodysNBqR8tw_l8CMrcESjK3XWrSGMjPn88Gst4K/s640/netsh-trace-stop.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
These are the trace files generated.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSVYiPWn577-nwPv_XYu_kZG2i5-eMctPysYF_qYrWghnYzOjEmwVhc5SG3wMCx6pERETCKp2sPiLjT_WWj5y9Z4Uq_aSR0pP8uVbxmtoHaNfPMYZIa5BsVh_jY7xiy6jWMu8ztvtl5M0z/s1600/netsh-trace-file.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;131&quot; data-original-width=&quot;240&quot; height=&quot;108&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSVYiPWn577-nwPv_XYu_kZG2i5-eMctPysYF_qYrWghnYzOjEmwVhc5SG3wMCx6pERETCKp2sPiLjT_WWj5y9Z4Uq_aSR0pP8uVbxmtoHaNfPMYZIa5BsVh_jY7xiy6jWMu8ztvtl5M0z/s200/netsh-trace-file.png&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
You can copy the trace file to a computer that has netmon (network monitor) installed. You may download the software &lt;a href=&quot;https://www.microsoft.com/en-us/download/details.aspx?id=4865&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. Presumably you can at least install the software easily on your computer.&lt;br /&gt;
&lt;br /&gt;
Open netmon and read the trace file.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4aVb7ogVfXDSElZN3HaSFNmkW8OmVUG4bAGqgfeCOZA3gnRJiwkTEc5M3Pj7RtriGpTtt7vUgfaQJY5vGtH1gdUo7SuWkEuHV5crrNBZM7deSuBSypa3cdGSyZL846s-MMUCvCXDry-Ul/s1600/netmon-open-trace.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;173&quot; data-original-width=&quot;395&quot; height=&quot;140&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4aVb7ogVfXDSElZN3HaSFNmkW8OmVUG4bAGqgfeCOZA3gnRJiwkTEc5M3Pj7RtriGpTtt7vUgfaQJY5vGtH1gdUo7SuWkEuHV5crrNBZM7deSuBSypa3cdGSyZL846s-MMUCvCXDry-Ul/s320/netmon-open-trace.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
You may see the parser issue on the description. 
&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgm8TWIlwO0sVFMXRRryrxs57n3NZIAJYTQXRw_pbGuVUvmoT-nkLmGlAGQNUb-w-QN1Vi3_GJbDlfg78fD54hRmCricn4lK_HcxEqIL8pcv8NhsWVgr8AQHLEFSVzUK9wp9nOusphyphenhyphenx1wH/s1600/netmon-parser-issue.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;430&quot; data-original-width=&quot;1419&quot; height=&quot;193&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgm8TWIlwO0sVFMXRRryrxs57n3NZIAJYTQXRw_pbGuVUvmoT-nkLmGlAGQNUb-w-QN1Vi3_GJbDlfg78fD54hRmCricn4lK_HcxEqIL8pcv8NhsWVgr8AQHLEFSVzUK9wp9nOusphyphenhyphenx1wH/s640/netmon-parser-issue.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
For those who paid close attention during the netmon installation, it also prompted to install the parser. However, the parser is not configured to be active by default. To configure the parser, go to tools&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyHZV9suLle-_kS-ZYfM4G3g5Iwm9Z3XtP3AKBOxAoorI3yxRoSapZx3Dl3O_gfuSZPg0kYqnBn97V1gdv-kFfDorg6-OYO3eOlfbMD5Bxj7drO-FOL026i2ZhqpGLfvi5auQSt9GmDO4K/s1600/netmon-tool.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;145&quot; data-original-width=&quot;406&quot; height=&quot;114&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyHZV9suLle-_kS-ZYfM4G3g5Iwm9Z3XtP3AKBOxAoorI3yxRoSapZx3Dl3O_gfuSZPg0kYqnBn97V1gdv-kFfDorg6-OYO3eOlfbMD5Bxj7drO-FOL026i2ZhqpGLfvi5auQSt9GmDO4K/s320/netmon-tool.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
Select Windows, and click Set As Active&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpU5J9G4Vf6oR1C4DQ2GbG7OANWzbx3odR_3LlQav3D8XKYV6bnX4oPaySoTkCybEf0fazjor-_NYVkbwUdZuQi2K_1GcEzgkjS1VF0y1-2dFdpr9Bu0nLcnm9WmPRkusssbsB0zjl1ykX/s1600/netsh-parser.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;504&quot; data-original-width=&quot;446&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpU5J9G4Vf6oR1C4DQ2GbG7OANWzbx3odR_3LlQav3D8XKYV6bnX4oPaySoTkCybEf0fazjor-_NYVkbwUdZuQi2K_1GcEzgkjS1VF0y1-2dFdpr9Bu0nLcnm9WmPRkusssbsB0zjl1ykX/s400/netsh-parser.png&quot; width=&quot;353&quot; /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
Now the description is more useful and ready for troubleshooting and analysis.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEFpX9W52no9H-NGlfDAF4fQlIx8GwyqAJ8sv_pud2xv1ZjiDF-K3MHSTewHme88cvV4FLgSF9GOl3_p_hyphenhyphenKjmSLBjYaxYTV-rfJU2M49ST8TzS7d9_Gf1_6-vJ3XEFg-_z1jdMw4cryB-/s1600/netmon-analysis.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;427&quot; data-original-width=&quot;1046&quot; height=&quot;260&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEFpX9W52no9H-NGlfDAF4fQlIx8GwyqAJ8sv_pud2xv1ZjiDF-K3MHSTewHme88cvV4FLgSF9GOl3_p_hyphenhyphenKjmSLBjYaxYTV-rfJU2M49ST8TzS7d9_Gf1_6-vJ3XEFg-_z1jdMw4cryB-/s640/netmon-analysis.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
That&#39;s it! Happy troubleshooting. More information on netmon filter could be found &lt;a href=&quot;https://social.technet.microsoft.com/wiki/contents/articles/1130.network-monitor-ipv4-filtering.aspx&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/7507163608548443699/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2019/04/network-traffic-capture-without.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/7507163608548443699'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/7507163608548443699'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2019/04/network-traffic-capture-without.html' title='Network Traffic Capture Without Installing Any Software'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMMUY0gx4K4ET8GQJdAr9eVfTHlIIbXzMIpNBEUp14Fdql7WZioJEofycZsDBz6ujxK2HzjbljaKcUokHfo7bhQLzMHpZo8bRajCK2pYjxp_IPMCYxP0dCq-3T3wsL0_QyEjzV9_Q7-XEF/s72-c/netmon.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-7460865691864655045</id><published>2019-02-11T23:04:00.000-06:00</published><updated>2019-02-14T21:19:01.736-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Azure"/><title type='text'>Inserting and Hosting 551 Millions Records Cheaply - Case Study</title><content type='html'>&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhznc8s_gtEkA9QTqneWFM9toTPocG6OH2tAED7VLYkbWJoOg8UKJKsBjUMDt_-pMRY9VKQCI8fheIuiE2BDhJtuVhK8fGj2erSvHL58xxQ6XveuK4BMDju0fXmnLkiCbweD0iYof6_wvhs/s1600/azureoffering.JPG&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;350&quot; data-original-width=&quot;732&quot; height=&quot;306&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhznc8s_gtEkA9QTqneWFM9toTPocG6OH2tAED7VLYkbWJoOg8UKJKsBjUMDt_-pMRY9VKQCI8fheIuiE2BDhJtuVhK8fGj2erSvHL58xxQ6XveuK4BMDju0fXmnLkiCbweD0iYof6_wvhs/s640/azureoffering.JPG&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
Recently I worked on a &lt;a href=&quot;https://passwordcheck.travisgan.com/&quot; target=&quot;_blank&quot;&gt;pet project&lt;/a&gt; with cloud implementation. There was a dump file in text format with 551 million line of records which needs to be stored in a way where filtered results could be quickly returned from a query, and with a goal of low cost implementation.&lt;br /&gt;
&lt;br /&gt;
The dataset is static. Large number of records. No relational requirement. The storage and querying has to be fast and low cost. Azure table storage seems to be a good candidate for this purpose. Azure table storage is a NoSQL key value store and is very cheap.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;The dataset (with some Azure table metadata) is estimated to be around 50 GB when it is stored in Azure table storage. With the choice of locally redundant storage (LRS), the price of the dataset storage is ~50 GB * $0.07 / GB per month = ~$3.50 per month. For the data transfer calculation, Microsoft charges $0.00036 / 10,000 transaction for table (that includes read, write, delete). If it was to insert 551 million record transactions, it would cost $19.84. But we could better. More on that later.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipTMnH0x8aTi0RH-SBc4I7dvUjaX11sn720tBQvezzbhPXINxTuuUGeZYEWyXmgjTO-W3pOsN7FNs4NxE2CuQamuJoM3CCHGCXD9Katz5SG-d160vnY3VeeijufUOBn1pLMx3VLNabhgGa/s1600/azuretablepricing.JPG&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;314&quot; data-original-width=&quot;1090&quot; height=&quot;184&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipTMnH0x8aTi0RH-SBc4I7dvUjaX11sn720tBQvezzbhPXINxTuuUGeZYEWyXmgjTO-W3pOsN7FNs4NxE2CuQamuJoM3CCHGCXD9Katz5SG-d160vnY3VeeijufUOBn1pLMx3VLNabhgGa/s640/azuretablepricing.JPG&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
Although the application and API used against the Azure table would be made public, this pet project is more an educational project for myself so I don&#39;t expect large traffic (way less than 10,000). The operating transaction cost would be minimal.&lt;br /&gt;
&lt;br /&gt;
Back to the text file. It contains 551 million records. To increase the insert performance into Azure table storage, here are the implemented strategy.&lt;br /&gt;
&lt;br /&gt;
- Provisioned an Azure VM in the same region. Network latency and throughput is much better compare with personal computer.&lt;br /&gt;
- Batched 100 records (max limit) in a transaction whenever possible.&lt;br /&gt;
- Built some logic to insert records parallelly. 24 consoles were used to process different records simultaneously.&lt;br /&gt;
- Turned Nagle off to increase throughput for table insert.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Azure VM&lt;/b&gt;&lt;br /&gt;
The process is mostly compute intensive. Fsv2, Fs, F Azure VM type are the compute optimized VM. In this case, Fsv2 (2 vcpu, 4 GB RAM, 4 data disks, 4000 max iops, $75.89 / month) and standard HDD disk was selected.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFy2OcTc5o_VmfdUjNUiV-z69hqmBa8DyjPwTvmGhFOGf2lM-EPxdOhA9dWzIIrI40zxXVTnygVBD1PWHncO4QLjVuwDkOqFU2EJ1jz8diAuF75p92oXS5HhdumdiU4Ag674NxZFag0SQC/s1600/vmsize.JPG&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;364&quot; data-original-width=&quot;787&quot; height=&quot;296&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFy2OcTc5o_VmfdUjNUiV-z69hqmBa8DyjPwTvmGhFOGf2lM-EPxdOhA9dWzIIrI40zxXVTnygVBD1PWHncO4QLjVuwDkOqFU2EJ1jz8diAuF75p92oXS5HhdumdiU4Ag674NxZFag0SQC/s640/vmsize.JPG&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Multiple records in batch&lt;/b&gt;&lt;br /&gt;
Azure table storage has a maximum of 100 records limit in a batch operation. All records in the batch must have same partition key. Batching not only increase insert performance, it also save cost. One batch operation is considered as a transaction. If all batches are 100 records, 551 millions records insert cost would be only $0.1984! In this case, due to the parallel logic put in place, average about 5 out of 6 batches contain 100 records.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Parallel Logic&lt;/b&gt;&lt;br /&gt;
Single process would be boring and inefficient. The process do need to read the text file line by line and insert into Azure table storage. It doesn&#39;t seem like there is any native parallel process of reading file and performing the insert. Especially consider the file is around 25 GB in size and the VM has only 4 GB of memory. I thought of a logic where it could have multiple powershell process its own unique section line by line simultaneously with minimum memory required. I picked 24, just an arbitrary number but with previous experience. It does consume the 2 vcpu on VM around 90% the entire time. Good.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvcws7qdyH8lcquFRbOVFJJLPHBF2fv6qlytCZJH9AL3X1Iqr7yey5Q3q4NMHQjFubVC5O6F6dza0ShaRGpWbeulJjUdfqWUJxVBpDjkYgWGa861IlBz6eluKb4MymnibxKNp0F2QNlSM7/s1600/parallel.JPG&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;899&quot; data-original-width=&quot;1595&quot; height=&quot;361&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvcws7qdyH8lcquFRbOVFJJLPHBF2fv6qlytCZJH9AL3X1Iqr7yey5Q3q4NMHQjFubVC5O6F6dza0ShaRGpWbeulJjUdfqWUJxVBpDjkYgWGa861IlBz6eluKb4MymnibxKNp0F2QNlSM7/s640/parallel.JPG&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3FrhLXjQXcEBWD7nqAfTV8Ur86di3o0Ob-0pUM7BR9iXSjhdl1ha-PRdfkB3Z-7KEf1RTUbmmP3VmGffsl9LwPaxOFHbTPl7H8BpsRmYvWEsJJ3ARN9rtkpmv5gtvuW1uDz_DsDkbbiwR/s1600/cpu_resource.JPG&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;429&quot; data-original-width=&quot;712&quot; height=&quot;386&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3FrhLXjQXcEBWD7nqAfTV8Ur86di3o0Ob-0pUM7BR9iXSjhdl1ha-PRdfkB3Z-7KEf1RTUbmmP3VmGffsl9LwPaxOFHbTPl7H8BpsRmYvWEsJJ3ARN9rtkpmv5gtvuW1uDz_DsDkbbiwR/s640/cpu_resource.JPG&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Turned off Nagle&lt;/b&gt;&lt;br /&gt;
Microsoft storage team has an&amp;nbsp;&lt;a href=&quot;https://blogs.msdn.microsoft.com/windowsazurestorage/2010/06/25/nagles-algorithm-is-not-friendly-towards-small-requests/&quot; target=&quot;_blank&quot;&gt;article&lt;/a&gt; of how the Nagle algorithm affects the Azure table storage insert performance. Personally has seen quite a significant improvement for Azure queue by turning off Nagle. [System.Net.ServicePointManager]::UseNagleAlgorithm = $false&lt;br /&gt;
&lt;br /&gt;
Once these are setup with a new Azure table created, I ran the powershell to process the records and insert into the Azure table. With 24 process running simultaneously, it inserts around 12k batch transaction / minute. Estimating ~90 records per batch transaction, that is average of 18,000 records insert / second.&lt;br /&gt;
&lt;br /&gt;
The total times for inserting 551 millions records into Azure table took about 8 and 1/2 hours.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Setup cost&lt;/b&gt; (including get server up and script ready),&lt;br /&gt;
Azure VM ~ $1.46&lt;br /&gt;
Azure table (storage + transaction) - $3.23&lt;br /&gt;
Bandwidth - $0.04&lt;br /&gt;
Total initial setup cost: $4.73&lt;br /&gt;
&lt;br /&gt;
VM is only needed for this initial setup process and is decommissioned right after.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Operating cost &lt;/b&gt;(including all website components in addition to Azure table)&lt;br /&gt;
Azure table (storage) ~ $3&lt;br /&gt;
Azure table transaction (it would need to be &amp;gt; 270k transaction to be charged $0.01) - $0&lt;br /&gt;
Azure functions (as backend API, $0.20 / million execution, 1 million execution free / month) - $0&lt;br /&gt;
Azure static website (angular app size is small &amp;lt; 850 KB) ~ $0&lt;br /&gt;
Outbound Bandwidth (First 5 GB is free, between 5GB - 10TB, $0.087 / GB. Estimate application returns around 100 byte per each request) ~ $0&lt;br /&gt;
Total monthly operating cost: ~$3&lt;br /&gt;
&lt;br /&gt;
Potential improvements. I could simplify the parallel logic to further increase the ratio of 100 records batches. Also increase the degree of parallelism (eg. 48 or more simultaneous process) along VM with more vpcu to further reduce the duration.</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/7460865691864655045/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2019/02/inserting-and-hosting-551-millions.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/7460865691864655045'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/7460865691864655045'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2019/02/inserting-and-hosting-551-millions.html' title='Inserting and Hosting 551 Millions Records Cheaply - Case Study'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhznc8s_gtEkA9QTqneWFM9toTPocG6OH2tAED7VLYkbWJoOg8UKJKsBjUMDt_-pMRY9VKQCI8fheIuiE2BDhJtuVhK8fGj2erSvHL58xxQ6XveuK4BMDju0fXmnLkiCbweD0iYof6_wvhs/s72-c/azureoffering.JPG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-4672753049553847595</id><published>2018-06-24T23:00:00.000-05:00</published><updated>2019-02-19T09:50:30.140-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Linux"/><title type='text'>Windows Guy Learning Linux - Basic Command for Linux</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;img border=&quot;0&quot; data-original-height=&quot;400&quot; data-original-width=&quot;200&quot; height=&quot;160&quot; imageanchor=&quot;1&quot; more=&quot;&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXiOJrUXR_efxlrJ9VNV4zPzdfFy_umV09pwaf4A0yrC2BluDLhARiy3pO_7rlDAFPp5gv0F_AE-g7w3o5q2fsGgiOdte-di5pRHBVrmlC2d1Z5dSisr7x5502J9UWC7TR6MxG-NYd3IYi/s320/ilovelinux.jpg&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot; width=&quot;200&quot; /&gt;&lt;/div&gt;
If you have been working mostly with Microsoft technology stacks, switching from Windows and learning Linux could be an exciting and daunting journey. So far my journey to Linux has been fun and I love it!
&lt;br /&gt;
&lt;br /&gt;
Linux has GUI but mostly (almost entirely) for server and application administration are performed using command line. If you are on Windows workstation, &lt;a href=&quot;https://www.putty.org/&quot; target=&quot;_blank&quot;&gt;PuTTY&lt;/a&gt; software could be used to ssh into Linux server.
&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;Here are some basic and commonly used commands. Hopefully the list is useful for you. Have fun learning!&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;Terminal&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;ctrl+c&lt;/b&gt;&amp;nbsp; stop the command.&lt;br /&gt;
&lt;b&gt;ctrl+d&lt;/b&gt;&amp;nbsp; log out from current terminal (just like the command &lt;b&gt;exit&lt;/b&gt;)&lt;br /&gt;
&lt;b&gt;ctrl+u&lt;/b&gt;&amp;nbsp; erase the current line. It clear from the cursor to the beginning of line&lt;br /&gt;
&lt;b&gt;ctrl+y&lt;/b&gt;&amp;nbsp; redo current line text from ctrl+u&lt;br /&gt;
&lt;b&gt;ctrl+l&lt;/b&gt;&amp;nbsp; clear screen (or use&amp;nbsp;&lt;b&gt;clear&lt;/b&gt; command). Just like cls in windows&lt;br /&gt;
&lt;b&gt;ctrl+z&lt;/b&gt;&amp;nbsp; send the process to background. Useful when you have a long running process and need to work on terminal. type &lt;b&gt;fg&lt;/b&gt; to bring it back&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
Not that useful on windows keyboard&lt;br /&gt;
&lt;b&gt;ctrl+a&amp;nbsp; &lt;/b&gt;move the cursor to beginning of line. Or just use keyboard home button&lt;br /&gt;
&lt;b&gt;ctrl+e&lt;/b&gt;&amp;nbsp; move the cursor to end of line. Or just use keyboard end button&lt;br /&gt;
&lt;br /&gt;
Tips: &lt;b&gt;tab&lt;/b&gt; button helps automatically complete the command or file. Just like in cmd or powershell.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;Help / Information&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;man&lt;/b&gt;&amp;nbsp; user manual or help for command. eg. &lt;i&gt;man ls &lt;/i&gt;show details and usage of ls command. To get out of man page, type q&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;Commonly use command&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;pwd&amp;nbsp; &lt;/b&gt;current folder path&lt;br /&gt;
&lt;b&gt;ls&lt;/b&gt;&amp;nbsp; list directory content. eg.&lt;br /&gt;
&lt;b&gt;ll&lt;/b&gt;&amp;nbsp; is an alias of ls -l. It shows long listing format&lt;br /&gt;
&lt;b&gt;ls -al&lt;/b&gt;&amp;nbsp; includes showing long listing format including file starting with . (dot). This file is treated as hidden file.&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
Use ll command to list file / directory permission. More permission info &lt;a href=&quot;https://www.linux.com/learn/understanding-linux-file-permissions&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. For example,&lt;br /&gt;
&lt;br /&gt;
drwxr-xr-x&amp;nbsp; (a directory)&lt;br /&gt;
-rw-r--r--&amp;nbsp; (a file)&lt;br /&gt;
&lt;br /&gt;
r as read permission&lt;br /&gt;
w as modify permission&lt;br /&gt;
x as execute file or view content of directory permission&lt;br /&gt;
&lt;br /&gt;
1st character indicates a file or directory. d as directory, - as file&lt;br /&gt;
2-4 characters indicate the permission of owner of the file/directory&lt;br /&gt;
5-7 characters indicate the permission of the group of the file/directory&lt;br /&gt;
8-10 characters indicate the permission of all other users&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;chown &lt;/b&gt;user1.group1&amp;nbsp;&lt;b&gt;&amp;lt;file/folder&amp;gt;&lt;/b&gt;&amp;nbsp; change ownership of files/directories. User, group, other (every users). Use -R as recursive for folder permission change&lt;br /&gt;
&lt;b&gt;chmod 644&lt;/b&gt;&amp;nbsp;&amp;lt;file/folder&amp;gt;&amp;nbsp; change file/folder permission. eg. If this is a file, 6 as binary is 110, 4 as binary as 100. The file permission would be -rw-r--r--&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;sudo&lt;/b&gt;&amp;nbsp; execute a command as superuser or another user&lt;br /&gt;
&lt;b&gt;su&lt;/b&gt;&amp;nbsp; change user&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;b&gt;cd&lt;/b&gt;&amp;nbsp;change current directory. Just like windows&lt;/div&gt;
&lt;b&gt;cd -&amp;nbsp; &lt;/b&gt;(dash), cd into previous folder (folder before the previous cd command)&lt;br /&gt;
&lt;b&gt;cd ~&amp;nbsp;&lt;/b&gt; (tilde), ~ refer to home directory. cd into home directory&lt;br /&gt;
&lt;b&gt;cd /&lt;/b&gt;&amp;nbsp; (slash), / is the root directory&lt;br /&gt;
&lt;b&gt;md&lt;/b&gt;&amp;nbsp; make (create) new directory&lt;br /&gt;
&lt;b&gt;rm&lt;/b&gt;&amp;nbsp; remove folder or file. eg. rm &amp;lt;file&amp;gt;.. rm -r &amp;lt;folder&amp;gt; (r is to remove directory and its contents recursively)&lt;br /&gt;
&lt;b&gt;cp&lt;/b&gt;&amp;nbsp; copy file/folder&lt;br /&gt;
&lt;b&gt;scp&lt;/b&gt;&amp;nbsp; secure copy file/folder. eg copying files between hosts using ssh. eg. scp file.txt username@destination_host:/folder/subfolder&lt;br /&gt;
&lt;b&gt;touch&lt;/b&gt; &amp;lt;file&amp;gt;&amp;nbsp; &amp;nbsp;create empty file. Or use text editor to create new file with content&lt;br /&gt;
&lt;b&gt;&amp;gt;&amp;nbsp;&lt;/b&gt; output to file. eg. ls &amp;gt; abc.txt&lt;br /&gt;
&lt;b&gt;&amp;gt;&amp;gt;&amp;nbsp;&lt;/b&gt;&amp;nbsp;append to file. eg. ls &amp;gt;&amp;gt; abc.txt&lt;br /&gt;
&lt;b&gt;find &lt;/b&gt;&amp;lt;directory&amp;gt;&lt;b&gt; -name &lt;/b&gt;&amp;lt;file/folder&amp;gt;&amp;nbsp; search file/folder in the directory. use -iname for insensitive search&lt;br /&gt;
&lt;b&gt;locate&lt;/b&gt;&amp;nbsp;&amp;lt;file&amp;gt;&amp;nbsp; quickly search through files index db on the system (maybe not up to date)&lt;br /&gt;
&lt;b&gt;file&lt;/b&gt;&amp;nbsp;&amp;lt;file&amp;gt;&amp;nbsp; show file type (eg. examine if a file is text or archive file)&lt;br /&gt;
&lt;b&gt;grep&lt;/b&gt;&amp;nbsp; search matching pattern or regular expression. Very useful for matching pattern. Eg. using in conjunction with ps -ef | grep java, to find java process&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;Archive/Compress&lt;/u&gt;&lt;/b&gt;
&lt;b&gt;tar&lt;/b&gt;&amp;nbsp; archive utility (to zip or/and compress). Eg.&lt;br /&gt;
&lt;b&gt;tar -cvf&lt;/b&gt;&amp;nbsp; test.tar test1&amp;nbsp; (to archive test1 folder into a tar file. -c as archive, -v for verbose and -f for file&lt;br /&gt;
&lt;b&gt;tar -xvf&lt;/b&gt;&amp;nbsp; test.tar (to extract all files from archive.tar)&lt;br /&gt;
&lt;b&gt;tar -cvzf&lt;/b&gt;&amp;nbsp; test.tar.gz test1 (to archive test1 folder and compress into tar.gz file) -z for compress&lt;br /&gt;
&lt;b&gt;tar -xvzf&lt;/b&gt; test.tar.gz (to extract and uncompress archive.tar.gz)&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;View / modify file content&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;cat&lt;/b&gt; &amp;lt;file&amp;gt;&amp;nbsp; concatenate and show all contents&lt;br /&gt;
&lt;b&gt;less&lt;/b&gt; &amp;lt;file&amp;gt;&amp;nbsp; show content one page at a time. Scroll forward with f (or page down button) and scroll backward with b (or page up button).&lt;br /&gt;
&lt;b&gt;less -p&lt;/b&gt; &amp;lt;pattern&amp;gt;&amp;nbsp; &amp;lt;file&amp;gt;&amp;nbsp; find pattern in the file&lt;br /&gt;
&lt;b&gt;nano&lt;/b&gt;&amp;nbsp; simple text editor. kind of like notepad. nano is pretty straight forward. For help, press ctrl+G. The ^ (caret) refers to ctrl key. M refer to alt key&lt;br /&gt;
&lt;b&gt;vi&lt;/b&gt;&amp;nbsp; more advanced (could be unintuitive for beginner) text editor. vi has two modes of operation: command mode and insert mode&lt;br /&gt;
&lt;br /&gt;
press Esc key to put vi in command mode&lt;br /&gt;
press i key to put vi in insert mode&lt;br /&gt;
&lt;br /&gt;
vi command mode,&lt;br /&gt;
&lt;b&gt;dd&lt;/b&gt;&amp;nbsp; delete line&lt;br /&gt;
&lt;b&gt;yy&lt;/b&gt;&amp;nbsp; copy line. Use mouse to select the word for&lt;br /&gt;
&lt;b&gt;p&lt;/b&gt;&amp;nbsp; paste&lt;br /&gt;
&lt;b&gt;u&lt;/b&gt;&amp;nbsp; undo&lt;br /&gt;
&lt;b&gt;ctrl+r&lt;/b&gt;&amp;nbsp; redo&lt;br /&gt;
&lt;b&gt;1G&lt;/b&gt;&amp;nbsp; beginning of first line (or :0)&lt;br /&gt;
&lt;b&gt;G$&lt;/b&gt;&amp;nbsp; end of last line&lt;br /&gt;
&lt;b&gt;:/searchword&lt;/b&gt;&amp;nbsp; find word (n for next found word, shift+n for previous)&lt;br /&gt;
&lt;b&gt;:%s/&amp;lt;searchword&amp;gt;/&amp;lt;replaceword&amp;gt;/g&lt;/b&gt;&amp;nbsp; find searchword and replace with replaceword&lt;br /&gt;
&lt;b&gt;:q&lt;/b&gt;&amp;nbsp; exit&lt;br /&gt;
&lt;div&gt;
&lt;b&gt;:q!&lt;/b&gt;&amp;nbsp; exit and cancel any change&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;:wq&lt;/b&gt;&amp;nbsp; save and exit&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;b&gt;&lt;u&gt;System (storage, process, services, etc)&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;uname -a&lt;/b&gt;&amp;nbsp; show all system information eg linux version&lt;br /&gt;
&lt;b&gt;df -h&lt;/b&gt;&amp;nbsp; display file system disk space usage. -h for human readable size (eg. MB, GB instead of bytes)&lt;br /&gt;
&lt;b&gt;du -sh&lt;/b&gt;&amp;nbsp;&amp;lt;folder&amp;gt;&amp;nbsp; display file usage of a folder. -s for summary and -h for human readable size&lt;br /&gt;
&lt;b&gt;ps -ef&lt;/b&gt;&amp;nbsp; display a snapshot of current process. -ef to show all process using standard syntax&lt;br /&gt;
&lt;b&gt;top&lt;/b&gt;&amp;nbsp; display linux processes. Like task manager in windows. Example,&lt;br /&gt;
h for help&lt;br /&gt;
z highlight&lt;br /&gt;
c show absolute path&lt;br /&gt;
k to kill process pid&lt;br /&gt;
u filter user&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;systemctl -at service &lt;/b&gt;systemctl is used to manage services. Show all services&lt;br /&gt;
&lt;b&gt;systemctl -t service --state=active&lt;/b&gt; Show only active services&lt;br /&gt;
&lt;b&gt;systemctl status &amp;lt;service&amp;gt;&lt;/b&gt;&amp;nbsp; show service status (eg. enabled, active)&lt;br /&gt;
&lt;b&gt;sudo systemctl start &amp;lt;service&amp;gt;&lt;/b&gt;&amp;nbsp; start specific service&lt;br /&gt;
&lt;b&gt;sudo systemctl stop &amp;lt;service&amp;gt;&amp;nbsp; &lt;/b&gt;stop specific service&lt;br /&gt;
&lt;br /&gt;
More administrative commands could be found on this &lt;a href=&quot;https://access.redhat.com/articles/1189123&quot; target=&quot;_blank&quot;&gt;page&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/4672753049553847595/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2018/06/windows-guy-learning-linux-basic.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/4672753049553847595'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/4672753049553847595'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2018/06/windows-guy-learning-linux-basic.html' title='Windows Guy Learning Linux - Basic Command for Linux'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXiOJrUXR_efxlrJ9VNV4zPzdfFy_umV09pwaf4A0yrC2BluDLhARiy3pO_7rlDAFPp5gv0F_AE-g7w3o5q2fsGgiOdte-di5pRHBVrmlC2d1Z5dSisr7x5502J9UWC7TR6MxG-NYd3IYi/s72-c/ilovelinux.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-4426617738316451871</id><published>2015-12-31T14:33:00.001-06:00</published><updated>2015-12-31T15:02:09.129-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Self Learning"/><title type='text'>Microsoft Recertification</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ36BxUqtHz2yEZH0BucmIzcm3cNUjGKH4qhwPIxCJ4okruFDyuVjmHqBI9bMgpkZovrb6s2ePPhZFNfPTYGB6rSFjGvQreVS1TYjTQ7i_nERHYCCITE1XLP3wpXNhwssO31qxXGkMgGo8/s1600/firework.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;132&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ36BxUqtHz2yEZH0BucmIzcm3cNUjGKH4qhwPIxCJ4okruFDyuVjmHqBI9bMgpkZovrb6s2ePPhZFNfPTYGB6rSFjGvQreVS1TYjTQ7i_nERHYCCITE1XLP3wpXNhwssO31qxXGkMgGo8/s200/firework.jpg&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
Year 2016 is just around the corner. People are in holiday mood and in fact a lot of folks are on vacation at this time around. Project usually take this slow period into account and avoid any heavy personnel involvement and introduce changes only where necessary. This slow time of the year could be a great opportunity to expand knowledge and skills, and possibly look into certification / recertification.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;Around 2012, Microsoft implemented a recertification requirement for Microsoft Certified Solution Expert (MCSE) certification and Microsoft Certified Solution Developer (MCSD). As oppose to the &#39;retired&#39; certificates (eg. MCITP SQL Server) that doesn&#39;t expire (but will be changed to legacy status), the new MCSE certification expires in 3 years (MCSD in 2 years) and require recertification to maintain active status.&lt;br /&gt;
&lt;br /&gt;
All MCSE recertification could be completed by passing the respective recertification exam. For example, Exam 70-469 for recertification for MCSE Data Platform. Besides taking the recertification exam, there is another option Microsoft &lt;a href=&quot;https://borntolearn.mslearn.net/b/weblog/archive/2015/04/02/introducing-recertification-through-microsoft-virtual-academy&quot; target=&quot;_blank&quot;&gt;announced&lt;/a&gt; early this year (2015) for&lt;a href=&quot;https://www.microsoft.com/en-us/learning/recertification-virtual-academy.aspx&quot; target=&quot;_blank&quot;&gt; recertification through Microsoft Virtual Academy&lt;/a&gt; (MVA). However this option is currently (as of end of 2015) available for only certain MCSE. That includes,&lt;br /&gt;
&lt;br /&gt;
MCSE: Data Platform&lt;br /&gt;
MCSE: Business Intelligence&lt;br /&gt;
MCSE: Communication&lt;br /&gt;
MCSE: Messaging&lt;br /&gt;
MCSE: SharePoint&lt;br /&gt;
&lt;br /&gt;
For recertification through MVA, it requires passing all the module assessment for the course. Below is an example showing the modules required for MCSE Data Platform.&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRHItgaMO3fRiY5gw7fIiAfPRgQ3fwwGel3Xv5w4yo2-T_yvMYuEZ78fzenCQwxPMG2i3EEvyssFDJBD4emLAwbe2N7kDPbPZBOC1Ft5BP3yPnIJKZo2Wc50rv9GqAHE0g7CKriSZXWYo7/s1600/recertification.png&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;404&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRHItgaMO3fRiY5gw7fIiAfPRgQ3fwwGel3Xv5w4yo2-T_yvMYuEZ78fzenCQwxPMG2i3EEvyssFDJBD4emLAwbe2N7kDPbPZBOC1Ft5BP3yPnIJKZo2Wc50rv9GqAHE0g7CKriSZXWYo7/s640/recertification.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
So which option is right for you?&lt;br /&gt;
&lt;br /&gt;
Recertification through recertification exam usually require only passing 1 exam. &amp;nbsp;The exam cost $150 USD. The exam usually takes 3-4 hours. Certain exam is available at certified test center throughout the word. Certain exam is also available as online proctored exam delivery. To prepare for the exam, exam taker could take advantage of practice test, prep videos or even taking instructor-led training with Microsoft learning partners. Also, Microsoft offers discount or &lt;a href=&quot;https://www.blogger.com/&quot;&gt;&lt;span id=&quot;goog_356747239&quot;&gt;&lt;/span&gt;Second Shot&lt;span id=&quot;goog_356747240&quot;&gt;&lt;/span&gt;&lt;/a&gt; (free retake) to these exam from times to times. There is a &lt;a href=&quot;http://between%20july%2012%2C%202015%2C%20and%20january%2012%2C%202016/&quot; target=&quot;_blank&quot;&gt;promotion&lt;/a&gt; offered currently for certain exam scheduled between July 12, 2015 to Jan 12, 2016.&lt;br /&gt;
&lt;br /&gt;
For recertification through MVA, since not all MCSE (or MCSD) is currently available, this maybe an option only for certain folks, at least for now. As seen in the example above, there are multiple modules. Each module consists of multiple parts with videos, presentation and assessment. All assessment required to be completed. MVA courses are available online and you can complete each module at your own pace. There isn&#39;t any monetary cost to sign up with MVA, viewing the module content as well as taking the assessment. In addition to taking the required module for recertitication, there are many modules for anyone to learn and expand their knowledge. Keep in mind that upon completion of MVA course, you need to submit your MVA user name and Microsoft Certification ID (MC ID) to certquest@microsoft.com for them to verify and update your transcript. There is also MVA community to provide support and helps for your questions.&lt;br /&gt;
&lt;br /&gt;
So, pick the option you seem more suitable for you. I chose the MVA route this time around as I wanted to explore this new option and the modules offered. My experience has been that while some modules emphasize on targeted expertise and new features, some modules also broaden to different areas as well as bring awareness of new product (eg. machine learning, azure platform). And yes, I am recertified as MCSE Data Platform and MCSE Business Intelligence.&lt;br /&gt;
&lt;br /&gt;
Happy New Year!</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/4426617738316451871/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2015/12/microsoft-recertification.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/4426617738316451871'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/4426617738316451871'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2015/12/microsoft-recertification.html' title='Microsoft Recertification'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ36BxUqtHz2yEZH0BucmIzcm3cNUjGKH4qhwPIxCJ4okruFDyuVjmHqBI9bMgpkZovrb6s2ePPhZFNfPTYGB6rSFjGvQreVS1TYjTQ7i_nERHYCCITE1XLP3wpXNhwssO31qxXGkMgGo8/s72-c/firework.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-6655396350232269497</id><published>2015-06-29T14:16:00.002-05:00</published><updated>2015-06-29T21:05:52.470-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Replication"/><category scheme="http://www.blogger.com/atom/ns#" term="Troubleshooting"/><title type='text'>SQL Replication - Subscriber On Different Domain</title><content type='html'>Setting up SQL Server replication with publisher / distributor on one domain and subscriber on another domain could be tricky. It could be even more interesting when these servers have the same host name. This blog post discuss issues encountered and the steps of addressing them.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
&lt;b&gt;Preparation&lt;/b&gt;
&lt;br /&gt;
In this example, two servers with SQL Server installed are setup in Microsoft Azure. Both using different cloud service (different domain) but within the same virtual network. In Microsoft Azure, when creating a virtual machine in a cloud service, Azure assigns the VM to a subdomain of cloudapp.net. Here is the server information.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxIaYTfUg2P_KHESXDYcOcjQU-kp6RYlEUh_gSr8fjqsPvQnlf7u2vVpwx1cjF0Eqt4pKJclHUF_4tDhMBr120VPOB5MtHVWucjbCtYxA50J2velm9qNOlXj3rL7-rvzhGKUlz6XtpTfin/s1600/SqlTopology.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;197&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxIaYTfUg2P_KHESXDYcOcjQU-kp6RYlEUh_gSr8fjqsPvQnlf7u2vVpwx1cjF0Eqt4pKJclHUF_4tDhMBr120VPOB5MtHVWucjbCtYxA50J2velm9qNOlXj3rL7-rvzhGKUlz6XtpTfin/s400/SqlTopology.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The first VM (Server A)&lt;br /&gt;
Host Name: SQL2014&lt;br /&gt;
Domain:&amp;nbsp;CloudLan.d10.internal.cloudapp.net&lt;br /&gt;
IP address: 10.0.0.5&lt;br /&gt;
&lt;br /&gt;
The second VM (Server B)&lt;br /&gt;
Host Name: SQL2014&lt;br /&gt;
Domain: MoonLan.d10.internal.cloudapp.net&lt;br /&gt;
IP address: 10.0.0.4&lt;br /&gt;
&lt;br /&gt;
There are both hosted on the same virtual network.&lt;br /&gt;
&lt;br /&gt;
A database, TEST has been previously setup with a publication (PUB_C) and configured for transactional replication. Refer to this &lt;a href=&quot;http://www.travisgan.com/2015/06/sql-server-replication-case-study.html&quot; target=&quot;_blank&quot;&gt;post&lt;/a&gt;&amp;nbsp;for setup.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;Communication Verification&lt;/b&gt;&lt;/div&gt;
The FQDN (Fully Qualified Domain Name) is required to be used for these two servers communication as they are on the different domain.&lt;br /&gt;
&lt;br /&gt;
Attempt to ping Server B (with its FQDN) from Server A successful. Verified that they are able to communicate to each other. Connecting to Server B default SQL server instance from Server A successful with the FQDN of Server B.&lt;br /&gt;
&lt;br /&gt;
Perform the same test and verify the connecting status on Server B connecting to Server A default SQL Server instance successful. So far so good. Each server is able to communicate to the other server as long as server FQDN is used.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Replication - Adding subscription&lt;/b&gt;&lt;br /&gt;
On SQL Server A, attempt to add SQL Server B as subscriber to this publication&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzUee5zRdZ528iGRnwSv4Iqapvuw-c9IArSEC30BImoT2P4JtWdxPc46fPsBYDWNek96JrAK0bwgfZTpVBIhLz4UHs8m-8yVXNGlcnUY4p6COcLYOYXkbUqVwXuMzz33N0HNzF7LHcNCcB/s1600/SqlSubcription1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzUee5zRdZ528iGRnwSv4Iqapvuw-c9IArSEC30BImoT2P4JtWdxPc46fPsBYDWNek96JrAK0bwgfZTpVBIhLz4UHs8m-8yVXNGlcnUY4p6COcLYOYXkbUqVwXuMzz33N0HNzF7LHcNCcB/s400/SqlSubcription1.png&quot; width=&quot;372&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV0jES7eqcFSF_X10ckxZ6XlLBa7CyBcvNXhLd1XQY5DzJ08dHNG2ExnyOO6fM3yU6h-2McYaeCttFwJiKNSxUZnXJ_yKHQZ2JN0zkSlvSFohk-hzi_3PFRYUpGUJcMaSamS4XAv5nPThJ/s1600/SqlSubcription2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;257&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV0jES7eqcFSF_X10ckxZ6XlLBa7CyBcvNXhLd1XQY5DzJ08dHNG2ExnyOO6fM3yU6h-2McYaeCttFwJiKNSxUZnXJ_yKHQZ2JN0zkSlvSFohk-hzi_3PFRYUpGUJcMaSamS4XAv5nPThJ/s400/SqlSubcription2.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
In this example, the Push subscription method is used&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOsVCas_Ncc1uaZKOwRm4EEWPM785ASAvQGjkvt8W4r9ynA0fIO8KtFLUWule8cXOxGkGomOupsQZwAARDGXxyw0qVLH1HXy6jm0tHX5DZCgEvOMJ7Bg4Py4ftjPaTaqnCMZcX7CSaqclL/s1600/SqlSubcription3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;155&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOsVCas_Ncc1uaZKOwRm4EEWPM785ASAvQGjkvt8W4r9ynA0fIO8KtFLUWule8cXOxGkGomOupsQZwAARDGXxyw0qVLH1HXy6jm0tHX5DZCgEvOMJ7Bg4Py4ftjPaTaqnCMZcX7CSaqclL/s400/SqlSubcription3.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkgs1MyFnxMh3ToLHtqlJebhqIPz5l83BRE9qrrMRkRX7TeSuSnrCnRYF_JqSnvlYx0rWCcTNd6otkz0mkYpG27sOCMxWrerbYdqpDXgiQu2SJGxpK_5hW9-9-luTWnBQyOK_g536ur17j/s1600/SqlSubcription4.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;325&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkgs1MyFnxMh3ToLHtqlJebhqIPz5l83BRE9qrrMRkRX7TeSuSnrCnRYF_JqSnvlYx0rWCcTNd6otkz0mkYpG27sOCMxWrerbYdqpDXgiQu2SJGxpK_5hW9-9-luTWnBQyOK_g536ur17j/s400/SqlSubcription4.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Connecting the Server B default SQL Server instance with the FQDN since it is on different domain&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh78a7v00rdZLhH8JN5cn9QzZGD6a0DhL11Dr7FxdQ_Am7PzXJBZrqEPy4TC8JUomumjKq83PqMC3pIxbcckGL_dYv7ikn0lFE_mad4GfCGZYXd0FOUP0GkVPdGLYuNh-3Vgnl6i0zKy9bE/s1600/SqlSubcription5.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;173&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh78a7v00rdZLhH8JN5cn9QzZGD6a0DhL11Dr7FxdQ_Am7PzXJBZrqEPy4TC8JUomumjKq83PqMC3pIxbcckGL_dYv7ikn0lFE_mad4GfCGZYXd0FOUP0GkVPdGLYuNh-3Vgnl6i0zKy9bE/s400/SqlSubcription5.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Error!&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjExuYno2AU_6wclt5rSiOCnWsFav_0SEEGdSBW61b3wEwqd2qSVK7sc5E8Ix0MxkMZPryInuExlAIO_bEVbASCltCvF-NJG5sp5TuMp4Tkm0dwH3xu4vcXOs64URRu4xqduuO9p8Zu00X/s1600/SqlSubcriptionError.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;111&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjExuYno2AU_6wclt5rSiOCnWsFav_0SEEGdSBW61b3wEwqd2qSVK7sc5E8Ix0MxkMZPryInuExlAIO_bEVbASCltCvF-NJG5sp5TuMp4Tkm0dwH3xu4vcXOs64URRu4xqduuO9p8Zu00X/s400/SqlSubcriptionError.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;Cannot connect to SQL2014.MoonLan.d9.internal.cloudapp.net.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: red;&quot;&gt;------------------------------&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;ADDITIONAL INFORMATION:&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;&lt;br /&gt;&lt;/span&gt;
&lt;span style=&quot;color: red;&quot;&gt;SQL Server replication requires the actual server name to make a connection to the server. Specify the actual server name, &#39;SQL2014&#39;. (Replication.Utilities)&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
If we read the error message, it states that the actual server name &#39;SQL2014&#39; is required. It appears that the subscriber server name (server B) used should be the host name of the server, rather than the FQDN name. Or is it?&lt;br /&gt;
&lt;br /&gt;
Let&#39;s investigate. On Server B, run this command,&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;SELECT @@SERVERNAME;
GO
SELECT *
FROM sys.servers;
GO
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivARMidPWiNcF8C9CUh0UQB4dceuPiA8JN-YaGN0wLtMARpOeOFSZg8hCxEccICEueHbapw7K9xfeGiyxP4HMgC7EjpYFCBX5sfbo1ELh0b6CnbXDeQ_WiGjoVQh_jHvM03Y1qV2IBM2QC/s1600/sqlsysservers.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;106&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivARMidPWiNcF8C9CUh0UQB4dceuPiA8JN-YaGN0wLtMARpOeOFSZg8hCxEccICEueHbapw7K9xfeGiyxP4HMgC7EjpYFCBX5sfbo1ELh0b6CnbXDeQ_WiGjoVQh_jHvM03Y1qV2IBM2QC/s320/sqlsysservers.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The sys.servers system table stores the local SQL Server instance name (when server_id = 0) and linked servers (if any, for server_id &amp;gt; 0). The current result for local instance is SQL2014.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;A word of caution for the steps below. You do not need to change the sys.servers usually unless the entry in sys.servers is different from the server host name. You may just need to add an alias on SQL Server A (Go to &#39;&lt;/b&gt;&lt;b&gt;Add an Alias for SQL Server B&#39; section below)&lt;/b&gt;&lt;b&gt;. More information on the conclusion section below.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
To verify if SQL replication subscriber error is referring to the host name or this entry in the sys.servers, if we change this record into SQL2014B (with the B),&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;EXEC sp_dropserver &#39;SQL2014&#39;
GO
EXEC sp_addserver &#39;SQL2014B&#39;, local;
GO
&lt;/pre&gt;
&lt;br /&gt;
Notice that @@SERVERNAME does not reflect the change. Let&#39;s restart the SQL Server service on Server B. Now both&amp;nbsp;@@SERVERNAME and the sys.servers return SQL2014B. A reminder that, the server name (host name) has not been changed. It is still SQL2014.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:ps&quot;&gt;$env:COMPUTERNAME
&lt;/pre&gt;
&lt;br /&gt;
Result returns as SQL2014&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Setting up an Alias for SQL Server B&lt;/b&gt;&lt;br /&gt;
Back to Server A. We need to connect to the SQL Server B instance (SQL2014B) on Server B (SQL2014). One way to accomplish this successfully is to create a SQL Server alias for SQL Server instance (SQL2014B) on Server A. SQL Server Alias provides alternate name to be used to connect to the target SQL Server. Adding SQL Server alias can be done with SQL Native Client in the SQL Server configuration manager, and in this case on Server A.&lt;br /&gt;
&lt;br /&gt;
Note that SSMS is running in 32-bit, while the SQL Server in this case is installed with 64-bit version. While the replication agent process is called from SQL Server agent, the replication process run in 64-bit. So, in this case we will setup an alias in both 32-bit and 64-bit SQL Native Client configuration.&lt;br /&gt;
&lt;br /&gt;
Alias: SQL2014B &amp;nbsp; &amp;nbsp; (Same as the record in sys.servers on SQL Server B)&lt;br /&gt;
Port: 1433 &amp;nbsp; (SQL Server default port since it&#39;s default instance)&lt;br /&gt;
Protocol: TCP/IP&lt;br /&gt;
Server: SQL2014.MoonLan.d10.internal.cloudapp.net &amp;nbsp; &amp;nbsp; (This is Server B FQDN)&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPu9u6U1Dm0KrM4QIAcVDD0RI4t3HU0Xz4ehMonC5eN5WWMQ6_gCq648tHz6OjinwD8C7jGsGZ-xrCNdv-COCdVW3qGYtXV3GLiokgiyuvqRgtzF-OnfeFnBBQ18M2lDtPFwg6O_fn_jA0/s1600/SqlAlias1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPu9u6U1Dm0KrM4QIAcVDD0RI4t3HU0Xz4ehMonC5eN5WWMQ6_gCq648tHz6OjinwD8C7jGsGZ-xrCNdv-COCdVW3qGYtXV3GLiokgiyuvqRgtzF-OnfeFnBBQ18M2lDtPFwg6O_fn_jA0/s1600/SqlAlias1.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYrcqwhb4lrAA219lGzbDTS3I9tGA62tRfui-UiZe9MqcxmbgBYyrhz_avdNwYbpHr9wo-RlL2P8tbhOfTV2ktJpSP8YgRvo7e86koef1QTVQSzwsCNDCnZoAoB9HE-c5cnIg0bTIhfyPj/s1600/SqlAlias2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;137&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYrcqwhb4lrAA219lGzbDTS3I9tGA62tRfui-UiZe9MqcxmbgBYyrhz_avdNwYbpHr9wo-RlL2P8tbhOfTV2ktJpSP8YgRvo7e86koef1QTVQSzwsCNDCnZoAoB9HE-c5cnIg0bTIhfyPj/s320/SqlAlias2.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Now back to addition the replication publication subscriber screen. Enter SQL2014B.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWAbHgG0Qk0SNbjj3nynO_VM9kxDBSTfwnMdXBdnAv3Zec4-srH7rdqz8uaGEOfBCGW88LMbT_MEh7vY4ksIZxoNTiztTk_gsDA_moh_HGAl0cYl2V42F7cFnsOeI_CP2g1MV15zC1XP1J/s1600/SqlSubcription5-1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;172&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWAbHgG0Qk0SNbjj3nynO_VM9kxDBSTfwnMdXBdnAv3Zec4-srH7rdqz8uaGEOfBCGW88LMbT_MEh7vY4ksIZxoNTiztTk_gsDA_moh_HGAl0cYl2V42F7cFnsOeI_CP2g1MV15zC1XP1J/s400/SqlSubcription5-1.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Now it works.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmbNLZ5W8jZhIXMgPHYZNUhiqVev9PGLb0KkqZB-0Rew7Ew7IV9KqeVunlKzEQ2cfKE3MRMJIdVvpwpAjgKKOB7KME4lKaqAJ2UPeeitJRhxQfzGI-UTQjPabqAsvmrH7pGiDzhwNh-R3g/s1600/SqlSubcription6.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;157&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmbNLZ5W8jZhIXMgPHYZNUhiqVev9PGLb0KkqZB-0Rew7Ew7IV9KqeVunlKzEQ2cfKE3MRMJIdVvpwpAjgKKOB7KME4lKaqAJ2UPeeitJRhxQfzGI-UTQjPabqAsvmrH7pGiDzhwNh-R3g/s400/SqlSubcription6.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;Observation&lt;/b&gt;&lt;br /&gt;
From we just observed, the server name enter during the subscriber addition needs to be same as the local server entry in the sys.servers of the subscriber. It has nothing to do with the host name of the subscriber server. The local server entry may differs from the host name due to host name (computer name) changes after SQL Server installation. Or like this case it was changed intentionally.&lt;br /&gt;
&lt;br /&gt;
If you didn&#39;t setup the alias in 32-bit SQL Native Client configuration, you may continue to encounter the error during setup. Vice versa, if you didn&#39;t setup the alias in 64-bit SQL Native Client configuration on 64-bit SQL Server, you may be to complete adding the subscriber, but replication encounter error during synchronization to subscriber.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnu4JFRhwjzPnjupyX9HVTWATwgZ7LfvhowBLX8gQNISFtn8mByP8pE9wRxRR4sbXF2D2egKpnWeHEDwipTAuvoPiZVqZSTPPlT4SqosBiumDG7jqb6vGb4e2lFBJGz-c3M_WBCbH3XL42/s1600/SqlSyncError.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;150&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnu4JFRhwjzPnjupyX9HVTWATwgZ7LfvhowBLX8gQNISFtn8mByP8pE9wRxRR4sbXF2D2egKpnWeHEDwipTAuvoPiZVqZSTPPlT4SqosBiumDG7jqb6vGb4e2lFBJGz-c3M_WBCbH3XL42/s320/SqlSyncError.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;
To regroup, the example above address two issues.&lt;br /&gt;
&lt;br /&gt;
- First, as we see that during the subscriber addition, the server name entered needs to be the same as the local server entry in the sys.servers of the subscriber.&lt;br /&gt;
- The second issue in this example is that the host name is the same on both servers, SQL2014 with different FQDN (this issue is not common)&lt;br /&gt;
&lt;br /&gt;
To address the first issue, all we have to do is to create an alias (the entry as in sys.servers, usually same as its server host name) for SQL Server B on Server A (the connecting side). Setting an alias redirect the SQL communication of the alias to the actual SQL Server instance (with FQDN).&lt;br /&gt;
&lt;br /&gt;
However, to address the second issue in this example, it becomes a little tricky. We could change the record in sys.servers of the SQL Server B to be something different (like we saw, SQL2014B) without changing the host name of the server. However, having a different name for SQL Server default instance and the server host name usually is not preferable. It could cause a lot of confusion and potential break some codes and setting configured previously. This may works when it is not an option to change the host name. With that said, always try to have the default SQL Server instance name similar as the server host name.&lt;br /&gt;
&lt;br /&gt;
Note. Changing the host name (compute name) of the server does not change the entry in the sys.servers. You will need to perform the sp_dropserver and sp_addserver steps as shown above to change the SQL Server instance name in sys.servers to match the host name.&lt;br /&gt;
&lt;br /&gt;
This example setup the Push subscription from SQL Server A. As a result, the alias of SQL Server B is setup on the SQL Server A. If you are setting Pull subscription from SQL Server B, the alias of SQL Server A will need to be setup on SQL Server B. Just remember that the alias (of the receiving side) is setup on the connecting side (the connection initializing side).</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/6655396350232269497/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2015/06/sql-replication-subscriber-on-different.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/6655396350232269497'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/6655396350232269497'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2015/06/sql-replication-subscriber-on-different.html' title='SQL Replication - Subscriber On Different Domain'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxIaYTfUg2P_KHESXDYcOcjQU-kp6RYlEUh_gSr8fjqsPvQnlf7u2vVpwx1cjF0Eqt4pKJclHUF_4tDhMBr120VPOB5MtHVWucjbCtYxA50J2velm9qNOlXj3rL7-rvzhGKUlz6XtpTfin/s72-c/SqlTopology.png" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-2399833994167725913</id><published>2015-06-25T07:00:00.000-05:00</published><updated>2015-06-28T22:54:54.407-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Replication"/><title type='text'>SQL Server Replication - Case Study &amp; Implementation</title><content type='html'>This post discuss of evaluating a business case, with designing and implementing SQL Server replication in a step-by-step tutorial to address the requirement.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;b&gt;Business Case&lt;/b&gt;&lt;br /&gt;
There are two SQL Servers. One hosted on Server A and one hosted on Server B. These two servers are stand alone server on a similar network but not joined to the same domain. Each SQL Server has a database with similar list of tables. Within this list of tables, a set of tables (A_Tbl*) have read/write transactions performed on the database hosted on SQL Server A, with its data also available on SQL Server B for read only purpose. Similarly, there are another set of tables (B_Tbl*) in the database that have read/write transactions performed on SQL Server B, with the data also available on SQL Server A for read only purpose. Any data change occurred on any table of that database on one SQL Server should be promptly reflected on the other SQL Server.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSmj2v-HKV26Sc-mpTLOrfOpxypuDj_Q0FS681rSTNKwNxM1T2apt2H45cNA3hEPOZkXGFSR2aRNCW0lNy1IMf3HnbMq6T-lYWN0JaScKWX1r9KgQwiRxTO6nT6SG9Ii6jBGSTiZpVvFcN/s1600/UseCase.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;286&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSmj2v-HKV26Sc-mpTLOrfOpxypuDj_Q0FS681rSTNKwNxM1T2apt2H45cNA3hEPOZkXGFSR2aRNCW0lNy1IMf3HnbMq6T-lYWN0JaScKWX1r9KgQwiRxTO6nT6SG9Ii6jBGSTiZpVvFcN/s320/UseCase.PNG&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
SQL Server replication stand out to be a good solution to address this business and technical requirement. Replication topology could includes servers that are not in the same domain. The servers do not need to be in a cluster environment or require any domain account. SQL Server replication also provides option for replicating selected objects (tables) to another SQL Server. The receiving database on the other SQL Server is available for use even when new data is being replicated over.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Design &amp;amp; Implementation&lt;/b&gt;&lt;br /&gt;
Transactional replication is chosen to allows data to be replicated in a continuous manner. The replication topology design in this case have each publisher utilize its own distributor for its publication. The distributor is hosted on the same server as the publisher. Push subscription method is chosen on each server to push the publication articles to another server. A local Windows account will be created and used for replication agent process account as well as connecting to SQL Server.&lt;br /&gt;
&lt;br /&gt;
SQL Server transactional replication is implemented by multiple agents, there are snapshot agent, log reader agent and distribution agent. Here are some description and requirement of each agent.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Snapshot agent&lt;/b&gt;&lt;br /&gt;
- Prepare snapshot files (schema, script, indexes, data, etc), and record synchronization status in distribution database. Snapshot files are used to initiate subscriber for transactional replication and also used for other replication&lt;br /&gt;
- Run at distributor&lt;br /&gt;
- Connect to publisher either with a Windows account or SQL account. Connecting account at least db_owner database role in publication database&lt;br /&gt;
- Connect to distributor with a Windows account (process account). Process account at least db_owner database role in&amp;nbsp;distribution&amp;nbsp;database. Process account has write permission to snapshot share&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Log reader agent&lt;/b&gt;&lt;br /&gt;
- Monitor transaction log on publication database and copy transaction marked for replication to distribution database&lt;br /&gt;
- Run at distributor&lt;br /&gt;
- Connect to Publisher either with a Windows account or SQL account. Windows account will be used in this case. The same Windows account (connecting account) with at least db_owner database role in publication database&lt;br /&gt;
- Connect to Distributor with a Windows account (process account). Process account with at least db_owner database role in&amp;nbsp;distribution&amp;nbsp;database&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Distributor agent&amp;nbsp;&lt;/b&gt;&lt;b&gt;(&lt;/b&gt;&lt;i&gt;&lt;b&gt;Push subscription)&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;
- Move snapshot in transaction stored in distribution database to the subscriber&lt;br /&gt;
- Run at distributor&lt;br /&gt;
- Connect to distributor with a Windows account (process account). Process account at least db_owner database role in&amp;nbsp;distribution&amp;nbsp;database. Process account has read permission to snapshot share. Process account a member of PAL&amp;nbsp;(Publication Access List)&lt;br /&gt;
-&amp;nbsp;Connect to subscriber with either with a Windows account or SQL account. Windows account will be used in this case. The same Windows account (connecting account) with at least db_owner database role in subscription&amp;nbsp;database&lt;br /&gt;
&lt;br /&gt;
This is the design layout,&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW_pDDDjJ6b2oEqA5bLU1Ds_fSW5bq4GNyBWt7S4qKljnHRmqCUIIho-ALgHh5GoMgbMD_M4_PxJ1XTAyl72K46WP1pj6eFLG3PgGp3Cp9m2wGQo0FvpNR8IaG2ydO2FNvns19ozsKLyo6/s1600/Design.PNG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;322&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW_pDDDjJ6b2oEqA5bLU1Ds_fSW5bq4GNyBWt7S4qKljnHRmqCUIIho-ALgHh5GoMgbMD_M4_PxJ1XTAyl72K46WP1pj6eFLG3PgGp3Cp9m2wGQo0FvpNR8IaG2ydO2FNvns19ozsKLyo6/s400/Design.PNG&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
As the distributor and publisher of each publication are hosted on the same server, for simplicity, one local Windows account is used for snapshot agent, log reader agent and distributor agent on each server. As both servers are not on the same domain, one Windows account with similar name and password will be used for authentication purpose.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Preparation&lt;/b&gt;&lt;br /&gt;
In this example, we will create a database called TEST on each SQL Server. The actual server name in the example is SQL2008R2 (acted as Server A) and SQL2008R2A (acted as Server B). Each server only have one default SQL Server instance.&lt;br /&gt;
&lt;br /&gt;
The SQL Server agent service startup type on each server has been changed to automatic. A firewall rule to allow TCP port 1433 has been created on each server for SQL Server communication.&lt;br /&gt;
&lt;br /&gt;
Lets create the database and tables. Connect to SQL Server A and run the database and table creation script. Insert some data into A_tbl tables&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE master;
GO
CREATE DATABASE TEST;
GO
USE TEST;
GO
CREATE TABLE [dbo].[A_tbl1] (
 [ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
 [Desc] [varchar](50) NULL ) 
GO
CREATE TABLE [dbo].[A_tbl2] (
 [ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
 [Desc] [varchar](50) NULL ) 
GO
CREATE TABLE [dbo].[A_tbl3] (
 [ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
 [Desc] [varchar](50) NULL ) 
GO
CREATE TABLE [dbo].[B_tbl1] (
 [ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
 [Desc] [varchar](50) NULL ) 
GO
CREATE TABLE [dbo].[B_tbl2] (
 [ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
 [Desc] [varchar](50) NULL ) 
GO
CREATE TABLE [dbo].[B_tbl3] (
 [ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
 [Desc] [varchar](50) NULL ) 
GO
INSERT INTO dbo.A_tbl1 VALUES (&#39;Hello&#39;), (&#39;Hi&#39;);
INSERT INTO dbo.A_tbl2 VALUES (&#39;Morning&#39;), (&#39;Night&#39;);
INSERT INTO dbo.A_tbl3 VALUES (&#39;SQL&#39;), (&#39;ORACLE&#39;);
GO
&lt;/pre&gt;
&lt;br /&gt;
On SQL Server B, create the database and same tables. Insert some data into B_tbl tables&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE master;
GO
CREATE DATABASE TEST;
GO
USE TEST;
GO
CREATE TABLE [dbo].[A_tbl1] (
 [ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
 [Desc] [varchar](50) NULL ) 
GO
CREATE TABLE [dbo].[A_tbl2] (
 [ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
 [Desc] [varchar](50) NULL ) 
GO
CREATE TABLE [dbo].[A_tbl3] (
 [ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
 [Desc] [varchar](50) NULL ) 
GO
CREATE TABLE [dbo].[B_tbl1] (
 [ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
 [Desc] [varchar](50) NULL ) 
GO
CREATE TABLE [dbo].[B_tbl2] (
 [ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
 [Desc] [varchar](50) NULL ) 
GO
CREATE TABLE [dbo].[B_tbl3] (
 [ID] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
 [Desc] [varchar](50) NULL ) 
GO
INSERT INTO dbo.B_tbl1 VALUES (&#39;Yes&#39;), (&#39;No&#39;);
INSERT INTO dbo.B_tbl2 VALUES (&#39;Food&#39;), (&#39;Drink&#39;);
INSERT INTO dbo.B_tbl3 VALUES (&#39;See ya&#39;), (&#39;Bye&#39;);
GO
&lt;/pre&gt;
&lt;br /&gt;
On Server A,&lt;br /&gt;
Creates a Windows account for replication agents through computer management&lt;br /&gt;
Account: sqlreplication&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmyXpubuYTMvn3RoAcZysAROZ2kVPvuk51MyOrgM2uJQ9sgt04vnqpf-q2YEJbwaNHKSl4jGqTxIw7-2AT9Cro4pk54ZkOswo-EkNzhpR0gE8UsDJf-W6fT8WyJZK-xINddjZS2j-_a5rf/s1600/WindowsAcctCreation.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;340&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmyXpubuYTMvn3RoAcZysAROZ2kVPvuk51MyOrgM2uJQ9sgt04vnqpf-q2YEJbwaNHKSl4jGqTxIw7-2AT9Cro4pk54ZkOswo-EkNzhpR0gE8UsDJf-W6fT8WyJZK-xINddjZS2j-_a5rf/s400/WindowsAcctCreation.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Grant the Windows account created read / write access to its snapshot replication folder (on its server). In this example, the default location is used. The replication folder is designated during replication setup.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;
eg. C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\repldata&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh-EWVTgrHb7XGeD75gmld-mW6ccTbPIfbhugDAOpG76lrAx33PPPwQE4ZlkmzYeLIqsMSWvFNC0BCZBJIhMyhz3RKljHBlr_tcoxEYJOp3SdxZ6WdXdSxKZf4-N0m_j3ZJPLgg8odqAUu/s1600/SnapshotFolderPermission.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh-EWVTgrHb7XGeD75gmld-mW6ccTbPIfbhugDAOpG76lrAx33PPPwQE4ZlkmzYeLIqsMSWvFNC0BCZBJIhMyhz3RKljHBlr_tcoxEYJOp3SdxZ6WdXdSxKZf4-N0m_j3ZJPLgg8odqAUu/s400/SnapshotFolderPermission.png&quot; width=&quot;353&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Create the account in the SQL Server A for the Windows account just created. Grant the local Windows account db_owner role on the publication database (in this case also a subscription database for other publication), TEST database.&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpicebldZovxSARA8Ux7r22SyGZgJj66Fwi4oc-DOo4UG1T3txzPaEOO1yP9Ja2K3e1QnpIqwOOJdGH-Bas9p2gGzXtD3fCqcCv4k7IfNygHKfth-m04kF79m3xsGkUib7x0AV1gjx8frl/s1600/sqlreplicationLogin.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;95&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpicebldZovxSARA8Ux7r22SyGZgJj66Fwi4oc-DOo4UG1T3txzPaEOO1yP9Ja2K3e1QnpIqwOOJdGH-Bas9p2gGzXtD3fCqcCv4k7IfNygHKfth-m04kF79m3xsGkUib7x0AV1gjx8frl/s400/sqlreplicationLogin.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrQMSdlqQsBk9w0PP0X0IPOZk6gzgxYC8BEnzxc5dwU9_pylQ7C_XFrgNxG6L4c6PIyo5K2-wZcxANm2mI82W3dzU_HzBFDZ8hyphenhyphenZQBmC6lHsbRV-bSPSKoIqLOfzRS91zMRCeFzQpTBAkS/s1600/sqlreplicationDBRole.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrQMSdlqQsBk9w0PP0X0IPOZk6gzgxYC8BEnzxc5dwU9_pylQ7C_XFrgNxG6L4c6PIyo5K2-wZcxANm2mI82W3dzU_HzBFDZ8hyphenhyphenZQBmC6lHsbRV-bSPSKoIqLOfzRS91zMRCeFzQpTBAkS/s400/sqlreplicationDBRole.png&quot; width=&quot;397&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
On Server B,&lt;br /&gt;
Perform the same action like Server A.&lt;br /&gt;
&lt;br /&gt;
Creates a Windows account with the &lt;b&gt;same name and same password&lt;/b&gt; like Server A for replication agents through computer management&lt;br /&gt;
Account: sqlreplication&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8qoQBByBo9iT8KxcGREeYi9Ma_dJ4tHLASpZP-h3QLYGIINoOgzGikvVinF6WjZ_so4Qjfvd4jQp_PQwXhyphenhyphenFuVYB1e_QmM2Htw0IYCmkbVZxucwA5_SZ7c-IR24Sfvyxy5GWHmIjRvHYJ/s1600/WindowsAcctCreation.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;340&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8qoQBByBo9iT8KxcGREeYi9Ma_dJ4tHLASpZP-h3QLYGIINoOgzGikvVinF6WjZ_so4Qjfvd4jQp_PQwXhyphenhyphenFuVYB1e_QmM2Htw0IYCmkbVZxucwA5_SZ7c-IR24Sfvyxy5GWHmIjRvHYJ/s400/WindowsAcctCreation.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Grant the Windows account just created with read / write access to its snapshot replication folder (on its server). In this example, the default location is used. The replication folder is designated during replication setup.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;
eg. C:\Program Files\Microsoft SQL Server\MSSQL10_50.MSSQLSERVER\MSSQL\repldata&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfX0nRaAoZgj4pV-PXsEGAGINFHff3buXnUKKlPiQo7w6erSDYk1wKaNc0Y3uyCrKPjWv-s6t-scJOvlXYQUf7WSfv8dEtblf6p99pvYc1dujZKNzDfbHJQawQViNHoEsgJNlx2XetCCmx/s1600/SnapshotFolderPermissionB.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfX0nRaAoZgj4pV-PXsEGAGINFHff3buXnUKKlPiQo7w6erSDYk1wKaNc0Y3uyCrKPjWv-s6t-scJOvlXYQUf7WSfv8dEtblf6p99pvYc1dujZKNzDfbHJQawQViNHoEsgJNlx2XetCCmx/s400/SnapshotFolderPermissionB.png&quot; width=&quot;352&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Create the account in the SQL Server B for the Windows account just created. Grant the local Windows account db_owner role on the publication database (in this case also a subscription database for other publication), TEST database.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEHUcxNX7xtXzX6Lrb0V7188NBIyNY_-YOmxGqGkqesdwbI_QZKdtxCliUT7MDwD2VGTkgJlUgfv4CKOfpzepuoKqPZbrf44q3vurQE9R0_nwdc71AkgRy-rEVEkRdvQZ3ourUPqfxA5QC/s1600/sqlreplicationLoginB.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;88&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEHUcxNX7xtXzX6Lrb0V7188NBIyNY_-YOmxGqGkqesdwbI_QZKdtxCliUT7MDwD2VGTkgJlUgfv4CKOfpzepuoKqPZbrf44q3vurQE9R0_nwdc71AkgRy-rEVEkRdvQZ3ourUPqfxA5QC/s400/sqlreplicationLoginB.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZYF968Nmf5eC1FxrFPUuj-x7OiC08pQtp-XvIbqfSvYNghPdecxrv4vJcU3PSrtwH_2MYQ6os-mvOgh99yF_iQKX2XseaOOeFpGrCbung2JRSh0ERi2I4DH9wiAnnq3KGcoL7HFHQXsmi/s1600/sqlreplicationDBRoleB.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;396&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZYF968Nmf5eC1FxrFPUuj-x7OiC08pQtp-XvIbqfSvYNghPdecxrv4vJcU3PSrtwH_2MYQ6os-mvOgh99yF_iQKX2XseaOOeFpGrCbung2JRSh0ERi2I4DH9wiAnnq3KGcoL7HFHQXsmi/s400/sqlreplicationDBRoleB.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;SQL Replication Implementation - Publication A&lt;/b&gt;&lt;br /&gt;
Now we are ready to setup the replication. Let&#39;s setup the publication A (PUB_A) on SQL Server A for the three articles (three tables A_tbl1, A_tbl2, A_tbl3). Keep in mind that we will be setting up with push subscription to SQL Server B.&lt;br /&gt;
&lt;br /&gt;
This example mainly use the SSMS GUI for all the replication tasks. So, ready for the screenshot?&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiw4sQklcscn_Fm7JxkQ0qSF865bKZ8ecjZWNZWZHqhhhOAAWn8JivpcNh_rX3GNJKgtA7qKGDeDlm1UrY8WCULKJUsBV2xXOHGC_6t0i9FSpkLQvq9YLOiTCA_0kHBeDCxdQsMHendccFp/s1600/SqlReplicationA1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;220&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiw4sQklcscn_Fm7JxkQ0qSF865bKZ8ecjZWNZWZHqhhhOAAWn8JivpcNh_rX3GNJKgtA7qKGDeDlm1UrY8WCULKJUsBV2xXOHGC_6t0i9FSpkLQvq9YLOiTCA_0kHBeDCxdQsMHendccFp/s320/SqlReplicationA1.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Distributor hosted on the same server.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSoF3ZdcgnZ6glN-sQPRYJtHShedFG7e1spz6IArmBedB0tPAy841Q3P965-E0cepZ8oDdLGnMg2eLkc2gFUfNcdzIuA_N1kyQurc3CaLlMibz7ID_pnagDNxIS5aIMSOnWO2rfD3ZwiPN/s1600/SqlReplicationA2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;217&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSoF3ZdcgnZ6glN-sQPRYJtHShedFG7e1spz6IArmBedB0tPAy841Q3P965-E0cepZ8oDdLGnMg2eLkc2gFUfNcdzIuA_N1kyQurc3CaLlMibz7ID_pnagDNxIS5aIMSOnWO2rfD3ZwiPN/s400/SqlReplicationA2.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
This is where we define the snapshot folder that we grant the sqlreplication Windows account the read write permission.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYQv18cDHTkDys_I1UnMIqkcmLMtDyMmJCNVCrwPi3itCXdMT9Touyy-iYeiFWp8-hvq5maooGvf6d7srqLjz7bUfbTQe9KmWDsSDGVv4CQUFFImFp9rDq-zIaiqxaIaxEKMDyUTVnSakP/s1600/SqlReplicationA3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;353&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYQv18cDHTkDys_I1UnMIqkcmLMtDyMmJCNVCrwPi3itCXdMT9Touyy-iYeiFWp8-hvq5maooGvf6d7srqLjz7bUfbTQe9KmWDsSDGVv4CQUFFImFp9rDq-zIaiqxaIaxEKMDyUTVnSakP/s400/SqlReplicationA3.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMbPxIFecERJHqqjFRZgzks_nCm_tfUxhcUDsxKPgvqiOpfZAMUu4EjgcsImSGghsihTzYngxjAvBKuwN2ACe8P-uRLJ8Ofs8J7tPbEL7gYKDQasmLU7le-EmgjZImeXbXU6trlO6vj_S2/s1600/SqlReplicationA4.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;142&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMbPxIFecERJHqqjFRZgzks_nCm_tfUxhcUDsxKPgvqiOpfZAMUu4EjgcsImSGghsihTzYngxjAvBKuwN2ACe8P-uRLJ8Ofs8J7tPbEL7gYKDQasmLU7le-EmgjZImeXbXU6trlO6vj_S2/s320/SqlReplicationA4.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&amp;nbsp;Use transactional replication&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9MbOlvO5zrVCBPI-ZsHuIj9AninJDuju5Ck1Bec68VVWrTO0AGLYL-2t368kDIwUtdrkmDUaewy7rXqRUnBf8AB7l_4U8TtkII9hKGKDYqH92arblKyxlOniOQujQFGbTWPbeFTo2QD1p/s1600/SqlReplicationA5.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;161&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9MbOlvO5zrVCBPI-ZsHuIj9AninJDuju5Ck1Bec68VVWrTO0AGLYL-2t368kDIwUtdrkmDUaewy7rXqRUnBf8AB7l_4U8TtkII9hKGKDYqH92arblKyxlOniOQujQFGbTWPbeFTo2QD1p/s400/SqlReplicationA5.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Selecting the A_tbl1, A_tbl2, and A_tbl3 for this publication&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYMf1egMc_PLgNfegP9uRM51QTGK9h-dR4m9xnrMOtlek4THjv02XzALD-LH20XVgZ5ezjeIP4hVJO45EqvyAyZtkkgzDgxxoanEBIevfuCFcD-tymppktk08WKrSwAP8RxW7vBQD_QOxu/s1600/SqlReplicationA6.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;196&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYMf1egMc_PLgNfegP9uRM51QTGK9h-dR4m9xnrMOtlek4THjv02XzALD-LH20XVgZ5ezjeIP4hVJO45EqvyAyZtkkgzDgxxoanEBIevfuCFcD-tymppktk08WKrSwAP8RxW7vBQD_QOxu/s400/SqlReplicationA6.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
We will initiate the snapshot after we finish all the configuration&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqMSca_u1Ng7MNUv8ISlK3WhwUOceFQqdtQKDf8aeYxVBN2SxKLih-17JaRxzmFOVTKjq4Br2Yns5RaeM7uNckkuLjgqKy8dpnZGqcaRXQ1fADneVqkc_OqaH3S0xTP3ymsS-05pv1hjBo/s1600/SqlReplicationA6-1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;163&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqMSca_u1Ng7MNUv8ISlK3WhwUOceFQqdtQKDf8aeYxVBN2SxKLih-17JaRxzmFOVTKjq4Br2Yns5RaeM7uNckkuLjgqKy8dpnZGqcaRXQ1fADneVqkc_OqaH3S0xTP3ymsS-05pv1hjBo/s400/SqlReplicationA6-1.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Using sqlreplication account on Server A for snapshot agent. We have previously configured the account with db_owner role in the TEST database.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKqMH0lV6LYepa_l3WBR8-WiXXKTVPXumwcAvgTjCFvn6fRSrvH_svjxoFbXq9MHESmmoeEGc1CcHafMAmZNWCM6sJWQfHqglR1j47721L-kxceDXqNwsjY_4mdjpKQJrIcngV1Oq2Vc5y/s1600/SqlReplicationA7.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;340&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKqMH0lV6LYepa_l3WBR8-WiXXKTVPXumwcAvgTjCFvn6fRSrvH_svjxoFbXq9MHESmmoeEGc1CcHafMAmZNWCM6sJWQfHqglR1j47721L-kxceDXqNwsjY_4mdjpKQJrIcngV1Oq2Vc5y/s400/SqlReplicationA7.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Both snapshot and log reader agent using the same local Windows account.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjewodpJ2VcbxaR4y6hLlfJNdkHAuDWOtGxHyJH6YaiFMdnVix9_WWljjyl2o5aq1pOBq1HrOvuzR_gL465ZRJY9CoKCUZthFoBznwSoZ9OvvMogXLwVDrARub6s7Eiy8JYHK1Rbfo3VDXC/s1600/SqlReplicationA8.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;167&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjewodpJ2VcbxaR4y6hLlfJNdkHAuDWOtGxHyJH6YaiFMdnVix9_WWljjyl2o5aq1pOBq1HrOvuzR_gL465ZRJY9CoKCUZthFoBznwSoZ9OvvMogXLwVDrARub6s7Eiy8JYHK1Rbfo3VDXC/s400/SqlReplicationA8.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKYMwiO9l-YuOlhp4k5_DCx0QDqSjJgb0RINqrsl3Q051FbJMedky0HhLjfNBCfGvJf9k5p1k2oSs1J6ZXJZlUKmaOI9BG_lGsXEFXiAmrguDSo3hP4Jr5beDeH8KvTKRfP7KzOqk4Cgj-/s1600/SqlReplicationA9.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;164&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKYMwiO9l-YuOlhp4k5_DCx0QDqSjJgb0RINqrsl3Q051FbJMedky0HhLjfNBCfGvJf9k5p1k2oSs1J6ZXJZlUKmaOI9BG_lGsXEFXiAmrguDSo3hP4Jr5beDeH8KvTKRfP7KzOqk4Cgj-/s320/SqlReplicationA9.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Name this publication as PUB_A&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4YuXSO0wX4svSZi0bQfb8XagZEDxUSL4XzDIPI7bI8Mj6Gpxn65crgXjgTx7pQoq2FcBmVF2MzJn661yiINv3XfmSaPLz1X342xiMDmMVr_dXS22COndP4KDB6j_pAEoOe3h1IBG9Ph4t/s1600/SqlReplicationA10.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;142&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4YuXSO0wX4svSZi0bQfb8XagZEDxUSL4XzDIPI7bI8Mj6Gpxn65crgXjgTx7pQoq2FcBmVF2MzJn661yiINv3XfmSaPLz1X342xiMDmMVr_dXS22COndP4KDB6j_pAEoOe3h1IBG9Ph4t/s320/SqlReplicationA10.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZPIPKGs-SQ2mmMS5RbWzz87dP1wde1JmJyAfeB8cokqYguuWV0CUNAzlHnJabOanhepfNw4YvfXFm-7jyfkCP7tX0r6RcgfF6DE0vub8IfSaEG1ooD1F6DjM2VIR6_oUzlzS_QnS0UoDV/s1600/SqlReplicationA11.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;226&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZPIPKGs-SQ2mmMS5RbWzz87dP1wde1JmJyAfeB8cokqYguuWV0CUNAzlHnJabOanhepfNw4YvfXFm-7jyfkCP7tX0r6RcgfF6DE0vub8IfSaEG1ooD1F6DjM2VIR6_oUzlzS_QnS0UoDV/s320/SqlReplicationA11.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
There is one more thing to do here. The sqlreplication Windows account needs to be granted the db_owner role in the newly created distribution database.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz06wCY_Ab0qa9_PTkN8qrYNWLLmpqWmiXISwBjH_VDmB5L_-m3YjDazSPYSoRkVBUmG1mVtaEn0OkaTWkZLpvS7BTciX6WqEwJq-gSiNMlpC1Jee1w2RtthqzvL4dVmvIU7UqHXJFJO_T/s1600/SqlReplicationDistRole.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz06wCY_Ab0qa9_PTkN8qrYNWLLmpqWmiXISwBjH_VDmB5L_-m3YjDazSPYSoRkVBUmG1mVtaEn0OkaTWkZLpvS7BTciX6WqEwJq-gSiNMlpC1Jee1w2RtthqzvL4dVmvIU7UqHXJFJO_T/s400/SqlReplicationDistRole.png&quot; width=&quot;388&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Now that the publication has been setup on SQL Server A, lets setup the subscriber (SQL Server B) for this publication. On SQL Server A, creates new subscription&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV2ODsF59vOMBOe-INUQ_zp1VrIz-fCtSfzsqdbILk-ssNTT_Z26Uj7BiOoFXVoaXuNxqCBp3sFVqZntX6f63INhcevAEyMribyJcPK4BVsrFHjoYSCA0pACHGvFXCbO6As68DfSuisWf4/s1600/SqlSubscriptionA1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;307&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgV2ODsF59vOMBOe-INUQ_zp1VrIz-fCtSfzsqdbILk-ssNTT_Z26Uj7BiOoFXVoaXuNxqCBp3sFVqZntX6f63INhcevAEyMribyJcPK4BVsrFHjoYSCA0pACHGvFXCbO6As68DfSuisWf4/s320/SqlSubscriptionA1.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhStZx2wcqVtDOhwAsA3OQXw6VZazY2RO8I8-5oAOQd-Zy0AAT6u00GSc09f1uq43FxPU-9kSDhRGq4_eXNWzvoJimRGboeyodw9D43VrN390qoYJLck6wBrGmYGCHPUgEBkpsXqfpAzyzy/s1600/SqlSubscriptionA2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;196&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhStZx2wcqVtDOhwAsA3OQXw6VZazY2RO8I8-5oAOQd-Zy0AAT6u00GSc09f1uq43FxPU-9kSDhRGq4_eXNWzvoJimRGboeyodw9D43VrN390qoYJLck6wBrGmYGCHPUgEBkpsXqfpAzyzy/s320/SqlSubscriptionA2.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Run all agents at distributor for push subscription.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsg_8DNAwsVqNZdJytq0EwbXZrTNC28H3ri5OCfPf0hZRf-P1bUnoEpFCN0VxQbTTD0fsLKoWHr7_Id2FG11xsCS6gAMSvgmmwKA0oUXt-l1HMIJXDMGyCnEK_ICrotCjPj4Xf4PZctI8z/s1600/SqlSubscriptionA3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;255&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsg_8DNAwsVqNZdJytq0EwbXZrTNC28H3ri5OCfPf0hZRf-P1bUnoEpFCN0VxQbTTD0fsLKoWHr7_Id2FG11xsCS6gAMSvgmmwKA0oUXt-l1HMIJXDMGyCnEK_ICrotCjPj4Xf4PZctI8z/s400/SqlSubscriptionA3.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Add SQL Server B as subscriber to this publication (PUB_A)&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDUI6QGNP42koX3jxkIlQq4Dpl7_97IwNo3JHldtZvLFHB48DSUehdkrL5RXWD56NjMO4fzFiXyIlaSCxk3H8b-h13zE4Wr235oW3swh-0ExxNmixs7pwnXpyfKFVSyLD6aVwpWDnUratj/s1600/SqlSubscriptionA4.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;311&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDUI6QGNP42koX3jxkIlQq4Dpl7_97IwNo3JHldtZvLFHB48DSUehdkrL5RXWD56NjMO4fzFiXyIlaSCxk3H8b-h13zE4Wr235oW3swh-0ExxNmixs7pwnXpyfKFVSyLD6aVwpWDnUratj/s400/SqlSubscriptionA4.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxTwrQ0-2dMIcYe-asI5_nJHs8UuFUkhkAOf8rbSNovfht7v7lGL5QOED1VmrdhYRUNYMe2OfsB7ZZKlSU_smskzeqLnXvuBvJ3DrTbJ4HKzsnzDoJ3-bPNo78rFvtlDlXndhrKxOEFYdm/s1600/SqlSubscriptionA5.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;135&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxTwrQ0-2dMIcYe-asI5_nJHs8UuFUkhkAOf8rbSNovfht7v7lGL5QOED1VmrdhYRUNYMe2OfsB7ZZKlSU_smskzeqLnXvuBvJ3DrTbJ4HKzsnzDoJ3-bPNo78rFvtlDlXndhrKxOEFYdm/s320/SqlSubscriptionA5.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTJp9FOvdFY1vj9azaR-_4lq81tckS3jDhXujB-oag3Yo92FQ33k8NDyl_sZR4aOxo4s_UJ2YaZIoehDdk3c2EHWNYC8MIHGvCzMwZ1uSn1VHmDDkTBcM8Phro5Q3LdXjsOlGcXMoFvkRJ/s1600/SqlSubscriptionA6.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;165&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTJp9FOvdFY1vj9azaR-_4lq81tckS3jDhXujB-oag3Yo92FQ33k8NDyl_sZR4aOxo4s_UJ2YaZIoehDdk3c2EHWNYC8MIHGvCzMwZ1uSn1VHmDDkTBcM8Phro5Q3LdXjsOlGcXMoFvkRJ/s400/SqlSubscriptionA6.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Run distribution agent with the sqlreplication local Windows account. Connect to subscriber (SQL Server B) with the Server B sqlreplication local Windows account. This will only work when both local Windows account has the same name and password.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZOmyzqqhdJj_1BxZ9MpHMzhozZrC7gjF6fkeWNb0yPqB-nRdJFBH_4Zlh18mkIPPM-rIdtWE8Cm-IbRnXCsMApH4YzBhuOxV-d_tIUUNv2PXw2IeuS8Of7Zt9z5UBq3K5GaDmeSut9Pux/s1600/SqlSubscriptionA7.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZOmyzqqhdJj_1BxZ9MpHMzhozZrC7gjF6fkeWNb0yPqB-nRdJFBH_4Zlh18mkIPPM-rIdtWE8Cm-IbRnXCsMApH4YzBhuOxV-d_tIUUNv2PXw2IeuS8Of7Zt9z5UBq3K5GaDmeSut9Pux/s400/SqlSubscriptionA7.png&quot; width=&quot;355&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOFhd-viThpz5qKmS7H94s6MeFs4_Z8-AljwL5w1HwUpZsvFzy6UHM0rj4wJQJSM0p7hrzae6Wu7c8qvZe1sWOdkA6nbnmFZjwqaFom6ZBt8suFtknVOZR2JEDGsweE2zs-YMqmLT8Wqgy/s1600/SqlSubscriptionA8.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;155&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOFhd-viThpz5qKmS7H94s6MeFs4_Z8-AljwL5w1HwUpZsvFzy6UHM0rj4wJQJSM0p7hrzae6Wu7c8qvZe1sWOdkA6nbnmFZjwqaFom6ZBt8suFtknVOZR2JEDGsweE2zs-YMqmLT8Wqgy/s400/SqlSubscriptionA8.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Synchronization (or replication) run continuously&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHyWolKn6xngFisqADhdb1jMIX9jlUpO3CPZ9w0G1yRYPZNRqrldO9o0zfk6XEOW3pJwRW-jX4fUwlBmDZVm-n8syBV6zibBfGGB7Dpwxhc_oVF82hxhfdT5P6QXExIxXnaXbI5V4PtrDh/s1600/SqlSubscriptionA9.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;135&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHyWolKn6xngFisqADhdb1jMIX9jlUpO3CPZ9w0G1yRYPZNRqrldO9o0zfk6XEOW3pJwRW-jX4fUwlBmDZVm-n8syBV6zibBfGGB7Dpwxhc_oVF82hxhfdT5P6QXExIxXnaXbI5V4PtrDh/s400/SqlSubscriptionA9.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Select initialize when to immediately. This will start the Snapshot agent when the subscription is setup. The snapshot agent will generate the snapshot and synchronization will follow after.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPm8rC0G-SfwKfNLSVSFsfaLBZh-cyPgrVD-37huuwv-gnlreoMhrKpAPj7XnyDyO6NWl9mXZrLMmAnj5aT17nBS1L8q85q5KaIlHc7mCAQ2HGORhrEDyGXZvUB1CzS_ZtOnh-r1-OisF2/s1600/SqlSubscriptionA10-1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;358&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPm8rC0G-SfwKfNLSVSFsfaLBZh-cyPgrVD-37huuwv-gnlreoMhrKpAPj7XnyDyO6NWl9mXZrLMmAnj5aT17nBS1L8q85q5KaIlHc7mCAQ2HGORhrEDyGXZvUB1CzS_ZtOnh-r1-OisF2/s400/SqlSubscriptionA10-1.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj33u3Dr5d53qnoM6CZ8lKNN9W3ONEj8ZQ4NyZLTRZOSpqTt2FV9Lp-n5fFjo5OWTXXriOiMnh9npQNd0QYZfeRMd6EGqxVWDi3kOK9Uv2TNzUmrbLTefLv6z9OnR_k2d9D6Y4Bf9YgBwGe/s1600/SqlSubscriptionA11.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;148&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj33u3Dr5d53qnoM6CZ8lKNN9W3ONEj8ZQ4NyZLTRZOSpqTt2FV9Lp-n5fFjo5OWTXXriOiMnh9npQNd0QYZfeRMd6EGqxVWDi3kOK9Uv2TNzUmrbLTefLv6z9OnR_k2d9D6Y4Bf9YgBwGe/s320/SqlSubscriptionA11.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUUGIi_lkCPkTFDJXn1g2i-JYU5Uf49Db1HqotuwrlZB_L3Vzp6TB3sL-19hy-X8vfqhYMjSy59_24EcXhHlU4v9I33t7sZVArd4uAXCc_mB0jgFjbWYKooIyLVBT3nUh_3opFhXdeEDwY/s1600/SqlSubscriptionA12-1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;152&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUUGIi_lkCPkTFDJXn1g2i-JYU5Uf49Db1HqotuwrlZB_L3Vzp6TB3sL-19hy-X8vfqhYMjSy59_24EcXhHlU4v9I33t7sZVArd4uAXCc_mB0jgFjbWYKooIyLVBT3nUh_3opFhXdeEDwY/s320/SqlSubscriptionA12-1.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Now with both the publication and subscriber setup.&lt;br /&gt;
&lt;br /&gt;
Go to SQL Server B and verify the three A_tbl tables have updated with the data. Data show up. Great! Let&#39;s insert a record at SQL Server A.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE TEST;
INSERT INTO A_tbl2 VALUES (&#39;Noon&#39;);
&lt;/pre&gt;
&lt;br /&gt;
Check the synchronization status if the new record has sent to the subscriber.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVDaPJXdQY-3i8NQA4LEyWF2yH6olhh3h9PHL2CowIdDNvauT-Y0OBBYEGMSGhhnD8RFJb1UWj8XwWXtf45FQd6dp6My6h1txGPoPNZOeVLYyNfsDE2n6qZleM3QfKpf2nKu08veU6fILH/s1600/SqlSyncA1.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;248&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVDaPJXdQY-3i8NQA4LEyWF2yH6olhh3h9PHL2CowIdDNvauT-Y0OBBYEGMSGhhnD8RFJb1UWj8XwWXtf45FQd6dp6My6h1txGPoPNZOeVLYyNfsDE2n6qZleM3QfKpf2nKu08veU6fILH/s320/SqlSyncA1.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
Synchronization completed&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjx4iP3zV3Ib7knpvZsCgc37tQVwWKjee4uiqPaTMdHZHRM7NZFxSlwi1R738I7wM9RTgO5IH7Pyc_O-gHKBZSVgD_Il451aHt_Woy0mTdlQpOWBGfdWJYiRJR8Hdfm0NXqwNe5QFFkrBg5/s1600/SqlSyncA2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;182&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjx4iP3zV3Ib7knpvZsCgc37tQVwWKjee4uiqPaTMdHZHRM7NZFxSlwi1R738I7wM9RTgO5IH7Pyc_O-gHKBZSVgD_Il451aHt_Woy0mTdlQpOWBGfdWJYiRJR8Hdfm0NXqwNe5QFFkrBg5/s400/SqlSyncA2.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Go to SQL Server B. Check the table A_tbl2, new record &#39;noon&#39; show up.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDAdHePr3NuPcIerVG_8RgMBTIx8Z_0bdDWlK2M-qhjQnc1sjSijKe0SfcSQKRaV7SaPQlNTWXrcA6xlCUNgIJiIxQIrMPOnnnTUGPgD8CHEq7Akq15du3UO3RAMT39RIcIoymgcl2HgLJ/s1600/SqlSyncA3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDAdHePr3NuPcIerVG_8RgMBTIx8Z_0bdDWlK2M-qhjQnc1sjSijKe0SfcSQKRaV7SaPQlNTWXrcA6xlCUNgIJiIxQIrMPOnnnTUGPgD8CHEq7Akq15du3UO3RAMT39RIcIoymgcl2HgLJ/s1600/SqlSyncA3.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;SQL Replication Implementation - Publication B&lt;/b&gt;&lt;br /&gt;
The first publication (PUB_A) has been completed. Now we just need to setup the other publication (PUB_B) at the SQL Server B. The steps are similar like the one above.&lt;br /&gt;
&lt;br /&gt;
Here are some of the difference,&lt;br /&gt;
&lt;br /&gt;
Select the B_tbl tables only for this publication&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1laEtSPGvT-z_SSbZxPam-3Di6Bw8kA9bgyA1cfK4T58S8VKguyl0QlrYwF_D5vcpFf2oda6Cfh8hIaZ8z1IYCwzu2hq_W2LUIJeaQcp3P_qJFKpt32jCoZAMoxtSjcZ1f88osJwSVAXk/s1600/SqlReplicationB6.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;220&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1laEtSPGvT-z_SSbZxPam-3Di6Bw8kA9bgyA1cfK4T58S8VKguyl0QlrYwF_D5vcpFf2oda6Cfh8hIaZ8z1IYCwzu2hq_W2LUIJeaQcp3P_qJFKpt32jCoZAMoxtSjcZ1f88osJwSVAXk/s400/SqlReplicationB6.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The local Windows account used is the &lt;b&gt;sqlreplication Windows on Server B (NOT Server A),&lt;/b&gt; although the name is the same. See the sql2008r2&lt;b&gt;a&lt;/b&gt; (with the a)?&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7Bf_bNzid155UJgvoMw-YfdWCWXo4qzH6PCBygT7FFsLedyRGBYc3dow1j4zeDA7D-dGQT74LjPEp1gSlsWAGvnoqh6sBY-HuNKDmrJKAFIg0aOfg0GwhJNA7HAbInLtD1K-HzpXUpiQN/s1600/SqlReplicationB7.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;343&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7Bf_bNzid155UJgvoMw-YfdWCWXo4qzH6PCBygT7FFsLedyRGBYc3dow1j4zeDA7D-dGQT74LjPEp1gSlsWAGvnoqh6sBY-HuNKDmrJKAFIg0aOfg0GwhJNA7HAbInLtD1K-HzpXUpiQN/s400/SqlReplicationB7.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEif-nUpZDN_F9EXK8_CqI9oHSCr9Y0AgrBoMMb1X7TZK3720eXaSb9Y4KKS8GdqMsP-0FkY3PQn7WESWdWYAdS36yVgd6kkWHWZ7PccaogJ8D6eC7yEw_FnKevc8HOJpvoBcmAWxJl-pH3q/s1600/SqlReplicationB8.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;183&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEif-nUpZDN_F9EXK8_CqI9oHSCr9Y0AgrBoMMb1X7TZK3720eXaSb9Y4KKS8GdqMsP-0FkY3PQn7WESWdWYAdS36yVgd6kkWHWZ7PccaogJ8D6eC7yEw_FnKevc8HOJpvoBcmAWxJl-pH3q/s400/SqlReplicationB8.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The publication named as PUB_B&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEht5kHq7B-oZlINT9b9cz924L95_1E8VB_L21iXyYUYVDr9yJTwhhI3LOSPMSAOHNP4B1Qu_vx7QvU3uPkbRLWd4wNVWFzjNLRbo9rZKYeCuOjGLH14cekrBh3n-pHv49_UKMT00RWAlSfk/s1600/SqlReplicationB10.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;157&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEht5kHq7B-oZlINT9b9cz924L95_1E8VB_L21iXyYUYVDr9yJTwhhI3LOSPMSAOHNP4B1Qu_vx7QvU3uPkbRLWd4wNVWFzjNLRbo9rZKYeCuOjGLH14cekrBh3n-pHv49_UKMT00RWAlSfk/s320/SqlReplicationB10.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
On subscriber setup, here are some of the difference.&lt;br /&gt;
&lt;br /&gt;
Connecting to SQL Server A to add it as subscriber&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIw-sF32M4-z69P8cBISl_DgjyTY1sfDIxSjY3qGgnM07CYr6p6CT8f8xNoYNkH8E2K64OdTm7n5Q17T9xr4qWl1QVUpCcuGYL_GFz5BuFb3VShahgv5f_OHps47AGxxQ4HlsFhB5U2w-Q/s1600/SqlSubscriptionB5.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;137&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIw-sF32M4-z69P8cBISl_DgjyTY1sfDIxSjY3qGgnM07CYr6p6CT8f8xNoYNkH8E2K64OdTm7n5Q17T9xr4qWl1QVUpCcuGYL_GFz5BuFb3VShahgv5f_OHps47AGxxQ4HlsFhB5U2w-Q/s320/SqlSubscriptionB5.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFloHPA-G_3P_GZf_FSopoyeZceORQVvW24TuisS74CKTo9GybPK34Vc_6zivFFpu7-6ebl3JIrXmBP-kHcjvEAurqIE03_DXjQLr71EVeQNYpfNDdqgiwszW5oWpTERmpV6DADha1CmHx/s1600/SqlSubscriptionB6.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;178&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFloHPA-G_3P_GZf_FSopoyeZceORQVvW24TuisS74CKTo9GybPK34Vc_6zivFFpu7-6ebl3JIrXmBP-kHcjvEAurqIE03_DXjQLr71EVeQNYpfNDdqgiwszW5oWpTERmpV6DADha1CmHx/s400/SqlSubscriptionB6.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Similarly, setup the Server B sqlreplication local Windows account (NOT the server A) as the process account.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjaHdTD3T9N7YrYdBRNIMe_72cTBLUpLpn1XpM7OEFwyLhiC2XbaxfSyHlNH0xEX48bIzW0vtl5ntbOKHnOcOhnRC8Rx_ixIrgerUO5lqh3UlRO4-wNmDPAeAxJ0yrNSiy0ljJVgb0ohyphenhyphenZ/s1600/SqlSubscriptionB7.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjaHdTD3T9N7YrYdBRNIMe_72cTBLUpLpn1XpM7OEFwyLhiC2XbaxfSyHlNH0xEX48bIzW0vtl5ntbOKHnOcOhnRC8Rx_ixIrgerUO5lqh3UlRO4-wNmDPAeAxJ0yrNSiy0ljJVgb0ohyphenhyphenZ/s400/SqlSubscriptionB7.png&quot; width=&quot;325&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghyphenhyphenQde00mwzLthgP0JTqpIRvCvssGWw_rx1pXwEhJiMx0hsdJIH-TDOHnFdzO9BtuAM-TKEpCg-nhzxyU7Cpv9nUfs5ws_hVfSLg9_G6P24YK-RXBWGMqjUPEfZsSzUgvBnFTYBPDAvNpA/s1600/SqlSubscriptionB8.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;142&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghyphenhyphenQde00mwzLthgP0JTqpIRvCvssGWw_rx1pXwEhJiMx0hsdJIH-TDOHnFdzO9BtuAM-TKEpCg-nhzxyU7Cpv9nUfs5ws_hVfSLg9_G6P24YK-RXBWGMqjUPEfZsSzUgvBnFTYBPDAvNpA/s400/SqlSubscriptionB8.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Insert a record to B_tbl1 on SQL Server B,&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE TEST;
INSERT INTO B_tbl1 VALUES (&#39;MayBe&#39;);
&lt;/pre&gt;
&lt;br /&gt;
Verify the synchronization status,&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1vNkpcOEGkSbQm_pfjH8SsDI7MO3tivxDYKghcUoAi7ivIPtNN4B73i3H3etj0grd2Nmk2yKXgLOwsdmgHUSJERpDZctuFZOEQhG94kB5du4UWFFUMzL4mslPE78M-ElotVkw8AWF1A1o/s1600/SqlSyncB2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;181&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1vNkpcOEGkSbQm_pfjH8SsDI7MO3tivxDYKghcUoAi7ivIPtNN4B73i3H3etj0grd2Nmk2yKXgLOwsdmgHUSJERpDZctuFZOEQhG94kB5du4UWFFUMzL4mslPE78M-ElotVkw8AWF1A1o/s400/SqlSyncB2.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Go to SQL Server A, check the B_tbl1 tables.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgZ2sAAophs_P59AKquyBGtxdDHQJWOLrm2hDQBzv6kPEqKdKu8aEZ44XIsj72tlLDtkJSqO2Ish49rauBYXFhC8MPUx6Qn9T5tMukZAqV3w6kjxs9QAuJPOYs_-2WoxiPgnWlugF33sKQ/s1600/SqlSyncB3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgZ2sAAophs_P59AKquyBGtxdDHQJWOLrm2hDQBzv6kPEqKdKu8aEZ44XIsj72tlLDtkJSqO2Ish49rauBYXFhC8MPUx6Qn9T5tMukZAqV3w6kjxs9QAuJPOYs_-2WoxiPgnWlugF33sKQ/s1600/SqlSyncB3.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
It has been verified that two publications successfully replicated to each other. Mission accomplished.</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/2399833994167725913/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2015/06/sql-server-replication-case-study.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/2399833994167725913'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/2399833994167725913'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2015/06/sql-server-replication-case-study.html' title='SQL Server Replication - Case Study &amp; Implementation'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSmj2v-HKV26Sc-mpTLOrfOpxypuDj_Q0FS681rSTNKwNxM1T2apt2H45cNA3hEPOZkXGFSR2aRNCW0lNy1IMf3HnbMq6T-lYWN0JaScKWX1r9KgQwiRxTO6nT6SG9Ii6jBGSTiZpVvFcN/s72-c/UseCase.PNG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-849990751283414222</id><published>2015-06-19T07:00:00.003-05:00</published><updated>2022-08-15T23:45:18.679-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="PowerShell"/><category scheme="http://www.blogger.com/atom/ns#" term="Security"/><title type='text'>Powershell Password Encryption &amp; Decryption</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgP6Y1_BW23k7a7XeK5HgKvTvWmbv-Ucak74stnV8EA_lqGWqU2S0YME-nhP6YEuHoJkGoG5DjflmP-t1gHIUOGNL71YtmUFm67O3g6Znq2LEhQUy06taOHrh-_0fyuXCDK9rj6OxWZPhYZ/s1600/Powershell_Encryption.jpg&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;133&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgP6Y1_BW23k7a7XeK5HgKvTvWmbv-Ucak74stnV8EA_lqGWqU2S0YME-nhP6YEuHoJkGoG5DjflmP-t1gHIUOGNL71YtmUFm67O3g6Znq2LEhQUy06taOHrh-_0fyuXCDK9rj6OxWZPhYZ/s200/Powershell_Encryption.jpg&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
One of the common task in PowerShell script design and execution is credential encryption requirement. Some privileged account is used and its credential need to pass to the script in order to access resources. It becomes crucial especially when the execution tasks are being delegated to other users or being automated. As storing the password as clear text is huge security risk and the last thing desired, here in this blog post we discuss a few options on storing the credential securely.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;If account credential could be avoided being stored in the script, attempt that first. If the predefined / privileged account used is a Windows account (eg. local or domain account), and its process is executed through some task like Windows task scheduler (or PowerShell scheduled job), the privileged account could be setup as the running account of the task and let Windows handles the credential encryption securely during the setup. When the delegated user initiate the task on demand or the task is executed in a schedule automatically, the predefined account as the running account is used to access all the required resources with Windows integrated authentication.&lt;br /&gt;

&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEii1BMeoAi_RkDEg0nluC-eMSxdCCZZz065oOx406USA23mwt6_kDb3JUWC4oqGCNYJ7qKEi50HcsGon8nGklRwPE1f1au2O50qgfmopPVtKPMQPKnj7wFLkeu430tUcQbAIvJhWxZF_ezqFYm9K-tmwDblXfIwkuxC5c5HpMixpFjpqFaTIPjVleFjiA/s598/taskrunningaccount.png&quot; style=&quot;display: block; padding: 1em 0; &quot;&gt;&lt;img alt=&quot;&quot; border=&quot;0&quot; width=&quot;400&quot; data-original-height=&quot;174&quot; data-original-width=&quot;598&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEii1BMeoAi_RkDEg0nluC-eMSxdCCZZz065oOx406USA23mwt6_kDb3JUWC4oqGCNYJ7qKEi50HcsGon8nGklRwPE1f1au2O50qgfmopPVtKPMQPKnj7wFLkeu430tUcQbAIvJhWxZF_ezqFYm9K-tmwDblXfIwkuxC5c5HpMixpFjpqFaTIPjVleFjiA/s400/taskrunningaccount.png&quot;/&gt;&lt;/a&gt;

Or assigning with PowerShell

&lt;pre class=&quot;brush:ps&quot;&gt;
$principal = New-ScheduledTaskPrincipal -UserId DomainA\TestUser
Set-ScheduledTask -TaskName TestingTask -Principal $principal
&lt;/pre&gt;

However often cases different Windows account(s) or third party software account(s) are used to access different type of resource within the execution. In these cases, it is likely that there will be a need to manage the account credential encryption ourselves.&lt;br /&gt;
&lt;br /&gt;
PowerShell provides some native command for encryption. The commonly use command is the ConvertTo-SecureString and ConvertFrom-SecureString.&lt;br /&gt;
&lt;br /&gt;
Take an example, this script is to obtain some server information of a remote server. However, the user who run the PowerShell script does not have the access to a remote server.

&lt;pre class=&quot;brush:ps&quot;&gt;Get-WmiObject -Class win32_OperatingSystem -ComputerName RemoteServerA
&lt;/pre&gt;

As the executing account (user Windows account) doesn&#39;t have enough privilege, the access denied error is encountered&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;Get-WmiObject : Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED))&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;At line:1 char:2&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;+ &amp;nbsp;Get-WmiObject -Class win32_OperatingSystem -ComputerName RemoteServerA&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;+ &amp;nbsp;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;&amp;nbsp; &amp;nbsp; + CategoryInfo &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;: NotSpecified: (:) [Get-WmiObject], UnauthorizedAccessException&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;&amp;nbsp; &amp;nbsp; + FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShell.Commands.GetWmiObjectCommand&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
To incorporate a predefined Windows account which has been granted permission to the server to obtain this information,

&lt;pre class=&quot;brush:ps&quot;&gt;$User = &#39;TestUser&#39;
$Password = &#39;B@dPassw0rd!&#39;
$SecurePassword =&amp;nbsp;$Password | ConvertTo-SecureString -AsPlainText -Force
$UserCred = New-Object System.Management.Automation.PSCredential ($User, $SecurePassword)
Get-WmiObject -Class win32_OperatingSystem -ComputerName RemoteServerA -Credential $UserCred
&lt;/pre&gt;

With the password in plain text, we would need to encrypt the password here.&lt;br /&gt;
&lt;br /&gt;
The ConvertTo-SecureString cmdlet converts the password into a System.Security.SecureString object. This object represents text that should be kept confidential, such as removing it from the computer memory when no longer needed. 
&lt;br /&gt;&lt;br /&gt;
To pre-encrypt the password, we could use the same command and hard code the encrypted password in the script, save it in another file, in the registry or some other places.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Store encrypted password in a file (txt file)&lt;/b&gt;&lt;br /&gt;
To save the encrypted password text into a file,

&lt;pre class=&quot;brush:ps&quot;&gt;$User = &#39;TestUser&#39;
$Password = &#39;B@dPassw0rd!&#39;&lt;br /&gt;$Password | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString | Out-File C:\encrypted.txt
&lt;/pre&gt;

Note: The Force flag is optional starting in PowerShell 7.
&lt;br /&gt;&lt;br /&gt;
ConvertFrom-SecureString cmdlet convert a secure string (System.Security.SecureString) to an encrypted standard string using the Windows Data Protection API (DPAPI) using user account as default (we will discuss some issues with this default setting later in this post) OR with an encryption key if provided. The encrypted password text (eg.&amp;nbsp;&amp;nbsp;&#39;01000000d08c9ddf0115d1118c7a00c04fc29...&#39;) is then save into a file.&lt;br /&gt;
&lt;br /&gt;
A better approach is not displaying the password in clear text at all,

&lt;pre class=&quot;brush:ps&quot;&gt;$UserCred = Get-Credential
$UserCred.Password | ConvertFrom-SecureString | Out-File C:\encrypted.txt
&lt;/pre&gt;

The Get-Credential prompt for user name and password as directly save into a secure PSCredential object.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Decrypt encrypted password in a file (txt file)&lt;/b&gt;&lt;br /&gt;
With the password encrypted as stored in the file, now the script simply have to extract the encrypted password and pass it to the PSCredential object,

&lt;pre class=&quot;brush:ps&quot;&gt;
$User = &#39;TestUser&#39;
$SecurePassword = Get-Content C:\encrypted.txt | ConvertTo-SecureString
$UserCred = New-Object System.Management.Automation.PSCredential ($User, $SecurePassword)
Get-WmiObject -Class win32_OperatingSystem -ComputerName RemoteServerA -Credential $UserCred
&lt;/pre&gt;

&lt;b&gt;Store encrypted password in a file using Export-Clixml (xml file)&lt;/b&gt;&lt;br /&gt;
Another option is to save it as xml file. We could save the PSCredential object with both user and encrypted password to a xml file.

&lt;pre class=&quot;brush:ps&quot;&gt;Get-Credential | Export-Clixml -Path C:\encrypted.xml
&lt;/pre&gt;

&lt;b&gt;Decrypt encrypted password in a file using Import-Clixml (xml file)&lt;/b&gt;&lt;br /&gt;
To load the xml directly back into a PSCredential object&lt;br /&gt;

&lt;pre class=&quot;brush:ps&quot;&gt;
$UserCred = Import-Clixml -Path C:\encrypted.xml
Get-WmiObject -Class win32_OperatingSystem -ComputerName RemoteServerA -Credential $UserCred
&lt;/pre&gt;

Note that the ConvertTo-SecureString converts the encrypted password text into a SecureString object. A lot of (but not all) Windows and 3rd party PowerShell cmdlet utilize the SecureString object for authentication purpose like PSCredential object or Compellent Get-SCConnection. In general, the password does not need to be decrypted back to plain text as string object as it is exposed in the memory until it is removed by the garbage collector.&lt;br /&gt;
&lt;br /&gt;
However, some cmdlet does not utilize the SecureString and require password in plain text. The SecureString object in this case will need to be decrypted into to plain text. If the account information has been constructed into a PSCredential object, the password could be extracted in plain text,

&lt;pre class=&quot;brush:ps&quot;&gt;$Password = $UserCred.GetNetworkCredential().Password
&lt;/pre&gt;

OR starting in PowerShell 7

&lt;pre class=&quot;brush:ps&quot;&gt;
$Password = $UserCred.Password | ConvertFrom-SecureString -AsPlainText
&lt;/pre&gt;

&lt;b&gt;Store encrypted password in a file for 3rd party password (txt file)&lt;/b&gt;&lt;br /&gt;
In the case of encrypting a third party account password,&lt;br /&gt;

&lt;pre class=&quot;brush:ps&quot;&gt;
$SecurePassword = Read-Host -AsSecureString
$SecurePassword | ConvertFrom-SecureString | Out-File C:\encrypted.txt
&lt;/pre&gt;

&lt;b&gt;Decrypt encrypted password in a file for 3rd party password (txt file)&lt;/b&gt;&lt;br /&gt;
Decrypt 3rd party password into plain text by converting the SecureString into Binary String object,&lt;br /&gt;

&lt;pre class=&quot;brush:ps&quot;&gt;
$User = &#39;TestUser&#39;
$SecurePassword = Get-Content C:\encrypted.txt | ConvertTo-SecureString
$Marshal = [System.Runtime.InteropServices.Marshal]
$Bstr = $Marshal::SecureStringToBSTR($SecurePassword)
$Password = $Marshal::PtrToStringAuto($Bstr)
$Marshal::ZeroFreeBSTR($Bstr)
&lt;/pre&gt;

The last line ZeroFreeBSTR is to clear the unmanaged memory.

OR starting in PowerShell 7

&lt;pre class=&quot;brush:ps&quot;&gt;$User = &#39;TestUser&#39;
$SecurePassword = Get-Content C:\encrypted.txt | ConvertTo-SecureString
$Password = $SecurePassword | ConvertFrom-SecureString -AsPlainText
&lt;/pre&gt;

&lt;b&gt;Store encrypted password in registry&lt;/b&gt;&lt;br /&gt;
Another method of saving encrypted password is to save it in registry. The example below uses HKCU (HKEY_CURRENT_USER) hive which the registry only applicable to the current user. You may want to use other more appropriate hives (eg. HKLM) for all other users access.

&lt;pre class=&quot;brush:ps&quot;&gt;
$SecurePasswordText = &#39;B@dPassw0rd!&#39; | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString
New-Item -Path HKCU:\Software\Test -Value $SecurePasswordText
&lt;/pre&gt;

OR

&lt;pre class=&quot;brush:ps&quot;&gt;$UserCred = Get-Credential
New-Item -Path HKCU:\Software\Test -Value ($UserCred.Password | ConvertFrom-SecureString)
&lt;/pre&gt;

OR storing both user name and password in the registry,&lt;br /&gt;

&lt;pre class=&quot;brush:ps&quot;&gt;
New-Item -Path HKCU:\Software\Test
New-ItemProperty -Path HKCU:\Software\Test -Name User -Value ($UserCred.UserName)
New-ItemProperty -Path HKCU:\Software\Test -Name Password -Value ($UserCred.Password | ConvertFrom-SecureString)
&lt;/pre&gt;

&lt;b&gt;To decrypt encrypted password stored in registry&lt;/b&gt;&lt;br /&gt;
With the password encrypted as stored in the registry, here is how to extract it for PSCredential in the script,&lt;br /&gt;

&lt;pre class=&quot;brush:ps&quot;&gt;
$User = &#39;TestUser&#39;
$SecurePassword = (Get-ItemProperty -Path HKCU:\Software\Test).&#39;(Default)&#39; | ConvertTo-SecureString
$UserCred = New-Object System.Management.Automation.PSCredential ($User, $SecurePassword)
Get-WmiObject -Class win32_OperatingSystem -ComputerName RemoteServerA -Credential $UserCred
&lt;/pre&gt;

OR

&lt;pre class=&quot;brush:ps&quot;&gt;$User = (Get-ItemProperty -Path HKCU:\Software\Test).User
$SecurePassword = (Get-ItemProperty -Path HKCU:\Software\Test).Password | ConvertTo-SecureString
$UserCred = New-Object System.Management.Automation.PSCredential ($User, $SecurePassword)
Get-WmiObject -Class win32_OperatingSystem -ComputerName RemoteServerA -Credential $UserCred
&lt;/pre&gt;

&lt;b&gt;Issue with default encryption using user account&lt;/b&gt;&lt;br /&gt;
By default, ConvertTo-SecureString cmdlet uses current user&#39;s password to generate an encryption key, and it is stored within the user profile (eg. %Userprofile%\Application Data\Microsoft\Crypto\RSA\User SID for RSA key). The encryption key is then used to encrypt the intended string. The same user&#39;s user profile is created independently on different computer. Unless the particular person&#39;s user account has been set as roaming profile, the encryption key on his user profile on one computer does not synchronize with his user profile on another computer. This creates some issues. If a person pre-encrypt the password on his computer, then deploy that to another server. First, the encrypted password text can&#39;t be decrypted because the encryption key is not present on the person&#39; user profile of the server. Needless to say, other users won&#39;t be able to decrypt the password as well because they don&#39;t have the encryption key. This is the error received.&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;ConvertTo-SecureString : Key not valid for use in specified state.&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;At line:1 char:336&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;+ ... bf6ff4d7ae3&quot; | ConvertTo-SecureString&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;+ &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;~~~~~~~~~~~~~~~~~~~~~~&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;&amp;nbsp; &amp;nbsp; + CategoryInfo &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;: InvalidArgument: (:) [ConvertTo-SecureString], CryptographicException&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;&amp;nbsp; &amp;nbsp; + FullyQualifiedErrorId : ImportSecureString_InvalidArgument_CryptographicError,Microsoft.PowerShell.Commands.Conv&lt;/span&gt;&lt;br /&gt;
&lt;span style=&quot;color: red;&quot;&gt;&amp;nbsp; &amp;nbsp;ertToSecureStringCommand&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
If it is only to resolve the issue locally on one server, a task could be setup in a way that always use the same running account which perform execute the script. This way, we could encrypt the predefined account password using the running account. As the running account has the encryption key stored in its user profile, it is able to decrypt the password during task execution regardless who initiated the task. However, that also means a person will be the developer of the script, the admin of the server, and have access to the running account credential.&lt;br /&gt;
&lt;br /&gt;
To perform encryption with a running account, we could login to the server as the running account, or use the RunAs command in our own remote session to open a PowerShell session as the running account to perform the encryption,

&lt;pre class=&quot;brush:ps&quot;&gt;runas /profile /user:RunningAcctA powershell.exe
&lt;/pre&gt;

Note that running account profile needs to be loaded in order to store the encryption key as discussed earlier. /Profile is a default parameter. The /profile parameter is listed as an explicit example.&lt;br /&gt;
&lt;br /&gt;
In the PowerShell session. use whoami to identify the account of that session. Once verified, generate the encrypted password file from that session.&lt;br /&gt;
&lt;br /&gt;
This trick applies to using LocalSystem account as well. The easiest way to run a PowerShell session as LocalSystem is using&amp;nbsp;PsExec&amp;nbsp;by&amp;nbsp;Mark Russinovich from Microsoft. You can find this download from this Windows internal&amp;nbsp;&lt;a href=&quot;https://technet.microsoft.com/en-us/sysinternals/bb897553.aspx&quot; target=&quot;_blank&quot;&gt;link&lt;/a&gt;&amp;nbsp;or I have some PsExec example here in this&amp;nbsp;&lt;a href=&quot;http://www.travisgan.com/2013/06/sql-server-locked-out-sysadmin-access.html&quot; target=&quot;_blank&quot;&gt;post&lt;/a&gt;&amp;nbsp;as well toward the middle section.

&lt;pre class=&quot;brush:ps&quot;&gt;psexec -s -i powershell.exe
&lt;/pre&gt;

Keep in mind that if the script is migrated to other server, the encryption step need to be re-performed again as the previous encryption key generated only store in the running account profile of that particular server.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Custom Encryption Key&lt;/b&gt;&lt;br /&gt;
One other way to address the multiple server and different user issue is to use a specific encryption key.&amp;nbsp;ConvertTo-SecureString cmdlet allows a key to be provided for the encryption. The valid encryption key lengths are 16, 24, and 32 bytes. With the use of encryption key, it allows the encrypted password to be decrypted on different server with different account.&lt;br /&gt;
&lt;br /&gt;
To generate a valid key, we could use RNGCryptoServiceProvider class to generate random number for the key.&lt;br /&gt;

&lt;pre class=&quot;brush:ps&quot;&gt;
$EncryptKey = New-Object Byte[] 16 &amp;nbsp;#An example of 16 bytes key
[Security.Cryptography.RNGCryptoServiceProvider]::Create().GetBytes($EncryptKey)
&lt;/pre&gt;

In this example, a simple 1 to 16 array is used as the key.

&lt;pre class=&quot;brush:ps&quot;&gt;
[byte[]] $EncryptKey = (1..16) &amp;nbsp; #An example of a simple key (1. 2, 3,...,14, 15, 16)
$UserCred = Get-Credential
$UserCred.Password | ConvertFrom-SecureString -Key $EncryptedKey | Out-File C:\encrypted.txt
&lt;/pre&gt;

In the script,

&lt;pre class=&quot;brush:ps&quot;&gt;
[byte[]] $EncryptedKey = (1..16)
$User = &#39;TestUser&#39;
$SecurePassword = Get-Content C:\encrypted.txt | ConvertTo-SecureString -Key $EncryptedKey
$UserCred = New-Object System.Management.Automation.PSCredential ($User, $SecurePassword)
Get-WmiObject -Class win32_OperatingSystem -ComputerName RemoteServerA -Credential $UserCred
&lt;/pre&gt;

However, by hard cording the encryption key in the script, it exposes the key and allows unintended person to potentially decrypt the encrypted password. There are a few options to manage the key.&lt;br /&gt;
&lt;br /&gt;
Store the key in a file with access privilege granted only to intended user or executing/service account, this should be done with the encrypted password file as well. For example,&lt;br /&gt;

&lt;pre class=&quot;brush:ps&quot;&gt;
$User = &#39;TestUser&#39;
$SecureKey = Get-Content C:\Key.txt | ConvertTo-SecureString
$SecurePassword = Get-Content C:\encrypted.txt | ConvertTo-SecureString -SecureKey $SecureKey
$UserCred = New-Object System.Management.Automation.PSCredential ($User, $SecurePassword)
Get-WmiObject -Class win32_OperatingSystem -ComputerName RemoteServerA -Credential $UserCred
&lt;/pre&gt;

Just a reminder that if the process is going to be executed by a delegated user, the user account will need the read access to the encrypted password and key file. This method prevent unintended person from obtaining the key. However, if the intent is to prevent the delegated user to obtain the password, this method would not be sufficient. One way to address this is setup a task / service to execute the script with a service account (executing account) and only that account has the read access (through Access Control List, ACL) to the encrypted password and key file. When the delegated user initiates the task / service, the process utilizes the service (executing account) to access the files. This limits the exposure of the encrypted password and key file only to the service account.&lt;br /&gt;
&lt;br /&gt;
There are other option like using a certificate to encrypt the key file. Dave Wyatt has a good post of this &lt;a href=&quot;http://powershell.org/wp/2014/02/01/revisited-powershell-and-encryption/&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;. The way it works is the user needs to have a private key of the certificate in order to decrypt the encryption key. This option faces the similar concern like the key file.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Using DPAPI&amp;nbsp;&lt;/b&gt;&lt;b&gt;ProtectedData Class for encryption&lt;/b&gt;&lt;br /&gt;
DPAPI ProtectedData class provides another method to encrypt and decrypt data. As we discussed earlier that ConvertTo-SecureString uses user account by default, this Protected class provide encryption options as CurrentUser or LocalMachine (LocalSystem profile). For scenario like having delegated users to run the script on the server, the predefined account password could be encrypted with LocalMachine option (scope) and any user could decrypt the password on that machine.

&lt;pre class=&quot;brush:ps&quot;&gt;
$Password = &quot;B@dPassw0rd!&quot;&lt;br /&gt;$PasswordBytes = [System.Text.Encoding]::Unicode.GetBytes($Password)
$SecurePassword = [Security.Cryptography.ProtectedData]::Protect($PasswordBytes, $null, [Security.Cryptography.DataProtectionScope]::LocalMachine)
$SecurePasswordStr = [System.Convert]::ToBase64String($SecurePassword)
&lt;/pre&gt;
To decrypt the encrypted password,&lt;br /&gt;
&lt;pre class=&quot;brush:ps&quot;&gt;
#$SecurePasswordStr
$SecureStr = [System.Convert]::FromBase64String($SecurePasswordStr)
$StringBytes = [Security.Cryptography.ProtectedData]::Unprotect($SecureStr, $null, [Security.Cryptography.DataProtectionScope]::LocalMachine)
$PasswordStr = [System.Text.Encoding]::Unicode.GetString($StringBytes)
&lt;/pre&gt;

Note that ProtectedData class is returning byte array object (and later convert to string) as opposed to SecureString object. If other cmdlet need the SecureString as its parameter, the password will need to be converted to the SecureString object.
&lt;br /&gt;&lt;br /&gt;
&lt;b&gt;Use SecretManagement and SecretStore (or other 3rd party extension vault) PowerShell module&lt;/b&gt;&lt;br /&gt;
Microsoft released these two new modules in 2021. SecretManagement module help user to manage secrets that are stored across vaults (local or remote). SecretStore module is a cross-platform local extension vault supported in all environment as PowerShell 7. The SecretStore stores the secrets locally for the current user and uses .NET Core cryptographic APIs to encrypt file content. Even without vault password option, it still encrypt the secrets but the key is stored in current user location.
&lt;br/&gt;&lt;Br/&gt;
The modules required to be installed first and some configuration.

&lt;pre class=&quot;brush:ps&quot;&gt;
Install-Module Microsoft.PowerShell.SecretManagement, Microsoft.PowerShell.SecretStore -Scope CurrentUser
Register-SecretVault -Name SecretStore -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault

# Setting Secret Store Password. It prompts to enter the vault password since not provided
Set-SecretStorePassword
&lt;/pre&gt;

Setting up a secret

&lt;pre class=&quot;brush:ps&quot;&gt;
# Setting a secret. If not provided, it will promot to enter in secure format
Set-Secret -Name TestUserPassword -Secret &#39;B@dPassw0rd!&#39;
&lt;/pre&gt;

To retrieve the password, first to unlock the vault. The vault stay unlocked within the PasswordTimeOut setting (default 15 minutes).

&lt;pre class=&quot;brush:ps&quot;&gt;
# Unlock the vault with the vault password. Provide valut password as SecureString. 
# If not provided, it prompts to enter vault password
Unlock-SecretStore

Get-Secret -Name TestUserPassword -AsPlainText
&lt;/pre&gt;
There are so much more to these SecretManagement and SecretStore (and also 3rd party vault) modules. More details in another post.
</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/849990751283414222/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2015/06/powershell-password-encryption.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/849990751283414222'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/849990751283414222'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2015/06/powershell-password-encryption.html' title='Powershell Password Encryption &amp; Decryption'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgP6Y1_BW23k7a7XeK5HgKvTvWmbv-Ucak74stnV8EA_lqGWqU2S0YME-nhP6YEuHoJkGoG5DjflmP-t1gHIUOGNL71YtmUFm67O3g6Znq2LEhQUy06taOHrh-_0fyuXCDK9rj6OxWZPhYZ/s72-c/Powershell_Encryption.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-85337532743026677</id><published>2015-05-05T05:00:00.000-05:00</published><updated>2015-05-13T13:56:17.923-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Performance Tuning"/><title type='text'>Query Hint and Plan Guide</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbYTKVTpVt2fifcbKbJ10D2ohge5odjS2cvAdEZkmJAl8LWFId_i6pEQLGysUk2GzpU43tHVlw2FLnR_JG2iDFCr4UUgO37A9yKyqJdAYIDOsOxxptZ1tHeCp1V50R5vMiL1dXgU346E0j/s1600/guide.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;133&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbYTKVTpVt2fifcbKbJ10D2ohge5odjS2cvAdEZkmJAl8LWFId_i6pEQLGysUk2GzpU43tHVlw2FLnR_JG2iDFCr4UUgO37A9yKyqJdAYIDOsOxxptZ1tHeCp1V50R5vMiL1dXgU346E0j/s200/guide.jpg&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
Query execution plan generated for a SQL query significantly affect the query performance. Parameter sniffing, indexes, statistics and other factors are taken into consideration for SQL Server query optimizer to produce an optimal query plan for the execution wit&lt;span id=&quot;goog_1777043371&quot;&gt;&lt;/span&gt;&lt;span id=&quot;goog_1777043372&quot;&gt;&lt;/span&gt;hin a timely manner. Sometimes, due to certain scenarios and limitation, the query plan generated may be suboptimal. There are multiple ways to address this problem, like modifying the way the query is written, or using query hint or even plan guides under certain circumstances. This blog post discuss an example of using a query hint as well as plan guide.


&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;First, this is a contrived stored procedure to retrieve some sales and product information based on the user input of the product number. It has a LIKE operator in the where condition to allows a specific product number or wildcard value.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;CREATE PROCEDURE sp_ProductOrderDetail 
(
  @ProductNumber nvarchar(25),
  @RecodOffset int,
  @RecordNumber int
)
AS
BEGIN
SELECT s.SalesOrderNumber, p.Name, d.OrderQty, d.LineTotal
FROM sales.SalesOrderHeaderEnlarged s
JOIN sales.SalesOrderDetailEnlarged d
 ON d.SalesOrderID = s.SalesOrderID
JOIN Production.Product p
 ON p.ProductID = d.ProductID
WHERE p.ProductNumber LIKE @ProductNumber
ORDER BY s.SalesOrderNumber 
OFFSET @RecodOffset ROWS
FETCH NEXT @RecordNumber ROWS ONLY;
END
GO
&lt;/pre&gt;
&lt;br /&gt;
Turn on IO and Time statistics

&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;SET STATISTICS IO ON;
SET STATISTICS TIME ON;
GO
&lt;/pre&gt;
&lt;br /&gt;
The performance comparison below is tested against warm cache where the data already loaded in memory. 

Execute first query to return result from the first 1000 records of all the frame (FR% as ProductNumber). Before the execution, we clear the query plan cache (not the data in memory) for better comparison later on.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;DBCC FREEPROCCACHE; --Remove the query plan cache (not the data)
GO

EXEC sp_ProductOrderDetail @ProductNumber = &#39;FR%&#39;, @RecodOffset = 0, @RecordNumber = 1000;
GO
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyZJaY0e01lULux7ppol3gfmhTwrh1kOV_vvwIX6elOqoRPVgLGN62OesrX06Zn70VDZC7fzPS7r4IW0BGeID_4tsEYZztyvUieDyUbp0HKNcp9fL2iWeWobXe7ewvtEtxKz9jG59I72y1/s1600/query1-stat.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyZJaY0e01lULux7ppol3gfmhTwrh1kOV_vvwIX6elOqoRPVgLGN62OesrX06Zn70VDZC7fzPS7r4IW0BGeID_4tsEYZztyvUieDyUbp0HKNcp9fL2iWeWobXe7ewvtEtxKz9jG59I72y1/s640/query1-stat.png&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4jrRkbcU1IoZ1cXCIXL2EPqpZuZJYDAIog5gnhYE-R4AkGXPQvKjvTcmlA2xhcNm2ldKT2oEWx7AXHI7BfWwjbxg4ubd41OHVYHCHZkaQSVYMGt_wOkTFcajfKR-HFH0TIVZ7h0rQeX3z/s1600/query1-plan.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;112&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4jrRkbcU1IoZ1cXCIXL2EPqpZuZJYDAIog5gnhYE-R4AkGXPQvKjvTcmlA2xhcNm2ldKT2oEWx7AXHI7BfWwjbxg4ubd41OHVYHCHZkaQSVYMGt_wOkTFcajfKR-HFH0TIVZ7h0rQeX3z/s400/query1-plan.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The query executed less than 1 second.

The query is written to also allow specific product number (SO-B909-L). Let&#39;s remove the previous query plan cache (not the data in memory).&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;DBCC FREEPROCCACHE; --Remove the query plan cache (not the data)
GO

EXEC sp_ProductOrderDetail @ProductNumber = &#39;SO-B909-L&#39;, @RecodOffset = 0, @RecordNumber = 1000;
GO
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEituhh3lCQ0IVkTniWWP2htwhdfXBMsAAAce2xWqHqeHZNbVWPzDJv39GInwtkvy_2ITxTUtyxJQWTo1VOdm4LTqlf4mhWl5TIQI2HK5M4o5ZqoXSViBYrA0ge0V-zeNX7SIErcIOjJmTYj/s1600/query2-stat.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEituhh3lCQ0IVkTniWWP2htwhdfXBMsAAAce2xWqHqeHZNbVWPzDJv39GInwtkvy_2ITxTUtyxJQWTo1VOdm4LTqlf4mhWl5TIQI2HK5M4o5ZqoXSViBYrA0ge0V-zeNX7SIErcIOjJmTYj/s640/query2-stat.png&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfCYPeyTDypz764kTTn8txbXpdpZ2Ww-y2jv_1hXTE0_26-uw8btN8I0Vj6FU0LUeT-XVPgoRrEa4tD44GVOcVivBRDJ8DbHhkRMYEtQ_7r5pKxNl5SO7_kURJ-JkLK5cSylOHA7BoZfBb/s1600/query2-plan.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfCYPeyTDypz764kTTn8txbXpdpZ2Ww-y2jv_1hXTE0_26-uw8btN8I0Vj6FU0LUeT-XVPgoRrEa4tD44GVOcVivBRDJ8DbHhkRMYEtQ_7r5pKxNl5SO7_kURJ-JkLK5cSylOHA7BoZfBb/s640/query2-plan.png&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
Notice that the execution plan is different from the previous query plan? SQL Server optimizer comes up with this query plan with the parameter value sniffed (SO-B909-L). Instead of scanning the respective sales header and sales details records and join with respective product, it identifies the product record first and join it with respective sales details and sales header. This may due to the records in scope is much smaller since the exact product number is provided.
&lt;br /&gt;
&lt;br /&gt;
As SQL Server reuse previously cached query plan, executing the first parameter with FR% yield this performance,

&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;EXEC sp_ProductOrderDetail @ProductNumber = &#39;SO-B909-L&#39;, @RecodOffset = 0, @RecordNumber = 1000;
GO
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4rJJrnWM9WQwy91lcRkcSQV-wQhpVZwAwOK43LyMbRN_-FyicPVtF4aPQ_2ENlHXcddQUn97-1uB8rAPEVNbcJCTQFLqUQFFgb4U0TDyC806tXwQaDsQFsjfQVxaAObPZQ1hMNLFy4q6E/s1600/query1-stat2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4rJJrnWM9WQwy91lcRkcSQV-wQhpVZwAwOK43LyMbRN_-FyicPVtF4aPQ_2ENlHXcddQUn97-1uB8rAPEVNbcJCTQFLqUQFFgb4U0TDyC806tXwQaDsQFsjfQVxaAObPZQ1hMNLFy4q6E/s640/query1-stat2.png&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIoAsRwQQEYeyUSb55pJceggnUI-oGvRYoXw70GhITBaK4UVAVWO3dlWQAD_bEJ4_3boRK7_Eu3KDVukayLrzl7cgR6kQpZ7FNXELtX7ilBq8HPQpbCa6qyHh4CFM1RwOdeBqkElH0lKsJ/s1600/query1-plan2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIoAsRwQQEYeyUSb55pJceggnUI-oGvRYoXw70GhITBaK4UVAVWO3dlWQAD_bEJ4_3boRK7_Eu3KDVukayLrzl7cgR6kQpZ7FNXELtX7ilBq8HPQpbCa6qyHh4CFM1RwOdeBqkElH0lKsJ/s640/query1-plan2.png&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The performance of the query went from less than 1 second to about 25 seconds! First, there is significant IO involved with this query plan for this parameter value, and also multiple of operation spilled into tempdb which makes the query run much slower.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-5KzLIljZXcUZgrepMgWhnwfXgQLlfkLMQ3eW-oOg4XbwSN1aktxhaxpFEvDJ0imhG1kD9AgYORpqUk4R5srkzwc1UjcVbMRCskzvtDtg9LPQUbJ-KYuZMpcGC1_NJq3DwDFqfvJzB51i/s1600/query1-plan2-spill-operator2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;492&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-5KzLIljZXcUZgrepMgWhnwfXgQLlfkLMQ3eW-oOg4XbwSN1aktxhaxpFEvDJ0imhG1kD9AgYORpqUk4R5srkzwc1UjcVbMRCskzvtDtg9LPQUbJ-KYuZMpcGC1_NJq3DwDFqfvJzB51i/s640/query1-plan2-spill-operator2.png&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
The query was designed to allow parameter value that have very different scope. One for specific value that usually yield small result set, but another for pattern with a wildcard that usually return much larger result set.&lt;br /&gt;
&lt;br /&gt;
There could be multiple options to address this. Often, the stored procedure may be rewritten in a way to allow different query plan to be used according to parameter value. Since this post is about query hint and plan guide, let&#39;s explore those method. 

One of the query hint is OPTIMIZE FOR UNKNOWN. OPTIMIZE FOR UNKNOWN hint asks SQL Server optimizer to use statistical data instead of the initial parameter values (sniffed value) when the query is compiled and optimized. It produces a consistent query plan regardless of what the initial value was used (sniffed).&lt;br /&gt;
&lt;br /&gt;
Since the parameter value provided yield a different query plan as noticed previously, could the OPTIMIZE FOR UNKNOWN query hint help here?

&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;ALTER PROCEDURE sp_ProductOrderDetail 
(
  @ProductNumber nvarchar(25),
  @RecodOffset int,
  @RecordNumber int
)
AS
BEGIN
SELECT s.SalesOrderNumber, p.Name, d.OrderQty, d.LineTotal
FROM sales.SalesOrderHeaderEnlarged s
JOIN sales.SalesOrderDetailEnlarged d
 ON d.SalesOrderID = s.SalesOrderID
JOIN Production.Product p
 ON p.ProductID = d.ProductID
WHERE p.ProductNumber LIKE @ProductNumber
ORDER BY s.SalesOrderNumber 
OFFSET @RecodOffset ROWS
FETCH NEXT @RecordNumber ROWS ONLY
OPTION (OPTIMIZE FOR UNKNOWN);
END
&lt;/pre&gt;
&lt;br /&gt;
Now, execute the stored procedure with either @ProductNumber = &#39;SO-B909-L&#39; or @ProductNumber = &#39;FR%&#39; produces the same query plan.

&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;DBCC FREEPROCCACHE; 
GO
EXEC sp_ProductOrderDetail @ProductNumber = &#39;SO-B909-L&#39;, @RecodOffset = 0, @RecordNumber = 1000;
GO
&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlYEds8MBZ5Q1DfBNTkXBxKQHTdxBYrMsecOKdUx0MvJQoOLkhkqfjK1uEubMzrsO-as1sLZiw7Z63KXiB_B5sK0CnswuvMSRGlGb89ND-U0lY5Jiz9ET9NlQojD2hdY2tHDgGBt_iyVKZ/s1600/query1-plan3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;112&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlYEds8MBZ5Q1DfBNTkXBxKQHTdxBYrMsecOKdUx0MvJQoOLkhkqfjK1uEubMzrsO-as1sLZiw7Z63KXiB_B5sK0CnswuvMSRGlGb89ND-U0lY5Jiz9ET9NlQojD2hdY2tHDgGBt_iyVKZ/s400/query1-plan3.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;DBCC FREEPROCCACHE; 
GO
EXEC sp_ProductOrderDetail @ProductNumber = &#39;FR%&#39;, @RecodOffset = 0, @RecordNumber = 1000;
GO
&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT-pVAXSJWv55_AWyhWSKza5nMmcEHzqrkvIRPe2cvOiL-TpqLbf41ToLD6KMCcqm6Oeqka_zllgU9D627jaK2wMzMW6NLFuEv1zg76nc1bo-AJuH9WYaxYuR5cloaMEktqNDNLkv0OqS0/s1600/query2-plan3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;109&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT-pVAXSJWv55_AWyhWSKza5nMmcEHzqrkvIRPe2cvOiL-TpqLbf41ToLD6KMCcqm6Oeqka_zllgU9D627jaK2wMzMW6NLFuEv1zg76nc1bo-AJuH9WYaxYuR5cloaMEktqNDNLkv0OqS0/s400/query2-plan3.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIb0LVEp11u6E670l6pv-Vv-0BiMKaDxNizjWbD5I5hKKewqp7RlGAxjW30HbyyHh78hBZGl95-sZotacUJ0GVB-BXQqX3PGVATWjYfEIgnagx2em28ckU6DQvXqsxitjSAYW-UXMl26BE/s1600/query2-stat3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIb0LVEp11u6E670l6pv-Vv-0BiMKaDxNizjWbD5I5hKKewqp7RlGAxjW30HbyyHh78hBZGl95-sZotacUJ0GVB-BXQqX3PGVATWjYfEIgnagx2em28ckU6DQvXqsxitjSAYW-UXMl26BE/s640/query2-stat3.png&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
It looks good on @ProductNumer = &#39;FR%&#39; performance, but the performance on @ProductNumber = &#39;SO-B909-L&#39; suffers with this plan. It went from about 1 second to around 20 seconds! One scenario becomes better in the expense of the other one. Not good.&lt;br /&gt;
&lt;br /&gt;
As we have noticed previously that these two types of parameter value passed yield very different query plans to obtain the most optimal performance, it seems like it is best not to use the query plan cached from the initial parameter value, but rather utilize an optimal plan depending on the provided parameter values.&lt;br /&gt;
&lt;br /&gt;
OPTION (RECOMPILE) is a query hint to discard query plan previously generated and force the query optimizer to recompile a new query plan the next time the same query is executed.

Let&#39;s see how it works here.

&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;ALTER PROCEDURE sp_ProductOrderDetail 
(
  @ProductNumber nvarchar(25),
  @RecodOffset int,
  @RecordNumber int
)
AS
BEGIN
SELECT s.SalesOrderNumber, p.Name, d.OrderQty, d.LineTotal
FROM sales.SalesOrderHeaderEnlarged s
JOIN sales.SalesOrderDetailEnlarged d
 ON d.SalesOrderID = s.SalesOrderID
JOIN Production.Product p
 ON p.ProductID = d.ProductID
WHERE p.ProductNumber LIKE @ProductNumber
ORDER BY s.SalesOrderNumber 
OFFSET @RecodOffset ROWS
FETCH NEXT @RecordNumber ROWS ONLY
OPTION (RECOMPILE);
END
&lt;/pre&gt;
&lt;br /&gt;
Now SQL Server optimizer generates an optimal query plan for parameter value ProductNumer = &#39;FR%&#39; or @ProductNumber = &#39;SO-B909-L&#39; respectively regardless the initial value used. Both different parameter value provided perform as expected with the optimal duration seen previously.&amp;nbsp;
&lt;br /&gt;
&lt;br /&gt;
One important note. As OPTION (RECOMPILE) recompile and regenerate new query plan on each execution, there is additional CPU overhead involved. Depending on how expensive is the query plan and the frequency the stored procedure is executed, RECOMPILE may or may not be a suitable solution Also, since the cache plan is discarded after execution, it may not show up on SQL Server DMV for monitoring or troubleshooting purpose.&lt;br /&gt;
&lt;br /&gt;
Now we learn that OPTION (RECOMPILE) is useful for the query to perform optimally for different parameter value, what happen if the query could not be modified due to multiple reasons, like the stored procedure is provided by vendor or ORM generated query?&lt;br /&gt;
&lt;br /&gt;
SQL Server plan guides could be useful in these type of scenario when we have our hand tied trying to address the query performance. Let&#39;s see how it works.&lt;br /&gt;
&lt;br /&gt;
First remove the query hint on the stored procedure.

&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;ALTER PROCEDURE sp_ProductOrderDetail 
(
  @ProductNumber nvarchar(25),
  @RecodOffset int,
  @RecordNumber int
)
AS
BEGIN
SELECT s.SalesOrderNumber, p.Name, d.OrderQty, d.LineTotal
FROM sales.SalesOrderHeaderEnlarged s
JOIN sales.SalesOrderDetailEnlarged d
 ON d.SalesOrderID = s.SalesOrderID
JOIN Production.Product p
 ON p.ProductID = d.ProductID
WHERE p.ProductNumber LIKE @ProductNumber
ORDER BY s.SalesOrderNumber 
OFFSET @RecodOffset ROWS
FETCH NEXT @RecordNumber ROWS ONLY;
END
&lt;/pre&gt;
&lt;br /&gt;
Using UI to create a plan guide for this query in sp_ProductOrderDetail stored procedure.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvxNZkSD7iQbfbek21KRnbZoSHjeVPGNG_RFXGa0D_ms0SaoW8UaRLlzmqgxnwtNRZJAaNsprp2SZZQ3Ue9_Duibhhn6G-gBT8sVi_ApEjwYemnBpWfQbh4VzDnPIX3pnJOXNV32sCg3ER/s1600/planguide-general.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvxNZkSD7iQbfbek21KRnbZoSHjeVPGNG_RFXGa0D_ms0SaoW8UaRLlzmqgxnwtNRZJAaNsprp2SZZQ3Ue9_Duibhhn6G-gBT8sVi_ApEjwYemnBpWfQbh4VzDnPIX3pnJOXNV32sCg3ER/s320/planguide-general.png&quot; width=&quot;260&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHwGMGyiU5aFNCq48qdVsQmlUMj-RFiNuTGjbOJCLrsbYw_cSKagazL3ALPPNeXQToRLNYHWh5sfMLgqhtigy9v4Hl5OKf9SzSAg1t-Mdpp7uP_t0nxV3yPizBBIVvEDpSJ4MpI1gxWRUz/s1600/planguide-sp.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHwGMGyiU5aFNCq48qdVsQmlUMj-RFiNuTGjbOJCLrsbYw_cSKagazL3ALPPNeXQToRLNYHWh5sfMLgqhtigy9v4Hl5OKf9SzSAg1t-Mdpp7uP_t0nxV3yPizBBIVvEDpSJ4MpI1gxWRUz/s400/planguide-sp.png&quot; width=&quot;374&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
Or use SQL Statement to create the plan guide.

&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE [AdventureWorks2014]
GO

EXEC sp_create_plan_guide @name = N&#39;[PlanGuide_sp_ProductOrderDetailPlanGuide]&#39;, 
@stmt = N&#39;SELECT s.SalesOrderNumber, p.Name, d.OrderQty, d.LineTotal
FROM sales.SalesOrderHeaderEnlarged s
JOIN sales.SalesOrderDetailEnlarged d
 ON d.SalesOrderID = s.SalesOrderID
JOIN Production.Product p
 ON p.ProductID = d.ProductID
WHERE p.ProductNumber LIKE @ProductNumber
ORDER BY s.SalesOrderNumber 
OFFSET @RecodOffset ROWS
FETCH NEXT @RecordNumber ROWS ONLY;&#39;, 
@type = N&#39;OBJECT&#39;, 
@module_or_batch = N&#39;[dbo].[sp_ProductOrderDetail]&#39;, 
@hints = N&#39;OPTION (RECOMPILE)&#39;
GO
&lt;/pre&gt;
&lt;br /&gt;
Execute the stored procedure with different parameter and examine the query plan. One way to verify if the plan guide is being used on the execution by right clicking on the SELECT node, click properties and examine the plan guide name value.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfo8adVjPl1AyEgb8FYloDQVmo6K5WnANofstswPBrV2KreLCv5vDyRdtjEP-DMGFIZlth9wfZ-0Dn5Y03-Q0eoSNwXutOLwfe2Y1-9mgfkZxRFb2F2nFU788lR8V9eyn8AQBo7NoNkDB1/s1600/sp-planguide-select.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;71&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfo8adVjPl1AyEgb8FYloDQVmo6K5WnANofstswPBrV2KreLCv5vDyRdtjEP-DMGFIZlth9wfZ-0Dn5Y03-Q0eoSNwXutOLwfe2Y1-9mgfkZxRFb2F2nFU788lR8V9eyn8AQBo7NoNkDB1/s320/sp-planguide-select.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgr6yckBz6SSZ8dS3KCnufE9Vcmw8-uXjBp5Oas1xd6G2Dvj3kB8flfhyphenhyphenOk-QDifZN4ZVu2EDSC2umzPPRk6g6NyydxxoRXN2L29TvmjTMZQ6NPCoejcEkuLr-ynXCYDrgYniKYFODcq9-i/s1600/sp-planguide-setting.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgr6yckBz6SSZ8dS3KCnufE9Vcmw8-uXjBp5Oas1xd6G2Dvj3kB8flfhyphenhyphenOk-QDifZN4ZVu2EDSC2umzPPRk6g6NyydxxoRXN2L29TvmjTMZQ6NPCoejcEkuLr-ynXCYDrgYniKYFODcq9-i/s400/sp-planguide-setting.png&quot; width=&quot;390&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
What about if the SQL query is not a stored procedure but rather a query from ORM application. Something like this,

&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;sp_executesql 
N&#39;SELECT s.SalesOrderNumber, p.Name, d.OrderQty, d.LineTotal
FROM sales.SalesOrderHeaderEnlarged s
JOIN sales.SalesOrderDetailEnlarged d
 ON d.SalesOrderID = s.SalesOrderID
JOIN Production.Product p
 ON p.ProductID = d.ProductID
WHERE p.ProductNumber LIKE @ProductNumber
ORDER BY s.SalesOrderNumber 
OFFSET @RecodOffset ROWS
FETCH NEXT @RecordNumber ROWS ONLY;&#39;, 
N&#39;@ProductNumber nvarchar(25), @RecodOffset int, @RecordNumber int&#39;, 
@ProductNumber = &#39;SO-B909-L&#39;, @RecodOffset = 0, @RecordNumber = 1000;
&lt;/pre&gt;
&lt;br /&gt;
OR with sql_prepare 

&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;DECLARE @p1 int
EXEC sp_prepare @p1 output, N&#39;@ProductNumber nvarchar(25), @RecodOffset int, @RecordNumber int&#39;, 
N&#39;SELECT s.SalesOrderNumber, p.Name, d.OrderQty, d.LineTotal
FROM sales.SalesOrderHeaderEnlarged s
JOIN sales.SalesOrderDetailEnlarged d
 ON d.SalesOrderID = s.SalesOrderID
JOIN Production.Product p
 ON p.ProductID = d.ProductID
WHERE p.ProductNumber LIKE @ProductNumber
ORDER BY s.SalesOrderNumber 
OFFSET @RecodOffset ROWS
FETCH NEXT @RecordNumber ROWS ONLY;&#39;;

EXEC sp_execute @p1, &#39;SO-B909-L&#39;, @RecodOffset = 0, @RecordNumber = 1000;
--EXEC sp_unbindefault @p1;
&lt;/pre&gt;
&lt;br /&gt;
From the verification step earlier, the query plan generated is not using the query hint specified in the plan guide. Plan guide only applied when very specific conditions are met like in this case both the scope as well as the SQL statement. The plan guide created earlier is only scoped for the sp_ProductOrderDetail stored procedure. A proper plan guide needs to be created for this SQL query.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCEZm6tthLzxyk3ldjKP9N1WU4HYpv-1VdkDfyEbXNoET1nIar41e4JgIXovJtDd6PJY6icUosvvWdfm2hRNvItu0mzzN1wbdwmp6fX05LsdLUZhSoAqML7JX_SvackXEYBqsVoPY48T3j/s1600/planguide-query.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCEZm6tthLzxyk3ldjKP9N1WU4HYpv-1VdkDfyEbXNoET1nIar41e4JgIXovJtDd6PJY6icUosvvWdfm2hRNvItu0mzzN1wbdwmp6fX05LsdLUZhSoAqML7JX_SvackXEYBqsVoPY48T3j/s400/planguide-query.png&quot; width=&quot;374&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE [AdventureWorks2014]
GO

EXEC sp_create_plan_guide @name = N&#39;[PlanGuide_Query1]&#39;, 
@stmt = N&#39;SELECT s.SalesOrderNumber, p.Name, d.OrderQty, d.LineTotal
FROM sales.SalesOrderHeaderEnlarged s
JOIN sales.SalesOrderDetailEnlarged d
 ON d.SalesOrderID = s.SalesOrderID
JOIN Production.Product p
 ON p.ProductID = d.ProductID
WHERE p.ProductNumber LIKE @ProductNumber
ORDER BY s.SalesOrderNumber 
OFFSET @RecodOffset ROWS
FETCH NEXT @RecordNumber ROWS ONLY;&#39;, 
@type = N&#39;SQL&#39;, 
@params = N&#39;@ProductNumber nvarchar(25), @RecodOffset int, @RecordNumber int&#39;, 
@hints = N&#39;OPTION (RECOMPILE)&#39;
GO
&lt;/pre&gt;
&lt;br /&gt;
Now, the SQL query execute with the OPTION (RECOMPILE) query as specified in the plan guide. The setting also shows that plan guide is being used.
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFUedF07eH9X9ROTTisyVJwfJALHXidxzuxSyKiRQudWsAlciH-nyRwbDBt0n01KOB-HLDMARak-7cWWAkNOYBqv_cWVnysy1CR5k1GL4n11vwGxOmcs-eQIjpN3U8CVl7TktfSjrZwOav/s1600/query-planguide-setting.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFUedF07eH9X9ROTTisyVJwfJALHXidxzuxSyKiRQudWsAlciH-nyRwbDBt0n01KOB-HLDMARak-7cWWAkNOYBqv_cWVnysy1CR5k1GL4n11vwGxOmcs-eQIjpN3U8CVl7TktfSjrZwOav/s400/query-planguide-setting.png&quot; width=&quot;371&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
To view the existing plan guide from UI or using&amp;nbsp;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/ms178010.aspx&quot; target=&quot;_blank&quot;&gt;sys.plan_guides&lt;/a&gt; dynamic management view&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-lxCoyTNpqwTN1RBdZypjO0aXBqo8MRAGDlg7fgMR0v-0v25YT7SjJbuUUM8YKL3JHxPUSIMhT_MdhPYXwlocnqL0wU-HlKR4L8O3vLSA4wMKlIFhnk4ZOx1FM-jrNYPmglQfNuqcqRYo/s1600/planguide.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-lxCoyTNpqwTN1RBdZypjO0aXBqo8MRAGDlg7fgMR0v-0v25YT7SjJbuUUM8YKL3JHxPUSIMhT_MdhPYXwlocnqL0wU-HlKR4L8O3vLSA4wMKlIFhnk4ZOx1FM-jrNYPmglQfNuqcqRYo/s1600/planguide.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Plan guide is only available on enterprise edition. Plan guide should be always used sparingly and only under very special circumstances.&lt;br /&gt;
&lt;br /&gt;
Hopefully you find this post on query hint and the plan guide helpful.</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/85337532743026677/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2015/05/query-hint-and-plan-guide.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/85337532743026677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/85337532743026677'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2015/05/query-hint-and-plan-guide.html' title='Query Hint and Plan Guide'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbYTKVTpVt2fifcbKbJ10D2ohge5odjS2cvAdEZkmJAl8LWFId_i6pEQLGysUk2GzpU43tHVlw2FLnR_JG2iDFCr4UUgO37A9yKyqJdAYIDOsOxxptZ1tHeCp1V50R5vMiL1dXgU346E0j/s72-c/guide.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-5048842932502111613</id><published>2015-03-30T05:00:00.000-05:00</published><updated>2015-03-30T11:33:32.060-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Performance Tuning"/><title type='text'>Query Performance Tuning Example</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl3HkBJzO7nBJSSx5Nx0JbseFq6zunD8eSymka7PcgO6nAJIOneXuKEXqb39VZvGMiPY379DfoVCyPEQWDw-5q1Akk4KbDHg_-HaWzntwSd19zJishjIO2cBimDK84ij1GBkSIghBJTrwg/s1600/tuning.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl3HkBJzO7nBJSSx5Nx0JbseFq6zunD8eSymka7PcgO6nAJIOneXuKEXqb39VZvGMiPY379DfoVCyPEQWDw-5q1Akk4KbDHg_-HaWzntwSd19zJishjIO2cBimDK84ij1GBkSIghBJTrwg/s1600/tuning.jpg&quot; height=&quot;80&quot; width=&quot;120&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
As DBA, application performance issue and complaints often thrown our way. It is often fun to troubleshoot those performance issue. When it has been identified that the problem lies on SQL Server, it get even more exciting. Here is one of the example of how I tuned a query a while ago and also some lessons learned.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;I received a request to identify bottlenecks of an application where its users has reported in a survey that they have been experienced slow application response in their daily operations. Without any information of which specific module or functionality that experience the &#39;problem&#39;, I did some digging and notice there were different types of queries and stored procedures developed in this application to serve both operational as well as a reporting purpose.&lt;br /&gt;
&lt;br /&gt;
Using one of my own query below, I look up the most run (Order by COUNT) query as well as the one that take the longest times (Order by TIME).&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;DECLARE @TopNumber smallint = 20;
DECLARE @OrderByField varchar(20) = &#39;COUNT&#39;;

--Top 10 CPU Usage Query
--Declare @TopNumber smallint = 10;
--DECLARE @OrderByField varchar(20) = &#39;CPU&#39;;

-- CPU   - avg_cpu_time
-- TIME   - avg_elapsed_time
-- READ   - avg_physical_read + avg_logical_read
-- WRITE  - avg_logical_write
-- COUNT  - cache_ob_lookup
-- RECOMPILE  - avg_ob_recompile
-- PARALLEL  - avg_over_parallel

WITH query_stat AS 
(SELECT TOP(@TopNumber)
  SUM(qt.plan_generation_num) / SUM(cp.usecounts) AS avg_ob_recompile,
  SUM(cp.usecounts) AS total_exec_count,
  SUM(qt.total_worker_time) / SUM(qt.execution_count) / 1000 AS avg_cpu_time,
  SUM(qt.total_elapsed_time) / SUM(qt.execution_count) / 1000 AS avg_elapsed_time,
  SUM(qt.total_logical_writes) / SUM(qt.execution_count) AS avg_logical_write,
  SUM(qt.total_logical_reads) / SUM(qt.execution_count) AS avg_logical_read,
  SUM(qt.total_physical_reads) / SUM(qt.execution_count) AS avg_physical_read,
  (SUM(qt.total_worker_time) - SUM(qt.total_elapsed_time)) / SUM(qt.execution_count) / 1000 AS avg_over_parallel,
  MIN(qt.sql_handle) AS sql_handle,
  qt.query_hash
FROM sys.dm_exec_cached_plans cp WITH (NOLOCK)
INNER JOIN sys.dm_exec_query_stats AS qt WITH (NOLOCK) ON
 cp.plan_handle = qt.plan_handle
CROSS APPLY sys.dm_exec_sql_text(qt.sql_handle) st 
GROUP BY qt.query_hash
ORDER BY 
 (CASE @OrderByField 
  WHEN &#39;CPU&#39; THEN SUM(qt.total_worker_time) / SUM(qt.execution_count)
  WHEN &#39;READ&#39; THEN SUM(qt.total_logical_reads + total_physical_reads) / SUM(qt.execution_count)
  WHEN &#39;WRITE&#39; THEN SUM(qt.total_logical_writes) / SUM(qt.execution_count)
  WHEN &#39;COUNT&#39; THEN SUM(cp.usecounts)
  WHEN &#39;TIME&#39; THEN SUM(qt.total_elapsed_time) / SUM(qt.execution_count)
  WHEN &#39;RECOMPILE&#39; THEN SUM(qt.plan_generation_num) / SUM(cp.usecounts)
  WHEN &#39;PARALLEL&#39; THEN (SUM(qt.total_worker_time) - SUM(qt.total_elapsed_time)) / SUM(qt.execution_count)
 END) DESC
)

SELECT TOP(@TopNumber)  
 DB_NAME(st.dbid) AS database_name,
 OBJECT_NAME(st.objectid, st.dbid) AS proc_name,
 cp.cacheobjtype,
 cp.objtype,
 qs.avg_ob_recompile,
 total_exec_count,
 avg_cpu_time,
 avg_elapsed_time,
 avg_logical_write,
 avg_logical_read,
 avg_physical_read,
 avg_over_parallel,
 SUBSTRING(st.text, (qt.statement_start_offset/2) + 1,
   ((CASE statement_end_offset 
    WHEN -1 THEN DATALENGTH(st.text)
    ELSE qt.statement_end_offset 
     END - qt.statement_start_offset)/2) + 1) AS sql_text, 
 st.text AS ob_text,
 ph.query_plan
FROM query_stat AS qs
INNER JOIN sys.dm_exec_query_stats AS qt WITH (NOLOCK) ON
 qs.query_hash = qt.query_hash AND
 qs.sql_handle = qt.sql_handle
INNER JOIN sys.dm_exec_cached_plans cp WITH (NOLOCK) ON
 cp.plan_handle = qt.plan_handle
CROSS APPLY sys.dm_exec_sql_text(qt.sql_handle) AS st
CROSS APPLY sys.dm_exec_query_plan(qt.plan_handle) AS ph
OPTION (RECOMPILE);&lt;/pre&gt;
&lt;br /&gt;
By analyzing the two different set of data, I was able to find some correlation and identified some frequently called slow queries. I decided to focus on the &#39;operational&#39; queries as that is more aligned with the user complaint, With some help from developer to filter out the reporting queries, I was able to identify one stored procedure that appear to have significant impact on user operational task experience.&lt;br /&gt;
&lt;br /&gt;
Here is the target, dbo.SPBuildFastIndex&lt;br /&gt;
&lt;br /&gt;
The stored procedure appears to insert new entry into a table when there a new entry, and in addition to that, it could also be executed to delete all entries in the table and reinsert all data. This stored procedure is one of the most run stored procedure with higher than average duration for its execution. From the system cached data, the stored procedure executed around 3000 times a day. From the statistics, it appears that there are certain SQL statements within the stored procedure take an average of 20 seconds to complete in production and some instances of that certain entries take between 1-2 minutes to complete.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;ALTER PROCEDURE dbo.SPBuildFastIndex (@ObjectID int)
AS
...
INSERT INTO FastIndexTbl
(LocID, FastIndexOTID, TransID, NameData, Info, Timestamp)
(
 SELECT 
  t.LocID, 
  100, 
  t.TransID, 
  dbo.NameStr(b.FirstName, b.MiddleName, b.LastName, b.CompanyName),
  CONVERT(varchar(50),d.DetailNumber) + &#39;, &#39; + 
  ISNULL(dbo.NameStr(i.FirstName, i.MiddleName, i.LastName, NULL), &#39;No Data&#39;), 
  ISNULL(CAST(o.OpsDate AS datetime), ISNULL(t.TransDate, &#39;1/1/1900&#39;))
 FROM Trans t 
 INNER JOIN Detail d ON t.TransID=d.TransID
 INNER JOIN Ind i ON d.DetailInd=i.IndID
 INNER JOIN Acct a ON t.AcctID=a.AcctID
 INNER JOIN Ind b ON b.IndID=a.CustID
 INNER JOIN [Ops] o ON d.DetailID=o.DetailID
 WHERE 
  d.DetailTypeID = 100  
  AND dbo.NameStr(b.FirstName, b.MiddleName, b.LastName, b.CompanyName) IS NOT NULL
  AND t.TransID = CASE WHEN @ObjectID = 0 THEN t.TransID ELSE @ObjectID END
);
&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
I picked the slow SQL Statement out for more isolated analysis and execute the select statement with the ObjectID parameter specified (eg. 100001),&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;--Turn on statistics to display IO and time information of the execution
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
&lt;/pre&gt;
&lt;br /&gt;
I execute these commands below to clear the cache each time I made changes for better comparison&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;--Delete cache and data in memory for comparison of each change
CHECKPOINT;
DBCC DROPCLEANBUFFERS;
DBCC FREEPROCCACHE;
&lt;/pre&gt;
&lt;br /&gt;
Here is the execution plan of the query (view from &lt;a href=&quot;http://www.sqlsentry.com/products/plan-explorer/sql-server-query-view&quot; target=&quot;_blank&quot;&gt;SQL Sentry Plan Explorer&lt;/a&gt;). Click on the picture for larger view.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHoNZjg3FeseLCRWymr70AsJGc8AM_OBK53NuIbuJNCKaYAGIUoGXQUzusDciCFATC4ux0Dl8ubvGyMXjNQRPc085Z_pVEFcbF7Qzq7Tn5nOeo_abJIwloMoJGdFQtPfUw3-rttk8c9iwe/s1600/Original_Performance.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHoNZjg3FeseLCRWymr70AsJGc8AM_OBK53NuIbuJNCKaYAGIUoGXQUzusDciCFATC4ux0Dl8ubvGyMXjNQRPc085Z_pVEFcbF7Qzq7Tn5nOeo_abJIwloMoJGdFQtPfUw3-rttk8c9iwe/s1600/Original_Performance.png&quot; height=&quot;200&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Here is some of the IO and time stat from cold cache.&lt;br /&gt;
&lt;br /&gt;
Table &#39;Worktable&#39;. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.&lt;br /&gt;
Table &#39;Acct&#39;. Scan count 1, logical reads 22357, physical reads 20, read-ahead reads 22739, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.&lt;br /&gt;
Table &#39;Ops&#39;. Scan count 1, logical reads 12541, physical reads 27, read-ahead reads 12555, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.&lt;br /&gt;
Table &#39;Detail&#39;. Scan count 1, logical reads 21372, physical reads 24, read-ahead reads 21437, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.&lt;br /&gt;
Table &#39;Trans&#39;. Scan count 1, logical reads 53846, physical reads 76, read-ahead reads 53872, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.&lt;br /&gt;
Table &#39;Ind&#39;. Scan count 2, logical reads 282086, physical reads 205, read-ahead reads 141176, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.&lt;br /&gt;
&lt;br /&gt;
&amp;nbsp;SQL Server Execution Times:&lt;br /&gt;
&amp;nbsp; &amp;nbsp;CPU time = 34203 ms, &amp;nbsp;elapsed time = 43402 ms&lt;br /&gt;
&lt;br /&gt;
Better view from &lt;a href=&quot;http://statisticsparser.com/&quot; target=&quot;_blank&quot;&gt;Statistics Parser&lt;/a&gt;&amp;nbsp;by Richie Rump.&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiw2bYXk6PbJiTiu-Ys_9Z-N2NdDxcqKiu_LyI7A42LYq7sLDROWR7hwUVYVfMDu_Xj9YthLAAZqnJ8xLwvWyGkxEKvaq7BF24GpZMO37IPSZsM0wAQNYZ-IUlWeFfgVPEyTo75fHUUYH8N/s1600/Original_Statistics.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiw2bYXk6PbJiTiu-Ys_9Z-N2NdDxcqKiu_LyI7A42LYq7sLDROWR7hwUVYVfMDu_Xj9YthLAAZqnJ8xLwvWyGkxEKvaq7BF24GpZMO37IPSZsM0wAQNYZ-IUlWeFfgVPEyTo75fHUUYH8N/s1600/Original_Statistics.png&quot; height=&quot;200&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Here is the IO and time statistics from warm cache (data available in memory)&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQ-B-eiArRFnw2tvniilnYG6ijQufUZRY4YTInNdc1733ia4L_k-afNcDiyATrGGuestwuTIqaMmOvIKzthL48YC6dTBQWS-9_cfW0ne0uOF5AAZrCjdZ7Cyk-he1rx6jEeG_M2V6jeB4s/s1600/Original_Statistic_Warms.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQ-B-eiArRFnw2tvniilnYG6ijQufUZRY4YTInNdc1733ia4L_k-afNcDiyATrGGuestwuTIqaMmOvIKzthL48YC6dTBQWS-9_cfW0ne0uOF5AAZrCjdZ7Cyk-he1rx6jEeG_M2V6jeB4s/s1600/Original_Statistic_Warms.png&quot; height=&quot;200&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The duration of reading the data from memory reduce around 10 seconds.&lt;br /&gt;
&lt;br /&gt;
The elapsed time for the warm cache likely to illustrate what is experiencing by end user. However, I would be using the cold cache data for better comparison with other improvement options.&lt;br /&gt;
&lt;br /&gt;
Below were a few methods I tried to identify the level of improvements.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Index implementation&lt;/b&gt;&lt;br /&gt;
The IO stat as well as the execution plan show that the dbo.Ind table returns and process large number of pages. Mainly because this table is wide and there is no useful narrower index to be used to fulfill this query. Other index scan operators are chosen likely due to the number of rows required to return for process. Implementing some indexes could help reduce number of pages required and returned.&lt;br /&gt;
&lt;br /&gt;
I examined the tables for redundant index as well as reusable index. I found&amp;nbsp;&lt;a href=&quot;http://www.brentozar.com/blitzindex/&quot; target=&quot;_blank&quot;&gt;sp_BlitzIndex&lt;/a&gt; from Brent Ozar Unlimited makes the index examination easier and faster. I cleaned up some redundant indexes and decided to create some indexes. Eg. on dbo.ind table.&lt;br /&gt;
&lt;br /&gt;
CREATE INDEX [IX_Ind_IndlID_Includes]&lt;br /&gt;
ON [dbo].[Ind] ([IndID]) INCLUDE ([FirstName], [MiddleName], [LastName], [CompanyName]);&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgY18-HlGItl7GMX6U6hFOrY00VNpwSWB8f_oaGtprR8MeBEQLESwWskaM88bNqMrGs74oCwwKuLlgmIyeae3En6KJtbpwECqWFZg5JOnv1o1xCVrnKVJ7AZFVPzzKeOR3fpranfNoaGYyg/s1600/Index_Implementation.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgY18-HlGItl7GMX6U6hFOrY00VNpwSWB8f_oaGtprR8MeBEQLESwWskaM88bNqMrGs74oCwwKuLlgmIyeae3En6KJtbpwECqWFZg5JOnv1o1xCVrnKVJ7AZFVPzzKeOR3fpranfNoaGYyg/s1600/Index_Implementation.png&quot; height=&quot;200&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFib1qUVdPs6kt2YKfa-vrMScDlz6foIvup-JIviJRINXiMzBVi6DvaQZLXqyUMd5SArsklcFagWpOdYA7629SVFREZUzsokt3jhMm8pEfQ8PaOtE6XZXSiQWLYVZt355dqUbWvn7P-X_p/s1600/Statistics_Index.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFib1qUVdPs6kt2YKfa-vrMScDlz6foIvup-JIviJRINXiMzBVi6DvaQZLXqyUMd5SArsklcFagWpOdYA7629SVFREZUzsokt3jhMm8pEfQ8PaOtE6XZXSiQWLYVZt355dqUbWvn7P-X_p/s1600/Statistics_Index.png&quot; height=&quot;200&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
The duration reduce around 10 seconds with the index implementation from cold cache. Total pages read reduces from ~392K to 47K.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Scalar User Defined Function and Inline Table Valued Function&lt;/b&gt;
&lt;br /&gt;
&lt;br /&gt;
The SQL statement uses a scalar user defined function (UDF) dbo.NameStr. The function is as innocent as concatenating first, middle, last name and sometimes company name into one string.&lt;br /&gt;
&lt;br /&gt;
Understanding that this function is implemented to make code maintenance simpler and avoid the need to rewrite the same script every times when there is a need to concatenate the name into one string/column.
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;CREATE FUNCTION [dbo].[NameStr]
(  
  @FirstName varchar(50), @MiddleName varchar(50), @LastName varchar(50), @CompanyName varchar(100) = &#39;&#39;
)
RETURNS varchar(150)
AS
BEGIN

DECLARE @ret varchar(150)

IF IsNull(@CompanyName,&#39;&#39;) = &#39;&#39;
  SET @ret = @LastName + CASE WHEN IsNull(@FirstName, &#39;&#39;) &amp;lt;&amp;gt; &#39;&#39; THEN &#39;, &#39; ELSE &#39;&#39; END + @FirstName + CASE WHEN @MiddleName is NULL THEN &#39;&#39; ELSE &#39; &#39; + @MiddleName END
ELSE
  SET @ret = @CompanyName

RETURN @ret 

END&lt;/pre&gt;
&lt;br /&gt;
However, scalar function has some disadvantages.&lt;br /&gt;
&lt;br /&gt;
1) When scalar UDF is used in a query, SQL Server invoke this function as a separate module for each row within the iterator. This introduces overhead from each invocation of the module of each row&lt;br /&gt;
2) Scalar UDF estimated cost is 0 which is not true and it affects how SQL optimizer choose its optimal plan&lt;br /&gt;
3) Scalar UDF does not use parallelism in a plan
&lt;br /&gt;
&lt;br /&gt;
SQL Profiler trace show that the scalar function is invoked and executed for each row.
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1GzpVUr-fxOtLWA7vcOe7xy1KXapwWmX6VNoMhiPzpDbNaG6XWKCuxRVPN3u1RJ9TSYH3xG6_n51Hm0H5Si4dDZfC09UpkBIrjUjwPfj7NZ_jjqF4C7K8WPmwEwUreAFYYvxCp_YBIDtf/s1600/Profiler_ScalarFunction.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1GzpVUr-fxOtLWA7vcOe7xy1KXapwWmX6VNoMhiPzpDbNaG6XWKCuxRVPN3u1RJ9TSYH3xG6_n51Hm0H5Si4dDZfC09UpkBIrjUjwPfj7NZ_jjqF4C7K8WPmwEwUreAFYYvxCp_YBIDtf/s1600/Profiler_ScalarFunction.png&quot; height=&quot;200&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
SQL plan show that the cost of the function is 0 which is not true&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSC8JziWw6m3r1eyis8ltOHLP9e9qiSGfvsrl7B2DGZUlB-RgnB6l3A5gN7tv6PQqg1iZu_WJlSyrzKgTSFBsMzvHYIiyZmlUS6FphtfbeECdamLZDnFBQKBucIryzK_X50Sb9Po9OlYLN/s1600/Cost_ScalarFunction.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSC8JziWw6m3r1eyis8ltOHLP9e9qiSGfvsrl7B2DGZUlB-RgnB6l3A5gN7tv6PQqg1iZu_WJlSyrzKgTSFBsMzvHYIiyZmlUS6FphtfbeECdamLZDnFBQKBucIryzK_X50Sb9Po9OlYLN/s1600/Cost_ScalarFunction.png&quot; height=&quot;204&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
All the execution plan so far has been with no parallelism involved.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmAo5qnGQDlziVdd6raGF1MlAUuVsUxyIKoHjjLqZTu8mVAafVZhELSBIq2OW4tb77KTvZxdmzMClWX5eibrLLpQcYNz1vM9ZeEliBquPfgtEWf5Iysy79AR1LLF6tOfZ_BIrPi4DkrQAN/s1600/Serial_ScalarFunction.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmAo5qnGQDlziVdd6raGF1MlAUuVsUxyIKoHjjLqZTu8mVAafVZhELSBIq2OW4tb77KTvZxdmzMClWX5eibrLLpQcYNz1vM9ZeEliBquPfgtEWf5Iysy79AR1LLF6tOfZ_BIrPi4DkrQAN/s1600/Serial_ScalarFunction.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
There are few ways to remedy this particular case,&lt;br /&gt;
&lt;br /&gt;
1) Rewrite query to include the logic in the query itself, or&lt;br /&gt;
2) Replace with inline table valued function&lt;br /&gt;
&lt;br /&gt;
If the decision is to use inline table valued function (TVF), an inline TVF can be created with very similar syntax as the existing scalar UDF, but instead of returning a scalar value, it returns a table with single value (in this case). Please refer to reference for the exact syntax. Here is an example of a inline TVF for this purpose,&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;CREATE FUNCTION [dbo].[NameStrTable]
( 
  @FirstName varchar(50), @MiddleName varchar(50), @LastName varchar(50), @CompanyName varchar(100) = &#39;&#39;
)
RETURNS TABLE
AS
RETURN 
…
);
&lt;/pre&gt;
&lt;br /&gt;
Replace any dbo.NameStr(FirstName, MiddleName, LastName, Company) with (SELECT Name FROM dbo.NameStrTable(FirstName, MiddleName, LastName, Company))&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;SELECT 
 t.LocID, 
 100, 
 t.TransID, 
 (SELECT Name FROM dbo.NameStrTable(b.FirstName, b.MiddleName, b.LastName, b.CompanyName)),
 CONVERT(varchar(50),d.DetailNumber) + &#39;, &#39; + 
 ISNULL((SELECT Name FROM dbo.NameStrTable(i.FirstName, i.MiddleName, i.LastName, NULL)), &#39;No Data&#39;), 
 ISNULL(CAST(o.OpsDate AS datetime), ISNULL(t.TransDate, &#39;1/1/1900&#39;))
FROM Trans t 
INNER JOIN Detail d ON t.TransID=d.TransID
INNER JOIN Ind i ON d.DetailID=i.IndID
INNER JOIN Acct a ON t.AcctID=a.AcctID
INNER JOIN Ind b ON b.IndID=a.CustID
INNER JOIN [Ops] o ON d.DetailID=o.DetailID
WHERE 
 d.DetailTypeID = 100  
 AND (SELECT Name FROM dbo.NameStrTable(b.FirstName, b.MiddleName, b.LastName, b.CompanyName)) IS NOT NULL
 AND t.TransID = CASE WHEN @ObjectID = 0 THEN t.TransID ELSE @ObjectID END;
&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
The execution plan below show that SQL Server optimizer chose a parallel plan for this execution. In this case the degree of parallelism is 4.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG02-5SWq7hLLRVw6sGSCRQY91puMpqqVvAZRqGT4lqs_MAypK0dClzCZ-PaEDeJ4PJhyphenhyphenMfe2rPA_JFoh7vjgN4PpHlrs9YTDwjEh19N7e1pwV_ATQnygTE5sO35aQG_9t-dEsKma1eFqw/s1600/Parallel_TableFunction.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjG02-5SWq7hLLRVw6sGSCRQY91puMpqqVvAZRqGT4lqs_MAypK0dClzCZ-PaEDeJ4PJhyphenhyphenMfe2rPA_JFoh7vjgN4PpHlrs9YTDwjEh19N7e1pwV_ATQnygTE5sO35aQG_9t-dEsKma1eFqw/s1600/Parallel_TableFunction.png&quot; height=&quot;200&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYwfS0_djbtxxsbesgf9ItlzkEnoKpFfhOYVs1xmpgYbyrECM0sZo9B13jurlq5JnbKzPt-wZAX1QqU2BYzq4jv8r1G7TTYNek_c-H4nkRLzRdxDGkUiOkHZcc9HiNWv8iBArSGkQ2B520/s1600/Statistics_Function.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYwfS0_djbtxxsbesgf9ItlzkEnoKpFfhOYVs1xmpgYbyrECM0sZo9B13jurlq5JnbKzPt-wZAX1QqU2BYzq4jv8r1G7TTYNek_c-H4nkRLzRdxDGkUiOkHZcc9HiNWv8iBArSGkQ2B520/s1600/Statistics_Function.png&quot; height=&quot;200&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
The duration reduces about 30 seconds from cold cache. The execution was performed without any new index implemented previously.&lt;br /&gt;
&lt;br /&gt;
From the SQL Profiler, it appears that SQL Server does not invoke the inline TVF for each row like scalar UDF and hence avoid the overheads that scalar UDF were experiencing.&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAE6WbNy-MZmnHT-iz8wNHgyw138ySH1zyFwUojktX4avFfqx86s6OQD7XloQeMEqkp-XodVE3tRR2VmjVxKlGsntj4C19gUb3S6uK9u7x6r6Zq38qq0lrH1TB66wWNx6YqQQsrh54wecm/s1600/Profiler_TableFunction.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAE6WbNy-MZmnHT-iz8wNHgyw138ySH1zyFwUojktX4avFfqx86s6OQD7XloQeMEqkp-XodVE3tRR2VmjVxKlGsntj4C19gUb3S6uK9u7x6r6Zq38qq0lrH1TB66wWNx6YqQQsrh54wecm/s1600/Profiler_TableFunction.png&quot; height=&quot;41&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
The combination of having the function in line with the query for better cost estimation, parallel plan and prevention of overhead cost of invoking the function for each row allow the query to perform much faster.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;strike&gt;Parameter Sniffing&lt;/strike&gt; CASE Condition and Parameter Embedding Optimization&lt;/b&gt;&lt;br /&gt;
I will get to the reason why I cross out parameter sniffing in a minute.&lt;br /&gt;
&lt;br /&gt;
If you remember, at the beginning of the review, we discussed about the purpose of this stored procedure. In order to build the fast index to speed up search lookup, any new entry entered executed this stored procedure to insert a record into the search index, &lt;b&gt;in addition&lt;/b&gt; this stored procedure could be also used build a new one from scratch.
&lt;br /&gt;
&lt;br /&gt;
The key part is that this stored procedure is serving two purposes. In order to do that, there is the CASE statement at the search condition (where clause)
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;t.TransID = CASE WHEN @ObjectID = 0 THEN t.TransID ELSE @ObjectID END;
&lt;/pre&gt;
&lt;br /&gt;
When a zero is passed in as ObjectID parameter, the query filter all rows in sales table along with other condition (e.g. other joins and condition); If a specific ObjectID is passed in, it only filter that specified sales item in sales table along with other condition (e.g. other joins and search condition).&lt;br /&gt;
&lt;br /&gt;
There are about 1.5 million rows in sales table. When the ObjectID parameter is zero, SQL Server needs to read all these 1.5 mil rows (if this is the only search condition) along with the relevant data required from other tables. SQL Server is likely to choose a scan operator along with merge or hash joins as they are more efficient due to the large rows to be processed. On the other hand, if a specified ObjectID parameter value is specified (non-zero), SQL Server only need to read this one row along with the data required from other tables. Since the SaleID is a clustered key, SQL Server is likely to choose a seek operator along with nested joins which is more efficient for small row set.&lt;br /&gt;
&lt;br /&gt;
Since this two search conditions are applied as a CASE statement, SQL Server would need to evaluate this two conditions and choose a plan that is good enough for both conditions. In this case, the plan shown previously was chosen. Mostly scan operators along with merge and hash joins.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimwZ8PhqR64-L-1-QGoDGhw-t5UgJqBg2PFH4BzogtTAz_NMbfN7ou4cPZtWSweaSW3wKI2yHC5nik5DU5MetOnmo_lOJx26iN0Udc_m8zGbqb-2q6EzD6odPVQyKYyuDW_g9_Q9-8xwVk/s1600/CASE_Predicate.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimwZ8PhqR64-L-1-QGoDGhw-t5UgJqBg2PFH4BzogtTAz_NMbfN7ou4cPZtWSweaSW3wKI2yHC5nik5DU5MetOnmo_lOJx26iN0Udc_m8zGbqb-2q6EzD6odPVQyKYyuDW_g9_Q9-8xwVk/s1600/CASE_Predicate.png&quot; height=&quot;141&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
So what is the performance difference of these two plans? Without any index implementation and function replacement, here is the statistics with current plan (statistics same with what shown previously)&lt;br /&gt;
&lt;br /&gt;
Plan SQL Server chose,&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHoNZjg3FeseLCRWymr70AsJGc8AM_OBK53NuIbuJNCKaYAGIUoGXQUzusDciCFATC4ux0Dl8ubvGyMXjNQRPc085Z_pVEFcbF7Qzq7Tn5nOeo_abJIwloMoJGdFQtPfUw3-rttk8c9iwe/s1600/Original_Performance.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHoNZjg3FeseLCRWymr70AsJGc8AM_OBK53NuIbuJNCKaYAGIUoGXQUzusDciCFATC4ux0Dl8ubvGyMXjNQRPc085Z_pVEFcbF7Qzq7Tn5nOeo_abJIwloMoJGdFQtPfUw3-rttk8c9iwe/s1600/Original_Performance.png&quot; height=&quot;200&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiw2bYXk6PbJiTiu-Ys_9Z-N2NdDxcqKiu_LyI7A42LYq7sLDROWR7hwUVYVfMDu_Xj9YthLAAZqnJ8xLwvWyGkxEKvaq7BF24GpZMO37IPSZsM0wAQNYZ-IUlWeFfgVPEyTo75fHUUYH8N/s1600/Original_Statistics.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiw2bYXk6PbJiTiu-Ys_9Z-N2NdDxcqKiu_LyI7A42LYq7sLDROWR7hwUVYVfMDu_Xj9YthLAAZqnJ8xLwvWyGkxEKvaq7BF24GpZMO37IPSZsM0wAQNYZ-IUlWeFfgVPEyTo75fHUUYH8N/s1600/Original_Statistics.png&quot; height=&quot;200&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
With optimal plan,&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB0rzhZOlTMKHpwRkK1SJ0jJmrj3qEbG5oZbOUZCFzb-11EhtWBFzYVNkZQ8QAkBqDX97u4kabwVcZEAjThcQvEyzMx9CTUTy94Sy_FvMvLvIPrzGGwtRkTmFInfuAhbBiRdWyfO4n0B3L/s1600/Optimal_Plan.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB0rzhZOlTMKHpwRkK1SJ0jJmrj3qEbG5oZbOUZCFzb-11EhtWBFzYVNkZQ8QAkBqDX97u4kabwVcZEAjThcQvEyzMx9CTUTy94Sy_FvMvLvIPrzGGwtRkTmFInfuAhbBiRdWyfO4n0B3L/s1600/Optimal_Plan.png&quot; height=&quot;200&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGBDgXTuOS28Hqunx5boxTm51GLBdJh2QEM_XQPJ1elecZgv8icpI_YT5bK24Bf4x28x7aZ-2GSpptE0VdpmFKnw2xKhBUDfse8EKfox5cu9Qp97W9u8WJzotJF-1tx8EbvBdZgBA-jH4A/s1600/Statistics_Condition.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGBDgXTuOS28Hqunx5boxTm51GLBdJh2QEM_XQPJ1elecZgv8icpI_YT5bK24Bf4x28x7aZ-2GSpptE0VdpmFKnw2xKhBUDfse8EKfox5cu9Qp97W9u8WJzotJF-1tx8EbvBdZgBA-jH4A/s1600/Statistics_Condition.png&quot; height=&quot;200&quot; width=&quot;500&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Wow. the huge performance difference between these two execution plans. This optimal execution plan takes less than a second. Even without any new indexes, the CPU and IO demand is very low compare to the current plan as it is able to seek that particular record through the index seek operation.&lt;br /&gt;
&lt;br /&gt;
As we know now that the current plan is not an optimal plan for single sales item due to the CASE statement, there are a few ways to remedy it. Before that, I want to discuss the topic on the parameter sniffing. When a parameter is passed in the first time, SQL Server evaluates the parameter to choose and generate a compiled plan, execute with that plan and then cache it for reuse. Parameter sniffing occurs when the plan that was chosen and cached is not optimal for subsequent parameter passed in. This usually occur when the data is not evenly distributed in the table. It may seem like the ObjectID with zero was passed in the first time and caused SQL Server to generate a less optimal plan for subsequent specified ObjectID execution. However, this is not the case here as SQL Server evaluates both condition in the CASE statement to generate a plan, regardless if the zero or specified value (non-zero) is passed in the first time. In addition, since the SaleID is an unique column, it allows accurate cardinality estimation and the same compiled plan should be optimal for different ObjectID value as long as it is not a zero.&lt;br /&gt;
&lt;br /&gt;
Now, let’s explore different ways to remedy this.&lt;br /&gt;
&lt;br /&gt;
1) OPTION (RECOMPILE)&lt;br /&gt;
2) Rewrite separate static query with IF statement&lt;br /&gt;
3) Rewrite as dynamic query with IF statement&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;OPTION (RECOMPILE)&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;SELECT 
 t.LocID, 
 100, 
 t.TransID, 
 dbo.NameStr(b.FirstName, b.MiddleName, b.LastName, b.CompanyName),
 CONVERT(varchar(50),d.DetailNumber) + &#39;, &#39; + 
 ISNULL(dbo.NameStr(i.FirstName, i.MiddleName, i.LastName, NULL), &#39;No Data&#39;), 
 ISNULL(CAST(o.OpsDate AS datetime), ISNULL(t.TransDate, &#39;1/1/1900&#39;))
FROM Trans t 
INNER JOIN Detail d ON t.TransID=d.TransID
INNER JOIN Ind i ON d.DetailInd=i.IndID
INNER JOIN Acct a ON t.AcctID=a.AcctID
INNER JOIN Ind b ON b.IndID=a.CustID
INNER JOIN [Ops] o ON d.DetailID=o.DetailID
WHERE 
 d.DetailTypeID = 100  
 AND dbo.NameStr(b.FirstName, b.MiddleName, b.LastName, b.CompanyName) IS NOT NULL
 AND t.TransID = CASE WHEN @ObjectID = 0 THEN t.TransID ELSE @ObjectID END
OPTION (RECOMPILE);
&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
Adding the OPTION (RECOMPILE) at the end of the statement force SQL Server to regenerate a new plan based on the parameter for every execution. In addition, it applies parameter embedding optimization that allows SQL parser to fully evaluating the CASE statement. Hence generating an optimal plan for both zero parameter and parameter with specified value. However, there is CPU overhead to recompile and generate a plan, and it may not be desirable especially for query that is executed frequently.
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg44GDesgP3qlUlxPuYJqnpDRXXHupOBXuDsUnNcu7tgPQkllPJ6xf5f8SXApJzGPWgoMiFK-kh1BPcZIL98TmuBrgiOwINNVNOqM5ymjfk0GduBsTbJY-ypztJVt_RE0RfKqqawIkFa6MR/s1600/Parameter_Embedding_Optimization.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg44GDesgP3qlUlxPuYJqnpDRXXHupOBXuDsUnNcu7tgPQkllPJ6xf5f8SXApJzGPWgoMiFK-kh1BPcZIL98TmuBrgiOwINNVNOqM5ymjfk0GduBsTbJY-ypztJVt_RE0RfKqqawIkFa6MR/s1600/Parameter_Embedding_Optimization.png&quot; height=&quot;100&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Static query with IF statement,&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;IF @ObjectID &amp;lt;&amp;gt; 0
BEGIN
 SELECT 
  t.LocID, 
  100, 
  t.TransID, 
  dbo.NameStr(b.FirstName, b.MiddleName, b.LastName, b.CompanyName),
  CONVERT(varchar(50),d.DetailNumber) + &#39;, &#39; + 
  ISNULL(dbo.NameStr(i.FirstName, i.MiddleName, i.LastName, NULL), &#39;No Data&#39;), 
  ISNULL(CAST(o.OpsDate AS datetime), ISNULL(t.TransDate, &#39;1/1/1900&#39;))
 FROM Trans t 
 INNER JOIN Detail d ON t.TransID=d.TransID
 INNER JOIN Ind i ON d.DetailInd=i.IndID
 INNER JOIN Acct a ON t.AcctID=a.AcctID
 INNER JOIN Ind b ON b.IndID=a.CustID
 INNER JOIN [Ops] o ON d.DetailID=o.DetailID
 WHERE 
  d.DetailTypeID = 100  
  AND dbo.NameStr(b.FirstName, b.MiddleName, b.LastName, b.CompanyName) IS NOT NULL
  AND t.TransID = @ObjectID;
END
ELSE
BEGIN
  SELECT 
  t.LocID, 
  100, 
  t.TransID, 
  dbo.NameStr(b.FirstName, b.MiddleName, b.LastName, b.CompanyName),
  CONVERT(varchar(50),d.DetailNumber) + &#39;, &#39; + 
  ISNULL(dbo.NameStr(i.FirstName, i.MiddleName, i.LastName, NULL), &#39;No Data&#39;), 
  ISNULL(CAST(o.OpsDate AS datetime), ISNULL(t.TransDate, &#39;1/1/1900&#39;))
 FROM Trans t 
 INNER JOIN Detail d ON t.TransID=d.TransID
 INNER JOIN Ind i ON d.DetailInd=i.IndID
 INNER JOIN Acct a ON t.AcctID=a.AcctID
 INNER JOIN Ind b ON b.IndID=a.CustID
 INNER JOIN [Ops] o ON d.DetailID=o.DetailID
 WHERE 
  d.DetailTypeID = 100  
  AND dbo.NameStr(b.FirstName, b.MiddleName, b.LastName, b.CompanyName) IS NOT NULL
END
&lt;/pre&gt;
&lt;br /&gt;
This method separates the two logics and allow SQL Server to generate, use and cache different plan for different search conditions. Once the compiled plan is cached, it could be reused again for next execution. This method may require the same select and join statements to be rewritten twice.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Dynamic query with IF statement&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;DECLARE @sql nvarchar(max),
  @parameter nvarchar(4000);

SELECT @parameter = &#39;@transid int&#39;;

SELECT @sql = 
 `SELECT 
  t.LocID, 
  100, 
  t.TransID, 
  dbo.NameStr(b.FirstName, b.MiddleName, b.LastName, b.CompanyName),
  CONVERT(varchar(50),d.DetailNumber) + &#39;, &#39; + 
  ISNULL(dbo.NameStr(i.FirstName, i.MiddleName, i.LastName, NULL), &#39;No Data&#39;), 
  ISNULL(CAST(o.OpsDate AS datetime), ISNULL(t.TransDate, &#39;1/1/1900&#39;))
 FROM Trans t 
 INNER JOIN Detail d ON t.TransID=d.TransID
 INNER JOIN Ind i ON d.DetailInd=i.IndID
 INNER JOIN Acct a ON t.AcctID=a.AcctID
 INNER JOIN Ind b ON b.IndID=a.CustID
 INNER JOIN [Ops] o ON d.DetailID=o.DetailID
 WHERE 
  d.DetailTypeID = 100  
  AND dbo.NameStr(b.FirstName, b.MiddleName, b.LastName, b.CompanyName) IS NOT NULL`;

  AND t.TransID = CASE WHEN @ObjectID = 0 THEN t.TransID ELSE @ObjectID END

IF @ObjectID &amp;lt;&amp;gt; 0 SELECT @sql = @sql + &#39; AND t.TransID = @transid;&#39;;
ELSE SELECT @sql = @sql + &#39;;&#39;;

EXEC sp_executesql @sql, @parameter, @ObjectID;
&lt;/pre&gt;
&lt;br /&gt;
&lt;br /&gt;
This method separates the two search condition logics and allow SQL Server to generate, use and cache different plan (using sp_executesql) for different search conditions. Once the compiled plan is cached, each statement doesn&#39;t required to be recompiled again for next execution. The dynamic query allows the reuse of the same select and join statement during coding.  However, the use of dynamic query may add another layer of complexity of coding and troubleshooting.
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Observation&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;Index Implementation&lt;/b&gt;&lt;br /&gt;
As we have seen that some indexes do improve the performance for current plan (optimized for CASE condition) from cold cache. It reduces the number of pages required, that translates to lower IO and with that, it indirectly reduces the memory demand. 
&lt;br /&gt;
&lt;br /&gt;
For this particular stored procedure, it does not appear that these new indexes will significantly improve the query performance that we are experiencing in production especially after the data have been cached in memory. However with an optimal query plan, some covering indexes could be helpful.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Replacing Scalar Function&lt;/b&gt;&lt;br /&gt;
The prevention of using parallel plan and the significant overhead incurred on scalar UDF could severely affect the performance when a large number of rows are involved like in this case. Although parallel plan is not always better than serial plan, in the case of large number of rows, it could be beneficial for multiple threads to split up the works. With small row set, the scalar UDF may not have significant impact as we have seen with the optimal plan.
&lt;br /&gt;
&lt;br /&gt;
From the observation, replacing scalar UDF would improve the performance in production especially when large number of rows are involved. The only thing is that the function is being used in many different stored procedures. As not all stored procedure will replace it with UDF, changing this stored procedure to use a new UDF mean there will be two separate functions to maintain. May not be a big deal but all developers will need to take note of that.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Replacing CASE statement&lt;/b&gt;&lt;br /&gt;
The CASE statement at the search condition appears to be the main culprit of low performance of this query. SQL optimizer has chosen and generated a plan that is suboptimal for parameter with specified value (non-zero), which is most of the cases here.
&lt;br /&gt;
&lt;br /&gt;
Since the situation is not caused by inaccurate cardinality estimation from uneven data distribution according to the parameter, recompilation on every execution may not be desired. There is only a need for two search condition; one with specified value and one for returning all rows. By rewriting the query with two separate logics allow optimal cached plan for each type of execution, and also prevent the overhead of recompiling the plan to generate an optimal plan for each execution.
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Lesson Learned&lt;/b&gt;&lt;br /&gt;
When performing query tuning, the query itself should be examined closely before performing any index changes. Index does help in many scenarios but should not be the first approach. Often, including this scenario, the performance of the query could be significantly improved just by changing the way it is written. With better understanding on different limitation and constraints (eg. scalar function) and how SQL Server Optimizer works would definitely help.&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/5048842932502111613/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2015/03/query-performance-tuning-example.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/5048842932502111613'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/5048842932502111613'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2015/03/query-performance-tuning-example.html' title='Query Performance Tuning Example'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl3HkBJzO7nBJSSx5Nx0JbseFq6zunD8eSymka7PcgO6nAJIOneXuKEXqb39VZvGMiPY379DfoVCyPEQWDw-5q1Akk4KbDHg_-HaWzntwSd19zJishjIO2cBimDK84ij1GBkSIghBJTrwg/s72-c/tuning.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-7965098821287716546</id><published>2015-02-14T16:12:00.003-06:00</published><updated>2015-03-17T15:48:49.368-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Self Learning"/><title type='text'>One Important Aspect - Mentor</title><content type='html'>Looking back to my education and career journey, there were times when I felt like it was totally stagnant, and other times of leap and bound moment. I have came to realize the importance of a mentor.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;I have been fortunate to have some great teachers helping me to build a strong foundation in math, engineering and logical type of subjects in my school years. I know my stuff and it wasn&#39;t hard to get a good grade in these fields.&lt;br /&gt;
&lt;br /&gt;
When it comes to my choice of educational advancement oversea, that was my first taste of lack of concrete direction. There were some information on the internet and material provided in university. However, there all seem no difference to me at that time and I wasn&#39;t sure what to look for. It wasn&#39;t too long until I met the then head of degree transfer program who I worked part time for. He shared some of his experience, led me to some relevant resources based on my question and what I was trying to achieve. The next thing happened was I picked up my bag and flied to United States myself.&lt;br /&gt;
&lt;br /&gt;
Fast forwarding to a few years ago when I was a manufacturing engineer. I found strong interest in .net and database development during company transitioning to MES (Manufacturing Enterprise System). I was surrounded by engineers and occasionally some IT folks, but I couldn&#39;t seem to able to find a path that would take me to the next level in this new field. Eventually I took an opportunity to become a .Net developer in a small company which allow me also work closely with database.
&lt;br /&gt;
&lt;br /&gt;
I learned as I work, through online tutorial and documentation, books and blogs. Despite the success of replacing company website and internal application with wide acceptance, there wasn&#39;t really anyone I could get help or work with on discussing different development approach, performance improvement methods and other things that would potentially reveal a much efficient and effective way.
&lt;br /&gt;
&lt;br /&gt;
Things started to change when I discovered user group. .NET user group, C# user group, SQL user group, I attended them all as much as I could. There are so many dedicated and passionate folks on the technology and everyone are there to learn, help and share knowledge. Wow!
&lt;br /&gt;
&lt;br /&gt;
Another best thing I came about is the SQL community. I stumbled upon a few blogs and they blew my mind. Folks at BrentOzar unlimited, awesome people at sqlskills and many many more who actively and voluntarily share and help others.&lt;br /&gt;
&lt;br /&gt;
On the awesome community spirit of SQL community, Paul Randal at SQLSkills who recently voted for most desired mentor, is offering to mentor a few people. Personally, I feel that he would be one of the ideal mentor for me. Reasons are some of our similar technical backgrounds, his previous and current involvement as Microsoft employee and manager, community contributor, consultant and a business owner in SQL server field, and last but not least his principles on time management with his 50% traveling and the other half for his family. Although I know a few great resources where I could learn and improve my technical skills, I constantly have questions in some other aspects which keep resurfacing for more decisive direction. Questions like generalist vs specialist, consultant vs employee and others. This may seems like common questions that has been widely addressed, but I am looking into something more specific to my own and some insight from someone in the same field personal experience.&lt;br /&gt;
&lt;br /&gt;
Whoever he picked as mentee would surely be lucky ones!&lt;br /&gt;
&lt;br /&gt;
Although these guys were not my mentors, I have learned so much from them through their blog, videos and interactions. They not only improve my technical skill set, it changes the way I view work and career advancement. Work is so much fun when everyone share their knowledge and make everyone better. &lt;br /&gt;
&lt;br /&gt;
Last note, from personal experience, regardless if a person is in school, workforce, or business, having right mentor play an important role and has great impact to the mentee. By sharing his/her personal experience and valuable insight with guidance and resources, often times broaden the vision and expand the horizon for the mentee which he/she would never thought of. On the other side, it is important for the mentee to willing to open for other opinions and to have some idea of his/her goal and what he/she is trying achieve in order for the mentoring process to be effective.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Update&lt;/b&gt;: &lt;a href=&quot;http://www.sqlskills.com/blogs/paul/mentoring-class-2015/&quot; target=&quot;_blank&quot;&gt;Class of 2015&lt;/a&gt;! I can&#39;t imagine a person (busy consulting owner) volunteers to mentor the whole class! But here I present you, Paul Randal!&lt;br /&gt;
&lt;br /&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/7965098821287716546/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2015/02/one-important-aspect-mentor.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/7965098821287716546'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/7965098821287716546'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2015/02/one-important-aspect-mentor.html' title='One Important Aspect - Mentor'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-1527530178142927237</id><published>2015-02-02T06:00:00.000-06:00</published><updated>2015-03-12T15:58:45.735-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="PowerShell"/><category scheme="http://www.blogger.com/atom/ns#" term="SSIS"/><title type='text'>Execute SSIS remotely - PowerShell</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpHpujfgrts7x0GdxvEEpoXB65fSKBbOdv233D4qgeW2HSfERBf-LQuZCXwbhLAvG3DQnb1kcz6k5Z3Ik6U8u7rYx2B1qASD7ZdXMBKS-7bHlcuohk8hqsX1XR_F5dA4IrJkZJC5DLLCwO/s1600/remotessis.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpHpujfgrts7x0GdxvEEpoXB65fSKBbOdv233D4qgeW2HSfERBf-LQuZCXwbhLAvG3DQnb1kcz6k5Z3Ik6U8u7rYx2B1qASD7ZdXMBKS-7bHlcuohk8hqsX1XR_F5dA4IrJkZJC5DLLCwO/s1600/remotessis.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
Since SQL Server 2012, Microsoft introduce a new way of interacting and storing SSIS package. SSIS packages are frequently executed in scheduling format often through SQL Server agent jobs. Today blog post will be focusing on&amp;nbsp;remotely calling SSIS package stored in this new&amp;nbsp;SSISDB Catalog&lt;b&gt;.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;SSISDB Catalog is the new center point of working with these objects including SSIS Projects, Packages and its parameter and environment. These objects are stored in the SSISDB database. The database is automatically created when Integration Service Catalog is created. Interaction with objects stored in SSISDB Catalog is performed mainly through the SSIS Catalog UI or calling the provided stored procedures in SSISDB.&lt;br /&gt;
&lt;br /&gt;
There are few methods to remotely call the SSIS packages in SSISDB Catalog.&amp;nbsp;One method is through&amp;nbsp;&lt;a href=&quot;https://msdn.microsoft.com/en-us/library/microsoft.sqlserver.management.integrationservices.aspx&quot; target=&quot;_blank&quot;&gt;Microsoft.SqlServer.Management.IntegrationServices&lt;/a&gt; Namespace, or utilize the SSISDB stored procedures. Please beware that executing SSIS stored in&amp;nbsp;msdb, SSIS package store or file system are different.&lt;br /&gt;
&lt;br /&gt;
The below PowerShell example utilize the SSISDB stored procedures. It first executes the SSISDB catalog.create_execution stored procedure with all the parameter values to create an instance of execution, and subsequently executes the catalog.start_execution stored procedure to start the particular execution instance just created.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:powershell&quot;&gt;function Invoke-SSISPackage {
    [CmdletBinding(DefaultParametersetName=&quot;SSPI&quot;)] 
    param(
        [parameter(Mandatory=$true, Position=0)]
        [string]$SQLInstance,

        [parameter(ParameterSetName=&#39;User&#39;, Mandatory=$true, Position=1)]
        [string]$User,

        [parameter(ParameterSetName=&#39;User&#39;, Mandatory=$true, Position=2)]
        [string]$password,

        [parameter(ParameterSetName=&#39;SSPI&#39;, Mandatory=$true, Position=3)]
        [switch]$SSPI,

        [parameter(Mandatory=$true, Position=4)]
        [string]$Folder,

        [parameter(Mandatory=$true, Position=5)]
        [string]$Project,

        [parameter(Mandatory=$true, Position=6)]
        [ValidatePattern(&#39;^.*\.dtsx$&#39;)]
        [string]$Package,

        [parameter(Position=7)]
        [switch]$RunIn32Bit
    )

    $sqlConn = New-Object System.Data.SqlClient.SqlConnection
    $sqlConn.ConnectionString = &quot;Server=$($SQLInstance);`
                                 Database=SSISDB;`
                                 User=$($User);`
                                 Password=$($Password);`
                                 Integrated Security=$( @{$true=&quot;SSPI&quot;; $false=&quot;False&quot;}[$SSPI -eq $true] )&quot; 
    try {
        $sqlConn.Open()

        Write-Host &quot;Creating SSIS execution..&quot; -ForegroundColor &quot;Yellow&quot;

        $sqlCmd = New-Object System.Data.SqlClient.SqlCommand (&quot;[catalog].[create_execution]&quot;, $sqlConn)
        $sqlCmd.CommandType = [System.Data.CommandType]::StoredProcedure
        $sqlCmd.Parameters.AddWithValue(&quot;folder_name&quot;, $Folder) | Out-Null
        $sqlCmd.Parameters.AddWithValue(&quot;project_name&quot;, $Project) | Out-Null
        $sqlCmd.Parameters.AddWithValue(&quot;package_name&quot;, $Package) | Out-Null
        $sqlCmd.Parameters.Add(&quot;use32bitruntime&quot;, [System.Data.SqlDbType]::Bit).Value = $RunIn32Bit.IsPresent
        $sqlCmd.Parameters.Add(&quot;execution_id&quot;, [System.Data.SqlDbType]::BigInt).Direction = [System.Data.ParameterDirection]::Output
        $sqlCmd.ExecuteNonQuery() | Out-Null
        
        [int64]$execID = $sqlCmd.Parameters[&quot;execution_id&quot;].Value 
        $sqlCmd.Dispose()

        Write-Host &quot;&quot;
        Write-Host &quot;Starting SSIS execution..&quot; -ForegroundColor &quot;Yellow&quot;

        $sqlCmd = New-Object System.Data.SqlClient.SqlCommand (&quot;[catalog].[start_execution]&quot;, $sqlConn)
        $sqlCmd.CommandType = [System.Data.CommandType]::StoredProcedure
        $sqlCmd.Parameters.AddWithValue(&quot;execution_id&quot;, $execID) | Out-Null
        $sqlCmd.ExecuteNonQuery() | Out-Null
        
        $sqlCmd.Dispose()
    }
    catch {
        throw
    }
    finally {
        $sqlConn.Dispose()
    }
}
&lt;/pre&gt;
&lt;br /&gt;
Although the example above uses PowerShell, implementing it in other programming language such as C#.NET or VB.NET could be easily followed with minor changes. Please note that the script returns result if execution is started successfully, but not if the SSIS completes its execution successfully.&lt;br /&gt;
&lt;br /&gt;
Hope you find the example script useful.</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/1527530178142927237/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2015/02/execute-ssis-remotely-powershell.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/1527530178142927237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/1527530178142927237'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2015/02/execute-ssis-remotely-powershell.html' title='Execute SSIS remotely - PowerShell'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpHpujfgrts7x0GdxvEEpoXB65fSKBbOdv233D4qgeW2HSfERBf-LQuZCXwbhLAvG3DQnb1kcz6k5Z3Ik6U8u7rYx2B1qASD7ZdXMBKS-7bHlcuohk8hqsX1XR_F5dA4IrJkZJC5DLLCwO/s72-c/remotessis.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-1805727327102455505</id><published>2015-01-07T08:00:00.000-06:00</published><updated>2015-01-07T12:16:32.250-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Backup/Recovery"/><category scheme="http://www.blogger.com/atom/ns#" term="Troubleshooting"/><title type='text'>SQL Server Database File - Date Modified</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZHZaDmQu8whIBJjrKJjrRDgmfae6k3GZvweUXOZUzaCoNZjYM1DJwuh283PYBypYoD8PG2gmViSiQuqA8-weTimE3UW3u502eLD5U_j4n7qKTy4Rb4yDlBIU9g5i-k9aDM7nFW9d58Np_/s1600/datemodified.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZHZaDmQu8whIBJjrKJjrRDgmfae6k3GZvweUXOZUzaCoNZjYM1DJwuh283PYBypYoD8PG2gmViSiQuqA8-weTimE3UW3u502eLD5U_j4n7qKTy4Rb4yDlBIU9g5i-k9aDM7nFW9d58Np_/s1600/datemodified.png&quot; height=&quot;55&quot; width=&quot;200&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
Recently, there was a discussion if the date modified of the database files shown in Windows explorer could be used to determine when the database was last used (or recently used). Often, this date is used to determine when a file (eg. word, excel) is last updated. Could that also be applied to SQL Serve database?&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
Let&#39;s do a few tests to see how the date correlate with the database change.&lt;br /&gt;
&lt;br /&gt;
Create a database called Testing,&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;CREATE DATABASE Testing
ON PRIMARY
(
 NAME = &#39;Testing&#39;,
 FILENAME = &#39;D:\MSSQL12.MSSQL2014\MSSQL\DATA\Testing.mdf&#39;,
 SIZE = 4 MB,
 FILEGROWTH = 8 KB
) 
LOG ON
(
 NAME = &#39;Testing_Log&#39;,
 FILENAME = &#39;D:\MSSQL12.MSSQL2014\MSSQL\DATA\Testing_log.ldf&#39;,
 SIZE = 1 MB
);
&lt;/pre&gt;
&lt;br /&gt;
Here is what it look slike with PowerShell Get-ChildItem cmdlet on the database file folder. LastWriteTime as Date Modified as seen in the explorer. Length as the size (in byte) of the file.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhse8flz4O4AWekznUKZnXKk8UmKM4jG9pQFswFrjFjT7WP2f9AKF6Y7GNX4fmouR0DmC4oedkkuta-sE3WIUC7AZPYYnMwJmqKv_kpjbqGc6pdzpQ34FrB7u55hECOnclyEE1P-BVQPelx/s1600/CreateTableAndInsertData.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhse8flz4O4AWekznUKZnXKk8UmKM4jG9pQFswFrjFjT7WP2f9AKF6Y7GNX4fmouR0DmC4oedkkuta-sE3WIUC7AZPYYnMwJmqKv_kpjbqGc6pdzpQ34FrB7u55hECOnclyEE1P-BVQPelx/s1600/CreateTableAndInsertData.png&quot; height=&quot;52&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
During the database creation, the size of the database files was specified (eg. 4 MB for the MDF data file). However, that doesn&#39;t mean the file has been entirely allocated (or being used). The script below shows the physical size of the database file as well as the size of what actually being used,&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE testing;

SELECT 
 CURRENT_TIMESTAMP AS [time],
 f.name [file_name], 
 f.size * 8192 [file_size_B], 
 FILEPROPERTY(f.name, &#39;SpaceUsed&#39;) * 8192 [Used_size_B]
FROM sys.database_files f;
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtKgIMfvrgKsSTI_3Y7XiMEx2q8enJ8lqgSJd2EuV0pIaF8HOVqRBTw0StmrhLZDJSEz6tmDviPxkhmsDn_CQGFGjx5KAgxuWVyoRwaL1hWKrGsafYrCNugNt2mr7IPMTk7IvivylAHULF/s1600/InitialUsedSize.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtKgIMfvrgKsSTI_3Y7XiMEx2q8enJ8lqgSJd2EuV0pIaF8HOVqRBTw0StmrhLZDJSEz6tmDviPxkhmsDn_CQGFGjx5KAgxuWVyoRwaL1hWKrGsafYrCNugNt2mr7IPMTk7IvivylAHULF/s1600/InitialUsedSize.png&quot; height=&quot;51&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Notice that the data file (MDF) used size is around 2.49 MB out of the 4 MB of the data file. Similarly to the log file.&lt;br /&gt;
&lt;br /&gt;
Now, we going to perform a few task and see what affect the modified date of the database file&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Create table (Data Definition Language, DDL)&lt;/b&gt;&lt;br /&gt;
The script below create a table in the database,&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE Testing;

CREATE TABLE dbo.tbl1
(
 ID int IDENTITY(1,1),
 Col1 nchar(4000) DEFAULT (REPLICATE(&#39;G&#39;, 4000)),
 Col2 char(49) DEFAULT (REPLICATE(&#39;A&#39;, 49))
);&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEic-axzl7oSYNegfzjUb8t1Hrxq3FL9_wOIYg8Feh_6qoJGIZpuR-AG0FJU0fr8Pg1IEoR0mD5zu4zNs-HcUVecif6lo_ZqR0apkz9eY7PwFn6YOCNdHFQvMSK3xRAPmv3cByZsFTHXTXch/s1600/CreateTable.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEic-axzl7oSYNegfzjUb8t1Hrxq3FL9_wOIYg8Feh_6qoJGIZpuR-AG0FJU0fr8Pg1IEoR0mD5zu4zNs-HcUVecif6lo_ZqR0apkz9eY7PwFn6YOCNdHFQvMSK3xRAPmv3cByZsFTHXTXch/s1600/CreateTable.png&quot; height=&quot;49&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Notice that the used size of the log file has increased slightly. Let&#39;s examine the date modified of the database files.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhse8flz4O4AWekznUKZnXKk8UmKM4jG9pQFswFrjFjT7WP2f9AKF6Y7GNX4fmouR0DmC4oedkkuta-sE3WIUC7AZPYYnMwJmqKv_kpjbqGc6pdzpQ34FrB7u55hECOnclyEE1P-BVQPelx/s1600/CreateTableAndInsertData.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhse8flz4O4AWekznUKZnXKk8UmKM4jG9pQFswFrjFjT7WP2f9AKF6Y7GNX4fmouR0DmC4oedkkuta-sE3WIUC7AZPYYnMwJmqKv_kpjbqGc6pdzpQ34FrB7u55hECOnclyEE1P-BVQPelx/s1600/CreateTableAndInsertData.png&quot; height=&quot;52&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Still the time where the database was first created for both data and log file.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Insert Data (Data Manipulation Language, DML)&lt;/b&gt;&lt;br /&gt;
Since the database is created on Simple recovery model (Full recovery model without any backup also behave like Simple recovery model) for this demo, the log file is automatically truncated and reused without growing if the transaction size is smaller than the file size. Because of this, we will only focus on the data file from now on.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;INSERT INTO dbo.tbl1
DEFAULT VALUES;
GO 2
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyPLWabDSY-t9Qrp1q-x4Wr2co_KegWIkIYgYFYFK9WYwbD2fjd7O4dlA2plk8sfqCJHfOTIYrL627xlRUx10g0Je-oOUUL9vFMsPoBJXq4-13HGs5g1Oq-5luwiMvOusT52K9Bp-takH_/s1600/InsertData.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyPLWabDSY-t9Qrp1q-x4Wr2co_KegWIkIYgYFYFK9WYwbD2fjd7O4dlA2plk8sfqCJHfOTIYrL627xlRUx10g0Je-oOUUL9vFMsPoBJXq4-13HGs5g1Oq-5luwiMvOusT52K9Bp-takH_/s1600/InsertData.png&quot; height=&quot;50&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhse8flz4O4AWekznUKZnXKk8UmKM4jG9pQFswFrjFjT7WP2f9AKF6Y7GNX4fmouR0DmC4oedkkuta-sE3WIUC7AZPYYnMwJmqKv_kpjbqGc6pdzpQ34FrB7u55hECOnclyEE1P-BVQPelx/s1600/CreateTableAndInsertData.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhse8flz4O4AWekznUKZnXKk8UmKM4jG9pQFswFrjFjT7WP2f9AKF6Y7GNX4fmouR0DmC4oedkkuta-sE3WIUC7AZPYYnMwJmqKv_kpjbqGc6pdzpQ34FrB7u55hECOnclyEE1P-BVQPelx/s1600/CreateTableAndInsertData.png&quot; height=&quot;52&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The data file used size has increased to around 2.56 MB. However, date modified of the data file still shown as time when the database first created.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Insert large data that require file growth&lt;/b&gt;&lt;br /&gt;
Now, we are going to insert more data that large enough to require the data file growth.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;INSERT INTO dbo.tbl1
DEFAULT VALUES;
GO 207
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2VcpKW691ykxAPkHf1sp_v6pl4w7A9J1loQtsFNyWEr4qP5livHDl1ZRDw3O4dt5YCkfjErRTeWEwQ1ijDfoJMnBcBsRMZ59hEU7CY_nzP0B0a3rc5-ln1qwkHJYFlZhhbHGGaX1LUYgU/s1600/FileGrowSize.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2VcpKW691ykxAPkHf1sp_v6pl4w7A9J1loQtsFNyWEr4qP5livHDl1ZRDw3O4dt5YCkfjErRTeWEwQ1ijDfoJMnBcBsRMZ59hEU7CY_nzP0B0a3rc5-ln1qwkHJYFlZhhbHGGaX1LUYgU/s1600/FileGrowSize.png&quot; height=&quot;49&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoXBcYokMVN9ieGHwXfYfQHXJBLlGGPiIYGdEeosOWQcIwQM2pBvthfhv3dHUQXEyop3llar8Zr3sbBvTfhl_HWS5vRbIJC5IHfUv9sy7UqZDpBuydNnXuLHTnCoi82lYH3wnfu88s4X6i/s1600/FileGrowModifiedDate.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoXBcYokMVN9ieGHwXfYfQHXJBLlGGPiIYGdEeosOWQcIwQM2pBvthfhv3dHUQXEyop3llar8Zr3sbBvTfhl_HWS5vRbIJC5IHfUv9sy7UqZDpBuydNnXuLHTnCoi82lYH3wnfu88s4X6i/s1600/FileGrowModifiedDate.png&quot; height=&quot;49&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The data file has grown to around 4.25 MB (64 KB more from the original size), and the data file modified date is updated.&lt;br /&gt;
&lt;br /&gt;
The database is configured to grow (mean auto grow) with 8 KB. Some of you with eagle eye may notice that during the database creation, 8 KB is specified as file growth rate. However, SQL Server grow by minimum an extent (8 data page (8 KB) = 64 KB).&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Modify database file size&lt;/b&gt;&lt;br /&gt;
The script below change the data file size to 10 MB.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;ALTER DATABASE Testing
MODIFY FILE (
 NAME = &#39;Testing&#39;,
 SIZE = 10MB );
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPb623eR6yOtjg1oectOz2xBl6C7CN930UK95BlUOwg7ZzDDp7Egi9OSEn7bVvxdgk53JYRcYR0dLdIV_JIFvEi8PDdez_YxF1kr3mf-XSRaqTJZQD7c8x2sAF2ObCEKowmmRu4CSd8OYG/s1600/AlterSize.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPb623eR6yOtjg1oectOz2xBl6C7CN930UK95BlUOwg7ZzDDp7Egi9OSEn7bVvxdgk53JYRcYR0dLdIV_JIFvEi8PDdez_YxF1kr3mf-XSRaqTJZQD7c8x2sAF2ObCEKowmmRu4CSd8OYG/s1600/AlterSize.png&quot; height=&quot;47&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWlUlHVJdT_HUqG5yo2oH9UqgcHriQM0chrCBz_fvTLpRuuQy7TTFYkQTuQlf_lkdw8Pmg64GDG0ddC_xkf8ff6sQaKOSgA-4R4zCMxCB0-TCiSOAnu5Ph6WFtUQ8tvpkxiZBIUpo2h5fl/s1600/AlterSizeModifiedDate.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWlUlHVJdT_HUqG5yo2oH9UqgcHriQM0chrCBz_fvTLpRuuQy7TTFYkQTuQlf_lkdw8Pmg64GDG0ddC_xkf8ff6sQaKOSgA-4R4zCMxCB0-TCiSOAnu5Ph6WFtUQ8tvpkxiZBIUpo2h5fl/s1600/AlterSizeModifiedDate.png&quot; height=&quot;56&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The modified date of the data file has been updated.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Shrink database file&lt;/b&gt;&lt;br /&gt;
Below is the script to shrink the data file to 6 MB. Some data was added before the shrink task just to show different used size.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;INSERT INTO dbo.tbl1
DEFAULT VALUES;
GO 10

DBCC SHRINKFILE (&#39;Testing&#39;, 6);
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9XKUnQhDG9-V237e-sADA3XliDqrt-Yd6QaIouclO08tk4qihlvqFtvskDDYdm0aa6at7ZR-wQlrd2oPAfbr3pIfN-Supx2vMLqLTDOxshTvWaWrlvMXEt47UK9i4OrdEDxno6CsioLrE/s1600/ShrinkSize.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9XKUnQhDG9-V237e-sADA3XliDqrt-Yd6QaIouclO08tk4qihlvqFtvskDDYdm0aa6at7ZR-wQlrd2oPAfbr3pIfN-Supx2vMLqLTDOxshTvWaWrlvMXEt47UK9i4OrdEDxno6CsioLrE/s1600/ShrinkSize.png&quot; height=&quot;52&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgaJeeMtje6qK3QoL7NPbMhwaXroM51YxGLwlXvzwkRPR9x4qALgr0P3W2sYneXq0DUhvFSLEcdsgUtOjjSGtdrcoE2l0Xt1lXjYXx71f2Xe7u_itWeUZlL2_D-E3usqqTlR0W960xFyOe/s1600/ShrinkSizeModifiedDate.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgaJeeMtje6qK3QoL7NPbMhwaXroM51YxGLwlXvzwkRPR9x4qALgr0P3W2sYneXq0DUhvFSLEcdsgUtOjjSGtdrcoE2l0Xt1lXjYXx71f2Xe7u_itWeUZlL2_D-E3usqqTlR0W960xFyOe/s1600/ShrinkSizeModifiedDate.png&quot; height=&quot;50&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The data file modified date reflect the time the shrink action was performed.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Database Backup&lt;/b&gt;&lt;br /&gt;
Below script perform a full backup. Some data is also added prior just for the sake of it.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;INSERT INTO dbo.tbl1
DEFAULT VALUES;
GO 10

BACKUP DATABASE Testing
TO DISK = &#39;Testing.bak&#39;;&lt;/pre&gt;
&lt;br /&gt;
Nothing change in data file size and its used size. Data file date modified remain the same as previous time.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Auto Close&lt;/b&gt;&lt;br /&gt;
By default, this auto_close database setting is set to false. As best practice, this should be false. Turning on this setting make SQL Server close the database after the last disconnected connection to the database. This is not recommended in practice as it takes time to re-obtain resources when it reopen the database.&lt;br /&gt;
&lt;br /&gt;
The script below set the Auto_Close setting to true&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;ALTER DATABASE Testing
SET AUTO_CLOSE ON;
&lt;/pre&gt;
&lt;br /&gt;
In another windows,&lt;br /&gt;
Using &lt;code&gt;sp_who2&lt;/code&gt;, we identify and kill all the connection to Testing database,&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvrtJKXXxjOOECOuFfYtyDldQCn8iQp8U719qBcqXEKQt8nA4YiTKJHebbJOtEH2EJQnRCGU92c7C7BbF1ic0qFm0LyP1cxGn5u-cDOrvQw1MeH2Jl8AunPO_DV2xMvohpBmIoMGUmSfM-/s1600/OpenConnection.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvrtJKXXxjOOECOuFfYtyDldQCn8iQp8U719qBcqXEKQt8nA4YiTKJHebbJOtEH2EJQnRCGU92c7C7BbF1ic0qFm0LyP1cxGn5u-cDOrvQw1MeH2Jl8AunPO_DV2xMvohpBmIoMGUmSfM-/s1600/OpenConnection.png&quot; height=&quot;22&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;KILL 56
KILL 57
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi78ScHYydBAHN6SFruoqsnvNIfMPnVxmkMIiQz19mxzVCrDqBViHuzI4GRTdSMA55QhYhnFBJleFXyb137YVJS6W2x3RkVS7Hgvlk5_5_rJ-VxzlDNyPm0v8ZmMWZnFun17aRhMaQOr4AJ/s1600/AutoClose.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi78ScHYydBAHN6SFruoqsnvNIfMPnVxmkMIiQz19mxzVCrDqBViHuzI4GRTdSMA55QhYhnFBJleFXyb137YVJS6W2x3RkVS7Hgvlk5_5_rJ-VxzlDNyPm0v8ZmMWZnFun17aRhMaQOr4AJ/s1600/AutoClose.png&quot; height=&quot;54&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Both data and log file date modified are updated due to the auto close.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Taking database offline&lt;/b&gt;&lt;br /&gt;
Below script take the database offline.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;ALTER DATABASE Testing
SET OFFLINE
WITH ROLLBACK IMMEDIATE;
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbb1WYahE41o8H2khM9o0g_4VkGfOVmAL5p-u2MNoCX7wYpCL6bXcFKl4S3AqpiSaKf7QoksotdvJVNuzTwDGK95_iiClv37yVZVHUpNEGZjzoAPxEQLEjlGGOgvRaQxXDLR_6hDai7D7y/s1600/TurnOfflineModifiedDate.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgbb1WYahE41o8H2khM9o0g_4VkGfOVmAL5p-u2MNoCX7wYpCL6bXcFKl4S3AqpiSaKf7QoksotdvJVNuzTwDGK95_iiClv37yVZVHUpNEGZjzoAPxEQLEjlGGOgvRaQxXDLR_6hDai7D7y/s1600/TurnOfflineModifiedDate.png&quot; height=&quot;55&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The date modified for both data and log file are updated upon this offline action.&lt;br /&gt;
&lt;br /&gt;
We do another test to turn the database back online and without any changes, take the database offline.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;ALTER DATABASE Testing
SET ONLINE;

ALTER DATABASE Testing
SET OFFLINE
WITH ROLLBACK IMMEDIATE;
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYBoDGY_oHPprnE-YE-YCP7DWblMKLZcrt1MKqkZviHxgK73P9dUJwzdHrj29C3afvFw35C2y7zj3qV-Jk8RF8jB5cYqqWB2Ct_UwYtcS7rbfa169Xzcx_6EohEF7vGR40BRMtJC2Bhp9z/s1600/TurnOfflineWithoutNewData.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYBoDGY_oHPprnE-YE-YCP7DWblMKLZcrt1MKqkZviHxgK73P9dUJwzdHrj29C3afvFw35C2y7zj3qV-Jk8RF8jB5cYqqWB2Ct_UwYtcS7rbfa169Xzcx_6EohEF7vGR40BRMtJC2Bhp9z/s1600/TurnOfflineWithoutNewData.png&quot; height=&quot;51&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The date modified also updated upon the last database offline action.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Stop SQL Server service&lt;/b&gt;&lt;br /&gt;
The testing database is brought online. Stop the SQL Server service from SQL Server Configuration Manager, both database files date modified are updated.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1_1-dMPpwFYkINOunRNYGlc7cYrFRNXHNq9CEO9lRRVQJc8ivk2fXSXBujX8Rk_8m2ddPy6xwe9vD6qv2JMamHsqEQgQKaOWw1CIB40DcinSKSMuxE3Vw_cGObn4uo-BYA8jO8qZ6Mbra/s1600/StopSQLService.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1_1-dMPpwFYkINOunRNYGlc7cYrFRNXHNq9CEO9lRRVQJc8ivk2fXSXBujX8Rk_8m2ddPy6xwe9vD6qv2JMamHsqEQgQKaOWw1CIB40DcinSKSMuxE3Vw_cGObn4uo-BYA8jO8qZ6Mbra/s1600/StopSQLService.png&quot; height=&quot;55&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;Observation&lt;/b&gt;&lt;br /&gt;
The database files date modified is updated due to these events,&lt;br /&gt;
&lt;br /&gt;
1) &lt;b&gt;Physical file growth&lt;/b&gt; - Alter statement, file auto grow&lt;br /&gt;
2) &lt;b&gt;Physical file shrink&lt;/b&gt; - Shrink command (eg. DBCC SHRINKFILE, DBCC SHRINKDABASE), auto shrink (not tested in this demo, but same concept applied)&lt;br /&gt;
3) &lt;b&gt;Database offline&lt;/b&gt; - Alter statement, SQL Server service&lt;br /&gt;
&lt;br /&gt;
For the first two observations, the key word is that only physical file size change will update the date modified. Data records are stored in the data pages within the data file. The database engine continue to allocate new data to unallocated data page in the existing data file until it run out of unallocated page, and request/perform a physical file growth which then trigger the update on date modified property.&lt;br /&gt;
&lt;br /&gt;
In this demo, it is not obvious on the log file because the database is behaving as Simple recovery model which it will automatically reuse the virtual log file (smaller logical log file within the log file) without growing if the transaction is small enough within the size of the log file. If it is on Full recovery model, similar date modified concept discussed apply to log file as well.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Conclusion&lt;/b&gt;&lt;br /&gt;
The date modified of the database files are not a good indicator to determine the recency of the database. The database could be actively being used and the date modified is dated a while back. It is possible that database file with plenty of unallocated space within could continue to allocate new data without triggering database file date modified property to be updated for a long time.&lt;br /&gt;
&lt;br /&gt;
Here are few suggestions without additional setup (eg. trace or audit) to determine if the database is recently used. Each of these method has its pro and con,&lt;br /&gt;
&lt;br /&gt;
- sp_who2 or sp_whoisactive to see if any current or sleeping connection on the database&lt;br /&gt;
- Query the&amp;nbsp;sys.dm_db_index_usage_stats dmv to examine the most recent seek, scan, lookup, and update of database table / index.&lt;br /&gt;
- Database backup size from backup history table (msdb.dbo.backupset) to see any size change historically.</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/1805727327102455505/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2015/01/sql-server-database-file-date-modified.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/1805727327102455505'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/1805727327102455505'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2015/01/sql-server-database-file-date-modified.html' title='SQL Server Database File - Date Modified'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZHZaDmQu8whIBJjrKJjrRDgmfae6k3GZvweUXOZUzaCoNZjYM1DJwuh283PYBypYoD8PG2gmViSiQuqA8-weTimE3UW3u502eLD5U_j4n7qKTy4Rb4yDlBIU9g5i-k9aDM7nFW9d58Np_/s72-c/datemodified.png" height="72" width="72"/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-5090748117510616511</id><published>2015-01-04T23:30:00.000-06:00</published><updated>2015-01-04T23:31:30.507-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Self Learning"/><title type='text'>Microsoft Specialist - Implementing Microsoft Azure Architecture Solutions</title><content type='html'>I have been using Microsoft Azure with credit from MSDN subscription (Free credit! See the MSDN subscription section on this &lt;a href=&quot;http://www.travisgan.com/2014/01/sql-server-licensing-test-environment.html&quot; target=&quot;_blank&quot;&gt;post&lt;/a&gt;) for sometimes now. Mostly for VMs and SQL Database (Microsoft Azure SQL Database). Recently I have also started exploring other Azure services like Azure Active Directory for cloud identity and access management, as well as deploying and implementing .NET web application to Azure Websites along with Visual Studio Online.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;Back in October 2014, Born To Learn (Microsoft Training and Certification Community) blogged about some &lt;a href=&quot;https://borntolearn.mslearn.net/b/weblog/archive/2014/10/16/get-certified-on-microsoft-azure-or-office-365-with-free-exams&quot; target=&quot;_blank&quot;&gt;free voucher&lt;/a&gt; to get certified for Microsoft Azure (The offer has ended). I was lucky enough to get the free voucher. In addition, there was a week long Azure IaaS for IT Pros Online Event on the first week on December 2014. There were multiple experts sharing some technical insights which could be helpful on this particular certification. If you missed the live event, the videos are also available &lt;a href=&quot;http://channel9.msdn.com/Events/Microsoft-Azure/Level-Up-Azure-IaaS-for-IT-Pros&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
Below are the technical tasks measured for the Implementing Microsoft Azure Infrastructure Solutions &lt;a href=&quot;https://www.microsoft.com/learning/en-us/exam-70-533.aspx&quot; target=&quot;_blank&quot;&gt;certification&lt;/a&gt;,&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT0LRP3O4Zw7X8IQqvt-OntxFfiIA53SG5uplh8XhLBKOKDmA7G5Y1MTiK8goBdczEopSUAXQetCd1wXf6BcJKdAkmbjbNAb3zLGNELpcBseQbwzvInTVhxZqQ1oFJohovqlzc9ECzwBz5/s1600/70-533.PNG&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT0LRP3O4Zw7X8IQqvt-OntxFfiIA53SG5uplh8XhLBKOKDmA7G5Y1MTiK8goBdczEopSUAXQetCd1wXf6BcJKdAkmbjbNAb3zLGNELpcBseQbwzvInTVhxZqQ1oFJohovqlzc9ECzwBz5/s1600/70-533.PNG&quot; height=&quot;255&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
With all these great incentive, I thought this might be the great opportunity for me to widen / deepen my understanding on the Microsoft Azure platform like the details of how virtual network is being implemented and different type of storage and its relevant performance.&lt;br /&gt;
&lt;br /&gt;
Although some times I have people downplayed the benefit and validity of certification,&amp;nbsp;I encourage people to get certified.&amp;nbsp;Out of many benefits as listed on this &lt;a href=&quot;https://www.microsoft.com/learning/en-us/certification-testimonials.aspx&quot; target=&quot;_blank&quot;&gt;Microsoft learning page&lt;/a&gt;, personally, one of the main reason is while preparing for the exam, I am often exposed to part and piece of some feature of a technology I don&#39;t usually work on, and sometimes not aware of. This give me an opportunity to verify my skills as well as to learn new knowledge in depth while exploring it. I always emphasize that passing an exam is not the end, but rather a part of the journey of getting better understanding of a technology.&lt;br /&gt;
&lt;br /&gt;
I passed the exam and is certified as Microsoft Specialist for Implementing Microsoft Azure Architecture Solutions. Microsoft Azure has been increasing its collection of services with new improvements and features at a very fast pace. So, learning continue, just like &lt;a href=&quot;http://www.travisgan.com/2013/04/learning-continue.html&quot;&gt;this&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-9s2uqrylErQyNDo5Np-QrUczLLxLvsrHd37QHHO_A-g37-R36PhaDx_4dRoGn5-tXw55wYW7Pt5lUXT6DuB05pimnT0IBzZHGOXd5t-jghbNURKWRhDVdsBNANsC1dwh3bAnhNhTFF_U/s1600/MS_2013(rgb)_2617.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-9s2uqrylErQyNDo5Np-QrUczLLxLvsrHd37QHHO_A-g37-R36PhaDx_4dRoGn5-tXw55wYW7Pt5lUXT6DuB05pimnT0IBzZHGOXd5t-jghbNURKWRhDVdsBNANsC1dwh3bAnhNhTFF_U/s1600/MS_2013(rgb)_2617.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/5090748117510616511/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2015/01/microsoft-specialist-implementing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/5090748117510616511'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/5090748117510616511'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2015/01/microsoft-specialist-implementing.html' title='Microsoft Specialist - Implementing Microsoft Azure Architecture Solutions'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT0LRP3O4Zw7X8IQqvt-OntxFfiIA53SG5uplh8XhLBKOKDmA7G5Y1MTiK8goBdczEopSUAXQetCd1wXf6BcJKdAkmbjbNAb3zLGNELpcBseQbwzvInTVhxZqQ1oFJohovqlzc9ECzwBz5/s72-c/70-533.PNG" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-1519887682847615138</id><published>2014-12-01T10:03:00.001-06:00</published><updated>2014-12-01T10:03:49.166-06:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="SSIS"/><title type='text'>SSIS Connection Manager Properties</title><content type='html'>The SSIS packages stored in SSIS Catalog (introduced in SQL Server 2012, and also SQL Server 2014) allow configuration changes made on the project or the package connection managers, depending how the connection was setup. It is important to have a good understanding of how the values in these properties are being used.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;I can&#39;t find any documentation on how these SSIS connection manager properties affect the actual connection value. In this post, Here is my observation on how these properties changes affect the actual value used during the execution.&lt;br /&gt;
&lt;br /&gt;
Here is the SSIS connection manager in the designer (in this case Visual Studio with BI)&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzORcgP6ugdTRwfpu8H4W-WbcU5mOqy-EdUNqn8i1bykvWShHdl6w4zj-eXgMEQymb0TJO5Ra0aGJvQrg5oG7wcxQJwURYLqe2JkVTwrN4SpzQepwpJLZmJ_kgKKydylRwL3E0j_EIIJKr/s1600/DesignerSSIS.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzORcgP6ugdTRwfpu8H4W-WbcU5mOqy-EdUNqn8i1bykvWShHdl6w4zj-eXgMEQymb0TJO5Ra0aGJvQrg5oG7wcxQJwURYLqe2JkVTwrN4SpzQepwpJLZmJ_kgKKydylRwL3E0j_EIIJKr/s1600/DesignerSSIS.png&quot; height=&quot;365&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
This is the connection manager property setting on the deployed package in SQL Server SSIS Catalogs.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJ4IZ2C5IjBKW0f9GCa2m_bymt39oW_kYC2h5su7uybynkLb2v3vyzChiPiqEjSqueRkR-wVExNa3U4YFSpxFRMhymu_NquVvji3YH2zqr8jmQca5vAGhNwxRn-sNH0DwZBME1hq5kkcJi/s1600/connproperties.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJ4IZ2C5IjBKW0f9GCa2m_bymt39oW_kYC2h5su7uybynkLb2v3vyzChiPiqEjSqueRkR-wVExNa3U4YFSpxFRMhymu_NquVvji3YH2zqr8jmQca5vAGhNwxRn-sNH0DwZBME1hq5kkcJi/s1600/connproperties.png&quot; height=&quot;223&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
It seems the ConnectionString value is generated based on the setting configured at the designer. Other properties (eg. ServerName and InitialCatalog) values appears to be similarly mapped, but not all the property like integrated security as SSPI.&lt;br /&gt;
&lt;br /&gt;
Looking at this example, the connection string property, along with other properties appear to be serving the similar purpose. Let&#39;s look at the detail. Click the &#39;...&#39; on the right of the property show the different options,&lt;br /&gt;
&lt;br /&gt;
1) Using the default value from package (not editable)&lt;br /&gt;
2) Edit value. new value could be entered here. This value is stored in the SQL Server.&lt;br /&gt;
3) Use environment value. If environment have been setup for the project, the value of the property could be mapped to the value setup in the environment value.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiA7m4el1SAhgMl6iTsIjjnnZfAy7A6h-MHcKd2HNDr1ChePkoSUG4jwIFGsTG4juu8iaZQmHmuCSZpwnKyMXmp6QPC1AvmExa573pKFW1PH2P1oEHEKP2xTlQOVK1TY39_CHBqI9vnafUi/s1600/ParameterValue.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiA7m4el1SAhgMl6iTsIjjnnZfAy7A6h-MHcKd2HNDr1ChePkoSUG4jwIFGsTG4juu8iaZQmHmuCSZpwnKyMXmp6QPC1AvmExa573pKFW1PH2P1oEHEKP2xTlQOVK1TY39_CHBqI9vnafUi/s1600/ParameterValue.png&quot; height=&quot;375&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Let&#39;s perform a few testings to see how changing these properties affect the connection values being used during execution.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;Preparation&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;u&gt;&lt;br /&gt;&lt;/u&gt;&lt;/b&gt;
I designed a simple SSIS package that obtain the server, database and login information, and store them in a table for examination.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_lEaO03HesUTE6pceut0Eepc5qCakYbWz9Iz6C6nqEqIV3PQ-ZbkaqUvgHTrWvK2Lvud7RVeNgr97k_JHRmkzyLVegXaEMpbK09uorlHlC90a5JeVvFZ-HhXmRDsUGkgG9UtD5HCdpzrz/s1600/SSISDesign.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_lEaO03HesUTE6pceut0Eepc5qCakYbWz9Iz6C6nqEqIV3PQ-ZbkaqUvgHTrWvK2Lvud7RVeNgr97k_JHRmkzyLVegXaEMpbK09uorlHlC90a5JeVvFZ-HhXmRDsUGkgG9UtD5HCdpzrz/s1600/SSISDesign.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
For the Get Session Info task, it query the connection server name, database name and the login.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV66L223w5gdr4eXZHQw_vk8qOQcleO5DJi-PuhYxE_erXMEZlDLg07XsRuQ0Fzy5yZUntuUFwUOD5yqmmP-HgTr_thTxzDTWtFdtM537G4FOmFARpU5Bt6N8ghxhOlrWevHPfBby7NQYs/s1600/GetInfo.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV66L223w5gdr4eXZHQw_vk8qOQcleO5DJi-PuhYxE_erXMEZlDLg07XsRuQ0Fzy5yZUntuUFwUOD5yqmmP-HgTr_thTxzDTWtFdtM537G4FOmFARpU5Bt6N8ghxhOlrWevHPfBby7NQYs/s1600/GetInfo.png&quot; height=&quot;191&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Store Session Info task pretty much store the information into a table.&lt;br /&gt;
&lt;br /&gt;
Once the package is ready, it is deployed to the SSIS Catalogs for testing&lt;br /&gt;
&lt;br /&gt;
&lt;u&gt;First Test&lt;/u&gt;&lt;br /&gt;
Change the ServerName value to TestServer, the ServerName value font is in&amp;nbsp;&lt;b&gt;bold&lt;/b&gt;. Notice that the Data Source value in the connection string still Server1.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPE5pmj-lp7_HlgeMSXq9FqBjIZedJ6ugaqVmt4mh1IqlLJyg8PUIc0P-uS5M6l3cnOyu-VQEOoRAhVUBDQ0-H3Pw2WkOGHPCl0yQ0SNbHQ2L7VsE2u3Z5sNLCbH8UMUMx_dSdYVMrVP_E/s1600/servername.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPE5pmj-lp7_HlgeMSXq9FqBjIZedJ6ugaqVmt4mh1IqlLJyg8PUIc0P-uS5M6l3cnOyu-VQEOoRAhVUBDQ0-H3Pw2WkOGHPCl0yQ0SNbHQ2L7VsE2u3Z5sNLCbH8UMUMx_dSdYVMrVP_E/s1600/servername.png&quot; height=&quot;220&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
Execution shows the server used is &lt;b&gt;&lt;i&gt;TestServer&lt;/i&gt;&lt;/b&gt;. Similar test shows that execution pick up the &lt;b&gt;edited&lt;/b&gt; InitialCatalog property (database)&amp;nbsp;&lt;b&gt;value&lt;/b&gt; as well.&lt;br /&gt;
&lt;br /&gt;
&lt;u&gt;Second Test&lt;/u&gt;&lt;br /&gt;
With ServerName property value edited as TestServer, now we change the Data Source value of the ConnectionString value to ProdServer.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid_AxmDEfQlV5ZRX3U2siYnkCgUM_zV9r-c7Cm2nMmOIGlz_c-YVN-nGlUzhiJhyVFz1s7f9jS82Iezwe51DiHM-jY22JUNEFLco1Sdq5hp3lZkmhMrkUrlrXFEmwEijbuse-9_5W6YVSt/s1600/connservername.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid_AxmDEfQlV5ZRX3U2siYnkCgUM_zV9r-c7Cm2nMmOIGlz_c-YVN-nGlUzhiJhyVFz1s7f9jS82Iezwe51DiHM-jY22JUNEFLco1Sdq5hp3lZkmhMrkUrlrXFEmwEijbuse-9_5W6YVSt/s1600/connservername.png&quot; height=&quot;222&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
Execution shows the server used is &lt;b&gt;&lt;i&gt;TestServer&lt;/i&gt;&lt;/b&gt;. In this case, it seems like the ServerName property &lt;b&gt;edited value&lt;/b&gt; takes precedence.&lt;br /&gt;
&lt;br /&gt;
&lt;u&gt;Third Test&lt;/u&gt;&lt;br /&gt;
The original (unedited) connection string is this,&lt;br /&gt;
Data Source=Server1;Initial Catalog=master;Provider=SQLNCLI11.1;&lt;b&gt;Integrated Security=SSPI&lt;/b&gt;;Auto Translate=False;&lt;br /&gt;
&lt;br /&gt;
Notice the connection string has the SSPI specified.&lt;br /&gt;
&lt;br /&gt;
Change the UserName property value to TestUser and add a Password to the Password property.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipvFBFRe8G7CK4UnKnl1aXIZIEo6LyGrygh1NWd4ZwOO17pW-LtyL0xUUy0QEUMur9ESyyOct4LUIsHTEOPjWMlXwNrKkbAETS4LGJkpcP3t2cA6iEWBC9mAv8K0ghZY1VsvfnKTw4wEb8/s1600/userpasswor.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipvFBFRe8G7CK4UnKnl1aXIZIEo6LyGrygh1NWd4ZwOO17pW-LtyL0xUUy0QEUMur9ESyyOct4LUIsHTEOPjWMlXwNrKkbAETS4LGJkpcP3t2cA6iEWBC9mAv8K0ghZY1VsvfnKTw4wEb8/s1600/userpasswor.png&quot; height=&quot;222&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Executions shows the SSPI is used. In this case, seems like the SSPI take precedence.&lt;br /&gt;
&lt;br /&gt;
&lt;u&gt;Fourth test&lt;/u&gt;&lt;br /&gt;
Remove the SSPI, add user and password on the connection string,&lt;br /&gt;
Data Source=Server1;Initial Catalog=master;Provider=SQLNCLI11.1;&lt;b&gt;User Name=User1; Password = CrazyPassword;&lt;/b&gt;&amp;nbsp;Auto Translate=False;&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg26bGmLcI8lb4EzIkAuUInLOdCQWeYAzGBrD7TFzOkBtcwm7PlDpA-_SqcwEmnoyaxsRovsjxVI3HuKzrMTaXhlIu5-ceLLYnCBN5Zs41MKRL81Pe1xroB040_Jl7zpG5Guw0hmenxUIoU/s1600/connuserpassword.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg26bGmLcI8lb4EzIkAuUInLOdCQWeYAzGBrD7TFzOkBtcwm7PlDpA-_SqcwEmnoyaxsRovsjxVI3HuKzrMTaXhlIu5-ceLLYnCBN5Zs41MKRL81Pe1xroB040_Jl7zpG5Guw0hmenxUIoU/s1600/connuserpassword.png&quot; height=&quot;222&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Execution shows &lt;b&gt;&lt;i&gt;TestUser&lt;/i&gt;&lt;/b&gt; from User property and its Password value (&lt;b&gt;&lt;i&gt;CrazyPassword&lt;/i&gt;&lt;/b&gt;) are being used. These two properties take precedence when there is no SSPI specified in the connection string property.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;u&gt;Observation&lt;/u&gt;&lt;/b&gt;&lt;br /&gt;
It appears that the &lt;b&gt;edited&lt;/b&gt; Server, InitialCatalog, UserName and Passoword property values take precedence over the values in ConnectionString. However, SSPI takes precedence wherever it is specified in the connection regardless of the value of the UserName and Password edited value specified in UserName and Password property.&lt;br /&gt;
&lt;br /&gt;
Based on this observation, if these properties value are different from the designer value (default value), we could change the ConnectionString property value to reflect the change, or we could also change these value directly and possibly clearer on the respective property. When I say directly, I mean changing a specific property (eg. only the server value), without changing the entire connection string. Apparently this is when SSPI is not specified.&lt;br /&gt;
&lt;br /&gt;
This also applicable when using environment variable. If we previously have an environment setup with its variables defined.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrCNZrbHL92U7TtnjCm9Fjyx63nKUi_p_GzS5NgGqw5zp5sCO9aG-qSZ-pTNogGA0uUYZAeqZ2zW1ff9eAYPjnG3xpmPirZCa4G-61VXdutRtoIP6eYZ7l0Ej7l9YLraok94fO_oXYjStl/s1600/EnvVariables.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrCNZrbHL92U7TtnjCm9Fjyx63nKUi_p_GzS5NgGqw5zp5sCO9aG-qSZ-pTNogGA0uUYZAeqZ2zW1ff9eAYPjnG3xpmPirZCa4G-61VXdutRtoIP6eYZ7l0Ej7l9YLraok94fO_oXYjStl/s1600/EnvVariables.png&quot; height=&quot;125&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
We could assign the ServerName property to use the environment value. Notice the property value now is &lt;u&gt;underlined&lt;/u&gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid5imOKK24DMtUdNpV68XgRGHVhMZBbXKu37WMLg_86QkyW3T70gQHH4loRiqHE0KnmUbtco7Kz8unxLI3zbmj-tGf1FFFuH3YkJQD6wg9FL8V197hkjrbbKJ13wa2oojWusLqR_aTJHzF/s1600/ConnPropertiesEnv.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid5imOKK24DMtUdNpV68XgRGHVhMZBbXKu37WMLg_86QkyW3T70gQHH4loRiqHE0KnmUbtco7Kz8unxLI3zbmj-tGf1FFFuH3YkJQD6wg9FL8V197hkjrbbKJ13wa2oojWusLqR_aTJHzF/s1600/ConnPropertiesEnv.png&quot; height=&quot;236&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
This is often the case when developer design the SSIS with connection pointing to developer database servers (sometimes different database as well), and these properties need to be modified when the packages are deployed into production environment that are to be pointed to production servers and databases, with different user account credential. With this clarification, we know where and how to change the connection configuration correctly and efficiently.</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/1519887682847615138/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2014/12/ssis-connection-manager-properties.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/1519887682847615138'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/1519887682847615138'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2014/12/ssis-connection-manager-properties.html' title='SSIS Connection Manager Properties'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzORcgP6ugdTRwfpu8H4W-WbcU5mOqy-EdUNqn8i1bykvWShHdl6w4zj-eXgMEQymb0TJO5Ra0aGJvQrg5oG7wcxQJwURYLqe2JkVTwrN4SpzQepwpJLZmJ_kgKKydylRwL3E0j_EIIJKr/s72-c/DesignerSSIS.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-7461132448512924440</id><published>2014-06-12T13:16:00.001-05:00</published><updated>2014-06-13T09:14:53.645-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Troubleshooting"/><title type='text'>Defunct File Cause SQL Server Upgrade Failure</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5fmnQKzor45GtxVLmqGjpVmSCY7F9EnMBKNgPSA5ecSFwluQAPiq8G0imWHOO0GW8nsN5kYUslLqtyfuq_reEnNtI0d_GVitJnBEggvkEPO1T4W8XwAV-JSSKolNw6HeVRGMGu8J7A1-B/s1600/upgradebroken.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5fmnQKzor45GtxVLmqGjpVmSCY7F9EnMBKNgPSA5ecSFwluQAPiq8G0imWHOO0GW8nsN5kYUslLqtyfuq_reEnNtI0d_GVitJnBEggvkEPO1T4W8XwAV-JSSKolNw6HeVRGMGu8J7A1-B/s1600/upgradebroken.jpg&quot; height=&quot;100&quot; width=&quot;125&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
In the previous &lt;a href=&quot;http://www.travisgan.com/2014/06/sql-server-defunct-filegroup-and.html&quot; target=&quot;_blank&quot;&gt;post&lt;/a&gt;, we discussed how a data file or its filegroup becomes defunct. As making data file defunct may be desirable in certain situation to resolve immediate issue and allow the database continue to be operational, you may encounter some surprise problem when you are ready to move up to the next version during the SQL Server upgrade.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;As shown in the previous post that in a situation where a data file is missing or could not be brought online, there is no backup available and has been determined that the data resides in the data file is no longer needed and does not need to be restored, one way to resolve this is making the file/filegroup defunct allows certain database operations like backup to be completed successfully.&lt;br /&gt;
&lt;br /&gt;
Warning. The only way to bring back offline or defunct file online is restore from a backup that was taken prior to making the data file offline/defunct.&lt;br /&gt;
&lt;br /&gt;
As making a data file/filegroup defunct resolve immediate issue, however it may generate problem during SQL Server upgrade. Let&#39;s see some example. First we create a database with primary and secondary filegroup on a SQL Server 2008 instance,&lt;br /&gt;
&lt;br /&gt;
Note: If you have performed the steps as in the previous blog &lt;a href=&quot;http://www.travisgan.com/2014/06/sql-server-defunct-filegroup-and.html&quot; target=&quot;_blank&quot;&gt;post&lt;/a&gt;, you may need to delete the example database (testdb), its data files and backups for the steps below.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE master;
GO

--Create database
CREATE DATABASE testdb
ON PRIMARY
( NAME = testdb_data, 
 FILENAME = &#39;D:\MSSQL10_50.MSSQL2008R2\MSSQL\Data\testdb.mdf&#39; ),
FILEGROUP FG1
( NAME = testdb_file1, 
 FILENAME = &#39;D:\MSSQL10_50.MSSQL2008R2\MSSQL\Data\testdb_file1.ndf&#39; )
LOG ON 
( NAME = testdb_log, 
 FILENAME = &#39;D:\MSSQL10_50.MSSQL2008R2\MSSQL\Data\testdb_log.ldf&#39; );
GO
&lt;/pre&gt;
&lt;br /&gt;
Creates a table on Primary filegroup and index on the secondary filegroup&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE testdb;
GO

--Create table on primary filegroup
CREATE TABLE tbl1
(
 col1 int IDENTITY (1,1) CONSTRAINT PK_tbl1_col1 PRIMARY KEY,
 col2 varchar(50)
);
GO

--Create index on FG1 filegroup
CREATE INDEX IX_tbl1_col2
ON tbl1
(
 col2
) ON FG1;
GO

--Insert data into table and index
INSERT INTO tbl1
VALUES (&#39;ABC&#39;);
GO&lt;/pre&gt;
&lt;br /&gt;
At the moment, the database has all the data file online. Let&#39;s backup the database for later use.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;--Backup for later use
BACKUP DATABASE testdb
TO DISK = &#39;testdb_good_backup.bak&#39;;&lt;/pre&gt;
&lt;br /&gt;
Make the secondary data file defunct.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;--Make the data file defunct
ALTER DATABASE testdb
MODIFY FILE 
( NAME = testdb_file1,
 OFFLINE );
GO

ALTER DATABASE testdb
REMOVE FILEGROUP FG1;
GO&lt;/pre&gt;
&lt;br /&gt;
This query below shows that the data file is now in defunct state&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;SELECT 
 f.name file_group, 
 d.name file_name, 
 d.state_desc file_state
FROM sys.filegroups f
JOIN sys.database_files d
 ON f.data_space_id = d.data_space_id;
GO&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkJm19wsuYG4_BFqYL0S7d1EiFWjShnW1-qOOX5jPBYBYrpAOZ_OkickHPnmht5KGcbPC4PpV7jSuWGXBh3MaQSRbFnAqwJwJIT7hPWOicdNCXEpGCYZjSJAcRppsttUCybWgD9TgVBfsR/s1600/file_defunct.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkJm19wsuYG4_BFqYL0S7d1EiFWjShnW1-qOOX5jPBYBYrpAOZ_OkickHPnmht5KGcbPC4PpV7jSuWGXBh3MaQSRbFnAqwJwJIT7hPWOicdNCXEpGCYZjSJAcRppsttUCybWgD9TgVBfsR/s1600/file_defunct.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Take a backup for the database with the defunct file&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;BACKUP DATABASE testdb
TO DISK = &#39;testdb_defunct_backup.bak&#39;;
GO
&lt;/pre&gt;
&lt;br /&gt;
In the newer version of SQL Server instance, in this case SQL Server 2014, restore the database from the backup with the defunct file.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE master; 
GO

RESTORE DATABASE testdb
FROM DISK = &#39;D:\MSSQL10_50.MSSQL2008R2\MSSQL\Backup\testdb_defunct_backup.bak&#39;
WITH 
 MOVE &#39;testdb_data&#39; TO &#39;D:\MSSQL12.MSSQL2014\MSSQL\Data\testdb.mdf&#39;,
 MOVE &#39;testdb_file1&#39; TO &#39;D:\MSSQL12.MSSQL2014\MSSQL\Data\testdb_file1.ndf&#39;,
 MOVE &#39;testdb_log&#39; TO &#39;D:\MSSQL12.MSSQL2014\MSSQL\Data\testdb_log.ldf&#39;;
GO 
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikNS5A6Ge59gc2QxezCpnET0Oji-ihP49qiTfeYc3I8hC1daiDjVcGgVB1-qGyoDktNZdRD-bPcoUTAdCqGOmhpnpq37ex5JHrixm9DDOMtnzqMMWwBksT6sUbXvYhUZ4SyluGkOH9PMwk/s1600/defunct_restore_success.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikNS5A6Ge59gc2QxezCpnET0Oji-ihP49qiTfeYc3I8hC1daiDjVcGgVB1-qGyoDktNZdRD-bPcoUTAdCqGOmhpnpq37ex5JHrixm9DDOMtnzqMMWwBksT6sUbXvYhUZ4SyluGkOH9PMwk/s1600/defunct_restore_success.png&quot; height=&quot;145&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The metadata of the index in defunct file doesn&#39;t affect the restore process.The database restored and upgraded successfully. Of course it will return error if the index is queried on the new version instance, just like in the original SQL Server instance.&lt;br /&gt;
&lt;br /&gt;
Let&#39;s take another example. This time with the full text index. First, restore the database back to the status before the secondary data file becomes defunct. We will restore from the good backup file.
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE master;
GO

ALTER DATABASE testdb
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
GO

--Restore the database back to prior defunct
RESTORE DATABASE testdb
FROM DISK = &#39;testdb_good_backup.bak&#39;
WITH REPLACE;
GO
&lt;/pre&gt;
&lt;br /&gt;
Create fulltext catalog and fulltext index on filegroup FG1&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE testdb;
GO

--Create fulltext catalog
CREATE FULLTEXT CATALOG catalog1 AS DEFAULT;
GO

--Create fulltext index on filegroup FG1
CREATE FULLTEXT INDEX ON tbl1 (col2)
KEY INDEX PK_tbl1_col1
ON (FILEGROUP FG1);
GO
&lt;/pre&gt;
&lt;br /&gt;
Now make the data file defunct like how it was done previously.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;-Make the data file defunct
ALTER DATABASE testdb
MODIFY FILE 
( NAME = testdb_file1,
 OFFLINE );
GO

ALTER DATABASE testdb
REMOVE FILEGROUP FG1;
GO
&lt;/pre&gt;
&lt;br /&gt;
Take another backup. The backup consist of defunct data file with fulltext index.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;BACKUP DATABASE testdb
TO DISK = &#39;testdb_defunct_fulltext_backup.bak&#39;;
GO
&lt;/pre&gt;
&lt;br /&gt;
In the new version of SQL Server instance, drop the testdb database and restore from the backup just taken.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE master; 
GO

ALTER DATABASE testdb
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE;
GO

DROP DATABASE testdb;
GO

RESTORE DATABASE testdb
FROM DISK = &#39;D:\MSSQL10_50.MSSQL2008R2\MSSQL\Backup\testdb_defunct_fulltext_backup.bak&#39;
WITH 
 MOVE &#39;testdb_data&#39; TO &#39;D:\MSSQL12.MSSQL2014\MSSQL\Data\testdb.mdf&#39;,
 MOVE &#39;testdb_file1&#39; TO &#39;D:\MSSQL12.MSSQL2014\MSSQL\Data\testdb_file1.ndf&#39;,
 MOVE &#39;testdb_log&#39; TO &#39;D:\MSSQL12.MSSQL2014\MSSQL\Data\testdb_log.ldf&#39;;
GO 
&lt;/pre&gt;
&lt;br /&gt;
The restore operation failed with error.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;
Processed 184 pages for database &#39;testdb&#39;, file &#39;testdb_data&#39; on file 1.&amp;nbsp;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;Processed 0 pages for database &#39;testdb&#39;, file &#39;testdb_file1&#39; on file 1.&amp;nbsp;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;Processed 7 pages for database &#39;testdb&#39;, file &#39;testdb_log&#39; on file 1.&amp;nbsp;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;Converting database &#39;testdb&#39; from version 661 to the current version 782.&amp;nbsp;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;Database &#39;testdb&#39; running the upgrade step from version 661 to version 668.&amp;nbsp;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;Database &#39;testdb&#39; running the upgrade step from version 668 to version 669.&amp;nbsp;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;Database &#39;testdb&#39; running the upgrade step from version 669 to version 670.&amp;nbsp;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;Database &#39;testdb&#39; running the upgrade step from version 670 to version 671.&amp;nbsp;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;Database &#39;testdb&#39; running the upgrade step from version 671 to version 672.&amp;nbsp;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style=&quot;color: red;&quot;&gt;Msg 667, Level 16, State 1, Line 24
The index &quot;&quot; for table &quot;&quot; (RowsetId 72057594039369728) resides on a filegroup (&quot;FG1&quot;) 
that cannot be accessed because it is offline, is being restored, or is defunct.&amp;nbsp;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style=&quot;color: red;&quot;&gt;Msg 3013, Level 16, State 1, Line 24&amp;nbsp;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style=&quot;color: red;&quot;&gt;RESTORE DATABASE is terminating abnormally.
&lt;/span&gt;&lt;/code&gt;
&lt;br /&gt;
&lt;br /&gt;
and also this error in Windows application log and SQL Server log,&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;During upgrade, database raised exception 3602, severity 25, state 53, address 000007FED2F36365. Use the exception number to determine the cause.&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
If we pay closer attention, we can see the the restore step completed successfully. The failure is during the conversion (upgrade) step.&lt;br /&gt;
&lt;br /&gt;
With the query below on the original SQL Server instance, it shows that the object is one of the ifts (integrated full text search) internal table.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;SELECT 
 p.partition_id,
 o.name [object_name], 
 o.type_desc,
 f.name [filegroup]
FROM sys.allocation_units a
JOIN sys.partitions p
 ON a.container_id = p.hobt_id
JOIN sys.filegroups f
 ON a.data_space_id = f.data_space_id
JOIN sys.objects o
 ON o.object_id = p.object_id
WHERE p.partition_id = &#39;72057594039369728&#39;
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC699Dsr2jTCBC94SmmlNcBIAITxSKy71yM4M5ibiKubs79vP1N9JMKFe7_3PNG3qOQzgV9YW7vJBo5dS5b2MGECwjfwv5wM9PwkWbg9M70QSQkt3H__MgZjfjIwNO5K1o3JAWJ_KJZxgk/s1600/jfts.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC699Dsr2jTCBC94SmmlNcBIAITxSKy71yM4M5ibiKubs79vP1N9JMKFe7_3PNG3qOQzgV9YW7vJBo5dS5b2MGECwjfwv5wM9PwkWbg9M70QSQkt3H__MgZjfjIwNO5K1o3JAWJ_KJZxgk/s1600/jfts.png&quot; height=&quot;50&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;Observation&lt;/b&gt;&lt;br /&gt;
As the data file becomes defunct, metadata of the objects in the defunct data file retained. SQL Server upgrade process attempts to upgrade objects (eg. fulltext index) to newer version including the ones in the defunct data file (as the metadata is retained), it leads to the upgrade failure. However, even the fulltext index was determined as no longer needed, it couldn&#39;t be removed as the data file is in defunct state and on the other hand it causes error during the upgrade. In this predicament, the only solution appears to be extract everything (minus the ones in defunct data file) from the existing database to a new database.&lt;br /&gt;
&lt;br /&gt;
This issue is observed on upgrade from SQL Server 2008 to SQL Server 2012 and SQL Server 2014. Restore from SQL Server 2008 to SQL Server 2008 R2 doesn&#39;t have any problem as there is no upgrade step involved during the restore operation between these two versions.</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/7461132448512924440/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2014/06/defunct-file-cause-sql-server-upgrade.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/7461132448512924440'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/7461132448512924440'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2014/06/defunct-file-cause-sql-server-upgrade.html' title='Defunct File Cause SQL Server Upgrade Failure'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5fmnQKzor45GtxVLmqGjpVmSCY7F9EnMBKNgPSA5ecSFwluQAPiq8G0imWHOO0GW8nsN5kYUslLqtyfuq_reEnNtI0d_GVitJnBEggvkEPO1T4W8XwAV-JSSKolNw6HeVRGMGu8J7A1-B/s72-c/upgradebroken.jpg" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-6236161335080378656</id><published>2014-06-06T10:17:00.000-05:00</published><updated>2014-06-11T22:28:16.481-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Backup/Recovery"/><title type='text'>SQL Server - Defunct Filegroup and Defunct Data File</title><content type='html'>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5rChHSH8RiLRS5ff3B8Lk8KBsrJaE1u0V0JsSIDNga1n2HuIyGnmD-tSPaTDfGC6vPxMlZZLDdeAnNay81-KXA-VX8H1PEC44v1H-X4J8lLri55yc8hhiMHtFlfMScC_rivrUCBKUEpsO/s1600/defunct.gif&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5rChHSH8RiLRS5ff3B8Lk8KBsrJaE1u0V0JsSIDNga1n2HuIyGnmD-tSPaTDfGC6vPxMlZZLDdeAnNay81-KXA-VX8H1PEC44v1H-X4J8lLri55yc8hhiMHtFlfMScC_rivrUCBKUEpsO/s1600/defunct.gif&quot; height=&quot;90&quot; width=&quot;120&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
A data file becomes DEFUNCT when its respective filegroup is removed or when the data file or its filegroup is not included during the piecemeal restore in simple recovery model. Usually a filegroup can not be removed if the data file is not empty. However, a filegroup could be removed if one of its data file is not online. This post illustrates how a data file becomes DEFUNCT.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;Let&#39;s create a database and show how a data file becomes DEFUNCT.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE master;
GO

--Create database
CREATE DATABASE testdb
ON PRIMARY
( NAME = testdb_data, 
 FILENAME = &#39;D:\MSSQL10_50.MSSQL2008R2\MSSQL\Data\testdb.mdf&#39; ),
FILEGROUP FG1
( NAME = testdb_file1, 
 FILENAME = &#39;D:\MSSQL10_50.MSSQL2008R2\MSSQL\Data\testdb_file1.ndf&#39; ),
( NAME = testdb_file2, 
 FILENAME = &#39;D:\MSSQL10_50.MSSQL2008R2\MSSQL\Data\testdb_file2.ndf&#39; )
LOG ON 
( NAME = testdb_log, 
 FILENAME = &#39;D:\MSSQL10_50.MSSQL2008R2\MSSQL\Data\testdb_log.ldf&#39; );
GO

--Set database to simple recovery model
ALTER DATABASE testdb
SET RECOVERY SIMPLE;
GO
&lt;/pre&gt;
&lt;br /&gt;
The query above creates a database with the PRIMARY filegroup and FG1 secondary filegroup. The PRIMARY filegroup has one data file (mdf) and FG1 filegroup has two data files (ndf). The database is also set to simple recovery model.&lt;br /&gt;
&lt;br /&gt;
Next, we creates some data.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE testdb;
GO

--Create table on primary filegroup
CREATE TABLE tbl1
(
 col1 int IDENTITY (1,1) CONSTRAINT PK_tbl1_col1 PRIMARY KEY,
 col2 char(800)
);
GO

--Create index on FG1 filegroup
CREATE INDEX IX_tbl1_col2
ON tbl1
(
 col2
) ON FG1;
GO

--Insert enough data so both secondary files in FG1 are filled with some data
INSERT INTO tbl1
VALUES (&#39;A&#39;);
GO 120
&lt;/pre&gt;
&lt;br /&gt;
The query above created the table is created on PRIMARY filegroup, while its index is created on FG1 filegroup.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;SELECT 
 o.name tbl_name,
 i.name index_name,
 i.type_desc,
 f.name fg_name
FROM sys.objects o
JOIN sys.indexes i
 ON o.object_id = i.object_id
JOIN sys.filegroups f
 ON i.data_space_id = f.data_space_id
WHERE o.is_ms_shipped = 0;
GO
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmicuF9cjxOqpaiKp_e-j18ZHss5GD9MXCjzWaHgzAlBh56FaDZA4DMyss9cs7ric1FZGADg7_903lari9SNN7Ox6yemCZvu9_UYlbixN0MWox_X7aBwbJMuty2U0mdbFP6xvkT9NkU9Ax/s1600/tbl_filegroup.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmicuF9cjxOqpaiKp_e-j18ZHss5GD9MXCjzWaHgzAlBh56FaDZA4DMyss9cs7ric1FZGADg7_903lari9SNN7Ox6yemCZvu9_UYlbixN0MWox_X7aBwbJMuty2U0mdbFP6xvkT9NkU9Ax/s1600/tbl_filegroup.png&quot; height=&quot;77&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The query above verified which filegroup the data is stored at. Now we try to make the data file in FG1 defunct. Let&#39;s first take a good backup for later use.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;BACKUP DATABASE testdb
TO DISK = &#39;testdb_good_backup.bak&#39;;
GO
&lt;/pre&gt;
&lt;br /&gt;
Now, takes the first secondary file (testdb_file1) offline.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;ALTER DATABASE testdb
MODIFY FILE 
( NAME = testdb_file1,
 OFFLINE );
GO
&lt;/pre&gt;
&lt;br /&gt;
Warning. Please beware that the only way to bring the data file back online is restore from a backup.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;sql:brush&quot;&gt;SELECT 
 f.name file_group, 
 d.name file_name, 
 d.state_desc file_state
FROM sys.filegroups f
JOIN sys.database_files d
 ON f.data_space_id = d.data_space_id;
GO
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwAHqXPQT_zqlu_XMfmkpJxLr36H0FMFwn-j5km2VKejlc14OWz4dZ7RRhRKQGRvYdocqun-mOJP1Kptfb8BoVzSrhLp48KGt3o8OdDUwomtvbzpbuHJWzM43lIO8_bnaqGbgcPQ9WetFU/s1600/datafile_status.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwAHqXPQT_zqlu_XMfmkpJxLr36H0FMFwn-j5km2VKejlc14OWz4dZ7RRhRKQGRvYdocqun-mOJP1Kptfb8BoVzSrhLp48KGt3o8OdDUwomtvbzpbuHJWzM43lIO8_bnaqGbgcPQ9WetFU/s1600/datafile_status.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The query above shows that the testdb_file1 data file is now offline. That also makes the FG1 filegroup offline as well. Any attempt to query data located on the offline filegroup returns error. The query below force the query to use the index located in the offline filegroup.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;SELECT col2
FROM dbo.tbl1 
WITH (INDEX(IX_tbl1_col2));
GO
&lt;/pre&gt;
&lt;br /&gt;
&lt;code&gt;&lt;span style=&quot;color: red;&quot;&gt;
Msg 315, Level 16, State 1, Line 56
Index &quot;IX_tbl1_col2&quot; on table &quot;dbo.tbl1&quot; (specified in the FROM clause) 
is disabled or resides in a filegroup which is not online.&lt;/span&gt;&lt;/code&gt;
&lt;br /&gt;
&lt;br /&gt;
What about backing up the database at this moment?
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;BACKUP DATABASE testdb
TO DISK = &#39;testdb_backup.bak&#39;;
GO
&lt;/pre&gt;
&lt;br /&gt;
&lt;code&gt;&lt;span style=&quot;color: red;&quot;&gt;Msg 3007, Level 16, State 1, Line 115&lt;br /&gt;
The backup of the file or filegroup &quot;testdb_file1&quot; is not permitted because it is not online. 
Container state: &quot;Offline&quot; (7). Restore status: 0. BACKUP can be performed by using the FILEGROUP 
or FILE clauses to restrict the selection to include only online data.&lt;br /&gt;
Msg 3013, Level 16, State 1, Line 115&amp;nbsp;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style=&quot;color: red;&quot;&gt;BACKUP DATABASE is terminating abnormally.&lt;/span&gt;&lt;/code&gt;
&lt;br /&gt;
&lt;br /&gt;
The only way to bring the data file back online is restore the database from the backup. You should always do that. However, if there is no backup and it has been determined that data in the offline filegroup is not needed, one way to allow backup is to make the filegroup defunct. That will remove the filegroup from the database but retain the metadata.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;ALTER DATABASE testdb
REMOVE FILEGROUP FG1;
GO
&lt;/pre&gt;
&lt;br /&gt;
Now if we check the status of the data files.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;SELECT 
 f.name file_group, 
 d.name file_name, 
 d.state_desc file_state
FROM sys.filegroups f
JOIN sys.database_files d
 ON f.data_space_id = d.data_space_id;
GO
&lt;/pre&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6e0g2ec1aQo1MjhtG6t7Gg9l3JygHTa73Vq0GtBzoG78yel9-p6K-ksRRhnFMAU-pCGxQ_iCT3YWsBI4Z6q7oSH9eBaRef3JAFSkv9LCqZErH1eKvenBSd3yJx4x5ZLWhz6LFJZXACB5p/s1600/defunct_file.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6e0g2ec1aQo1MjhtG6t7Gg9l3JygHTa73Vq0GtBzoG78yel9-p6K-ksRRhnFMAU-pCGxQ_iCT3YWsBI4Z6q7oSH9eBaRef3JAFSkv9LCqZErH1eKvenBSd3yJx4x5ZLWhz6LFJZXACB5p/s1600/defunct_file.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Even the testdb_file2 secondary file is online and is not empty, the FG1 filegroup removal completed successfully. Notice that both secondary files in FG1 filegroup are now in DEFUNCT state. Query against data located in this filegroup returns same error.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;BACKUP DATABASE testdb
TO DISK = &#39;testdb_defunct_backup.bak&#39;;
GO
&lt;/pre&gt;
&lt;br /&gt;
Backup could be performed successful now with the understanding the data in the FG1 filegroup is no longer available.
&lt;br /&gt;
&lt;br /&gt;
As mentioned earlier, another way data files become DEFUNCT is during the piecemeal restore for database in simple recovery model. In this case, we restore the good testdb backup (testdb_good_backup.bak) taken earlier and restore only the primary filegroup to a new database (testdb_copy).
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;RESTORE DATABASE testdb_copy
FILEGROUP = &#39;PRIMARY&#39;
FROM DISK = &#39;testdb_good_backup.bak&#39;
WITH 
 MOVE &#39;testdb_data&#39; TO &#39;D:\MSSQL10_50.MSSQL2008R2\MSSQL\Data\testdb_copy.mdf&#39;,
 MOVE &#39;testdb_log&#39; TO &#39;D:\MSSQL10_50.MSSQL2008R2\MSSQL\Data\testdb_copy_log.ldf&#39;,
 PARTIAL;
GO
&lt;/pre&gt;
&lt;br /&gt;
&lt;code&gt;
Processed 184 pages for database &#39;testdb_copy&#39;, file &#39;testdb_data&#39; on file 1.
Processed 2 pages for database &#39;testdb_copy&#39;, file &#39;testdb_log&#39; on file 1.&amp;nbsp;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style=&quot;color: red;&quot;&gt;Msg 3127, Level 16, State 1, Line 75&amp;nbsp;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style=&quot;color: red;&quot;&gt;The file &#39;testdb_file1&#39; of restored database &#39;testdb_copy&#39; is being left in the defunct state because the database is using the simple recovery model and the file is marked for read-write access. Therefore, only read-only files can be recovered by piecemeal restore.&amp;nbsp;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style=&quot;color: red;&quot;&gt;Msg 3127, Level 16, State 1, Line 75&amp;nbsp;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;&lt;span style=&quot;color: red;&quot;&gt;The file &#39;testdb_file2&#39; of restored database &#39;testdb_copy&#39; is being left in the defunct state because the database is using the simple recovery model and the file is marked for read-write access. Therefore, only read-only files can be recovered by piecemeal restore.&amp;nbsp;&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;RESTORE DATABASE ... FILE=&lt;name&gt; successfully processed 186 pages in 0.049 seconds (29.595 MB/sec).
&lt;/name&gt;&lt;/code&gt;
&lt;br /&gt;
&lt;br /&gt;
Running the below script shows the two secondary files in FG1 filegroup in DEFUNCT state.
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE testdb_copy;
GO

SELECT 
 f.name file_group, 
 d.name file_name, 
 d.state_desc file_state
FROM sys.filegroups f
JOIN sys.database_files d
 ON f.data_space_id = d.data_space_id;
GO
&lt;/pre&gt;
&lt;br /&gt;
Note that the new database is piecemeal restore from a good complete backup. Full restore from the backup (testdb_defunct_backup.bak) taken after the defunct files creates database with the secondary files in DEFUNCT state, just like how it was.&lt;br /&gt;
&lt;br /&gt;
Summary, data file becomes DEFUNCT when its filegroup is removed when one of its data file is not online. This action may be intentionally taken to allow tasks like backup to perform successfully when the offline file could not be found and there is no backup and more importantly, the data in the filegroup has been determined not needed or restored. Another situation is during piecemeal restore for database in simple recovery model that left the non-recovered file or filegroup in DEFUNCT state.&lt;br /&gt;
&lt;br /&gt;
DEFUNCT data files affects SQL Server upgrade in some cases. More detail in next post.</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/6236161335080378656/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2014/06/sql-server-defunct-filegroup-and.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/6236161335080378656'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/6236161335080378656'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2014/06/sql-server-defunct-filegroup-and.html' title='SQL Server - Defunct Filegroup and Defunct Data File'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5rChHSH8RiLRS5ff3B8Lk8KBsrJaE1u0V0JsSIDNga1n2HuIyGnmD-tSPaTDfGC6vPxMlZZLDdeAnNay81-KXA-VX8H1PEC44v1H-X4J8lLri55yc8hhiMHtFlfMScC_rivrUCBKUEpsO/s72-c/defunct.gif" height="72" width="72"/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-5209260195598265989</id><published>2014-04-15T06:00:00.000-05:00</published><updated>2014-04-15T10:42:15.459-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="SQL Agent"/><category scheme="http://www.blogger.com/atom/ns#" term="sqlcmd"/><title type='text'>SQL Server Agent Job and SQLCMD</title><content type='html'>Have you ever encountered missing database backup or maintenance plan didn&#39;t complete as it was scheduled, especially error was encountered? This post discusses a behavior difference between SQL Server agent using T-SQL type and SQLCMD with CmdExec type.&lt;br /&gt;
&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;SQL Server agent job is widely used to execute scheduled SQL Server tasks like backup, maintenance, SQL stored procedures or scripts as well as non-SQL tasks like executing program.&lt;br /&gt;
&lt;br /&gt;
When creating a SQL Server job, a job step(s) define the functionality or task it performs. SQL Server job step provides different &lt;a href=&quot;http://technet.microsoft.com/en-us/library/ms189237.aspx#Security&quot; target=&quot;_blank&quot;&gt;types (subsystems)&lt;/a&gt;. Here listed some of the commonly used types (subsystems),&lt;br /&gt;
&lt;br /&gt;
Transact-SQL script (T-SQL)&lt;br /&gt;
Operating System (CmdExec)&lt;br /&gt;
PowerShell&lt;br /&gt;
SQL Server Analysis Service (SSAS) command / query&lt;br /&gt;
SQL Server Integration Services (SSIS) package&lt;br /&gt;
&lt;br /&gt;
T-SQL type is the most common job step type. It is used to execute T-SQL Script or stored procedures. CmdExec is used to run program, utility. batch files and etc. PowerShell type is used to execute PowerShell script within the job step command. SSAS type is used to execute SSAS XMLA script to backup SSAS or batch processing. SSIS type is used to execute SSIS package including maintenance plan created through SSMS.&lt;br /&gt;
&lt;br /&gt;
Let also discuss briefly about the SQLCMD utility. SQLCMD utility allows us to run T-SQL statement in command prompt. This nice little utility allows administrator to perform SQL Server execution without bringing up the SSMS. Often this tool is used for quick admin check or some automated tasks.&lt;br /&gt;
&lt;br /&gt;
Now back to SQL Agent job. When executing T-SQL command or stored procedure (SP) through SQL Server Agent, most of time the T-SQL type in the job step is selected. This type is best for most of the functionality. However, there is one behavior executing T-SQL or SP this way when an error is encountered during the execution. Let&#39;s examine. Here is a simple example,&lt;br /&gt;
&lt;br /&gt;
Create a stored procedure to perform backup for multiple databases.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE [master];
GO

CREATE PROCEDURE dbo.backupsp
AS
BACKUP DATABASE [msdb] TO DISK = &#39;msdb.bak&#39;;
BACKUP DATABASE [nono] TO DISK = &#39;nono.bak&#39;;
BACKUP DATABASE [master] TO DISK = &#39;master.bak&#39;;
GO
&lt;/pre&gt;
&lt;br /&gt;
There is no &#39;nono&#39; database. The second backup statement should throw an error.&lt;br /&gt;
&lt;br /&gt;
Now we create a SQL Agent job with job step type T-SQL to execute this stored procedure.&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE [msdb]
GO

EXEC dbo.sp_add_job
  @job_name=N&#39;Test Job (TSQL)&#39;,
  @enabled=1;

EXEC dbo.sp_add_jobstep
  @job_name=N&#39;Test Job (TSQL)&#39;,
  @step_name=N&#39;Test Step (TSQL)&#39;,
  @subsystem=N&#39;TSQL&#39;,
  @command=N&#39;EXEC [master].dbo.backupsp;&#39;,
  @flags=4;

EXEC dbo.sp_add_jobserver
  @job_name=N&#39;Test Job (TSQL)&#39;, @server_name = N&#39;(LOCAL)&#39;;
GO&lt;/pre&gt;
&lt;br /&gt;
Start the job,&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;EXEC dbo.sp_start_job @job_name = N&#39;Test Job (TSQL)&#39;;
GO&lt;/pre&gt;
&lt;br /&gt;
&lt;b&gt;SQL Server 2014 and SQL Server 2012 job step detail&lt;/b&gt;&lt;br /&gt;
&lt;code&gt;Executed as user: NT SERVICE\SQLSERVERAGENT. Processed 1728 pages for database &#39;msdb&#39;, file &#39;MSDBData&#39; on file 3. [SQLSTATE 01000] (Message 4035) &amp;nbsp;Processed 7 pages for database &#39;msdb&#39;, file &#39;MSDBLog&#39; on file 3. [SQLSTATE 01000] (Message 4035) &amp;nbsp;BACKUP DATABASE successfully processed 1735 pages in 3.124 seconds (4.337 MB/sec). [SQLSTATE 01000] (Message 3014) &amp;nbsp;&lt;span style=&quot;color: red;&quot;&gt;Database &#39;nono&#39; does not exist. Make sure that the name is entered correctly. [SQLSTATE 08004] (Error 911) &amp;nbsp;BACKUP DATABASE is terminating abnormally. [SQLSTATE 42000] (Error 3013) &lt;/span&gt;&amp;nbsp;Processed 472 pages for database &#39;master&#39;, file &#39;master&#39; on file 2. [SQLSTATE 01000] (Message 4035) &amp;nbsp;Processed 2 pages for database &#39;master&#39;, file &#39;mastlog&#39; on file 2. [SQLSTATE 01000] (Message 4035) &amp;nbsp;BACKUP DATABASE successfully processed 474 pages in 0.534 seconds (6.933 MB/sec). [SQLSTATE 01000] (Message 3014). &amp;nbsp;The step failed.&lt;/code&gt;

&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;SQL Server 2008 R2 and earlier job step detail&lt;/b&gt;&lt;br /&gt;
&lt;code&gt;Executed as user: NT AUTHORITY\NETWORK SERVICE. Processed 1856 pages for database &#39;msdb&#39;, file &#39;MSDBData&#39; on file 2. [SQLSTATE 01000] (Message 4035) &amp;nbsp;Processed 2 pages for database &#39;msdb&#39;, file &#39;MSDBLog&#39; on file 2. [SQLSTATE 01000] (Message 4035) &amp;nbsp;&lt;span style=&quot;color: red;&quot;&gt;BACKUP DATABASE successfully processed 1858 pages in 2.716 seconds (5.344 MB/sec). [SQLSTATE 01000] (Message 3014) &amp;nbsp;Database &#39;nono&#39; does not exist. Make sure that the name is entered correctly. [SQLSTATE 08004] (Error 911) &amp;nbsp;BACKUP DATABASE is terminating abnormally. [SQLSTATE 42000] (Error 3013). &lt;/span&gt;&amp;nbsp;The step failed.&lt;/code&gt;
&lt;br /&gt;
&lt;br /&gt;
You may have already notice that in SQL Server 2008 R2 and earlier version, there is no backup for master database. The SQL agent job is terminated when it encounters an error. This is likely not the intended goal in this scenario. All other database backup should be performed even if there an error was encountered.&lt;br /&gt;
&lt;br /&gt;
In order to mitigate this issue, we could utilize the sqlcmd with job step CmdExec within the SQL Agent job.
&lt;br /&gt;
&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;USE [msdb]
GO

EXEC dbo.sp_add_job
  @job_name=N&#39;Test Job (CmdExec)&#39;,
  @enabled=1;

EXEC dbo.sp_add_jobstep
  @job_name=N&#39;Test Job (CmdExec)&#39;,
  @step_name=N&#39;Test Step (CmdExec)&#39;,
  @subsystem=N&#39;CmdExec&#39;,
  @command=N&#39;sqlcmd -E -S $(ESCAPE_SQUOTE(SRVR)) -Q &quot;EXEC [master].dbo.backupsp&quot; -b&#39;;

EXEC dbo.sp_add_jobserver
  @job_name=N&#39;Test Job (CmdExec)&#39;, @server_name = N&#39;(LOCAL)&#39;;
GO&lt;/pre&gt;
&lt;br /&gt;
The -b option at the end of command returns a DOS ERRORLEVEL value when an error is encountered within the sqlcmd. This give us a better indication of the job failure. The SQL Server Agent token (SRVR) returns the computer and SQL Server instance name.&lt;br /&gt;
&lt;br /&gt;
Start the job,&lt;br /&gt;
&lt;pre class=&quot;brush:sql&quot;&gt;EXEC dbo.sp_start_job @job_name = N&#39;Test Job (CmdExec)&#39;;
GO&lt;/pre&gt;
&lt;br /&gt;
&lt;b&gt;SQL Server 2014, SQL Server 2012, SQL Server 2008 R2 job step detail&lt;/b&gt;&lt;br /&gt;
&lt;code&gt;Executed as user: NT Service\SQLSERVERAGENT. Processed 1728 pages for database &#39;msdb&#39;, file &#39;MSDBData&#39; on file 4. &amp;nbsp;Processed 7 pages for database &#39;msdb&#39;, file &#39;MSDBLog&#39; on file 4. &amp;nbsp;BACKUP DATABASE successfully processed 1735 pages in 2.613 seconds (5.186 MB/sec). &amp;nbsp;&lt;span style=&quot;color: red;&quot;&gt;Msg 911, Level 16, State 11, Server SQL2014, Procedure backupsp, Line 5 &amp;nbsp;Database &#39;nono&#39; does not exist. Make sure that the name is entered correctly. &amp;nbsp;Msg 3013, Level 16, State 1, Server SQL2014, Procedure backupsp, Line 5 &amp;nbsp;BACKUP DATABASE is terminating abnormally. &lt;/span&gt;&amp;nbsp;Processed 472 pages for database &#39;master&#39;, file &#39;master&#39; on file 3. &amp;nbsp;Processed 2 pages for database &#39;master&#39;, file &#39;mastlog&#39; on file 3. &amp;nbsp;BACKUP DATABASE successfully processed 474 pages in 0.656 seconds (5.644 MB/sec). &amp;nbsp;Process Exit Code 1. &amp;nbsp;The step failed.&lt;/code&gt;
&lt;br /&gt;
&lt;br /&gt;
Except the executing user difference (WORKGROUP\SQL2008R2$ for SQL Server 2008 R2), master database backup is performed for all SQL Server versions. Also, if you pay close attention, with sqlcmd, there is no&amp;nbsp;[SQLSTATE 08004] (Error 911) funky stuff at the end of each execution like T-SQL type does.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Observation&lt;/b&gt;&lt;br /&gt;
For earlier SQL Server version, SQL Server agent job terminates immediately when it encounters error. This may produce unintended results as all the subsequent statements are not executed. This may become potential issue for backup, integrity check or index maintenance operations as it misses the rest of the executions.
&lt;br /&gt;
&lt;br /&gt;
One way to mitigate this behavior is to utilize the sqlcmd with CmdExec type within the SQL Server agent job. Sqlcmd complete the entire executions even if it encounters error.
&lt;br /&gt;
&lt;br /&gt;
From SQL Server 2012 and later version, SQL Server agent job appears to complete all execution regardless if it encounters error.</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/5209260195598265989/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2014/04/sql-server-agent-job-and-sqlcmd.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/5209260195598265989'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/5209260195598265989'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2014/04/sql-server-agent-job-and-sqlcmd.html' title='SQL Server Agent Job and SQLCMD'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-8734448967395161608</id><published>2014-04-01T22:47:00.004-05:00</published><updated>2014-04-01T22:49:20.279-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="Self Learning"/><title type='text'>SQL Server 2014 RTM Release!</title><content type='html'>Microsoft releases SQL Server 2014 RTM to public today! Yes, it&#39;s April Fool day, I know what you thinking but this is real. This post discuss different ways of getting the software.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
SQL Server 2014 packs with a lot of new features like In-memory technologies (Code name Hekaton), buffer pool extension, new cardinality estimation logic, tighter integration with Windows Azure and many more!&lt;br /&gt;
&lt;br /&gt;
Excited? Let&#39;s get it!. There are multiple ways you could download SQL Server 2014. You could download the evaluation edition with 180-day trial at &lt;a href=&quot;http://technet.microsoft.com/en-US/evalcenter/dn205290.aspx&quot; target=&quot;_blank&quot;&gt;TechNet Evaulation Center&lt;/a&gt;. The evaluation edition contains all features available for Enterprise edition so you can test all these new features.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://technet.microsoft.com/en-US/evalcenter/dn205290.aspx&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtUid5WwU9YcpkFYMdFxGZqEGOFiqhB_2ouj0OoIRKXyqpwwC5TSV3z5kKZ7lNR1x0uIdTOfCWUBHZ3RsKF3JwVxnfHIyudqsovdYbdupQ25HMRsHwtgam1cpYEYQoBXDzO0dmtw3a9LDL/s1600/evaluation.png&quot; height=&quot;148&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
If you are MSDN subscriber, all SQL Server 2014 editions are available for download. Once you login to &lt;a href=&quot;https://msdn.microsoft.com/en-us/subscriptions/downloads/&quot; target=&quot;_blank&quot;&gt;MSDN download page&lt;/a&gt;, there is a new link for SQL Server 2014.&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;https://msdn.microsoft.com/en-us/subscriptions/downloads/&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjO39z8BOos4bGzmLlHrppBBaJADilG7hQEq180Imz8p6YrgHXwnYO12muucCiYAn7HZHcDT7rVVBdP9J5bIAb_tkuvoiBu9risv1uhXaUnP9WI6VmFZUKf411bkTzka55qZ2iRdkHugGy_/s1600/SQLServer2014DownloadOption.png&quot; /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;br /&gt;
Here shows partial list of downloadable images available for SQL Server 2014&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS_J_su02j6NI0707hAOvP_wdU_HCOVUsYbMGiLN13N1BkDgKdl7Iofv1ClzN9upboFwi7pblvaAeWCLgl5qOfqI1gf1tTvkKGZUbBFfgR_VvxouoougzIt_5k8U2-QV3QSB7ed1-b5Qyo/s1600/SQLServer2014DownloadList.png&quot; height=&quot;380&quot; width=&quot;400&quot; /&gt;
&lt;/div&gt;
&lt;br /&gt;
Another way to get your hand on SQL Server 2014 is through &lt;a href=&quot;http://www.windowsazure.com/en-us/&quot; target=&quot;_blank&quot;&gt;Windows Azure&lt;/a&gt;. It takes only matter of minutes to provision a Windows Azure virtual machine with SQL Server 2014. The Windows Azure virtual machine gallery provides several images contain different version and edition of SQL Server. The picture below shows the selected image for Windows Server 2012 R2 with SQL Server 2014 RTM. &amp;nbsp;If you already a MSDN subscriber, definitely check this option out since there is free monthly Window Azure credits as well as discounted rate.&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;http://www.windowsazure.com/en-us/&quot; target=&quot;_blank&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgs3rIfZvJAADWxaArV6eBU_2EJ3SoipWa39CmhTG6Qf2ktZZlNYhFXShd2ukDtRUvZXIu7TjwQhO2KQkLRjUHeb8KLokQ4XEZqPmx4vyV8sPYUCvQMAMczAO-nYpGFDLDVh3wF8QhrYZk0/s1600/SQLServer2014Azure.png&quot; height=&quot;256&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;br /&gt;
Traditionally there will be a developer edition for sales (more detail &lt;a href=&quot;http://www.travisgan.com/2014/01/sql-server-licensing-test-environment.html&quot; target=&quot;_blank&quot;&gt;here&lt;/a&gt;). Since Microsoft just release SQL Server 2014 today, it may takes some time for any online or physical stores to start selling them.</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/8734448967395161608/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2014/04/sql-server-2014-rtm-release.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/8734448967395161608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/8734448967395161608'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2014/04/sql-server-2014-rtm-release.html' title='SQL Server 2014 RTM Release!'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtUid5WwU9YcpkFYMdFxGZqEGOFiqhB_2ouj0OoIRKXyqpwwC5TSV3z5kKZ7lNR1x0uIdTOfCWUBHZ3RsKF3JwVxnfHIyudqsovdYbdupQ25HMRsHwtgam1cpYEYQoBXDzO0dmtw3a9LDL/s72-c/evaluation.png" height="72" width="72"/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-927285248526539903</id><published>2014-03-25T06:00:00.001-05:00</published><updated>2022-08-15T23:57:45.989-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="PowerShell"/><category scheme="http://www.blogger.com/atom/ns#" term="Troubleshooting"/><title type='text'>Use PowerShell To Test Port</title><content type='html'>There are times when we need to identify or troubleshoot if firewall exception is configured correctly and the desired remote server port is open. Often time, I have seen IT Professional use Telnet or PuTTY to test. However, there is another way to do it with PowerShell.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
By default, Telnet is not installed on Windows or Windows Server. In order to use Telnet, the Telnet client has to be installed. In Windows, this can be done through Control Panel &amp;gt; Programs and Features &amp;gt; Turn Windows features on or off, and check the Telnet Client. For Windows Server, go to Server Manager &amp;gt; Features &amp;gt; Add Features &amp;gt; Telnet Client.&lt;br /&gt;
&lt;br /&gt;
Or add the Telnet Client feature through PowerShell,

&lt;pre class=&quot;brush:ps&quot;&gt;
Import-Module ServerManager
Add-WindowsFeature -Name Telnet-Client
&lt;/pre&gt;

Instead of installing the Telnet client, alternatively we could use Windows Sockets through the System.Net.Sockets provided in .NET framework.
&lt;br /&gt;&lt;br /&gt;
To test TCP port,

&lt;pre class=&quot;brush:ps&quot;&gt;
$tcp = New-Object System.Net.Sockets.TcpClient
$tcp.connect(&#39;&amp;lt;remote server&amp;gt;&#39;, &amp;lt;port&amp;gt;)
&lt;/pre&gt;

Or even one line of code if you wish,

&lt;pre class=&quot;brush:ps&quot;&gt;
(New-Object System.Net.Sockets.TcpClient).Connect(&#39;&amp;lt;remote server&amp;gt;&#39;, &amp;lt;port&amp;gt;)
&lt;/pre&gt;

That&#39;s it! No additional installation (provided that PowerShell is installed, of course). 
&lt;br/&gt;&lt;br/&gt;
Starting in Windows Server 2012 R2, Microsoft includes Test-NetConnection in NetTCPIP Module. This command could be used to simulate the traditional ping, tracert, and testing TCP connectivity.

&lt;pre class=&quot;brush:ps&quot;&gt;
# ping
Test-NetConnection -ComputerName &amp;lt;remote server&amp;gt;

# tcp test on a port
Test-NetConnection -ComputerName &amp;lt;remote server&amp;gt; -Port &amp;lt;port&amp;gt;

# tracert
Test-NetConnection -ComputerName &amp;lt;remote server&amp;gt; -TraceRoute
&lt;/pre&gt;

A note on UDP port testing. UDPClient.connect only specify the host/port to that UDP client without actually connecting the remote host. Since UDP uses connectionless transmission which is hard to reliably determining if a remote UDP port is open/close. it seems like one way to test is to send some packets to the remote UDP port and if &quot;ICMP port unreachable&quot; is received back, the UDP port is considered as close. Otherwise, it is unknown.
</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/927285248526539903/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2014/03/use-powershell-to-test-port.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/927285248526539903'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/927285248526539903'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2014/03/use-powershell-to-test-port.html' title='Use PowerShell To Test Port'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3846425018755283358.post-4923524882632074827</id><published>2014-03-24T06:00:00.000-05:00</published><updated>2014-03-24T06:00:08.657-05:00</updated><category scheme="http://www.blogger.com/atom/ns#" term="PowerShell"/><title type='text'>PowerShell Cim Cmdlet to PowerShell 2.0 or older</title><content type='html'>Starting in PowerShell 3.0, Microsoft introduces a new set of cmdlets to manage servers or devices that complies to &lt;a href=&quot;http://www.dmtf.org/standards/cim&quot; target=&quot;_blank&quot;&gt;DMTF&lt;/a&gt; standards. These new set of cmdlets, CIM (Common Information Model (CIM) Cmdlets allow IT professional to better manage their data center especially when it consists of servers from different vendors.&lt;br /&gt;
&lt;a name=&#39;more&#39;&gt;&lt;/a&gt;&lt;br /&gt;
Before PowerShell 3.0, IT professional commonly use WMI Cmdlets (WMI is Windows standard of the CIM Server) to obtain resources information for Windows, and WSMan Cmdlets for Non-Windows. In order to allow and simplify management on heterogeneous environment, a standard compliance and richer PowerShell CIM Cmdlets are introduced. You can find a list of CIM Cmdlets from this&amp;nbsp;&lt;a href=&quot;http://technet.microsoft.com/en-us/library/jj553783.aspx&quot; target=&quot;_blank&quot;&gt;TechNet article&lt;/a&gt;. This &lt;a href=&quot;http://blogs.msdn.com/b/powershell/archive/2012/08/24/introduction-to-cim-cmdlets.aspx&quot; target=&quot;_blank&quot;&gt;msdn article&lt;/a&gt; provide a good introduction of CIM cmdlet as well comparison with the older WMI.&lt;br /&gt;
&lt;br /&gt;
Windows Server 2012 and Windows 8 shipped with PowerShell 3.0 which support these new CIM Cmdlets. For servers or devices with older version of PowerShell, it doesn&#39;t support CIM Cmdlets directly.&lt;br /&gt;
&lt;br /&gt;
For example, if we want to get some processor information of a remote server (e.g. Server1), traditionally we use WMI cmdlets like this,&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;
Get-WmiObject -Class Win32_Processor -ComputerName Server1 | Select-Object -Property SocketDesignation, NumberOfCores, NumberOfLogicalProcessors, CurrentClockSpeed&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8uO6mLJagMG182-VCSa0yjrK79hHuUnH6yXhn9xYiCJ6nbvJvZ09VV09gKQbNPjNjSQZ5aro9H3GPg38Ntv-IJgCRRoifygl5bczK5oXcs5i74WrffsVS4CSSdkYnmqM7aWdFHUNyFWpy/s1600/wmi.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8uO6mLJagMG182-VCSa0yjrK79hHuUnH6yXhn9xYiCJ6nbvJvZ09VV09gKQbNPjNjSQZ5aro9H3GPg38Ntv-IJgCRRoifygl5bczK5oXcs5i74WrffsVS4CSSdkYnmqM7aWdFHUNyFWpy/s1600/wmi.png&quot; height=&quot;50&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Win32_Processor represents instances for each processor&lt;br /&gt;
Get-WmiObject get instance of WMI class in this case Win32_Processor&lt;br /&gt;
&lt;br /&gt;
To use the new CIM Cmdlet, for the remote server (e.g. Server1) that has at least PowerShell 3.0, we could execute the command like this (try using the tab completion for ClassName like *processor for for richer experience),&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;
Get-CimInstance -ClassName CIM_Processor -ComputerName Server1 | Select-Object&amp;nbsp;SocketDesignation, NumberOfCores, NumberOfLogicalProcessors, CurrentClockSpeed&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDVrcWyN5hWowiz2jeQ5BrMKQJrecIEnBkN7h6lrw_ZFypE9J6sS79OFw5VUasY9FV3aJSD76Uw3VZl5qhWEG-dBIP1maBLVsKuhqNfQFnuypV7cYGCk6qsOcXAdLM38AZWfLQ5QBXhFlZ/s1600/ciminstance-3.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDVrcWyN5hWowiz2jeQ5BrMKQJrecIEnBkN7h6lrw_ZFypE9J6sS79OFw5VUasY9FV3aJSD76Uw3VZl5qhWEG-dBIP1maBLVsKuhqNfQFnuypV7cYGCk6qsOcXAdLM38AZWfLQ5QBXhFlZ/s1600/ciminstance-3.png&quot; height=&quot;55&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
CIM_Processor represents instance for each processor. This CIM_Processor class is parent class for Win32_Processor&lt;br /&gt;
Get-CimInstance gets the CIM instances of a class, in this case CIM_Processor&lt;br /&gt;
&lt;br /&gt;
If the same command issued to obtain processor information from another server (e.g server2) that currently installed with PowerShell 2.0,&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaPcztFUENIxrB4aR2Hltwau8hZhAm1HZ3Nv-cQGtzmd3Dh1Tl6yPaoJrI7ZwXjqU3iTEbMDP8uQhfOvAvqtZEztVBXUkcga0bNnhban-dzjKfhMaEV8jfNOjCNHujcciCBt9a8X7itXyk/s1600/ciminstance-2-fail.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaPcztFUENIxrB4aR2Hltwau8hZhAm1HZ3Nv-cQGtzmd3Dh1Tl6yPaoJrI7ZwXjqU3iTEbMDP8uQhfOvAvqtZEztVBXUkcga0bNnhban-dzjKfhMaEV8jfNOjCNHujcciCBt9a8X7itXyk/s1600/ciminstance-2-fail.png&quot; height=&quot;75&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
The command failed.&lt;br /&gt;
&lt;br /&gt;
The reason is because Get-CIMInstance Cmdlet is using&amp;nbsp;WinRM (Windows implementation of WSMAN protocol)&amp;nbsp;which is only supported from PowerShell&amp;nbsp;3.0. In order to execute the Get-CIMInstance command on Server2 with PowerShell 2.0 version installed, we have to force the CIM cmdlet to use DCOM protocol (used by WMI) which is supported in all PowerShell version (As of this post).&lt;br /&gt;
&lt;br /&gt;
To do that, we use the CIM Session which provide an option of using DCOM or WSMAN. In this case, select the DCOM and execute the Get-CIMInstance against the CIM session.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;
$so = New-CimSessionOption -Protocol Dcom&lt;br /&gt;
$s = New-CimSession -ComputerName Server2 -SessionOption $so&lt;br /&gt;
Get-CimInstance -ClassName CIM_Processor -CimSession $s |&lt;br /&gt;
Select-Object -Property SocketDesignation, NumberOfCores, NumberOfLogicalProcessors, CurrentClockSpeed&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFhN7Uew9vh4VJVOCOQwvTIVhGlVabX_2WMDzVafh-4XSXJqG_6G-1obwy1PR4RUAafkPi_tME9mmsROp1EgtQemZRSF9K2EFHOuhhKVCP6BO6iWHYi76RQfqREKNO6yu_s88bWdzMWgiR/s1600/ciminstance-2-success.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFhN7Uew9vh4VJVOCOQwvTIVhGlVabX_2WMDzVafh-4XSXJqG_6G-1obwy1PR4RUAafkPi_tME9mmsROp1EgtQemZRSF9K2EFHOuhhKVCP6BO6iWHYi76RQfqREKNO6yu_s88bWdzMWgiR/s1600/ciminstance-2-success.png&quot; height=&quot;87&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
It is important to know which protocol is being used as it uses different ports which will affect firewall exception setup.&lt;br /&gt;
&lt;br /&gt;
If the firewall is not configured correctly, you may receive this error.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;&lt;span style=&quot;color: red;&quot;&gt;New-CimSession : The RPC server is unavailable.&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
DCOM assigns TCP port dynamically from 1024 through 65535. In order for WMI cmdlets to connect the remote server successfully, at remote server enable the Windows Firewall: Allow remote administrator exception (gpedit.msc &amp;gt; local computer policy &amp;gt; computer configuration &amp;gt; administrative templates &amp;gt; network, network connections &amp;gt; windows firewall &amp;gt; domain/standard profile), which will enable these RPC firewall rules as shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTGTqYFlfRLNe3rsslrmoAqfZs3V3qnr-SYhcAxr_81ZXujP3vmSX1N_YJHidaG18amfKl-4VK9X55RlVhFxF3OVXTrMeyTxB5xHRELFAkuOTBovn03x4MmzvNqTmammqiw5mpNxkYYsOy/s1600/RPC-firewall.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTGTqYFlfRLNe3rsslrmoAqfZs3V3qnr-SYhcAxr_81ZXujP3vmSX1N_YJHidaG18amfKl-4VK9X55RlVhFxF3OVXTrMeyTxB5xHRELFAkuOTBovn03x4MmzvNqTmammqiw5mpNxkYYsOy/s1600/RPC-firewall.png&quot; height=&quot;52&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
For asynchronous callback from remote computer, the local computer need to enable the Windows Management Instrumentation (Async-In) firewall rule and sometimes (DCOM-In) firewall rule. One example is the remote SQL Server Configuration Manager. More information at this &lt;a href=&quot;http://www.travisgan.com/2013/12/windows-firewall-to-remote-sql-server.html&quot; target=&quot;_blank&quot;&gt;post&lt;/a&gt;.&lt;br /&gt;
&lt;br /&gt;
The easier way for WMI firewall setup I found is go to control panel &amp;gt; System and Security &amp;gt; Windows Firewall &amp;gt; &amp;nbsp;Allow a program or feature through Windows Firewall, Select Windows Management Instrumentation (WMI).&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibd7ZdzfgK71Zs7kEK3c6oPWJq5_2q4X971Nt5n41udGuxKVAOjwKIVeIZWy4zARWFcMVyEE1TwZj-dMiHG1-yxDhyphenhyphenRYeCszrPylQtRMNS7LPsZeD3PVUnmRbqvqOq0TinU6PRSeR333cO/s1600/wmi-allow.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibd7ZdzfgK71Zs7kEK3c6oPWJq5_2q4X971Nt5n41udGuxKVAOjwKIVeIZWy4zARWFcMVyEE1TwZj-dMiHG1-yxDhyphenhyphenRYeCszrPylQtRMNS7LPsZeD3PVUnmRbqvqOq0TinU6PRSeR333cO/s1600/wmi-allow.png&quot; height=&quot;144&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
This will enable the firewall rules as shown below.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgChqopntcXWNAJjAkBc_d851T_sPdT9xyb3-O3q7sth0rngOXwE8mmY16nZ4DdJAp4NIRM3i6dhhaigaNQLwci0Hj6r23vFl6sHsMQXysOPOHC65MqkRR6kJa3TOGNOHcp-ut-ihGPk7DC/s1600/wmi-allow-firewall.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgChqopntcXWNAJjAkBc_d851T_sPdT9xyb3-O3q7sth0rngOXwE8mmY16nZ4DdJAp4NIRM3i6dhhaigaNQLwci0Hj6r23vFl6sHsMQXysOPOHC65MqkRR6kJa3TOGNOHcp-ut-ihGPk7DC/s1600/wmi-allow-firewall.png&quot; height=&quot;40&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
As long as there are no RPC or other similar firewall rules blocking the ports, these WMI firewall take care of the required WMI firewall exceptions.&lt;br /&gt;
&lt;br /&gt;
WinRM protocol is a SOAP (standard simple object protocol) based protocol that uses TCP port 5985 for HTTP transport and 5986 for HTTPS.&lt;br /&gt;
&lt;br /&gt;
To enable the firewall, you can run &lt;code&gt;winrm quickconfig&lt;/code&gt; on powershell on the remote server, which will enable the firewall rules show below.&lt;br /&gt;
&lt;br /&gt;
Similarly, we could configure the Windows Remote Management through Windows Firewall and Allow a program or feature through Windows Firewall.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimLX2NITu2zmWG4ZEF91M6JDkZIgHS_Lu8muIMzkOaaM4UgnisBi7hVRh6HsCNPWQmI__PeHX3nWczAiJHw64cnwF29yK63_6YL3dIqLcLx6VegxgJKEDKHtsl8xMN2jy5ojpAXzjg4Y6t/s1600/winrm-allow.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimLX2NITu2zmWG4ZEF91M6JDkZIgHS_Lu8muIMzkOaaM4UgnisBi7hVRh6HsCNPWQmI__PeHX3nWczAiJHw64cnwF29yK63_6YL3dIqLcLx6VegxgJKEDKHtsl8xMN2jy5ojpAXzjg4Y6t/s1600/winrm-allow.png&quot; height=&quot;170&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
These are the firewall rules enabled.&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-wLw6NfvOqZ973WE0TBeBV95FmX_gY_K_B9e-YrSb_9JFBEsoVYW3NZEUzufkoDLA3Sm2PXYB1zj29BtMKeL1HqamqP2UiOyaEQrJKb7qUvjeohRsvSPDkQKuHWPdWjp8Ff-jCof0gVQS/s1600/winrm-allow-firewall.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-wLw6NfvOqZ973WE0TBeBV95FmX_gY_K_B9e-YrSb_9JFBEsoVYW3NZEUzufkoDLA3Sm2PXYB1zj29BtMKeL1HqamqP2UiOyaEQrJKb7qUvjeohRsvSPDkQKuHWPdWjp8Ff-jCof0gVQS/s1600/winrm-allow-firewall.png&quot; height=&quot;30&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
The compatibility mode is for the older WinRM (WinRM 1.1) which uses the common port 80. Since WinRM 2.0, the port has changed to 5985. Also, if required, HTTPS firewall rule could be configured.&lt;br /&gt;
&lt;br /&gt;
If the firewall for WinRM is not configured correctly, you may receive this error when using CIM Cmdlets with the default WinRM (or specified in the session option to use WSMAN) protocol.&lt;br /&gt;
&lt;br /&gt;
&lt;code&gt;&lt;span style=&quot;color: red;&quot;&gt;
Get-CimInstance : WinRM cannot complete the operation. Verify that the specified computer name is valid, that the computer is accessible over the network, and that a firewall exception for the WinRM service is enabled and allows access from this computer. By default, the WinRM firewall exception for public profiles limits access to remote computers within the same local subnet.&lt;/span&gt;&lt;/code&gt;&lt;br /&gt;
&lt;br /&gt;
Hope this post help you to use CIM cmdlets against older PowerShell version and the firewall setup.</content><link rel='replies' type='application/atom+xml' href='http://www.travisgan.com/feeds/4923524882632074827/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.travisgan.com/2014/03/powershell-cim-cmdlet-to-powershell-20.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/4923524882632074827'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3846425018755283358/posts/default/4923524882632074827'/><link rel='alternate' type='text/html' href='http://www.travisgan.com/2014/03/powershell-cim-cmdlet-to-powershell-20.html' title='PowerShell Cim Cmdlet to PowerShell 2.0 or older'/><author><name>Unknown</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='https://img1.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8uO6mLJagMG182-VCSa0yjrK79hHuUnH6yXhn9xYiCJ6nbvJvZ09VV09gKQbNPjNjSQZ5aro9H3GPg38Ntv-IJgCRRoifygl5bczK5oXcs5i74WrffsVS4CSSdkYnmqM7aWdFHUNyFWpy/s72-c/wmi.png" height="72" width="72"/><thr:total>0</thr:total></entry></feed>