Author Archives: Jiří Kanda

MsDeploy s NTLM zabezpečením

Nasazuji aplikace pomocí WDP na server (Windows Server 2008). Pokud msdeploy.exe předám v argumentech authType=basic a dále username a password, které používám pro přihlášení, vše funguje. Tím mám ověřeno, že můj účet má oprávnění, existuje website, atp. Nechci však psát své heslo a rád bych využil integrovaného zabezpečení.

Proto z příkazové řádky vynechávám username i password a nastavuji authType=ntlm. Pokus o nasazení aplikace se nedaří, jsem odmítnut (unauthorized).

Po drobném bádání dohledávám, že Windows autentizace je pro službu wmsvc standardně vypnutá a musí se zapnout v registrech: Pod HKEY_LOCAL_MACHINE\Software\Microsoft\WebManagement\Server je potřeba vložit DWORD klíč WindowsAuthenticationEnabled s hodnotou 1. A restartovat službu wmsvc.

Poté je již možné se k serveru připojit s využitím NTLM.

 

Azure Powershell – nejsou vidět subscriptions

Dnes jsem narazil na situaci, kdy jsem v powershellu neviděl subscriptions, které jsem měl dříve k dispozici. Nepomáhalo ani Add-AzureAccount, ani odebrání (všech) accountů a jejich opětovné přidání.

Jako řešení zafungovalo smazání složky C:\Users\USERNAME\AppData\Roaming\Windows Azure Powershell, ve které jsou standardně uloženy “Subscription Data Files”.

Azure WebJob se občas spouští dvakrát

Problém

Máme v Azure nasazeno několik Azure Website spolu s naplanovanými (scheduled) web job. Řešíme situaci, kdy se webjob spustí dvakrát, zhruba v rozmezí jedné minuty. (V našem konkrétním případě posíláme notifikační emaily a tyto nám přijdou dva.)

Analýzou historie zjišťuji, že scheduler se webjob pokusil úlohu spustit dokonce třikrát.

webjob-history

Přičemž log jednotlivých pokusů vypadá takto:

  1. Http Action – Request to host ‚(…).scm.azurewebsites.net‘ failed: The timeout (30 seconds) was reached.
  2. Http Action – Response from host ‚(…).scm.azurewebsites.net‘: ‚Conflict‘ Response

    Body: Cannot start a new run since job is already running.
  3. Http Action – Response from host ‚(…).scm.azurewebsites.net‘: ‚Accepted‘ Response

Položky logu interpretuji takto:

  • Bod 1 úspěšně spustit webjob, ale nebyl schopen v intervalu 30 sekund toto zjistit.
  • Bod 2 se pokusil spustit webjob, ale ten již běžel, takže byl oznámen konflikt.
  • Bod 3 se opět pokusil spustit webjob, ten již doběhl, takže byl webjob úspěšně (ve skutečnosti podruhé) spuštěn.

Řešení

Po přečtení

se pokouším interpretovat (snad) klíčovou větu „If your site runs continuous web jobs, you should enable Always On, or the web jobs may not run reliably“ tak, že Always On zapínám i pro náš scheduled Web Job. Pomáhá to.

Volba Always On se nastavuje v Azure portálu, v nastavení Azure Website, se přepínačem Always On na záložce Configure.

Přístup k Team Foundation Serveru z Azure Website

Problém

V jedné z aplikací přistupujeme pomocí API jako klient do Team Foundation Serveru 2013.

ICredentials credentials = new NetworkCredential(...);
TfsTeamProjectCollection collection = new TfsTeamProjectCollection(new Uri(...), credentials);
collection.EnsureAuthenticated();

Po migraci aplikace z vlastních serverů do Azure Website nám připojení přestalo fungovat, konkrétně začalo docházet k této výjimce:

System.IO.IOException: The specified registry key does not exist.
   at Microsoft.Win32.RegistryKey.Win32Error(Int32 errorCode, String str)
   at Microsoft.Win32.RegistryKey.CreateSubKeyInternal(String subkey, RegistryKeyPermissionCheck permissionCheck, Object registrySecurityObj, RegistryOptions registryOptions)
   at Microsoft.Win32.RegistryKey.CreateSubKey(String subkey, RegistryKeyPermissionCheck permissionCheck, RegistryOptions options)
   at Microsoft.VisualStudio.Services.Common.TokenStorage.RegistryTokenStorageHelper.GetRootKey(String subkeyName)
   at Microsoft.VisualStudio.Services.Common.TokenStorage.RegistryTokenStorage.RetrieveToken(VssTokenKey tokenKey)
   at Microsoft.VisualStudio.Services.Common.TokenStorage.VssTokenStorage.Retrieve(VssTokenKey tokenKey)
   at Microsoft.TeamFoundation.Client.TfsClientCredentialStorage.RetrieveToken(Uri serverUrl, VssCredentialsType credentialType)
   at Microsoft.TeamFoundation.Client.CookieCredential.OnCreateTokenProvider(Uri serverUrl, HttpWebResponse response)
   at Microsoft.TeamFoundation.Client.IssuedTokenCredential.CreateTokenProvider(Uri serverUrl, HttpWebResponse response, IssuedToken failedToken)
   at Microsoft.TeamFoundation.Client.TfsClientCredentials.TryGetTokenProvider(Uri serverUrl, IssuedTokenProvider& provider)
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpRequestHelpers.PrepareWebRequest(HttpWebRequest webRequest, Guid sessionId, String operationName, CultureInfo cultureInfo, TfsRequestSettings settings, TfsClientCredentials credentials, IdentityDescriptor impersonate, IssuedToken& currentToken, IssuedTokenProvider& tokenProvider)
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpRequestHelpers.CreateSoapRequest(Uri requestUri, Guid sessionId, String soapAction, String operationName, CultureInfo cultureInfo, TfsRequestSettings settings, TfsClientCredentials credentials, IdentityDescriptor impersonate, IssuedToken& currentToken, IssuedTokenProvider& tokenProvider)
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpWebRequest.CreateWebRequest()
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpWebRequest.SendRequest()
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpRequestChannel.Request(TfsMessage message, TimeSpan timeout)
   at Microsoft.TeamFoundation.Client.Channels.TfsHttpClientBase.Invoke(TfsClientOperation operation, Object[] parameters, TimeSpan timeout, Object[]& outputs)
   at Microsoft.TeamFoundation.Framework.Client.Registration.GetRegistrationEntries(String toolId)
   at Microsoft.TeamFoundation.Framework.Client.RegistrationProxy.GetRegistrationEntries(String toolId)
   at Microsoft.TeamFoundation.Framework.Client.RegistrationService.RefreshMemoryCache()
   at Microsoft.TeamFoundation.Framework.Client.RegistrationService.RefreshCachesIfNeeded(Boolean direct)
   at Microsoft.TeamFoundation.Framework.Client.RegistrationService.GetRegistrationEntries(String toolId)
   at Microsoft.TeamFoundation.Framework.Client.PreFrameworkServerDataProvider.FindServiceLocation(String serviceType, String toolId)
   at Microsoft.TeamFoundation.Framework.Client.PreFrameworkServerDataProvider.LocationForCurrentConnection(String serviceType, Guid serviceIdentifier)
   at Microsoft.TeamFoundation.Client.TfsConnection.EnsureProviderConnected()
   at Microsoft.TeamFoundation.Client.TfsConnection.EnsureAuthenticated()

TFS klient v průběhu přihlašování šahá do registrů. Po dlouhém pátrání pomocí .NET Reflectoru jsem dospěl k názoru, že v nich hledá dříve uložený autentizační token. To pro nás není podstatné, důležité je, že se bez toho můžeme obejít.

Řešení

Řešením je použití třídy TfsClientCredentials. Tu vyrobíme pomocí WindowCredentials a tu s pomocí dvou tříd: NetworkCredentials a SimpleWebTokenCredential, přičemž v NetworkCredentials máme údaje k autorizaci a SimpleWebTokenCredential nemusí nést žádné hodnoty, pouze zajišťuje workaround popsaného problému.

NetworkCredential networkCredentials = new NetworkCredential(...)
TfsClientCredentials tfsCredentials = new TfsClientCredentials(new WindowsCredential(networkCredentials), new SimpleWebTokenCredential(null, null), allowInteractive: false);
TfsTeamProjectCollection collection = new TfsTeamProjectCollection(new Uri(...), tfsCredentials);
collection.EnsureAuthenticated();

Azure Website: Pokus o stažení souboru *.woff souboru končí chybou 404, přestože existuje

Abych byl spravedlivý, nejde jen o Azure Website, ale i o Windows Servery 2008. Důvodem je nedefinovaný Content-Type (resp. MimeType) pro daný typ.

Ten můžeme přidat do web.configu:

<system.webServer>
<staticContent>
<mimeMap fileExtension=".woff" mimeType="application/x-woff" />
</staticContent>
</system.webServer>

Pokud však takovouto konfiguraci nasadíme na server, kde je již Content-Type (resp. MimeType) pro danou příponu nastaven, dojde k pádu aplikace. Zobrazená chyba nám oznámí, že se pokoušíme nastavit MimeType pro typ, který je již definován.

Pokud chceme mít jednu konfiguraci pro všechny serveru, ať již mají nebo nemají definovaný MimeType pro příponu woff, můžeme použít trik s remove:

<system.webServer>
<staticContent>
<remove fileExtension=".woff" />
<mimeMap fileExtension=".woff" mimeType="application/x-woff" />
</staticContent>
</system.webServer>

Azure: Nedaří se vytvořit databázi importem z *.bacpac

Při pokusu o založení nové databáze importem z *.bacpac v portálu se zobrazuje potvrzení, že přístup do Azure Storage je v pořádku a toto oznámení je následováno chybou oznamující, že nelze vložit požadavek na import databáze.

Heslo k databázovému serveru obsahovalo diakritiku, nápad zkusit heslo bez diakritiky se osvědčil –  po změně hesla funguje import bez problémů.

CS1647: An expression is too long or complex to compile (workaround)

Na jednom z projektů jsme po úpravě ASPX stránky s enormním objemem html kódu narazili na zajímavou chybu zobrazenou v kompilátorem: CS1647: An expression is too long or complex to compile. Velmi pozoruhodný je též popis chyby na MSDN a doporučené řešení.

Jako funkční workaround zafungovalo doporučení z blogu henrywrites a sice vložit do ASPX stránky serverový komentář, například

<%
// workaround pro CS1647: An expression is too long or complex to compile
%>