## Tuesday, November 29, 2016

### Troubleshooting TF14098 permission denied while deleting a branch

Recently I encountered the error TF14098: Access Denied: User DOMAIN\user needs Checkin permission(s) for $/teamproject/folder/* while delete a TFS branch. In this post, I want to share experience troubleshooting this issue. I am using TFS 2015 that is hosted on permises with latest update. I am also a TFS admin with project collection administrator rights. When I first saw this error, I was felt that I knew why this happened. We had two branches DEV and TEST. TEST was branched from DEV. DEV had folder called Binaries and in it there was a particular .dll which had explicit DENY checkin permissions set for [Project]\Contributors, [Project]Project Administrators, [Collection]\Project Collection Administrators. I thought that since TEST was branched from DEV all those permissions would have been in effect in TEST. I right clicked on the TEST/Binaries/our.dll from within TFS source control explorer, then click on Advanced and then on Security. After that I made sure that those three permission were set to Allow and nothing was deny. I was confident that this would work but it did not worked. Again the same error. Access Denied. Next I opened visual studio developer command prompt in administrator mode and then navigated to the actual workspace folder locally and then ran the command tf permission or tf vc permission as follows: >tf vc permission$/project/test /recursive | clip

This will copy all the permissions on files in your workspace recursively and copy it to your clipboard which you then can paste inside notepad and do search on deny. I found the Groups that had Deny permission set for that particular file.  I double checked that file’s permission inside TFS source control explorer thinking something might have gone wrong.  I tried deleting the branch again. Same error. Then I deleted just that file to get rid of the permission error.  This time I was super confident that this would fix the issue since there is no file locally or on the server.  I tried again. Same error.  Then I read that in this msdn forum that permissions can even exists on a path even if no file exists on the server.  Which makes sense if you think about it that on the server path could still exists even if there are no file in a branch since a branch only has pointers to the contents in it.  I used the following command to set allow on that file as follows:

>tf vc permission $/project/test/binaries/our.dll /allow:* /group:”[Project]\Contributors”,”[Project]\Project Administrators” I tried deleting the branch again inside visual studio and then tried checking in again and finally it worked this time. I hope this helps. ## Thursday, November 24, 2016 ### Adding Google authentication to Azure App Service web app In this post, I want to show you how quickly you can enable Authentication using Google to web app hosted in azure app service. You can configure other authentication providers also if you wish. The documentation for this feature can be found here. First of all create a blank asp.net web app and publish it to azure. It can be any web application. For demonstration purposes I have created a .net core web app and hosted on Azure App Service. Open the App Services blade and open your website blade then click on settings. Under settings you will see Authentication and Authorization option. Turn on authentication feature as shown below. After you turn on authentication, for “Action to take when request is not authenticate” select “Log in with Google You can see different authentication providers in the list above. Click on Google. On the next screen it will ask you Google Authentication Settings i.e. a Client ID and Client Secret. Let’s head over to developer.console.google.com. You should on the dashboard page and click on Enable API. Then Click on the Google+ API to enable that api. Your screen should have Google+ API like the screenshot below. After you have enabled Google+API, go the Credentails page and Click on Credentials button and click on OAuth Client ID. On Create Client ID page, select Web application. As soon as you click on Create you will be asked to provide following information. Let’s go over these in detail. 1. Name (provide any name you wish, I used the name of the web app) 2. Under Restrictions – Authorized JavaScript origins, you have to provide different urls that you want to be protected so when someone hits that url they will be presented with Google’s login dialog. You cannot provide wildcards in the url. My url is http://[webappname].azurewebsites.net 3. Under Restrictions – Authorized redirect URIs, you have to provide a redirect url. What is a redirect url? So after you are successfully authenticated Google doesn’t know where it should redirect you, so you will have to provide a redirect url for your website. Now you cannot provide any url. It has to be https://[webappname].azurewebsite.net/.auth/login/google/callback. After you save you will be provided with a Client ID and Client Secret. Copy them and paste them in the Azure Portal and save. Navigate to your website and you will be provided with Google login option. After your are successfully logged in, you will be redirect to your website’s home page. I think it is very easy to setup but real world apps need more than just one size fits all approach. For example, what if I wanted anonymous access to home page but authenticated access to the entire application. You might have questions like,”How do I prevent anybody with a Google account from accessing my app?” Then that piece is upon you to configure in your application. “How do I know the email of the user and which login provider they used?” These are provided to you as HTTP header attributes as follows: X-MS-CLIENT-PRINCIPAL-NAME : youremail@gmail.com X-MS-CLIENT-PRINCIPAL-IDP: google In Asp.Net Core you can access them as follows  public IActionResult Index() { var user = Request.Headers["X-MS-CLIENT-PRINCIPAL-NAME"].ToString(); if (user == "youremail@gmail.com") { return View("Index"); //Authorized View } else { return View("Error"); //UnAuthorized View } }  If you want to see more information returned from Google you can access your sites but typing the following url https://[webappname].azurewebsites.net/.auth/me This will return a json object with all the information. Inside Asp.Net 4.6 application you can access this information via Claims already populated. ## Friday, November 18, 2016 ### How to read Google SpreadSheet using Sheets API v4, .Net and a Service Account In this post, I want to show how to read google spreadsheet data using google sheets v4 apis in .net via a service account. First of all you need an active google account and then next head to google developer console and create a project. I created a project called My Project. Next on the dashboard screen click on Enable API. On the following screen click on Google Sheets API link and Sheets API will be Enabled. Next go to Credentials page and then Click on Create Credentials. Click on the third option Service account key to create a service account. On the Create service account key click on New Service Account. Provide a service account name, select a role for your project. I am choosing owner here. Select Key type to be JSON. Finally click on create and the file will be downloaded on your machine. Know where this file is downloaded we will need this in a later step. On the credentials page, under service account keys you will be able to see the account you created in earlier step. Create a spreadsheet called Employees with one row and two columns and keep a note of the spreadsheet ID. The spreadsheetId can be found from the url of the google spreadsheet as shown below. In the url below the id in the {} bracket. Keep a note of this spreadsheet id we will need this in a later step. https://docs.google.com/spreadsheets/d/{your-spreadsheet-id}/edit#gid=0 Finally all setup is done. Lets head over to Visual Studio and create a new Console Project. Install nuget package Google.Sheets.Api.v4 from the nuget package manager. Next create three .cs classes and paste the following code as shown below. Fix all the references. 1. GoogleService.cs. This class is responsible for creating a sheetsservice using googlecredential.  public class GoogleService { private readonly string _googleSecretJsonFilePath; private readonly string _applicationName; private readonly string[] _scopes; public GoogleService(string googleSecretJsonFilePath, string applicationName, string[] scopes) { _googleSecretJsonFilePath = googleSecretJsonFilePath; _applicationName = applicationName; _scopes = scopes; } public GoogleCredential GetGoogleCredential() { GoogleCredential credential; using (var stream = new FileStream(_googleSecretJsonFilePath, FileMode.Open, FileAccess.Read)) { credential = GoogleCredential.FromStream(stream).CreateScoped(_scopes); } return credential; } public SheetsService GetSheetsService() { var credential = GetGoogleCredential(); var sheetsService = new SheetsService(new BaseClientService.Initializer() { HttpClientInitializer = credential, ApplicationName = _applicationName, }); return sheetsService; } }  2. SpreadSheet.cs. I created a spreadsheet class to store values that we read from the spreadsheet. There is a headerrow and there is rows. Each spreadsheetrow can have multiple rows. For demo purposes I created 2 columns. We need only two  public class SpreadSheet { public SpreadSheetRow HeaderRow { get; set; } public List<SpreadSheetRow> Rows { get; set; } } public class SpreadSheetRow { private readonly IList<Object> _values; public SpreadSheetRow(IList<Object> values) { _values = values; } public string Value0 => _getValue(0); public string Value1 => _getValue(1); private string _getValue(int columnIndex) { try { var s = _values[columnIndex].ToString(); return s; } catch (Exception ex) { return String.Empty; } } }  3. GoogleSpreadSheetReader.cs. This class relies on GoogleService class from step1. The GetSpreadSheet method accepts parameter spreadSheetId and a range parameter. I have a spreadsheet in which the first row was header row. If you wanted to read all the rows then you will have to modify this method.  public class GoogleSpreadSheetReader { private readonly SheetsService _sheetService; public GoogleSpreadSheetReader(GoogleService googleService) { _sheetService = googleService.GetSheetsService(); } public SpreadSheet GetSpreadSheet(string spreadSheetId,string range) { SpreadsheetsResource.ValuesResource.GetRequest request = _sheetService.Spreadsheets.Values.Get(spreadSheetId, range); ValueRange response = request.Execute(); IList<IList<Object>> values = response.Values; var rows = new List<SpreadSheetRow>(); for (int i = 1; i < values.Count; i++) { var row = new SpreadSheetRow(values[i]); rows.Add(row); } var headerRow = new SpreadSheetRow(values[0]); var spreadSheet = new SpreadSheet(); spreadSheet.HeaderRow = headerRow; spreadSheet.Rows = new List<SpreadSheetRow>(); spreadSheet.Rows.AddRange(rows); return spreadSheet; } }  4. Remember the JSON file we downloaded from Google put that file inside a folder called GoogleSecret inside your solution as shown below. Right click on the the file and hit F4 to view properties and change Copy to Output Directory to Copy Always. 5. Putting it all together inside Program.cs together to finally read data from Google SpreadSheet.  class Program { static void Main(string[] args) { Console.WriteLine(System.AppDomain.CurrentDomain.BaseDirectory.ToString()); var googleSecretJsonFilePath =$"{System.AppDomain.CurrentDomain.BaseDirectory}\\GoogleSecret\\GoogleSecret.json";
var applicationName = "My Project";

var range = "A:B";

}
}


Explanation of the above code. First we get hold of the JSON file path from the Bin directory then provide the name of your application, I called mine “My Project”.  Using scopes, we mention what level of access we have eg. SpreadsheetsReadonly.  We create a new instance of GoogleService then pass googleservice into GoogleSpreadSheetReader.  In the Employees spreadsheet we are using only two columns so we provide a range as A : B.  The range option is very interesting and you can get crazy with these ranges. Finally you get a spreadsheet by calling reader.GetSpreadSheet(spreadSheetId, range); Run the application by pressing F5 and you will see the output as JSON string.

If you have any questions then please let me know in the comments below.

## Monday, November 7, 2016

### Compare TFS 2015 Build definition changes

Today I discovered a feature that I felt was quite useful.  I always wanted to know what changes were made to build definition. If someone made a change to build defnition then you would want to know what changed. If you are using TFS 2015 or on Visual Studio Team Services then go to Build> Right click on Build definition and click on edit build definition. Under History, click on a build definition to see the diff button highlighted.

Once you click on that, you will see all the changes that were made to this build definition from the last time it was changed. Enjoy.