Contact Us
Como aplicar deployments no Tableau Server através de uma abordagem DevOps?
Tempo de leitura: 9 minutos

Como aplicar deployments no Tableau Server através de uma abordagem DevOps?

por Andreia Barros, Business Intelligence Developer @ Xpand IT

Quando falamos do ciclo de vida do desenvolvimento de software, existem três grandes etapas pelas quais todos os programadores precisam de passar – Desenvolvimento, Staging e Implementação.

No Tableau, embora estejamos a falar de dashboards e bases de dados/fontes de dados, este processo é uma necessidade para assegurar a coisa mais importante quando falamos em dados – a qualidade dos dados.

Para implementar este processo, é necessário existirem três ambientes diferentes:

  • Desenvolvimento (onde o programador cria as fontes de dados e os workbooks)
  • Staging (onde as fontes de dados e os workbooks são implantados pelos programadores para serem testados pelos QA analysts)
  • Produção (Onde as fontes de dados e os workbooks são finalmente implantados para serem utilizados pelos utilizadores finais)

Isto pode ser facilmente implementado através do Tableau Server/Online com duas abordagens possíveis – múltiplas instâncias do Tableau Server ou uma instância com multi-tenancy (múltiplos sites no mesmo Tableau Server).

Então, qual é o problema principal?

Como podemos migrar o conteúdo de uma instância do Tableau para outra? Haverá uma forma automática? Ou será que o programador/QA analyst precisa de descarregar o conteúdo de um Servidor Tableau e carregá-lo para outro?

Qual é o objetivo?

O objetivo é criar um processo que faça esta migração de conteúdos de forma automática para que o utilizador não tenha de o fazer manualmente. Esta migração incluirá objetos, tais como fontes de dados, workbooks e projetos, e incluirá também as permissões atribuídas a cada um deles.

Existe também um segundo objetivo que é o de assegurar o controlo das versões destes objetos e permissões.

Casos de uso

Caso 1 – Criação/atualização de objetos

Um utilizador criou ou atualizou um objeto no ambiente de teste. Estas alterações têm de ser transpostas para o ambiente de produção.

Caso 2 – Mudança de permissões

Um utilizador alterou as permissões para um objeto no ambiente de teste. Estas alterações têm de ser transpostas para o ambiente de produção, para garantir a segurança dos objetos.

Caso 3 – Eliminar objetos

Um utilizador eliminou um objeto no ambiente de teste. Essa alteração deve ser transposta no ambiente de produção.

Assim, todas as alterações destes casos de uso têm de ser transpostas para um ambiente de produção.

Tipos de Migração

A migração de dados pode ser dividida em dois tipos:

  • Migração Completa – todos os objetos serão migrados, a máquina final será uma cópia da fonte (normalmente entre Teste → Produção)
  • Migração Parcial – existem filtros que dividem o conteúdo que está pronto para ser migrado do restante conteúdo. (Normalmente entre Desenvolvimento → Teste)

Esta separação é importante porque o método de migração a aplicar será diferente para cada um deles.

Tableau Server Dev Staging Prod
Fonte: Xpand IT

Na Migração Parcial, o criador do conteúdo terá a tarefa de criar uma pasta “Sandbox” no Tableau Server que incluirá todos os objetos aprovados e prontos para serem migrados. No final da migração, o ambiente de teste será uma cópia exata desta pasta.

Qual é a solução?

A solução estará dividida em três grandes etapas:

  1. Migração de objetos (download e upload)
  2. Migração de permissões
  3. Remoção de objetos

O processo tem de estar dividido nestas etapas porque a implementação de cada uma delas será distinta e dependerá de diferentes plataformas e métodos.

Como fazer a migração de objetos?

Solução – Usar o TabMigrate, uma ferramenta desenvolvida pela Tableau, para fazer o download e o upload dos objetos.

Tableau Server
Fonte: Xpand IT

