viernes, 15 de julio de 2016

Voracidad de Search afecta a los Workflows de Project Server. HTTP 500 The requested service, xxx could not be activated.


"Cuando una mariposa agita sus alas en Nueva Zelanda, alguien se come un puñado de garbanzos torraos en Albacete".

O algo así decía el refrán creo recordar, pero viene a cuento por la extraña sucesión de causas encadenadas de hoy.

Nuestro paciente venía aquejado de errores en un flujo intestinal de trabajo, el cual daba un error para algunos proyectos que se quedaban bloqueados, uno de estos diversos errores que los workflows de Project se afanan, con mucha creatividad siempre, en producir:


RequestorId: dd6ae4d8-59fc-a419-0000-000000000000. Details: An unhandled exception occurred during the execution of the workflow instance. Exception details: System.ApplicationException: HTTP 500 The requested service, 'http://es1intra02v:32843/32dbfb0876734f6493e48270df96c4cf/AppMng.svc' could not be activated. See the server's diagnostic trace logs for more information. {"SPRequestGuid":["dd6ae4d8-59fc-a419-8ae7-fb670df49745"],"request-id":["dd6ae4d8-59fc-a419-8ae7-fb670df49745"],"X-FRAME-OPTIONS":["SAMEORIGIN"],"SPRequestDuration":["9294"],"SPIisLatency":["0"],"MicrosoftSharePointTeamServices":["15.0.0.4569"],"X-Content-Type-Options":["nosniff"],"X-MS-InvokeApp":["1; RequireReadOnly"],"Date":["Tue, 12 Jul 2016 10:04:41 GMT"],"Server":["Microsoft-IIS\/8.5"],"X-Powered-By":["ASP.NET"]} at Microsoft.Activities.Hosting.Runtime.Subroutine.SubroutineChild.Execute(CodeActivityContext context) at System.Activities.CodeActivity.InternalExecute(ActivityInstance instance, ActivityExecutor executor, BookmarkManager bookmarkManager) at System.Activities.Runtime.ActivityExecutor.ExecuteActivityWorkItem.ExecuteBody(ActivityExecutor executor, BookmarkManager bookmarkManager, Location resultLocation)

En negrita dejo lo importante del asunto, y lo importante no es que ese servicio falle, si no que un servicio falla con "could not be activated", investigando un poquito en el vademecum google, hallamos que la caída de servicios con un "could not be activated" suele estar íntimamente relacionada a la falta de recursos en el servidor.

Efectivamente, así era:

Memoria, casi a tope.


Disco duro, como vimos en otro post, no respeta la recomendación de Microsoft de mantener libre en disco al menos el doble de la memoria RAM instalada.

Task Manager, indicando que el proceso noderunner.exe, el encargado de dar vida al sistema Search de SharePoint, estaba siendo un elemento voraz con los recursos:


El fonendoscopio Health Analyzer, nuestro sempiterno amigo que llevamos siempre colgado del cuello:



Para salir del embrollo por ahora, bastó con reiniciar los servicios de Windows SharePoint Search y Host Controller:


Y reiniciar los workflows que estaban dando problemas desde la configuración de PWA en "Change or restart workflows", causando a los usuarios la molestia de comenzar de nuevo con los flujos, el menor de los males en estos casos.

Debido a que esto es pan para hoy y hambre para mañana, se ha echado la bronca recomendado al cliente que aumente los recursos de sus máquinas, poniendo unos 40 o 50 GB más de disco duro en cada servidor como mínimo tanto en los servidores SharePoint como en el de SQL Server, donde además debería echar un vistazo al crecimiento de las bases de datos, y que aumente la RAM hasta los 24 GB, para evitar que esto se siga repitiendo.

jueves, 14 de julio de 2016

Ejecutar scripts no firmados en PowerShell. Script is not digitally signed. The script will not execute on the system.


