j.a.estevan

Si no eres parte de la solución, eres parte del problema

[HOWTO] Tomar el control de un backup de AX 2012 mediante PowerShell

Hace unos días hacía un comentario en twitter acerca de las posibilidades de PowerShell que me trajo mucho feedback.

Supongo que hay mucho interés en conocer más sobre las bondades de PowerShell aplicadas a Microsoft Dynamics AX, y estoy preparando un artículo largo (probablemente una serie de varios artículos) sobre el tema que estarán terminados en las próximas semanas. PowerShell es una herramienta extremadamente potente y a la vez extraña. Los programadores habitualmente la consideran una herramienta de administración (es una consola) mientras que los sysadmin suelen verla como algo para programadores (es código). Curioso, ¿verdad?

Mientras termino el artículo, y para ir abriendo boca, un problema común en todas las versiones de AX es que, tras restaurar una copia de seguridad de AX de un entorno a otro, hay varias situaciones en las que no podremos acceder al sistema porque nuestro usuario no está dado de alta en esa aplicación. Casos típicos es que la copia es de un cliente lo restauramos en una máquina propia en otro dominio, o que nuestro usuario de desarrollo no está dado de alta en la aplicación de producción.

Sin importar mucho el por qué, la solución siempre ha sido ir a la base de datos manualmente, buscar en la tabla de usuarios y actualizar el SID del usuario administrador por el nuestro; o bien crear manualmente un nuevo usuario en la tabla. Esta es, de hecho, la única solución. Pero no siempre es fácil conocer nuestro SID y esta es una tarea que, por definición, podemos automatizar.

Para eso podemos utilizar un script PowerShell que podrá ser ejecutado cada vez que restauramos un backup, y nos permitirá tomar el control del usuario admin de ese entorno automáticamente (todo este código puede pegarse en un nuevo fichero con extensión .ps1 y utilizarse como un cmdlet):

[CmdletBinding()]
Param
(
   [Parameter()]
   [string] $dbInstance = "localhost",

   [Parameter(Mandatory=$True)]
   [string] $dbName,

   [Parameter()]
   [string] $adDomain = [Environment]::UserDomainName,

   [Parameter()]
   [string] $adUser = [Environment]::UserName
)

Import-Module sqlps -DisableNameChecking

$ntAccount = New-Object System.Security.Principal.NTAccount($adDomain, $adUser)
$adSID = $ntAccount.Translate([System.Security.Principal.SecurityIdentifier])

$params = "adDomain = $adDomain",
          "adUser = $adUser",
          "adSID = $adSID"
         
Invoke-Sqlcmd -Query "
        SELECT ID, NAME, SID, NETWORKDOMAIN, NETWORKALIAS
        FROM [dbo].[USERINFO]
        WHERE ID = 'admin';
    "
-ServerInstance "$dbInstance" –Database “$dbName| Format-Table
   
Invoke-Sqlcmd -Query "
        UPDATE [dbo].[USERINFO]
        SET NAME = 'Admin',
            SID = '`$(adSID)',
            NETWORKDOMAIN = '`$(adDomain)',
            NETWORKALIAS = '`$(adUser)'
        WHERE ID = 'admin';
    "
-ServerInstance "$dbInstance" –Database “$dbName-Variable $params

Invoke-Sqlcmd -Query "
        SELECT ID, NAME, SID, NETWORKDOMAIN, NETWORKALIAS
        FROM [dbo].[USERINFO]
        WHERE ID = 'admin';
    "
-ServerInstance "$dbInstance" –Database “$dbName| Format-Table

El usuario nos permite pasar por parámetros los datos de nuestro usuario y la base de datos, y también asume valores por defecto. Si se utilizan los valores por defecto, el script utilizará el usuario actual. Después se utiliza el módulo PowerShell de SQL Server para lanzar el update necesario a la tabla, y nos mostrará por consola los datos antes y después de su ejecución para asegurarnos, o por si queremos guardarlo en un fichero de texto, por si acaso ;)

Este es el resultado:

Restore-AXAdminUser

Después de ejecutar el script ya puedo entrar en la aplicación correspondiente porque desde este momento mi usuario esta enlazado al usuario admin de esa aplicación.

Más sobre PowerShell en los próximos días! :)

11-03-2015 | deja un comentario

Consultar roles de seguridad desde SQL en AX 2012

No es algo común pero en algún caso nos puede venir bien consultar qué roles de seguridad tiene un usuario consultando la base de datos. Lo ideal sería utilizar servicios web o algo parecido, utilizando un código X++ parecido al siguiente (código extraído del capítulo 10: “Licencia, Configuración y Seguridad” de mi libro):

static void QueryRoles(Args _args)
{
    SecurityRole            securityRole;
    SecurityUserRole        securityUserRole;

    while select securityUserRole
            where securityUserRole.User == 'admin'
        join securityRole
            where securityRole.RecId == securityUserRole.SecurityRole
    {
        info(strFmt("%1", SysLabel::labelId2String(securityRole.Name)));
    }
}

Si insistimos en hacer esta consulta desde SQL Server nos vamos a llevar una sorpresa. La tabla SecurityUserRole que almacena los roles asignados a cada usuario existe en la base de datos transaccional de AX, pero la tabla SecurityRole, que almacena los roles en si, no existe. Sería lógico pensar que, a lo mejor, la tabla existe en la base de datos modelo (_model), pero tampoco la encontramos ahí.

Tras recurrir al truco que comentaba unos cuantos post atrás, nos encontramos con que la tabla está efectivamente en la base de datos modelo, pero abstraída tras un procedimiento almacenado, y tiene esta pinta:

SELECT T1.NAME, T1.ALLOWPASTRECORDS, ...
FROM [XXXX_model].[dbo].SECURITYROLE_INLINEFUNC(N'en_us') T1

Por tanto, jugando un poco con la consulta, podemos obtener el mismo resultado que consultando desde X++:

SecurityUserRole X++

Pero desde SQL Server, y por tanto desde cualquier aplicación externa:

SecurityUserRole SQL

Utilizar con cuidado ;)

 

10-03-2015 | deja un comentario

Errores Interop CLR y manejo de Infolog en Microsoft Dynamics AX 2012

Al trabajar con objetos del framework .NET (algo muy común en AX 2012), debemos tener en cuenta el tratamiento de errores CLR. El tratamiento de errores en Dynamics AX es transparente y no necesita dedicarle demasiada atención durante el desarrollo en X++, pero cuando interviene el Interop con .NET, el manejo de los errores CLR se debe especificar de forma explícita.

El código que suelo utilizar habitualmente (copiado y pegado de mi repositorio personal) como punto de inicio es el siguiente:

static void JAEE_CLRError(Args _args)
{
    System.Exception ex;
   
    new InteropPermission(InteropKind::ClrInterop).assert();  
   
    try
    {
        System.Int32::Parse("JAEE");  // Error: no se puede convertir un texto a Int32
    }
    catch (Exception::CLRError)
    {
        ex = ClrInterop::getLastException();
        if (ex != null)
        {
            info(ex.get_Message());
            ex = ex.get_InnerException();
            if (ex != null)
                error(ex.ToString());
        }
    }    
    catch (Exception::Internal)
    {
        ex = ClrInterop::getLastException();
        if (ex != null)
            info(ex.get_Message());
    }  
   
    CodeAccessPermission::revertAssert();
}

21-01-2015 | deja un comentario

Leer elementos de Team Foundation Server desde Microsoft Dynamics AX 2012 (ALM-V)

Vuelvo brevemente sobre una de mis series de post mas largas hasta la fecha, lo referente a la integración de AX 2012 con Team Foundation Server, no sólo para la gestión del código fuente, sino también como herramienta de gestión del trabajo del equipo. En capítulos anteriores ya vimos cómo se instala y configura TFS (esto cambia entre versiones, también vimos que no necesitamos instalar nada si utilizamos la versión en la nube) y cómo podíamos crear elementos de trabajo en TFS (WorkItems) para gestionar el trabajo desde esta herramienta.

