Gestión de reintentos. Orquestador

En ocasiones nos vemos en la necesidad de diseñar un paquete «Orquestador» en el que a través de tareas Execute package task establecemos un orden de ejecución. De esta manera nuestro paquete Orquestador será el encargado de ir ejecutando los paquetes contenidos en un orden. Pero puede ocurrir que en dicha ejecución se produzca un fallo, por ejemplo, por pérdida de conexión con el servidor, por lo que aunque nuestro job esté configurado para realizar reintentos el paquete Orquestador volvería a ejecutarse desde el inicio. ¿Y si en vez de ejecutarse desde un inicio pudiéramos ir registrando los paquetes del Orquestador que se han ejecutado correctamente para que en el reintento ejecute desde el paquete donde se quedó?

(más…)

ScaleOut SSIS

(más…)

Convertir ficheros Excel en CSV y cargar ficheros usando SSIS

Convertir ficheros Excel en CSV y cargar ficheros usando SSIS

SQL Server Integration Services, SSIS de aquí en adelante, tiene la capacidad de cargar archivos Excel, pero en muchas ocasiones suele ser tedioso porque cualquier mínimo cambio en ese fichero Excel puede hacer fallar el paquete de SSIS, por eso normalmente la mejor opción es transformar dichos ficheros de formato Excel a CSV, ya que cargar ficheros de texto da muchos menos problemas que los ficheros Excel.

Convertir un solo fichero Excel a CSV se hace rápidamente de forma manual guardando el archivo como CSV desde Excel, el problema es cuando tienes que realizar este proceso con muchos ficheros Excel o necesitas que este cambio de formato se realice de forma automática.

Desde SolidQ queremos ayudarte a hacer este cambio de formato automáticamente usando PowerShell y como iterar por directorios para cargar varios ficheros Excel usando SSIS como herramienta principal para realizar todo el proceso.

1. Preparación del entorno

En nuestro ejemplo vamos a disponer de 2 de ficheros Excel, que se van a llamar Excel1.xlsx y Excel2.xlsx con 3 pestañas cada uno, estos ficheros contienen información de inventario sobre productos y están divididos por categorías en cada pestaña. En la siguiente imagen se muestra como es la estructura de los ficheros Excel:

Contenido fichero Excel

Fichero “Excel1.xlsx”, pestaña “cat1”

 

Contenido fichero Excel

Fichero “Excel1.xlsx”, pestaña cat2

 

Esta información va a ser guardada en la tabla Inventario que tiene la siguiente estructura:

CREATE TABLE [dbo].[Inventario](
  [Item] [varchar](20) NULL,
  [Categoria] [varchar](20) NULL,
  [Stock] [int] NULL,
  [NombreFichero] [varchar](50) NULL,
  [InsertDate] [datetime] NULL
)

Las columnas de NombreFichero e InsertDate son columnas que nos ayudaran para saber a qué fichero pertenece cada fila y a que día y hora se realizó la inserción en la tabla.

 

 

2. Conversión de ficheros Excel a CSV.

Lo primero que tenemos que hacer es crear 4 parámetros en nuestro paquete de SSIS:

  • ExcelExtension: donde vamos a indicar la extensión de nuestro fichero Excel (xls o xlsx).
  • RutaCSV: ruta donde se van a dejar los ficheros csv.
  • RutaExcel: ruta donde se van a dejar los ficheros Excel.
  • RutaPowerShell: ruta donde se va a dejar el script de PowerShell.

Parametros

También vamos a necesitar 3 variables:

  • commandPowerShell: donde vamos a guardar el comando para ejecutar el script de PowerShell.
  • FileName: donde vamos a guardar el nombre del fichero una vez transformado a csv, para guardarnos el nombre en nuestra tabla de base de datos.
  • FullFilePath: donde se va a guardar la ruta completa del fichero csv, esta variable se usará posteriormente cuando creemos el loop que iterará por los diferentes ficheros para ir cargándolos.

El siguiente paso es preparar la variable commandPowerShell parametrizada con las rutas y la extensión del fichero Excel, para ello tendremos que poner la siguiente “Expression” en la variable:

"-command \""+@[$Package::RutaPowerShell] + " -rutaExcel " + @[$Package::RutaExcel] + " -rutaCSV " + @[$Package::RutaCSV] + " -excelExt xlsx\""