Al intentar darle la medicina a nuestro paciente intentando ejecutar un script ps1, éste parecía presentar un cuadro alérgico que le impedía aceptar el tratamiento, generando una respuesta autoinmune de falta de confianza por no estar firmado:


PS Microsoft.PowerShell.Core\FileSystem::\\...> .
\patchfarm.ps1
.\patchfarm.ps1 : File \\...\PatchFarm.ps1
cannot be loaded. The file \\...\PatchFarm.ps1
is not digitally signed. The script will not execute on the system. For more
information, see about_Execution_Policies at
http://go.microsoft.com/fwlink/?LinkID=135170.
At line:1 char:1
+ .\patchfarm.ps1
+ ~~~~~~~~~~~~~~~
    + CategoryInfo          : SecurityError: (:) [], PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess

Para que esto dejase de producirse se ha pedido al sistema que confíe en nuestro script mediante un Set-ExecutionPolicy, pero ojo, no de cualquier forma, ya que si no indicamos explícitamente un ámbito, podríamos estar generando un enorme agujero de seguridad, dejando la granja disponible para cualquier malhechor.



De modo que nuestro comando queda así.

Set-ExecutionPolicy Unrestricted -Scope Process

Indicar el ámbito Process hará que al cerrar la ventana de PowerShell todo vuelva a su estado original, de modo que si volvemos a abrir una ventana los scripts sin firmar no se ejecutarán.

Especificación formal de Set-ExecutionPolicy:

https://technet.microsoft.com/es-es/library/hh849812.aspx

lunes, 4 de julio de 2016

La mejor manera (que sepamos) de instalar Cumulative Updates o Service Pack 1


A veces nuestros pacientes llegan a nuestra clínica porque simplemente tienen algún complejo, partes de su cuerpo que funcionan mal y requieren correcciones o simplemente quieren sentirse mejor con su aspecto, por lo que se apuntan a hacerse un lifting, una liposucción para verse mejor en el espejo.

Qué mejor que una buena acutalización de software para que nuestros SharePoints salgan de la clínica con una nueva vida por delante llena de mejoras y fortalezas.

La parte fea del asunto es que hay que someter a nuestros pacientes a intensos dolores mientras dura la cirugía, y estos dolores afectan a sus usuarios, que podrían quejarse si les dejamos sin servicio demasiado tiempo o la cosa se nos va de las manos y la cirugía se complica haciendo que nuestro paciente pueda quedar tendido en la cama de operaciones sin constantes vitales.

Para evitar problemas, hemos elaborado buscando aquí y allá tras una serie de experiencias, una guía para realizar las operaciones sin muchos quebraderos de cabeza.

Necesitamos:

1.) Parche que queremos instalar, bien sea el Service Pack 1 o un Cumulative Update de cualquier mes, debemos recordar que para instalar cualquier Cumulative Update después de Abril de 2013 es obligatorio tener instalado el Service Pack 1.

Guia de actualizaciones de SharePoint.

2.) Un script de aprovisionamiento provisto por MSFT, código al final del artículo, que prepara el entorno para la instalación desactivando algunos servicios, algunos muy críticos cuya desactivación es muy importante como el servicio de búsquedas, instalando el parche y reactivando de nuevo los servicios para que todo vuelva a la normalidad

3.) Pasar el asistente de tecnologías y productos SharePoint una vez hecho lo anterior, preferentemente con un comando de PowerShell psconfig.

Es importante que tanto el parche como el script lo tengamos en la misma carpeta de Windows, la instalación puede ser hecha desde una ruta de red sin problemas.


Pasos:

1.) Ingresamos en el sistema con la cuenta de instalación y abrimos un PowerShell como administrador para ejecutar PatchFarm.ps1

                            

Recomendamos a los doctores aprovechar para tomar un relaxing cup of café con leche mientras se instala todo, a no ser que tengan más pacientes en lista, que deberían estar siendo atendidos, no invitamos desde aquí en absoluto a la inactividad habiendo cosas por hacer.

chaca-chaca-chaca...