Este processo terá cinco passos:

  1. Eliminar todo o conteúdo presente na pasta definida no TabMigrate para o download
  2. Descarregar os objetos, usando o comando TabMigrate “siteExport
  3. Adicionar os objetos ao Git Repository
  4. Se estiveres a utilizar uma pasta diferente para carregar o conteúdo, envia o conteúdo do repositório para essa pasta
  5. Carregar os objetos para o Tableau Server, usando o comando do TabMigratesiteImport

TabMigrate Download

C:\Users\{User}\AppData\Local\Apps\2.0\NL4VADC2.ZZ7\MPVM8832.QG4\tabr..tion_0000000000000000_0001.0000_47e7c75dc9f35068\TabRESTMigrate.exe

    -command siteExport

    -fromSiteUserId userdev@example.com

    -fromSiteUserPassword *****

    -exportDirectory C:\path

    -fromSiteUrl https://dev.tableau.com/#/site/

    -fromSiteIsSystemAdmin true

    -backgroundKeepAlive false

    -logVerbose true

    -downloadInfoFiles true

    -exportOnlyTagged prod (opcional)

    -exportOnlyTaggedRemoveTag true (vem com o export tag, senão é opcional )

    -exportSingleProject Sandbox (opcional)

    -logFile C:\path\siteExport_log.txt

    -errorFile C:\path\siteExport_errors.txt

    -exitWhenDone true

Este comando permite que o utilizador defina as seguintes variáveis:

  • fromSiteUserId – Tableau Server Username
  • fromSiteUserPassword – Tableau Server Password
  • fromSiteUrl – Tableau Server Site Path, deixar “https://dev.tableau.com/#/” se for o predefinido
  • exportDirectory – Diretório local para onde o TabMigrate irá descarregar o conteúdo

Também permite ao utilizador definir uma pasta específica, usando o “exportSingleProject“. Ou uma etiqueta específica usando a variável “exportOnlyTagged“.

TabMigrate Upload

C:\Users\{User}\AppData\Local\Apps\2.0\NL4VADC2.ZZ7\MPVM8832.QG4\tabr..tion_0000000000000000_0001.0000_47e7c75dc9f35068\TabRESTMigrate.exe

    -command siteImport

    -toSiteUserId afpb

    -toSiteUserPassword *****

    -importDirectory C:\path

    -toSiteUrl https://dev.tableau.com/#/site/site

    -toSiteIsSiteAdmin true

    -remapDataserverReferences true

    -remapContentOwnership true

    -logVerbose true

    -dbCredentialsFile C:\path1\credentials.xml

    -logFile C:\path\siteImport_log.txt

    -errorFile C:\path\siteImport_errors.txt

    -manualStepsFile C:\path\siteImport_manualSteps.csv

    -exitWhenDone true

Este comando permite que o utilizador defina as seguintes variáveis:

  • toSiteUserId – Tableau Server Username
  • toSiteUserPassword – Tableau Server Password
  • toSiteUrl – Tableau Server Site Path, deixar “https://dev.tableau.com/#/” se for o predefinido
  • importDirectory – Directório local a partir do qual o TabMigrate irá ler o conteúdo a carregar

Ao carregar fontes de dados ou workbooks ligados a fontes de dados, é necessário adicionares um ficheiro XML com as credenciais (dbCredentialsFile).

<xml>

<credential contentType="workbook" contentProjectName="Test Site Import" contentName="test.twbx" dbUser="SimpleUser" dbPassword="q.123456" credentialIsEmbedded="true"> </credential>

<credential contentType="workbook" contentProjectName="Test Site Import" contentName="test2.twbx" dbUser="SimpleUser2" dbPassword="q.1234567" credentialIsEmbedded="false”> </credential>

<credential contentType="datasource" contentProjectName="Test Site Import" contentName="test2.tds" dbUser="SimpleUser3" dbPassword="q.12345678" credentialIsEmbedded="true"> </credential>

</xml>

Há algumas questões que deves ter em conta em relação às permissões:

  • Ao carregar um objeto, se o utilizador do objeto não existir no Tableau Server onde está a carregar, podes colocar a opção “remapContentContentContent” falso. E o TabMigrate vai deixar de tentar encontrar esse utilizador, e simplesmente fará do ToSiteUser o proprietário do conteúdo
  • Onde o objeto já exista no Tableau Server que está a carregar, o objeto permanece com as permissões já aí definidas

