mercoledì 30 luglio 2014

Indirizzo IP dedicato sugli Azure Website

Quando si ha un indirizzo IP condiviso con altri siti / clienti (come accade in un ambiente multi-tenant), si possono avere alcuni problemi come, per esempio, vedersi inserito l'IP in una blacklist a causa del contenuto di altri siti.

L'unico modo affidabile per risolvere questo ed altri problemi è quello di configurare il website con un IP dedicato. Ciò significa che il sito utilizzerà un proprio IP, che quindi non sarà condiviso con altri siti. 

In Azure, è possibile facilmente ottenere un IP dedicato con l'attivazione dell'IP SSL. Questa opzione è disponibile solo per i siti del tier Standard e se il website utilizza un dominio personalizzato ci sono alcune considerazioni supplementari.

Se si utilizza un dominio personalizzato ed è stato configurato un record CNAME che punta al sito su Azure (ad esempio, mysite.azurewebsites.net), allora è piuttosto semplice - basta cambiare il record con il proprio provider DNS e quindi configurare IP-SSL.

Se, invece, è stato configurato un record A per risolvere l'hostname è necessario seguire questa procedura:

  1. Cambiare la mappatura dell'hostname (cioè www.mysite.com) da un record a un CNAME che punta al Website su Azure (cioè mysite.azurewebsites.net). Questa operazione non dovrebbe causare nessun downtime visto che entrambi i record risolveranno lo stesso indirizzo IP. Attendere un po' di tempo per essere sicuri che i DNS si siano replicati correttamente.
  2. Caricare un certificato valido per www.mysite.com sul Website utilizzando la pagina "Domains" nella tab "Configure". Normalmente il certificato va acquistato da un provider abilitato (una certification authority), ma se non si intende utilizzare effettivamente SSL, è possibile utilizzare un certificato auto-firmato che è facile da generare e, soprattutto, è gratis.
  3. Configurare un binding SSL IP Based per www.mysite.com. Questa opzione è disponibile sotto "SSL Binding" nella scheda "Configure". Per maggiori informazioni è possibile consultare la sezione "Configure SSL" nella guida per attivare l'SSL su Azure.

martedì 22 luglio 2014

Visual Studio Online ora supporta Azure Active Directory


Ieri il team vsalm di Microsoft ha iniziato il deploy dello sprint 68.

La parte più grande ed interessante dell'annuncio è la nuova fase del supporto di Azure Active Directory in VS Online. Hanno iniziato questo "viaggio" in maggio quando hanno annunciato le prime parti del supporto all'AAD a "Build". Poi hanno aggiunto alcune (poche) altre informazioni durante "TechEd" ma era una cosa passata un po' in sordina visto che, fino a questa settimana, non era possibile convertire o associare un account esistente ad AAD. Con questo deploy, invece, diventa possibile! Ufficialmente è in preview e quindi è necessario richiedere l'accesso alla funzionalità, ma sembra accettino tutte le richieste quindi non dovrebbe essere un grosso problema (anche perchè va fatto una volta sola). 