chaca-chaca-chaca...


chaca-chaca-chaca...


2.) Una vez haya terminado exitosamente la instalación del parche, ejecutamos el asistente de productos y tecnologías con:

PSConfig.exe -cmd upgrade -inplace b2b -wait -force

Con esto ya habremos acabado la intervención, el post operatorio exige a nuestro paciente un leve reposo basado en el reinicio del servidor.

3.) Verificar desde la Central de Aministración que la actualización se ha llevado a cabo con éxito, desde patch installation status:



Código del script PathFarm.ps1:

<#============================================================
  //
  // Microsoft provides programming examples for illustration only,
  // without warranty either expressed or implied, including, but not
 // limited to, the implied warranties of merchantability and/or
  // fitness for a particular purpose.
  //
  // This sample assumes that you are familiar with the programming
  // language being demonstrated and the tools used to create and debug
  // procedures. Microsoft support professionals can help explain the
  // functionality of a particular procedure, but they will not modify
  // these examples to provide added functionality or construct
  // procedures to meet your specific needs. If you have limited
  // programming experience, you may want to contact a Microsoft
  // Certified Partner or the Microsoft fee-based consulting line at
  //  (800) 936-5200 .
  //
  // For more information about Microsoft Certified Partners, please
  // visit the following Microsoft Web site:
  // https://partner.microsoft.com/global/30000104 
  //
  // Author: Russ Maxwell (russmax@microsoft.com)
  //
  // ---------------------------------------------------------- #>
###########################
##Ensure Patch is Present##
###########################
$patchfile = Get-ChildItem | where{$_.Extension -eq ".exe"}
if($patchfile -eq $null)
{
  Write-Host "Unable to retrieve the file.  Exiting Script" -ForegroundColor Red
  Return
}
########################
##Stop Search Services##
########################
##Checking Search services##
$srchctr = 1
$srch4srvctr = 1
$srch5srvctr = 1
$srv4 = get-service "OSearch15"
$srv5 = get-service "SPSearchHostController"
If(($srv4.status -eq "Running") -or ($srv5.status-eq "Running"))
  {
    Write-Host "Choose 1 to Pause Search Service Application" -ForegroundColor Cyan
    Write-Host "Choose 2 to leave Search Service Application running" -ForegroundColor Cyan
    $searchappresult = Read-Host "Press 1 or 2 and hit enter"
    Write-Host
  
   if($searchappresult -eq 1)
    {
        $srchctr = 2
        Write-Host "Pausing the Search Service Application" -foregroundcolor yellow
        Write-Host "This could take a few minutes" -ForegroundColor Yellow
        $ssa = get-spenterprisesearchserviceapplication
        $ssa.pause()
    }
  
    elseif($searchappresult -eq 2)
    {
        Write-Host "Continuing without pausing the Search Service Application"
    }
    else
    {
        Write-Host "Run the script again and choose option 1 or 2" -ForegroundColor Red
        Write-Host "Exiting Script" -ForegroundColor Red
        Return
    }
  }
Write-Host "Stopping Search Services if they are running" -foregroundcolor yellow
if($srv4.status -eq "Running")
  {
    $srch4srvctr = 2
    set-service -Name "OSearch15" -startuptype Disabled
    $srv4.stop()
  }
if($srv5.status -eq "Running")
  {
    $srch5srvctr = 2
    Set-service "SPSearchHostController" -startuptype Disabled
    $srv5.stop()
  }
do
  {
    $srv6 = get-service "SPSearchHostController"
    if($srv6.status -eq "Stopped")
    {
        $yes = 1
    }
    Start-Sleep -seconds 10
  }
  until ($yes -eq 1)