Se o objeto não existir no Tableau Server que está a carregar, o objeto irá assumir as permissões do seu projeto padrão.

Inventário do TabMigrate

C:\Users\User\AppData\Local\Apps\2.0\NL4VADC2.ZZ7\MPVM8832.QG4\tabr..tion_0000000000000000_0001.0000_47e7c75dc9f35068\TabRESTMigrate.exe

    -command siteInventory

    -fromSiteUserId userdev@example.com

    -fromSiteUserPassword *****

    -inventoryOutputFile C:\path\siteInventory.csv

    -fromSiteUrl https://dev.tableau.com/#/site/testesite

    -fromSiteIsSystemAdmin true

    -logVerbose true

    -logFile C:\path\siteExport_log.txt

    -errorFile C:\path\siteExport

    -exitWhenDone true

Este comando permite que o utilizador defina as seguintes variáveis:

  • fromSiteUserId – Tableau Server Username
  • fromSiteUserPassword – Tableau Server Password
  • fromSiteUrl – Tableau Server Site Path, deixar “https://dev.tableau.com/#/” se for o predefinido
  • inventoryOutputFile – Diretório local para o qual TabMigrate irá gerar e descarregar o inventário CSV

Se o utilizador for administrador, este pode mudar a flagfromSiteIsSystemAdmin” para verdadeiro, e TabMigrate também irá gerar os utilizadores do site e informações sobre o site.

Migração de Permissões

Existem duas soluções principais para o caso das permissões:

  • O utilizador bloqueia as permissões do projeto no target e na máquina de origem, por isso quando os objetos são migrados da máquina de origem, as permissões que lhes estão aplicadas permanecem as mesmas que as definidas no Servidor Tableau de destino (manual/automático)
  • O utilizador não bloqueia as permissões, pelo que é necessário um processo que migre a permissão da fonte para a instância do Servidor Target Tableau (automático)

Permissões bloqueadas

O criador tem que definir as permissões para a pasta e depois bloqueá-las. Isto pode ser feito manualmente ou através do REST API.

Tableau Server Content permissions
Fonte: Xpand IT

Se decidires utilizar o REST API, terás de implementar dois passos:

Migração de Permissões (Automática)

Esta solução está localizada no REST API. Utilizaremos o REST API para recuperar as permissões da origem que pretendemos migrar e para depois inseri-las no Servidor Tableau de destino.

Atualmente, a REST API não tem uma operação padrão para permissões de ATUALIZAÇÃO. Para atualizar as permissões no Tableau Server de destino, precisamos primeiro de EXCLUIR as permissões que desejamos substituir e, em seguida, INSERIR as novas.

Tableau Server Rest Api
Fonte: Xpand IT

Exemplo (Migrar Permissões de Fontes de Dados)

1 – Tableau Server Sign in

O primeiro passo é entrar no Tableau Server para resgatar um token. Através do “username/password” ou “personal_access_token_name/personal_access_token_secret“.

Para encontrar o token de Acesso Pessoal (que é o mais seguro), precisas de entrar no Tableau Server como administrador, ir a “Account Settings” e depois criar um novo “Personal Access Token“.

Depois disso, podes facilmente entrar no Tableau Server usando o seguinte código python:

REST API + Python – Login

#Define server name, version and site (empty if default)server_name = "{domain}"

version = "3.9"

site_url_id = ""

personal_access_token_name = "token_name"

personal_access_token_secret = "token_secret"

signin_url = "https://{server}/api/{version}/auth/signin".format(server=server_name, version=version)

payload = { "credentials": { "personalAccessTokenName": personal_access_token_name, "personalAccessTokenSecret": personal_access_token_secret, "site": {"contentUrl": site_url_id }}}

headers = {

    'accept': 'application/json',

    'content-type': 'application/json'

}

#Rest API call to login

req = requests.post(signin_url, json=payload, headers=headers, verify=False)