Al hacer un check-in de código desde Microsoft Dynamics AX 2012 se nos plantea la posibilidad de asociar ese check-in a algunos elementos de trabajo de TFS. Esto es casi obligatorio para poder llevar un buen control de para qué se realiza cada cambio, pero la herramienta que nos ofrece AX es bastante limitada. Sin embargo, gracias al API de TFS, podemos ampliarla e incluso desarrollar nuestras propias herramientas e informes en AX 2012 obteniendo datos del servidor de TFS (sí, también desde Visual Studio Online).

Una funcionalidad muy interesante para ahorrarnos tiempo de desarrollo es poder ejecutar desde AX 2012 consultas que están guardadas y utilizamos en TFS. Esto nos permite tener las mismas vistas de datos en los dos entornos, lo que facilita el trabajo. Profundizaré sobre esto en los siguientes post sobre este tema, pero ahí va un código de ejemplo para realizar esta tarea.

NOTA: Para que estos ejemplos funcionen, es necesario tener instalado el SDK de Visual Studio de la versión de TFS que estemos utilizando. En mi caso es el SDK de la versión 2013 porque estoy utilizando Visual Studio Online, pero esto puede variar en cada instalación. Se puede descargar del siguiente enlace:

Después de instalar el SDK, hay que añadir las referencias necesarias al AOT para que AX 2012 pueda utilizarlas:

Add references to AOT

Ahora si, el código:

20-01-2015 | deja un comentario

Error: “The database XXX is not recognized as a model store” al copiar una base de datos AX 2012

Hay un error muy común desde la versión AX 2012 R2 que ocurre normalmente al mover o copiar bases de datos de un servidor o de un entorno a otro. Este error (The database XXX is not recognized as a model store) ocurre al iniciar el AOS tras mover las bases de datos y tiene el siguiente aspecto:

Error "The database XXX is not recognized as a model store"

Este error es bastante extraño, porque recordemos que la base de datos modelo (Model Store, base de datos separada que existe desde la versión R2) no puede configurarse en la utilidad de configurador del cliente ni del servidor:

Server Config

Sin embargo, a pesar de esto, esta base de datos sí se configura mediante varios parámetros del AOS que almacenan tanto el nombre de la base de datos como el servidor, y se almacenan en el registro de Windows (HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Dynamics Server\6.0\XX\NombreConfig).

Registro de windows

Por lo tanto para solucionar este error podemos modificar directamente estos valores en el registro de Windows (es la forma más sencilla), o podemos exportar la configuración del servidor desde la utilidad de configuración, modificar este parámetro en el fichero editándolo directamente con un editor de texto, e importando la configuración de vuelta mediante la utilidad de configuración. Quizás esta última opción es más trabajosa, pero es más fácil de exportar e importar rápidamente en otros servidores.

07-01-2015 | deja un comentario

Error: Concurrent number of AOS for this application exceeds the licensed number

Otro error recurrente al mover o copiar entornos o bases de datos es referente al número máximo de AOS que podemos ejecutar en una misma instalación por cuestiones de licencia. Esto ocurre en diferentes versiones y revisiones pero en mi caso me lo estoy encontrando sobre todo en AX 2012:

Object Server 01:  Concurrent number of AOS' for this application exceeds the licensed number AX 2012

Para solucionarlo, podemos ejecutar esta sentencia en la base de datos que hemos copiado:

UPDATE dbo.SysServerSessions SET Status = 0 WHERE Status = 1;

Esto liberará las sesiones que hayan quedado almacenadas en la base de datos antes de copiarla, permitiendo conectarle un AOS diferente, lo que es totalmente válido para una licencia normal y bastante típico al copiar bases de datos de producción para refrescar entornos de pruebas o desarrollo.

NOTA: Ejecutar sentencias SQL directamente sobre la base de datos siempre supone un riesgo. Ejecutar con precaución y en entornos de testeo.

16-12-2014 | deja un comentario