Con los valores que yo tengo puestos en mis parámetros la variable quedaría de la siguiente manera:

Comando PowerShell

Este es el código del archivo PowerShell que vamos a ejecutar:

<#
#### Script para convertir archivos Excel a CSV ####
Parametros:
    $rutaExcel: ruta origen donde se encuentran los ficheros excel
    $rutaCSV: ruta destino donde se van a dejar los ficheros csv
    $excelExt: extension del fichero excel (xls, xlsx)
#>
param ([string] $rutaExcel, [string] $rutaCSV, [string] $excelExt )

<# 
#### Funcion para convertir de excel a csv ####
Parametros:
    $excelFileName: nombre del fichero excel
    $csvLoc: ruta destino de los ficheros csv
    $excelLoc: ruta origen donde se encuentras los ficheros excel
    $excelExtension: extension del fichero excel (xls, xlsx)
#> 
Function ExportExcelToCSV ($excelFileName, $csvLoc, $excelLoc ,$excelExtension)
{
    #guardamos la ruta del fichero excel
    $excelFile = $excelLoc + $excelFileName + $excelExtension
    $E = New-Object -ComObject Excel.Application
    $E.Visible = $false
    $E.DisplayAlerts = $false
    $wb = $E.Workbooks.Open($excelFile)

    #iteramos por cada sheet del fichero para convertirlo a csv
    foreach ($ws in $wb.Worksheets)
    {
        $n = $excelFileName + "_" + $ws.Name
        $ws.SaveAs($csvLoc + $n + ".csv", 6)
    }
    $E.Quit()
}

#mascara para coger todos los ficheros excel con la extension proporcionada
$mascara = "*."+$excelExt
#añadimos el punto a la extension
$ext = "."+$excelExt

#obtenemos todos los ficheros excel que cumplen con la mascara en la ruta especificada
$ens = Get-ChildItem $rutaExcel -filter $mascara

#iteramos por todos los ficheros excel encontrados y llamamos a la funcion para convertirlos a csv
foreach($e in $ens)
{
    ExportExcelToCSV -excelFileName $e.BaseName -csvLoc $rutaCSV -excelLoc $rutaExcel -excelExtension $ext
} 

 

Lo siguiente es realizar la llamada a nuestro script de PowerShell, para ello usaremos el componente “Execute Process Task”

Toolboox execute process task

Abrimos el componente y en “Process”, en la parte de Executable seleccionamos donde tenemos instalado PowerShell en nuestra máquina:

Configuración Execute Process Task

Luego en “Expressions” vamos a poner una expresión para parametrizar la parte de “Arguments” con la variable commandPowerShell que hemos configurado anteriormente.

Configuracion arguments del execute process taskq

Con esto ya podríamos realizar una pequeña prueba y ver si realmente funciona la parte de transformar los ficheros Excel a CSV.

Colocamos los ficheros Excel en la ruta:

Ruta ficheros Excel

Ejecutamos el componente:

Ejecución de la tarea PowerShell

Y una vez finalizada la ejecución, revisamos la ruta donde deberían estar los ficheros csv

Ruta ficheros csv

Como se puede observar en la imagen se han creado 6 ficheros CSV, dichos ficheros tienen la nomenclatura de [NombreFicheroExcel]_[NombrePestañaExcel].csv

 

 

3. Carga de ficheros CSV en base de datos

Ahora toca la parte de cargar dichos ficheros csv en nuestra tabla de base de datos. Para ello vamos a usar el componente “Foreach Loop Container” para iterar por todos los directorios e ir cargando los ficheros csv.

Para configurarlo, en la sección de “Collection” vamos a seleccionar “Foreach File Enumerator” como Enumerator, después en ”Expressions” vamos a parametrizar la ruta de origen donde se encuentran nuestros ficheros csv usando nuestro parámetro del paquete “RutaCSV”, más abajo en el apartado de “Files:” pondremos la máscara “*.csv” para cargar todos los ficheros csv y en “Retrieve file name” seleccionamos “Fully qualified” para guardar la ruta completa del fichero, esta ruta completa del fichero la vamos a necesitar posteriormente para indicarle a nuestro origen de lectura donde se encuentra el fichero csv:

Excel a CSV SSIS

Luego en la sección de “Variable Mappings” vamos mapear nuestra variable FullFilePath, aquí es donde se va a guardar la ruta completa del fichero csv:

Con esto ya tendríamos configurado nuestro loop.

Ahora vamos a configurar las tareas que van a ir dentro de ese loop. Una de ellas será un “Script Task” para obtener el nombre del fichero csv a partir de la ruta completa del fichero y la otra será un “Data Flow” para cargar los ficheros csv en nuestra base de datos.

Vamos a empezar con el “Script Task”, para ello arrastramos el componente dentro del “Foreach Loop Container”. Hacemos doble click y en la sección de “Script” ponemos como ReadOnlyVariables la variable FullFilePath y como ReadWriteVariables la variable FileName:

Pulsamos sobre el botón de “Edit Script” e introducimos el siguiente código en el script dentro de la función Main:

public void Main()
{
    //guardamos la ruta completa en una variable auxiliar
    string aux = Dts.Variables["User::FullFilePath"].Value.ToString();
    //dividimos la ruta usando \
    string[] split = aux.Split('\\');
    //nos quedamos con la ultima parte de la ruta que es la que contiene el nombre del fichero
    string fileName = split[split.Length - 1];
    //guardamos el nombre del fichero en nuestra varible
    Dts.Variables["User::FileName"].Value = fileName;

    Dts.TaskResult = (int)ScriptResults.Success;
}

Seguimos con el “Data Flow”, lo arrastramos también dentro del componente loop y después del Script Task. Una vez dentro vamos a empezar con el origen, este origen va a ser un “Flat File Source”:

Lo abrimos y pulsamos sobre “New” para crear un nuevo Flat File Connection Manager.

La primera ventana del Flat File Connection Manager quedaría configurada de la siguiente manera:

Excel a CSV SSIS

En “File name” deberemos poner la ruta de alguno de los ficheros para hacer una primera configuración, más tarde esa ruta la parametrizaremos para que la coja automáticamente de lo que se va obteniendo de forma dinámica en el loop.

Luego pasaremos a la parte de “Advanced” para configurar las columnas que tendrá el fichero, vamos a tener 3 columnas, Item, Categoria y Stock.

Con esto ya quedaría configurado, el siguiente paso es la parametrización de la ruta del fichero, para esto en la parte inferior en “Connection Managers” seleccionamos la conexión InventarioCSV que acabamos de crear, botón derecho y “Propiedades”, se nos abrirán las propiedades en la parte derecha de la pantalla y en la propiedad “Expressions” pondremos la variable “FullFilePath” en el ConnectionString:

Excel a CSV SSIS

El siguiente paso será poner un “Conditional Split” para descartar las filas que pertenecen a la cabecera de los ficheros.

Como se puede ver en la siguiente imagen, cada pestaña de los ficheros Excel tenían una primera fila con la cabecera de la tabla, pero estas filas no las queremos guardar en nuestra tabla de base de datos:

Cabecera fichero Excel

Dentro de nuestro Conditional Split vamos a descartar dichas filas con la siguiente condición:

Item == "Item" || Item == ""

Todas las filas que tengan la columna Item vacía o que contengan la palabra “Item” no serán cargadas.

Configuracion Conditional Split

Llamamos “Detalle” a la salida por defecto y esta salida es la que vamos a conectar con nuestro siguiente componente que será un “Derived Column”:

Data flow estado final

Este Derived Column lo vamos a usar para añadir las columnas que contendrán el nombre del fichero y la fecha de inserción, la configuración sería la siguiente:

Configuracion Derived Column

Y por último ya solo quedaría nuestro destino, que será un componente “OLE DB Destination”

Lo abrimos y pulsamos sobre “New” para crear el connection manager con la conexión con nuestra base de datos.

Una vez configurada esta conexión seleccionaremos la tabla donde queremos guardar los datos, en nuestro ejemplo será la tabla Inventario que hemos creado anteriormente:

Y por último en la parte de “Mappings” mapearemos las columnas que llegan por nuestro flujo con las columnas de la tabla:

Esta sería una imagen final de como quedaría nuestro Data Flow:

Data flow

Y esto es una imagen final de como quedaría nuestro Control Flow:

Control Flow Excel a CSV SSIS

Al principio de todo hemos añadido un “Execute SQL Task” que contiene un truncado de la tabla Inventario.

Ahora ya lo tenemos todo listo para ejecutar nuestro paquete y revisar el resultado:

Control Flow ejecutado

Como se puede observar se han creado los ficheros CSV en nuestro directorio:

Excel a CSV SSIS

Y posteriormente se han cargado estos ficheros en nuestra tabla:

Excel a CSV SSIS

Ahora ya sabéis qué importante es cambiar de formato Excel a CSV  para cuestiones con SSIS.

Si queréis más información sobre SSIS, aquí te dejamos un artículo donde uno de nuestro expertos trata en mayor profundidad este tema.

¡Esperamos que os sirva de ayuda y si queréis seguir recibiendo consejos y trucos sobre este tema y relacionados podéis suscribiros a nuestra newsletter!   🙂

Consumiendo Facebook Marketing API desde SSIS

Consumiendo Facebook Marketing API desde SSIS

Cualquier compañía hace grandes esfuerzos en publicitar sus productos y servicios en redes sociales como Facebook. A menudo se plantea la necesidad de evaluar dichas campañas y contrastar su rendimiento con otros parámetros de la empresa. Lo que nos proponemos, en resumen, es obtener datos de la Marketing API de Facebook e introducirlos en un flujo de datos de SSIS. (más…)

Autenticación Integrada en Azure Database con SSIS

Autenticación Integrada en Azure Database con SSIS

En muchos escenarios se nos presenta la necesidad de usar autenticación integrada para acceder a los orígenes de datos necesarios para alimentar nuestro sistema analítico. Con el uso cada vez más extendido de Azure, como al menos parte de nuestra infraestructura, algunos de estos orígenes van a estar alojados en bases de datos en Azure. En este caso vamos hablar de un error real que hemos tenido en la configuración y uso de la autenticación integrada contra bases de datos Azure con SSIS. (más…)

Miembros inferidos en SSIS

Miembros inferidos en SSIS

La primera vez que tuve que crear un paquete de SSIS teniendo en cuenta los miembros inferidos me encontré un poco perdido. Me sabía la teoría de esos miembros de una dimensión que llegan en los hechos y que aún no están cargados en su correspondiente tabla de dimensiones, pero no tenía ni idea de cómo podría implementarlo de una forma eficiente y efectiva. Vamos, quiero decir, que lo hiciera rápido y bien, que es como hay que hacerlo. Para ello hay que tener en cuenta dos cosas, la primera es que se debe hacer la inserción del miembro desconocido pero además recuperar la SK que se le asigna. La segunda es que pueden venir varias filas referenciando el mismo miembro y no se puede insertar cada vez que aparezca para evitar duplicidades. Y además, debemos intentar hacer las mínimas consultas posibles al DW para no sobrecargarlo.

 

Buscando por la bibliografía de la que disponía y por Internet encontré muchos artículos en los que se usaban distintos métodos, la mayoría con componentes script. Pero entre ellos apareció un artículo de Thomas Kejser donde explicaba cómo hacerlo de una forma muy interesante: con dos lookup y un stored procedure en el DW. Como mi escenario era bastante complejo y su ejemplo demasiado sencillo, me llevó un tiempo implementarlo de forma que funcionara y sacarle todo el partido que se puede obtener de esta solución. Es por ello que, a continuación, voy a elaborar un ejemplo también sencillo pero con varias situaciones que se nos pueden presentar.

Empezaremos creando 3 tablas que vamos a necesitar con los valores necesarios:

CREATE TABLE StageCompras(
  Alimento VARCHAR(25) NOT NULL,
  Color VARCHAR(25) NULL,
  Unidades INT NOT NULL,
  PrecioUnidad FLOAT NOT NULL
  )

CREATE TABLE DimAlimentos(
  SKAlimento INT PRIMARY KEY IDENTITY(1,1),
  Alimento VARCHAR(25) NOT NULL,
  Color VARCHAR(25),
  Tipo VARCHAR(25),
  Calorias100gr INT,
  FlagInferido BIT
  )

CREATE TABLE FactCompras(
  Unidades INT NOT NULL,
  PrecioUnidad FLOAT NOT NULL,
  FKAlimento INT NOT NULL,
  Fecha SMALLDATETIME DEFAULT GETDATE()
  )
GO