req.raise_for_status()

#Keep the token into one variable (token)

token = response["credentials"]["token"]

site_id = response["credentials"]["site"]["id"]

headers['X-tableau-auth']=token

2 – Descarregar as permissões e guardá-las num CSV

Existem 4 grandes etapas a ter em conta:

  1. Encontrar o ID da fonte de dados
  2. Chamar o REST API para descarregar as permissões da fonte de dados (REST API – Query Datasource Permissions)
  3. Chamar o REST API irá recuperar apenas os IDs dos utilizadores/grupos, mas é necessário descobrir os seus nomes, para mapeá-los para o Servidor Tableau de destino
  4. Chamar o REST API para recuperar a informação sobre os utilizadores/grupos (REST API – Users and Groups)
  5. Descarregar esta informação para um CSV

REST API – Descarregar Permissões

# Get id by CSV

datasource_name = "example"

with open("C:path\\inventory.csv", "r") as filestream:

    for line in filestream:

        lines= line.split(",")

        if lines[3] == datasource_name:

            id = lines[2]

#Get permissions

permissions_url = "https://{server}/api/{version}/sites/{site_id}/datasources/{datasource_id}/permissions".format(server=server_name, version=version, site_id =site_id, datasource_id=id)

server_response = requests.get(url=permissions_url, data=None, headers=headers)

response1 = json.loads(server_response.content)

owner = response1["permissions"]["datasource"]

permissions = response1["permissions"]["granteeCapabilities"]

groups_of_permissions = response1["permissions"]["granteeCapabilities"]

#Transform the json into a list of permissions

a = 0

full_list = []

for group in groups_of_permissions:

    a = a + 1

    list = []

    for item in group.keys():

        if item == "group" :

            type1 = (group["group"]["id"])

            typedsc=item

        elif item == "user":

            type1 = (group["user"]["id"])

            typedsc=item

        else :

            j = []

            for type in group["capabilities"]["capability"]:

                j.append((type["name"],type["mode"]))

            list.append((type1, typedsc,j))

    full_list.append(list)

#Retrieve the name of the users/groups

users_url = "https://{server}/api/{version}/sites/{site_id}/users/".format(server=server_name, version=version, site_id=site_id)

groups_url = "https://{server}/api/{version}/sites/{site_id}/groups/".format(server=server_name, version=version, site_id=site_id)

users_response = requests.get(url=users_url, data=None, headers=headers)

users = json.loads(users_response.content)["users"]["user"]

groups_response = requests.get(url=groups_url, data=None, headers=headers)

groups1 = json.loads(groups_response.content)["groups"]["group"]

#Create final list and save into a CSV (datasource_id, datasource_name, type, user_id, username, permission_name, permission_mode)

final_list = []

for x in full_list :

    user_id=x[0][0]

    type=x[0][1]

    if type == "group":

        for group in (group for group in groups1 if group["id"]==user_id):

            uname = group["name"]

    else:

        for user in (user for user in users if user["id"]==user_id):

            uname = user["name"]

    for (name,mode) in x[0][2]:

        final_list.append([id,datasource_name, type, user_id, uname, name, mode])

 with open('C:\\path\\permissions_origin.csv', mode='w', newline='') as csv1:

    writer = csv.writer(csv1)

    writer.writerows(final_list)

Neste momento, tem um CSV com todas as permissões. Isto tem de ser executado tanto para a origem como para o Servidor Tableau de destino:

  • csv (origem)
  • csv (target)

3 – Eliminar as permissões existentes no target

Uma vez que já temos um CSV, todos os dados sobre as permissões que serão apagadas, esta tarefa torna-se muito fácil. Seguindo estes passos:

REST API + Python – Eliminar Permissões

#Get the permissions

with open('C:\\{path_to_file}\\permissions_origin.csv', 'r') as permissions:

    reader = csv.reader(permissions)

    permission = list(reader)

#Delete