Con queste modifiche, è possibile:

  • Associare un OrgID (ovvero credenziali AAD/AD) alla sottoscrizione MSDN, se ce n'è una, ed usarla come grant per la licenza VSO
  • Creare un nuovo account collecato ad AAD
  • Collegare un account esistente ad AAD
  • Scollegare un account da AAD
  • Accedere sia con un Microsoft Account che con un OrgID (solo AAD oppure in sycn con l'Active Directory on premises) creando di fatto un SSO con le credenziali corporate, Office 365, ecc.



Per vedere tutti i dettagli riguardo il supporto AD e le altre cose incluse nell'update è possibile leggere il post originale di Brian Harry su MSDN.

lunedì 14 luglio 2014

ASP.net MVC ActionLink con un'Immagine: Parte 3 (Ajax)

Nei mie precedenti post abbiamo visto come implementare alcuni helper custom per aggiungere dei Glifi Bootstap o delle Immagini agli ActionLinks.

Ora invece vedremo come fare più o meno la stessa cosa ma aggiungendo il supporto Ajax.

Sostanzialmente quello che bisogna fare è costruire l'html tenendo conto di tutte le "cose" relative ad ajax (attributi, parametri, ecc) che il normale helper Ajax.ActionLink aggiunge.

In questo esempio ho usato la versione che utilizza i glifi, ma è ovviamente possibile usare lo stesso approccio anche nel caso si vogliano usare immagini normali.

/// <summary>
/// Create an Ajax.ActionLink with an associated glyphicon
/// </summary>
/// <param name="htmlHelper"></param>
/// <param name="linkText"></param>
/// <param name="actionName"></param>
/// <param name="controllerName"></param>
/// <param name="glyphicon"></param>
/// <param name="routeValues"></param>
/// <param name="htmlAttributes"></param>
/// <returns></returns>
public static MvcHtmlString ImageActionLink(this AjaxHelper ajaxHelper, string linkText, string actionName, string controllerName, string glyphicon, AjaxOptions ajaxOptions, RouteValueDictionary routeValues = null, object htmlAttributes = null)
{
 //Example of result:           
 //<a id="btnShow" href="/Customers/ShowArtworks?customerId=1" data-ajax-update="#pnlArtworks" data-ajax-success="jsSuccess" 
 //data-ajax-mode="replace" data-ajax-method="POST" data-ajax-failure="jsFailure" data-ajax-confirm="confirm" data-ajax-complete="jsComplete" 
 //data-ajax-begin="jsBegin" data-ajax="true">
 //  <i class="glyphicon glyphicon-pencil"></i>
 //  <span>Edit</span>
 //</a>

 var builderI = new TagBuilder("i");
 builderI.MergeAttribute("class", "glyphicon " + glyphicon);
 string iTag = builderI.ToString(TagRenderMode.Normal);

 string spanTag = "";
 if (!string.IsNullOrEmpty(linkText))
 {
  var builderSPAN = new TagBuilder("span");
  builderSPAN.InnerHtml = " " + linkText;
  spanTag = builderSPAN.ToString(TagRenderMode.Normal);
 }

 //Create the "a" tag that wraps
 var builderA = new TagBuilder("a");

 var requestContext = HttpContext.Current.Request.RequestContext;
 var uh = new UrlHelper(requestContext);

 builderA.MergeAttribute("href", uh.Action(actionName, controllerName, routeValues));

 //Ajax section
 builderA.MergeAttribute("data-ajax", "true");
 builderA.MergeAttribute("data-ajax-update", ajaxOptions.UpdateTargetId.StartsWith("#") ? ajaxOptions.UpdateTargetId : "#" + ajaxOptions.UpdateTargetId);
   
 if (!string.IsNullOrEmpty(ajaxOptions.InsertionMode.ToString()))
  builderA.MergeAttribute("data-ajax-mode", ajaxOptions.InsertionMode.ToString());            
 
 if (!string.IsNullOrEmpty(ajaxOptions.OnBegin))
  builderA.MergeAttribute("data-ajax-begin", ajaxOptions.OnBegin);
 
 if (!string.IsNullOrEmpty(ajaxOptions.OnComplete))
  builderA.MergeAttribute("data-ajax-complete", ajaxOptions.OnComplete);
   
 if (!string.IsNullOrEmpty(ajaxOptions.OnFailure))
  builderA.MergeAttribute("data-ajax-failure", ajaxOptions.OnFailure);
   
 if (!string.IsNullOrEmpty(ajaxOptions.OnSuccess))
  builderA.MergeAttribute("data-ajax-success", ajaxOptions.OnSuccess);
   
 if (!string.IsNullOrEmpty(ajaxOptions.Confirm))
  builderA.MergeAttribute("data-ajax-confirm", ajaxOptions.Confirm);
  
 if (!string.IsNullOrEmpty(ajaxOptions.HttpMethod))
  builderA.MergeAttribute("data-ajax-method", ajaxOptions.HttpMethod);

 if (htmlAttributes != null)
 {
  IDictionary<string, object> attributes = new RouteValueDictionary(htmlAttributes);
  builderA.MergeAttributes(attributes);
 }

 builderA.InnerHtml = iTag + spanTag;

 return new MvcHtmlString(builderA.ToString(TagRenderMode.Normal));
}


Come si può vedere, il codice è simile a quello che abbiamo utilizzato nei post precedenti con l'eccezione del fatto che stiamo estendendo un "AjaxHelper" invece che un "HtmlHelper" come accadeva prima e che abbiamo aggiunto una "ajax section".

giovedì 10 luglio 2014

Modifiche alle licenze di Visual Studio Online (in meglio)

La scorsa primavera Visual Studio Online ha visto la sua "promozione" da "Preview" a "General Availability". Tale processo ha incluso modifiche al branding, gli SLA, l'annuncio dei prezzi, la fine del programma early adopter e altro ancora.

Ora, il team VSO / TFS ha deciso di apportare questi due importanti cambiamenti al licensing (indicativamente nei prossimi due mesi):


  • qualsiasi account VS Online sarà in grado di avere un numero illimitato di utenti "stakeholder" con accesso ad un sottoinsieme di funzionalità, senza alcun costo aggiuntivo. 
  • il piano Visual Studio Online Advanced comprenderà l'accesso a tutte le funzionalità del Test hub.


Il team sta lavorando sodo per implementare questi cambiamenti di licenza e l'aspettativa è che avranno circa 2 sprint da copmletare per dinire tutto. Il che posizionerebbe la data effettiva di rilascio attorno a metà agosto.

In generale, l'obiettivo del team è quello di mantenere le licenze per VS Online e Team Foundation Server più "parallele" possibili - per limitare il grado di complessità. Come risultato, stanno evolvendo l'attuale esenzione dalle CAL di TFS "Work Item Web Access" per allinearla al modello di "stakeholder" di VSO. Ciò si tradurrà in più funzionalità a disposizione degli utenti TFS senza CAL. La speranza è di ottenere questo cambiamento a partire da Team Foundation Server 2013 Update 4.

Per leggere l'annunco originale di Brian Harry, visitate:
http://blogs.msdn.com/b/bharry/archive/2014/07/09/upcoming-vs-online-licensing-changes.aspx

mercoledì 9 luglio 2014

ASP.net MVC ActionLink con un'Immagine: Parte 2 (Immagine)

Nel mio precedente post ho parlato di come creare un helper custom per renderizzare un ActionLink con un glifo.

In questo post, invece, vedremo come creare un helper simile ma che usa delle immagini "normali" al posto dei bootstrap glyphs.

/// <summary>
/// Create an ActionLink with an associated image
/// </summary>
/// <param name="htmlHelper"></param>
/// <param name="linkText"></param>
/// <param name="actionName"></param>
/// <param name="controllerName"></param>
/// <param name="imagePath"></param>
/// <param name="routeValues"></param>
/// <param name="htmlAttributes"></param>
/// <returns></returns>
public static MvcHtmlString ImageImgActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, string imagePath, object routeValues = null, object htmlAttributes = null)
{
 //Exemple of result:
 //<a href="@Url.Action("Edit", new { id = Model.id_rod })">
 //  <i class="glyphicon glyphicon-pencil"></i>
 //  <span>Edit</span>
 //</a>

 if (imagePath.StartsWith("~/"))
 {
  imagePath = VirtualPathUtility.ToAbsolute(imagePath);
 }

 var builderImage = new TagBuilder("image");
 builderImage.MergeAttribute("src", imagePath);
 builderImage.MergeAttribute("alt", linkText);
 builderImage.MergeAttribute("style", "border=0");
 string imageTag = builderImage.ToString(TagRenderMode.SelfClosing);

 string spanTag = "";
 if (!string.IsNullOrEmpty(linkText))
 {
  var builderSPAN = new TagBuilder("span");
  builderSPAN.InnerHtml = " " + linkText;
  spanTag = builderSPAN.ToString(TagRenderMode.Normal);
 }

 //Create the "a" tag that wraps
 var builderA = new TagBuilder("a");

 var requestContext = HttpContext.Current.Request.RequestContext;
 var uh = new UrlHelper(requestContext);

 builderA.MergeAttribute("href", uh.Action(actionName, controllerName, routeValues));

 if (htmlAttributes != null)
 {
  IDictionary<string, object> attributes = new RouteValueDictionary(htmlAttributes);
  builderA.MergeAttributes(attributes);
 }

 builderA.InnerHtml = imageTag + spanTag;

 return new MvcHtmlString(builderA.ToString(TagRenderMode.Normal));
}