Write-Host "Search Services are stopped" -foregroundcolor Green
Write-Host
#######################
##Stop Other Services##
#######################
Set-Service -Name "IISADMIN" -startuptype Disabled
Set-Service -Name "SPTimerV4" -startuptype Disabled
Write-Host "Gracefully stopping IIS W3WP Processes" -foregroundcolor yellow
Write-Host
iisreset -stop -noforce
Write-Host "Stopping Services" -foregroundcolor yellow
Write-Host
$srv2 = get-service "SPTimerV4"
  if($srv2.status -eq "Running")
  {$srv2.stop()}
Write-Host "Services are Stopped" -ForegroundColor Green
Write-Host
Write-Host
##################
##Start patching##
##################
Write-Host "Patching now keep this PowerShell window open" -ForegroundColor Magenta
Write-Host
$starttime = Get-Date
$filename = $patchfile.basename
$arg = "/passive"
Start-Process $filename $arg
Start-Sleep -seconds 20
$proc = get-process $filename
$proc.WaitForExit()
$finishtime = get-date
Write-Host
Write-Host "Patch installation complete" -foregroundcolor green
Write-Host

##################
##Start Services##
##################
Write-Host "Starting Services Backup" -foregroundcolor yellow
Set-Service -Name "SPTimerV4" -startuptype Automatic
Set-Service -Name "IISADMIN" -startuptype Automatic
##Grabbing local server and starting services##
$servername = hostname
$server = get-spserver $servername
$srv2 = get-service "SPTimerV4"
$srv2.start()
$srv3 = get-service "IISADMIN"
$srv3.start()
$srv4 = get-service "OSearch15"
$srv5 = get-service "SPSearchHostController"
###Ensuring Search Services were stopped by script before Starting"
if($srch4srvctr -eq 2)
{
    set-service -Name "OSearch15" -startuptype Automatic
    $srv4.start()
}
if($srch5srvctr -eq 2)
{
    Set-service "SPSearchHostController" -startuptype Automatic
    $srv5.start()
}
###Resuming Search Service Application if paused###
if($srchctr -eq 2)
{
    Write-Host "Resuming the Search Service Application" -foregroundcolor yellow
    $ssa = get-spenterprisesearchserviceapplication
    $ssa.resume()
}
Write-Host "Services are Started" -foregroundcolor green
Write-Host
Write-Host
Write-Host "Script Duration" -foregroundcolor yellow
Write-Host "Started: " $starttime -foregroundcolor yellow
Write-Host "Finished: " $finishtime -foregroundcolor yellow
Write-Host "Script Complete"


Referencias del presente artículo, aparte de la propia experiencia:

https://www.linkedin.com/pulse/step-by-step-cumulative-update-sharepoint-2013-dyung-ngo
http://blogs.msdn.com/b/russmax/archive/2013/04/01/why-sharepoint-2013-cumulative-update-takes-5-hours-to-install.aspx


martes, 28 de junio de 2016

FIMSynchronizationService. No se puede cambiar la cuenta de servicio en Sincronización de Perfiles de Usuario.


Health Analyzer de SharePoint, le quitó el termómetro bajo la axila a nuestro paciente avisando de que la cuenta de la granja estaba siendo utilizada para otros fines que no fuera precisamente el suyo, el de administrar la granja y poco más.


Descubrimos en un primer exámen que la cuenta estaba siendo utilizada en la aplicación de servicio de Sincronización de Perfiles de Usuario, por lo que se procedió inmediatamente a solucionar el problema cambiándola por otra.

Sin embargo, al intentar cambiar la cuenta de servicio utilizada en Sincronización de Perfiles por otra cuenta, apareció otro síntoma de la nada:

An object of the type Microsoft.SharePoint.Administration.SPWindowsServiceCredentialDeploymentJobDefinition named "windows-service-credentials-FIMSynchronizationService" already exists under the parent Microsoft.Office.Server.Administration.ProfileSynchronizationService named 
"FIMSynchronizationService".  Rename your object or delete the existing object.

El problema se resolvió buscando el Timer Job que daba problemas y eliminando su instancia y finalmente se pudo sin problemas cambiar la cuenta de la granja en el servicio de Sincronización de Perfiles por otra cuenta.