for perm in permission:

    if perm[2] == "group":

        deleteURL = "https://{server}/api/{version}/sites/{site_id}/datasources/{datasource_id}/permissions/groups/{group_id}/{cap_name}/{cap_mode}" \

            .format(server=server_name, version=version, site_id=site_id, datasource_id=perm[0], group_id=perm[3],

                    cap_name=perm[5], cap_mode=perm[6])

        req = requests.delete(url=deleteURL, json=payload, headers=headers, verify=False)

    else:

        deleteURL = "https://{server}/api/{version}/sites/{site_id}/datasources/{datasource_id}/permissions/users/{user_id}/{cap_name}/{cap_mode}" \

            .format(server=server_name, version=version, site_id=site_id, datasource_id=perm[0], user_id=perm[3],

                    cap_name=perm[5], cap_mode=perm[6])

4 – Inserir novas permissões no target

As permissões que serão inseridas já estão no ficheiro “permissions_staging.csv”, mas estão com o antigo ID, pelo que é preciso mapeá-las para o ID correto (target ID)

Assim, as etapas serão as seguintes:

REST API + Python – Adicionar Permissões

#Get the users and groups ids

users_url = "https://{server}/api/{version}/sites/{site_id}/users/".format(server=server_name, version=version, site_id=site_id)

groups_url = "https://{server}/api/{version}/sites/{site_id}/groups/".format(server=server_name, version=version, site_id=site_id)

users_response = requests.get(url=users_url, data=None, headers=headers)

users = json.loads(users_response.content)["users"]["user"]

groups_response = requests.get(url=groups_url, data=None, headers=headers)

groups1 = json.loads(groups_response.content)["groups"]["group"]

inventory_lines = []

with open("C:\\path\\inventory.csv", "r") as filestream:

    for line in filestream:

        inventory_lines.append(line.split(","))

#Get the permissions to add and map them to the correct id

with open('C:\\path\\permissions_origin.csv', 'r') as permissions:

    reader = csv.reader(permissions)

    permission = list(reader)

permissions_ids = []

for perm in permission:

    if perm[2] == "group":

        for group in (group for group in groups1 if group["name"] == perm[4]):

            uid = group["id"]

    else:

        for user in (user for user in users if user["name"] == perm[4]):

            uid = user["id"]

    for lines in (lines for lines in inventory_lines if lines[3] == perm[1]):

        datasource_id = lines[2]

    permissions_ids.append([perm[2],datasource_id,uid,perm[5],perm[6]])

#Generate the XML and call the REST API

headers = {

    'X-tableau-auth': token,
}

for perm in permissions_ids:

    if perm[0] == "user":

        request_xml = ET.Element('tsRequest')

        permissions_xml = ET.SubElement(request_xml, 'permissions')

        datasource_xml = ET.SubElement(permissions_xml, 'datasource', id=perm[1])

        grantee = ET.SubElement(permissions_xml, 'granteeCapabilities')

        user_xml = ET.SubElement(grantee, 'user', id=perm[2])

        capabilities_xml = ET.SubElement(grantee, 'capabilities')

        capability_xml = ET.SubElement(capabilities_xml, 'capability', name=perm[3], mode=perm[4])

        token_url = "https://{server}/api/{version}/sites/{site_id}/datasources/{ds}/permissions" \

            .format(server=server_name, version=version, site_id=site_id, ds=perm[1])

        request_data = ET.tostring(request_xml)

        x = requests.put(url=token_url, data=request_data,headers=headers)

    else:

        request_xml = ET.Element('tsRequest')

        permissions_xml = ET.SubElement(request_xml, 'permissions')

        datasource_xml = ET.SubElement(permissions_xml, 'datasource', id=perm[1])

        grantee = ET.SubElement(permissions_xml, 'granteeCapabilities')

        group_xml = ET.SubElement(grantee, 'group', id=perm[2])

        capabilities_xml = ET.SubElement(grantee, 'capabilities')

        capability_xml = ET.SubElement(capabilities_xml, 'capability', name=perm[3], mode=perm[4])

        token_url = "https://{server}/api/{version}/sites/{site_id}/datasources/{ds}/permissions" \

            .format(server=server_name, version=version, site_id=site_id, ds=perm[1])

        request_data = ET.tostring(request_xml)

        x = requests.put(url=token_url, data=request_data, headers=headers)

