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.

Drives running out of free space in SharePoint 2013, not always.False positive.


Detectamos en uno de nuestros pacientes que Health Analyzer emitía el siguiente problema:


Cuando encontremos esto de que tenemos que tener el doble de espacio libre en los discos duros que memoria tenemos instalada en la máquina, no debemos echarnos a la cabeza las manos así sin más, no debemos llamar a sistemas para que nos aumente el tamaño de los discos de forma que tengamos espacio libre para almacenar 5 millones de copias de Lo que el viento se llevó en HD 4K versión extendida, no.

Debemos de tener en cuenta que esta regla salta teniendo en cuenta a TODOS los discos duros que tenemos y con que sólo uno de ellos no la cumpla aunque no sea ese disco duro crítico donde almacenamos logs o archivos de instalación, la regla saltará.

Al revisar nuestros discos duros vimos que no era el disco duro de la instalación o de logs de SharePoint el afectado.



Lo mejor por tanto en nuestro caso fue ignorar esta regla.

También tenemos la opción de crear nuestras propias reglas para que Health Analyzer funcione con un poco más de sentido común.