È possibile passare all'helper sia url relativi che assoluti (parametro imagePath) in modo da avere la massima flessibilità.

lunedì 7 luglio 2014

ASP.net MVC ActionLink con un'Immagine: Parte 1 (Glyph)

Se state usando ASP.net MVC saprete sicuramente che c'è un helper piuttosto utile che permette di creare dei link a delle Action dei controller: si tratta del Html.ActionLink.

È possibile utilizzare questo helper solo con link testuali, ma se invece volessimo aggiungere al link anche un glifo di bootstrap, come in questo esempio?



Beh, la risposta "fast&dirty" è quella di scrivere un po' di codice html direttamente nella pagina cshtml. Qualcosa tipo:

<a href="@Url.Action("Edit", new { id = Model.id })">
  <i class="glyphicon glyphicon-pencil"></i>
  <span>Edit</span>
</a>


Questo approccio è sicuramente più veloce di qualsiasi altro, se abbiamo bisogno di avere un comportamento di questo tipo solo in un paio di posti. Ma se avessimo necessità di avere i link con le immagini un po' ovunque nella nostra applicazione? (come ad esempio nel mio caso) Dovremmo copiare quel codice in N posti diversi e cambiarlo di volta in volta a seconda delle necessità... non è il massimo.

In questo caso, la soluzione migliore è quella di scrivere un custom helper che lo faccia per noi:

/// <summary>
/// Create an ActionLink with an associated glyphicon
/// </summary>
/// <param name="htmlHelper"></param>
/// <param name="linkText"></param>
/// <param name="actionName"></param>
/// <param name="controllerName"></param>
/// <param name="glyphicon"></param>
/// <param name="routeValues"></param>
/// <param name="htmlAttributes"></param>
/// <returns></returns>
public static MvcHtmlString ImageActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string controllerName, string glyphicon, object routeValues = null, object htmlAttributes = null)
{
 //Exemple of result:
 //<a href="@Url.Action("Edit", new { id = Model.id_rod })">
 //  <i class="glyphicon glyphicon-pencil"></i>
 //  <span>Edit</span>
 //</a>

 var builderI = new TagBuilder("i");
 builderI.MergeAttribute("class", "glyphicon " + glyphicon);
 string iTag = builderI.ToString(TagRenderMode.Normal);

 string spanTag = "";
 if (!string.IsNullOrEmpty(linkText))
 {
  var builderSPAN = new TagBuilder("span");
  builderSPAN.InnerHtml = " " + linkText;
  spanTag = builderSPAN.ToString(TagRenderMode.Normal);
 }            

 //Create the "a" tag that wraps
 var builderA = new TagBuilder("a");

 var requestContext = HttpContext.Current.Request.RequestContext;
 var uh = new UrlHelper(requestContext);
 
 builderA.MergeAttribute("href", uh.Action(actionName, controllerName, routeValues));

 if (htmlAttributes != null)
 {
  IDictionary<string, object> attributes = new RouteValueDictionary(htmlAttributes);
  builderA.MergeAttributes(attributes);
 }
  
 builderA.InnerHtml = iTag + spanTag;
 
 return new MvcHtmlString(builderA.ToString(TagRenderMode.Normal));
}

A questo punto sarà possibile invocare l'helper Html.ImageActionLink con gli stessi parametri che passeremmo al normale Html.ActionLink ma con in più la classe glyphicon della glyph image che vogliamo aggiungere al nostro link.

mercoledì 2 luglio 2014

Eliminare work item da TFS e VSO

Vi è mai capitato di creare un sacco di work item che in seguito si sono dovuti eliminare? A me si... soprattutto come utente della TFS Integration Platform. E quando le cose vanno male possono andare molto, molto male.

Anche se i work item si possono mettere nello stato "removed", rimangono ancora presenti nel sistema. L'unico modo disponibile out of the box per rimuovere gli elementi è quello recuperare l'ID per ogni elemento di lavoro che si desidera eliminare ed eseguire il seguente comando (da riga di comando) per ciascuno di essi:

witadmin destroywi /collection:CollectionURL /id:id [/noprompt]

Beh, questo approccio è ottimo se non avete un qualche migliaio di work item da eliminare.
Per questo motivo ho scritto un po' di codice che lo fa per me; in questo esempio ho creato un piccolo programma console ma è possibile utilizzare lo stesso codice in qualsiasi tipo di progetto.

using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

[...]

TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(new Uri("http://your_tfs_url:8080/tfs/CollectionName"));
WorkItemStore store = tpc.GetService();

string query = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = 'projectName'  AND  [System.AreaPath] UNDER 'projectName\_TOBEDELETED' ORDER BY [System.Id]";

WorkItemCollection wis = store.Query(query);
var wisIds = wis.Select( wi => wi.Id);

Console.WriteLine(string.Format("DESTROY {0} work items (they really can't be resurrected): y/n?", wis.Count));
ConsoleKeyInfo cki = Console.ReadKey();
Console.WriteLine();

if (cki.Key.ToString().ToLower() == "y")
{
 try
 {
  Console.WriteLine("Deleting....");
  var items = store.DestroyWorkItems(wisIds.ToArray());
  Console.WriteLine("DONE");
  foreach (var item in items)
  {
   Console.WriteLine(item.ToString());
  }
 }
 catch (Exception ex)
 {
  [...]
 }

}

Console.WriteLine("Finished");


La prima cosa che si può notare è che cerco elementi in una specifica area. Ho usato _TOBEDELETED in modo che sia ovvio ciò che accadrà agli elementi che finiscono lì dentro. Anche se ho lavorato con un utente che si lamentava che tutti i suoi file erano scomparsi e quando gli è stato chiesto dove li teneva ha indicato il cestino sul suo desktop!

Comunque, nel caso in cui si sia fatto un errore fatto un errore, il software consente di sapere quanti elementi si stanno per eliminare. È un semplice controllo, ma grazie a questo una volta ho evitato di eliminare più di 100.000 work item... come potete immaginare ho killato molto attentamente il programma (mai fidarsi l'opzione 'no' :) ).