Eliminar Artefactos

O último passo é descobrir se existe algum ficheiro que tenha sido eliminado no Origin Tableau Server, para eliminar o mesmo ficheiro no Target Tableau Server.

A tarefa fundamental nesta etapa é descobrir que objetos foram eliminados.

Dependendo do tipo de migração, podemos utilizar uma das duas soluções seguintes:

  1. Descobrir o que foi eliminado através da diferença entre os dois últimos Git commits (aplicável à Migração Parcial e à Migração Completa)
  2. Descobrir o que foi eliminado através da diferença entre o inventário das duas máquinas Tableau Server (aplicável apenas à Migração Completa)

A segunda solução é mais fácil de implementar, por isso, se só utilizas a Migração Completa na tua empresa, aconselhamos a que escolhas essa solução. Se utilizares alguma Migração Parcial, terás de escolher a primeira.

Git difference

Git differences
Fonte: Xpand IT

Existem três passos principais se escolher esta abordagem:

  1. Descobrir o que foi eliminado, através do Gif Diff
  2. Descobrir o tipo de objeto
    1. Workbooks (.twb ou .twbx)
    2. Fonte de dados (.tds ou .tdsx)
    3. Projetos (no extension)
  3. Chamar o REST API apropriado para apagar o objeto

Diferença de Inventário

Existem quatro etapas se escolher esta abordagem:

  1. Descarregar o Origin Tableau Server Inventory
  2. Descarregar o Target Tableau Server Inventory
  3. Descobrir o que é que foi eliminado através da diferença entre eles
  4. Chamar o REST API apropriado para apagar o objeto

Conclusão

Existem algumas áreas que este modelo não cobre, tais como:

  • Migração de um extrato com um tamanho maior do que o limite do ficheiro Git
  • Cronograma dos extratos/workbooks
  • Quando fazemos a migração de um extrato ou workbook usando o TabMigrate, o cronograma é perdido aquando da migração

Uma solução possível para a migração de extratos maiores é:

  1. Descarregar a fonte de dados sem extrair
  2. Carregar a fonte de dados, sem substituir a existente
  3. Chamar o REST API para extrair a fonte de dados
  4. Quando a extração estiver terminada, substituir a antiga fonte de dados pela nova

Leave a comment

Comments are closed.

Comments

  1. … [Trackback]

    […] Find More on on that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  2. … [Trackback]

    […] Find More Information here to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  3. … [Trackback]

    […] Read More on that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  4. … [Trackback]

    […] Find More on on that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  5. … [Trackback]

    […] There you can find 72640 additional Information to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  6. … [Trackback]

    […] Read More to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  7. … [Trackback]

    […] Read More Information here on that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  8. … [Trackback]

    […] Information to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  9. … [Trackback]

    […] Read More Info here to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  10. … [Trackback]

    […] Find More Info here to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  11. … [Trackback]

    […] Read More on that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  12. … [Trackback]

    […] Find More on on that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  13. … [Trackback]

    […] Read More on on that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  14. … [Trackback]

    […] Info on that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  15. … [Trackback]

    […] Find More here to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  16. … [Trackback]

    […] There you can find 94945 more Info to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  17. … [Trackback]

    […] Read More Info here to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  18. … [Trackback]

    […] Read More on that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  19. … [Trackback]

    […] Find More here to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  20. … [Trackback]

    […] Find More Info here on that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  21. … [Trackback]

    […] There you will find 82255 additional Information to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  22. … [Trackback]

    […] Find More on that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  23. … [Trackback]

    […] Find More to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  24. … [Trackback]

    […] Find More on to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  25. … [Trackback]

    […] Information to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  26. … [Trackback]

    […] Information on that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  27. … [Trackback]

    […] Read More to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]

  28. … [Trackback]

    […] Read More here to that Topic: careers.xpand-it.com/blog/como-aplicar-deployments-tableau-server-atraves-devops/ […]