INSERT INTO StageCompras VALUES 
  ('Patata', 'Amarillo', 4, 2.95)
  ,('Naranja', 'Naranja', 1, 1.50)
  ,('Kiwi', 'Marrón', 5, 2.75)
  ,('Manzana', 'Roja', 4, 1.99)
  , ('Manzana', 'Roja', 6, 1.99);

INSERT INTO DimAlimentos VALUES 
  ('Patata', 'Amarillo', 'Planta', 77, 0)
  ,('Kiwi', 'Marrón', 'Fruta', 61, 0)
  ,('Manzana', 'Verde', 'Fruta', 52, 0)

Lo siguiente será crear un paquete SSIS, añadir un Data Flow y construir el sistema. Para ponernos en situación al final nos quedará algo así:

1

Para comenzar con el flujo debemos tener un origen de datos que lea de la tabla StageCompras.

2

A continuación añadiremos un Lookup conectado a la tabla DimAlimentos. En la pestaña General le indicaremos que las filas que no hagan join las derive a la salida de no encontradas. En la pestaña de Columnas enlazaremos Alimento y Color y le marcaremos SKAlimento para que nos devuelva ese campo. Con esto tenemos un lookup que tendrá en caché todos los elementos de la dimensión DimAlimentos con una única consulta.

3

Añadiremos un segundo Lookup, que es el que tiene la mitad de la magia de esta solución, a la salida del no encontrados del primer Lookup. En la pestaña General, en el grupo Cache mode le indicaremos que queremos que haga Partial cache. Esto quiere decir que se guardará en caché todas aquellas filas por las que se le pregunte. Y a él solo llegaran las filas que contienen miembros desconocidos puesto que está unido a esa salida del anterior. Por esto solo consultará al DW la primera vez que aparezca un elemento desconocido. Las siguientes veces que aparezca el mismo elemento desconocido ya lo tendrá en memoria y no necesitará repetir la consulta.

4

En la pestaña de Connection haremos la conexión con la tabla DimAlimentos utilizando la opción de usar una tabla o vista. Este punto es importante y volveremos sobre él más adelante.

5

En el apartado de columnas haremos lo mismo que en el caso del anterior Lookup, enlazar Alimento y Color y marcar SKAlimento.

Nos queda una pestaña por configurar, la de Advanced. En este punto nos dará error puesto que nos falta la otra parte del truco, pero vamos a indicar ya lo que haremos. Marcaremos el check de Modify the SQL statement y escribiremos la siguiente sentencia

EXEC Generate_SKAlimento ?, ?, 'NA'

Le estamos indicando que cuando llegue una fila desconocida, que no tenga ya en caché, ejecute esa llamada a un Stored Procedure el cual insertará el elemento en el DW y nos devolverá la SK que le asigne SQL Server (por ser un Identity).

6

Y presionando sobre el botón de parámetros configuraremos dos de ellos

7

Para terminar el Data Flow nos falta hacer una unión de los dos lookup y un destino OLE DB conectado a la tabla FactCompras.

8

Y configurando el mapeo de filas

9

Y con esto hemos acabado con el paquete SSIS. Vayamos con el comienzo del procedimiento almacenado que debemos crear en el DW.

CREATE PROCEDURE Generate_SKAlimento 
  @Alimento VARCHAR(25)
  ,@Color VARCHAR(25)
  ,@Tipo VARCHAR(25)
AS
SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

Éste va a tener tres parámetros: Alimento, Color y Tipo. En nuestro caso solo tenemos el nombre del alimento y el color pero, ¿y si en otro punto de nuestro ETL también pudiéramos tener miembros inferidos que sean de esta dimensión y conociéramos más datos de ellos? Por ejemplo, el tipo de alimento que es (igual proviene de una receta con más datos). Para eso podemos generalizar un poco el procedimiento y que admita más entradas. Es por ello que en la sentencia SQL del Lookup había dos interrogaciones, una para Alimento, otra para Color, y luego le estábamos diciendo que el tipo era ‘NA’.

Prosigamos con el código del script:

DECLARE @SKAlimento INT
  ,@Calorias100gr INT = 0
  ,@FlagInferido BIT = 1

Necesitamos declarar al menos la variable SKAlimento del tipo int, pero ya de paso podemos inicializar dos más con los valores por defecto que le vamos a dar a los campos de los miembros inferidos que no conocemos. Esto no es completamente necesario hacerlo así ni en este punto, pero queda muy organizado y en un futuro, si queremos cambiar algo, no tendremos que mirar el resto del código.