lunes, 20 de junio de 2016

El botón Enviar - Submit en las etapas de un workflow de Project Server aparece desactivado.


Nos acaba de llegar a urgencias un señor que se encontraba más gris de lo normal en uno de sus órganos más importantes, el botón Enviar en la cinta, para enviar datos al flujo de trabajo de un proyecto.

Después de intentar Editar en la PDP del proyecto, el botón no se activaba, por lo que era imposible continuar con el trabajo.


Al mismo tiempo, al observar más abajo pudimos comprobar que anteriormente el flujo había alcanzado el estado de error:



La primera ocurrencia fue por tanto la de entrar al estado del workflow y tomarle el pulso, estado: Suspendido. Lo más obvio, terminarlo.

Sin embargo esto no fue suficiente para que nuestro paciente se curara pues éste seguía presentando los mismo síntomas.

En nuestro caso funcionó hacer un restart masivo de todos los proyectos desde la administración.

PWA settings / Change or restart workflows

Aquí seleccionamos los tipos de proyectos, y dentro de cada tipo podemos ir haciendo un reinicio de masivo de los proyectos que hayan presentado problemas del botón submit inactivo.


Hemos de asegurarnos de que marcamos reiniciar el actual workflow para los proyectos seleccionados.

En este caso, el error marcado era un HTTP500, por lo que hemos de advertir, eso sí, que existen otros motivos por los que a veces el importante botón Enviar podría aparecer inactivo, por ejemplo por problemas de permisos.

Project Professional siempre pregunta por las credenciales.


Nuestro paciente llegó a la consulta aquejado de una molestísima hemorroide  caja de diálogo, aparentemente imposible de quitar, que aparecía cada vez que ingresaba en Project Professional causándole molestos y fuertes picores por la zona:



Para evitar que el prompt siguiera apareciendo, hubo que entrar en opciones de Internet, pestaña Seguridad, Zona de intranet, y marcar la opción "ingresar con usuario y contraseñas actuales"



Nuestro paciente ya nunca más se quejó de picores por la zona y pudo entrar a Project Professional sin ser preguntado en todo momento por las credenciales.

Es importante que ya antes hubiéramos metido las credenciales con las que queremos ingresar, bien fuere en Project o en Windows.

También tenemos que tener marcada, dentro de Project Professional la opción "usar cuenta predeterminada", en Archivo, Información, Administrar Cuentas.


jueves, 16 de junio de 2016

Crear contextos ProjectContext o ClientContext para Project Online en una Provider-hosted app con Project o SharePoint Online con CSOM y TokenHelper


CSOM, nuestra api de desarrollo para conectar desde un cliente contra servicios web de SharePoint, tiene un comportamiento muy distinto a la antigua api de servidor de SharePoint o a la muy apreciada por mí, digo con mucho sarcasmo con conste, api PSI de Project.

En este caso nuestro paciente quería autenticarse remotamente contra los servicios de Project Server, hasta aquí bien, utilicemos un objeto ProjectContext o ClientContext como hemos hecho siempre y ya está, idea feliz.

Entonces nos damos cuenta de que la cosa no es tan sencilla. Como muchos ya sabréis, Microsoft nos provee en nuestros proyectos de una clase llamada TokenHelper para hacernos la vida más fácil, o eso nos dijeron al menos.

Además, nuestro paciente tuvo la increíble ocurrencia de que no le bastaba sólo con necesitar obtener un contexto de sí mismo (la persona que se conecta a SharePoint) si no que además también quiere obtener contextos de otros usuarios, como por ejemplo alguien con privilegios de administrador para poder obtener datos de toda la colección de sitios o de datos de Project Server de otros usuarios. Esto es, lo que antiguamente conocíamos como "pf, tío no te compliques!, haz un RunWithElevatedPrivileges y yatá!"