El siguiente paso es comprobar si ese alimento ya existe o debemos insertarlo.

SELECT @SKAlimento = SKAlimento
FROM DimAlimentos
WHERE @Alimento = Alimento
  AND @Color = Color

IF @SKAlimento IS NULL
BEGIN
  INSERT INTO DimAlimentos
  VALUES (
    rtrim(@Alimento)
    ,rtrim(@Color)
    ,@Tipo
    ,@Calorias100gr
    ,@FlagInferido
    )

  SET @SKAlimento = SCOPE_IDENTITY()
END

Los rtrim() son muy importantes puesto que las cadenas que reciba el procedimiento van a rellenarse con espacios en blanco por el final hasta rellenar el máximo del tipo y nos dará problemas al hacer joins por estos campos.

En nuestro ejemplo no lo hemos recogido, pero podría darse el caso que pudiéramos complementar información de otros miembros inferidos que se hayan cargado en otros puntos. Para contemplar esto primero tendríamos que añadir ese campo en el Lookup a la hora de hacer join (para que no lo encuentre y utilice el segundo Lookup) y luego escribir algo similar a:

ELSE IF @Tipo != 'NA' AND @TipoDW = 'NA' BEGIN
    UPDATE DimAlimentos SET Tipo = @Tipo
    WHERE SKAlimento = @SKAlimento
END

Por último, debemos devolver la consulta con el nuevo SK. Y en este punto tenemos que pararnos un momento. Al crear el segundo Lookup indicamos que íbamos a usar la opción de vista o tabla para consultar la tabla DimAlimentos, pero también podríamos haber hecho una consulta propia SQL. Pues bien, en el procedimiento, la consulta que devolvamos debe responder exactamente igual a la consulta que indicamos al configurar el Lookup. Si usamos la primera opción deberemos devolver todos los campos que tenga la tabla, si escribimos una consulta propia solo aquellos que indicásemos.

SELECT @SKAlimento AS SKAlimento
  ,@Alimento AS Alimento
  ,@Color AS Color
  ,@Tipo AS Tipo
  ,@Calorias100gr AS Calorias100gr
  ,@FlagInferido AS FlagInferido
FROM DimAlimentos

Los tipos deben ser iguales y los nombres de las columnas también. Podríamos haber hecho lo siguiente (aunque no aconsejo esta práctica).

SELECT *
FROM DimAlimentos
WHERE SKAlimento = @SKAlimento

Con todo esto habríamos completado el ejemplo, solo nos quedaría ejecutarlo para comprobar que todo ha funcionado correctamente y no hemos insertado dos manzanas en la dimensión DimAlimentos. El código al completo sería:

CREATE PROCEDURE Generate_SKAlimento 
  @Alimento VARCHAR(25)
  ,@Color VARCHAR(25)
  ,@Tipo VARCHAR(25)
AS
SET NOCOUNT ON
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE

DECLARE @SKAlimento INT
  ,@Calorias100gr INT = 0
  ,@FlagInferido BIT = 1

SELECT @SKAlimento = SKAlimento
FROM DimAlimentos
WHERE @Alimento = Alimento
  AND @Color = Color

IF @SKAlimento IS NULL
BEGIN
  INSERT INTO DimAlimentos
  VALUES (
    rtrim(@Alimento)
    ,rtrim(@Color)
    ,@Tipo
    ,@Calorias100gr
    ,@FlagInferido
    )

  SET @SKAlimento = SCOPE_IDENTITY()
END

SELECT @SKAlimento AS SKAlimento
  ,@Alimento AS Alimento
  ,@Color AS Color
  ,@Tipo AS Tipo
  ,@Calorias100gr AS Calorias100gr
  ,@FlagInferido AS FlagInferido
FROM DimAlimentos

(más…)

Automatizar el Despliegue de Paquetes de SSIS con PowerShell

Automatizar el Despliegue de Paquetes de SSIS con PowerShell

Hola a todos,

En el último artículo hablábamos sobre cómo automatizar el despliegue de informes de Reporting Services de forma “automática” mediante un pequeño script de Power Shell.

En esta ocasión vamos a ver cómo podemos hacer algo similar para desplegar paquetes de Integration Services también mediante la ejecución de un script de Power Shell. (más…)