Tanta elucubración dio como resultado una clase que llamamos Connection.cs, con un método estático y público que inicializaremos al principio de la carga de la página o del servicio que estemos programando y que mágicamente nos generará dos contextos de ejecución, uno para el usuario que está conectándose y otro para la cuenta que nosotros queramos, todo ello sacado del HttpContext que realiza el ingreso en SharePoint, cosas del funcionamiento de TokenHelper y el querystring que no vamos a explicar aquí.

En primer lugar, en nuestra clase hemos de tener dos propiedades estáticas de sólo lectura de tipo ProjectContext, llamadas pwaContextServicio y pwaContextUsuario, y después utilizar este método.
public static void InitializeProjectServerContext(HttpRequest request, bool pageIsPostback)
        {
            if (!pageIsPostback)
            {
                pwaContextServicio = ProjectConnector.GetProjectOnlineContext(
                    request.QueryString["SPHostUrl"].ToString(),
                    "usuario",
                    "password",
                    "dominio");

                pwaContextUsuario = TokenHelper.GetProjectContextWithContextToken(
                 request.QueryString["SPHostUrl"].ToString(),
                 TokenHelper.GetContextTokenFromRequest(request),
                 request.Url.Authority);
             
            }
        }
pwaContextServicio se cargará con la cuenta elegida por nosotros y pwaContextUsuario ser cargará con la cuenta de las credenciales provistas al ingresar a SharePoint Online.

El asunto más gordo que requirió más horas de cirugía invasiva fue la clase ProjectConnector, que a su vez es otra clase creada por nosotros que utilizamos para este menester, su método estático GetProjectOnlineContext es el que realiza la magia.

Esta clase está creada a partir de encapsular en un proyecto la clase Office365ClaimsHelper, todo esto se puede encontrar en GitHub.

ProjectConnector, creada por nosotros, la gente de Doctor SharePoint:
using Microsoft.ProjectServer.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RC.Project
{
    public static class ProjectConnector
    {
        private static string connectUserName;
        private static string connectPwd;
        private static string pwaPath;
        private static string projDomain;
        public static ProjectContext GetProjectOnlineContext(string pPwaPath, string pUserName, string pPassword, string pDomain)
        {
            connectUserName = pUserName;
            connectPwd = pPassword;
            projDomain = pDomain;
            pwaPath = pPwaPath;
            return ProjectContext;
        }

        private static ProjectContext ProjectContext
        {
            get
            {
                var pc = new ProjectContext(pwaPath);
                var claimsHelper = ClaimsHelper;
                pc.ExecutingWebRequest += claimsHelper.clientContext_ExecutingWebRequest;
                return pc;
            }
        }
        private static Office365ClaimsHelper ClaimsHelper
        {
            get
            {
                return new Office365ClaimsHelper(new Uri(pwaPath),
                                                    string.Format("{0}@{1}", connectUserName, projDomain),
                                                    connectPwd);
            }
        }
    }
}
A su vez, Office365ClaimsHelper se apoya en otras clases, por tanto, nos encargaremos primero de descargar del GitHub las siguientes clases y encapsularlas en nuestra biblioteca de clases junto a ProjectContext:

IWSTrustFeb2005Contract
Office365ClaimsHelper
RequestBodyWriter
WsTrustFeb2005ContractClient

Una vez construído todo esto ya tendremos una forma de conectar con el usuario que queramos a Project Server (o a SharePoint) pasándole el dominio, usuario y password.

Recordemos siempre que un objeto de contexto de Project siempre está siendo heredado de uno de SharePoint, por lo que crear a partir de ProjectContext un ClientRuntimeContext es tan sencillo como añadir esto:
private static ClientRuntimeContext InitializeSharePointContext(ProjectContext pwaContextServicio)
        {
            Web web = pwaContextServicio.Web;
            pwaContextServicio.Load(web);
            pwaContextServicio.ExecuteQuery();
            return web.Context;
        }
Y nada más, le damos a nuestro paciente un par de Nolotiles  test de ejecución y para casa.