private void btnProvisionFromAzureAd_Click(object sender, EventArgs e) { var statusLogs = new TaskStatusLogs(); statusLogs.AddStatus("Starting..."); UpdateStatusText(statusLogs, true); string pathSecrets = txtPathToSecrets.Text; if (!File.Exists(pathSecrets)) { MessageBox.Show("Secrets file does not exist at specified path (" + pathSecrets + ")"); return; } string pathProvisionPlan = txtPathToAzureAdProvisioningConfig.Text; if (!File.Exists(pathProvisionPlan)) { MessageBox.Show("Config file does not exist at specified path (" + pathProvisionPlan + ")"); return; } string pathOutput = Path.Combine( Path.GetDirectoryName(pathProvisionPlan), "out"); FileIOHelper.CreatePathIfNeeded(pathOutput); //Show the user a command line that they can use to run this same work GenerateProvisioningCommandLine( CommandLineParser.Command_ProvisionFromAzure, pathSecrets, pathProvisionPlan, pathOutput); //Run the work try { ProvisionFromAzureAd( statusLogs, pathSecrets, txtPathToAzureAdProvisioningConfig.Text, pathOutput); } catch (Exception exError) { MessageBox.Show("Error: " + exError.Message); } UpdateStatusText(statusLogs, true); //Open the file explorer to the output directory if (Directory.Exists(pathOutput)) { System.Diagnostics.Process.Start(pathOutput); } }
/// <summary> /// Provision the site based on the provisioning manifest in a file /// </summary> /// <param name="statusLogs">Store status logs here</param> /// <param name="pathSecrets">Where the log in secrets are</param> /// <param name="pathProvisioningManifest">Where the provisioning steps are</param> /// <param name="outputPath">Where output files go</param> private void ProvisionFromFileManifest(TaskStatusLogs statusLogs, string pathSecrets, string pathProvisioningManifest, string outputPath) { //Load the config from the files var secretsConfig = new ProvisionConfigSiteAccess(pathSecrets); //Load the user provisioning instructions var provisionUsersInfo = new ProvisionUserInstructions( pathProvisioningManifest); var provisionSite = new ProvisionSite(secretsConfig, provisionUsersInfo, this, statusLogs); provisionSite.Execute(); //--------------------------------------------------------------------- //Generate an output file //--------------------------------------------------------------------- FileIOHelper.CreatePathIfNeeded(outputPath); var outputFilePath = Path.Combine(outputPath, "ProvisionSiteOutput.csv"); provisionSite.CSVResultsReport.GenerateCSVFile(outputFilePath); statusLogs.AddStatusHeader("Done!"); ((IShowLogs)this).NewLogResultsToShow(statusLogs); }
/// <summary> /// Path for storing site thumbnails /// </summary> /// <param name="siteSignIn"></param> /// <returns></returns> public static string LocalFileSystemPath_TempSpace_SiteWorkbookThumbnails(TableauServerSignIn siteSignIn, string workbookId) { if (!RegExHelper.IsValidIdTableauContentId(workbookId)) { throw new Exception("1029-816, invalid workbook id"); } var fullPath = System.IO.Path.Combine( LocalFileSystemPath_TempSpace_Site(siteSignIn), "wb_thumb_" + workbookId); //Create the directory if we need to FileIOHelper.CreatePathIfNeeded(fullPath); return(fullPath); }
/// <summary> /// Returns a file system path we can use to store information about the specified site /// </summary> /// <param name="siteUrl"></param> /// <returns></returns> private string GeneratePathFromSiteUrl(TableauServerUrls siteUrl) { string appPath = Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "TabMigrate"); //Add the server name to the path appPath = Path.Combine(appPath, FileIOHelper.GenerateWindowsSafeFilename(siteUrl.ServerName)); //Add the site name to the path string siteName = siteUrl.SiteUrlSegement; if (!string.IsNullOrEmpty(siteName)) { appPath = Path.Combine(appPath, siteName); } FileIOHelper.CreatePathIfNeeded(appPath); return(appPath); }
/// <summary> /// Path for storing temp data about a site (e.g. downloaded thumbnails) /// </summary> /// <param name="siteSignIn"></param> /// <returns></returns> public static string LocalFileSystemPath_TempSpace_Site(TableauServerSignIn siteSignIn) { if ((siteSignIn == null) || (string.IsNullOrEmpty(siteSignIn.SiteId))) { throw new Exception("1025-1053: No signed in site info"); } var siteId = siteSignIn.SiteId; if (!RegExHelper.IsValidIdTableauContentId(siteId)) { throw new Exception("1029-815, invalid site id"); } var fullPath = System.IO.Path.Combine(AppSettings.LocalFileSystemPath_TempSpace, @"Sites"); fullPath = System.IO.Path.Combine(fullPath, "site_" + siteId); //Create the directory if we need to FileIOHelper.CreatePathIfNeeded(fullPath); return(fullPath); }
/// <summary> /// Writes out a chunk of text to a diagnostic file /// </summary> /// <param name="filenameBase">(e.g. 'assert', 'debug'). Must be only A-Z characters</param> /// <param name="writeText"></param> private static void WriteDiagnisticTextToFile(string filenameBase, string writeText) { //SECURITY CHECK: Allow only simple alphabetic filename. //We should NEVER hit this if we are called with normal file names if (!RegExHelper.IsAlphabeticText(filenameBase)) { throw new Exception("67-1258: Unexpected debugging filename: " + filenameBase); } var dateTimeNow = DateTime.UtcNow; string filename = filenameBase + dateTimeNow.Year.ToString() + dateTimeNow.Month.ToString("00") + dateTimeNow.Day.ToString("00") + ".txt"; //Gent the directory var directoryAsserts = AppSettings.LocalFileSystemPath_Diagnostics; FileIOHelper.CreatePathIfNeeded(directoryAsserts); var filenameWithPath = Path.Combine(directoryAsserts, filename); //Write the assert contents into the file File.AppendAllText(filenameWithPath, writeText); }
/// <summary> /// Generate a maniefest file based on the current Online site /// </summary> /// <param name="statusLogs"></param> /// <param name="pathSecrets"></param> /// <param name="pathOutputFile"></param> /// <param name="ignoreAllUsersGroup">(recommend TRUE) If false, the manifest file will contain the "all users" group</param> private void GenerateManifestFromOnlineSite( TaskStatusLogs statusLogs, string pathSecrets, string pathOutputFile, bool ignoreAllUsersGroup) { var pathOutputs = Path.GetDirectoryName(pathOutputFile); ProvisionConfigSiteAccess secretsConfig; //=========================================================================================== //Get the sign in information //=========================================================================================== try { //Load the config from the files secretsConfig = new ProvisionConfigSiteAccess(pathSecrets); } catch (Exception exSignInConfig) { statusLogs.AddError("Error loading sign in config file"); throw new Exception("1012-327: Error parsing sign in config, " + exSignInConfig.Message); } //=========================================================================================== //Create a place for out output files //=========================================================================================== FileIOHelper.CreatePathIfNeeded(pathOutputs); var provisionSettings = ProvisionConfigExternalDirectorySync.FromDefaults(); //=========================================================================================== //Download all the data we need from the Tableau Online site //=========================================================================================== statusLogs.AddStatusHeader("Retrieving information from Tableau"); UpdateStatusText(statusLogs); var tableauDownload = new TableauProvisionDownload( secretsConfig, this, statusLogs, ignoreAllUsersGroup); try { tableauDownload.Execute(); } catch (Exception exTableauDownload) { statusLogs.AddError("Error retrieving data from Tableau"); throw new Exception("813-0148: Error in Tableau Download, " + exTableauDownload.Message); } //=========================================================================================== //Write the provisioning manifest out to a file //=========================================================================================== statusLogs.AddStatusHeader("Writing out manifest file for Tableau provisioning"); UpdateStatusText(statusLogs); var outputProvisioningRoles = tableauDownload.ProvisioningManifestResults; try { outputProvisioningRoles.GenerateProvisioningManifestFile(pathOutputFile, provisionSettings); } catch (Exception exWriteProvisioningManifest) { statusLogs.AddError("Error creating provisioning manifest"); throw new Exception("1012-252: Error writing provisioning manifest, " + exWriteProvisioningManifest.Message); } }
/// <summary> /// Called to generate a provisioning manifest file from a Tableau site /// This is useful for creating a "backup" of the sites existing users/groups /// provisioning /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnCreateBackupManifestFile_Click(object sender, EventArgs e) { var statusLogs = new TaskStatusLogs(); statusLogs.AddStatus("Starting..."); UpdateStatusText(statusLogs, true); bool ignoreAllUsersGroup = chkIgoreAllUsersGroup.Checked; string pathSecrets = txtPathToSecrets.Text; if (!File.Exists(pathSecrets)) { MessageBox.Show("Secrets file does not exist at specified path (" + pathSecrets + ")"); return; } string pathOutputManifestFile = txtPathToGenerateManifestFile.Text; //If they gave us a directory, not a file-name, then add filename to it if (Directory.Exists(pathOutputManifestFile)) { pathOutputManifestFile = Path.Combine( pathOutputManifestFile, FileIOHelper.FilenameWithDateTimeUnique("SiteProvisionManifest.xml")); //Show it in the UI txtPathToGenerateManifestFile.Text = pathOutputManifestFile; } var pathOutput = Path.GetDirectoryName(pathOutputManifestFile); FileIOHelper.CreatePathIfNeeded(pathOutput); //Show the user a command line that they can use to run this same work GenerateProvisioningCommandLine( CommandLineParser.Command_GenerateManifestFromOnlineSite, pathSecrets, pathOutputManifestFile, pathOutput, ignoreAllUsersGroup); //Run the work try { GenerateManifestFromOnlineSite( statusLogs, pathSecrets, pathOutputManifestFile, ignoreAllUsersGroup); } catch (Exception exError) { MessageBox.Show("Error: " + exError.Message); } UpdateStatusText(statusLogs, true); //Open the file explorer to the output directory if (Directory.Exists(pathOutput)) { System.Diagnostics.Process.Start(pathOutput); } }
/// <summary> /// Run the provisioning pulling from AzureAD /// </summary> /// <param name="pathSecrets"></param> /// <param name="pathProvisionPlan"></param> private void ProvisionFromAzureAd( TaskStatusLogs statusLogs, string pathSecrets, string pathProvisionPlan, bool deployToTableauTarget, string pathOutputs) { //=========================================================================================== //Create a place for out output files //=========================================================================================== FileIOHelper.CreatePathIfNeeded(pathOutputs); AzureAdConfig configSignInAzure; ProvisionConfigExternalDirectorySync configGroupsMapping; //=========================================================================================== //Get the sign in information //=========================================================================================== try { //Load the config from the files configSignInAzure = new AzureAdConfig(pathSecrets); } catch (Exception exSignInConfig) { statusLogs.AddError("Error loading sign in config file. Error: " + exSignInConfig.Message); throw new Exception("813-1212: Error parsing sign in config, " + exSignInConfig.Message); } //=========================================================================================== //Get the Groups/Roles mapping information //=========================================================================================== try { configGroupsMapping = new ProvisionConfigExternalDirectorySync(pathProvisionPlan); } catch (Exception exGroupsMapping) { statusLogs.AddError("Error loading sync groups provisioning file. Error: " + exGroupsMapping.Message); throw new Exception("813-1214: Error parsing sync groups, " + exGroupsMapping.Message); } //=========================================================================================== //Download all the data we need from Azure //=========================================================================================== statusLogs.AddStatusHeader("Retrieving information from Azure AD"); UpdateStatusText(statusLogs); var azureDownload = new AzureDownload(configSignInAzure, configGroupsMapping, this, statusLogs, null); try { azureDownload.Execute(); //Sanity test IwsDiagnostics.Assert(azureDownload.IsExecuteComplete.Value, "813-834: Internal error. Async work still running"); } catch (Exception exAzureDownload) { statusLogs.AddError("Error retrieving data from Azure AD. Error: " + exAzureDownload.Message); throw new Exception("813-0148: Error in Azure Download, " + exAzureDownload.Message); } //=========================================================================================== //Write the provisioning manifest out to an intermediary file //=========================================================================================== statusLogs.AddStatusHeader("Writing out manifest file for Tableau provisioning"); UpdateStatusText(statusLogs); var outputProvisioningRoles = azureDownload.ProvisioningManifestResults; string provisioningManifest = Path.Combine(pathOutputs, "ProvisioningManifest.xml"); try { outputProvisioningRoles.GenerateProvisioningManifestFile(provisioningManifest, configGroupsMapping); } catch (Exception exWriteProvisioningManifest) { statusLogs.AddError("Error creating provisioning manifest. Error: " + exWriteProvisioningManifest.Message); throw new Exception("813-739: Error writing provisioning manifest, " + exWriteProvisioningManifest.Message); } //================================================================================================= //See if this is a test run, or whether we want to actually deploy the provisioning //================================================================================================= if (deployToTableauTarget) { //=========================================================================================== //Provision the Tableau site using the manifest file we just created //=========================================================================================== statusLogs.AddStatusHeader("Provision Tableau site using generated manifest file"); UpdateStatusText(statusLogs); try { ProvisionFromFileManifest(statusLogs, pathSecrets, provisioningManifest, pathOutputs); } catch (Exception exProvisionSite) { statusLogs.AddError("Error provisioning Tableau Online site. Error: " + exProvisionSite.Message); throw new Exception("814-353: Error provisioning Tableau Online site, " + exProvisionSite.Message); } } else { statusLogs.AddStatusHeader("Skipping Tableau site provisioning step (generate manifest only)"); } }
private void ProvisionFromAzureAd_FromUISetup(bool deployProvisioningToTableau) { var statusLogs = new TaskStatusLogs(); statusLogs.AddStatus("Starting..."); UpdateStatusText(statusLogs, true); string pathSecrets = txtPathToSecrets.Text; if (!File.Exists(pathSecrets)) { MessageBox.Show("Secrets file does not exist at specified path (" + pathSecrets + ")"); return; } string pathProvisionPlan = txtPathToAzureAdProvisioningConfig.Text; if (!File.Exists(pathProvisionPlan)) { MessageBox.Show("Config file does not exist at specified path (" + pathProvisionPlan + ")"); return; } string pathOutput = Path.Combine( Path.GetDirectoryName(pathProvisionPlan), "out"); FileIOHelper.CreatePathIfNeeded(pathOutput); //======================================================================================= //Is this a test run, or a "deploy changes to Tableau run" //======================================================================================= string primaryCommand; if (deployProvisioningToTableau) { primaryCommand = CommandLineParser.Command_ProvisionFromAzure; } else { primaryCommand = CommandLineParser.Command_GenerateManifestFromAzure; } //Show the user a command line that they can use to run this same work GenerateProvisioningCommandLine( primaryCommand, pathSecrets, pathProvisionPlan, pathOutput); //Run the work try { ProvisionFromAzureAd( statusLogs, pathSecrets, txtPathToAzureAdProvisioningConfig.Text, deployProvisioningToTableau, pathOutput); } catch (Exception exError) { MessageBox.Show("Error: " + exError.Message); } UpdateStatusText(statusLogs, true); //Open the file explorer to the output directory if (Directory.Exists(pathOutput)) { System.Diagnostics.Process.Start(pathOutput); } }
/// <summary> /// Download the workbooks /// </summary> /// <param name="onlineLogin">logged in session</param> /// <param name="exportToPath">local path to export to</param> /// <param name="projectsList">project id/name mapping</param> /// <param name="singleProjectIdFilter">if specified, export only from a single project</param> /// <param name="exportOnlyWithThisTag">if specified, export only content with this tag</param> /// <param name="deleteTagAfterExport">TRUE: Remove the server-side tag from exported content (only valid if we have an export tag)</param> private void Execute_DownloadWorkbooks( TableauServerSignIn onlineLogin, string exportToPath, IProjectsList projectsList, SiteProject singleProjectIdFilter = null, string exportOnlyWithThisTag = null, bool deleteTagAfterExport = false) { var onlineUrls = _onlineUrls; _statusLog.AddStatusHeader("Download workbooks"); //Get the UserID we need to use for the workbooks query. var explicitUserId = onlineLogin.UserId; //See if we have a default user id //=================================================================================== //Workbooks... //=================================================================================== ICollection <SiteWorkbook> workbooksList = null; try { var workbooks = new DownloadWorkbooksList(onlineUrls, onlineLogin, explicitUserId); //Query for the list of workbook workbooks.ExecuteRequest(); workbooksList = workbooks.Workbooks; } catch (Exception exWorkbooksList) { _statusLog.AddError("Error querying for list of workbooks, " + exWorkbooksList.Message.ToString()); return; } //No list of workbooks? Exit. if (workbooksList == null) { _statusLog.AddStatus("Aborting workbooks download"); return; } //==================================================================================================== //Apply filters to the list of content to see if we need to reduce the set of content to be downloaded //==================================================================================================== var filteredList = workbooksList; _statusLog.AddStatus("Download workbooks count before filters: " + filteredList.Count.ToString()); //See if we have a PROJECTS filter to apply to the set of content to be downloaded filteredList = FilterProjectMembership <SiteWorkbook> .KeepOnlyProjectMembers( filteredList, singleProjectIdFilter, true); _statusLog.AddStatus("Download workbooks count after projects filter: " + filteredList.Count.ToString()); //See if we have a TAGS filter to apply to the set of content to be downloaded filteredList = FilterTagSet <SiteWorkbook> .KeepOnlyTagged( filteredList, exportOnlyWithThisTag, true); _statusLog.AddStatus("Download workbooks count after tags filter: " + filteredList.Count.ToString()); //----------------------------------------------------------- //Download the workbooks //----------------------------------------------------------- var workbookPath = Path.Combine(exportToPath, "workbooks"); ICollection <SiteWorkbook> successfullExportSet = null; FileIOHelper.CreatePathIfNeeded(workbookPath); try { var workbookDownloads = new DownloadWorkbooks(onlineUrls, onlineLogin, filteredList, workbookPath, projectsList); successfullExportSet = workbookDownloads.ExecuteRequest(); } catch (Exception exWorkbooksDownload) { _statusLog.AddError("Error during workbooks download, " + exWorkbooksDownload.ToString()); } //-------------------------------------------------------------------------------- //Do we want to remove tags from successfully downloaded content? //-------------------------------------------------------------------------------- if ((successfullExportSet != null) && (deleteTagAfterExport) && (!string.IsNullOrWhiteSpace(exportOnlyWithThisTag))) { Execute_DeleteTagFromWorkbooks(onlineLogin, successfullExportSet, exportOnlyWithThisTag); } }
/// <summary> /// Download the data sources /// </summary> /// <param name="onlineLogin"></param> private void Execute_DownloadDatasources( TableauServerSignIn onlineLogin, string exportToPath, IProjectsList projectsList, SiteProject singleProjectIdFilter = null, string exportOnlyWithThisTag = null, bool deleteTagAfterExport = false) { _statusLog.AddStatusHeader("Download datasources"); ICollection <SiteDatasource> datasourcesList = null; try { //Get the list of datasources var datasourcesManager = new DownloadDatasourcesList(_onlineUrls, onlineLogin); datasourcesManager.ExecuteRequest(); datasourcesList = datasourcesManager.Datasources; } catch (Exception exGetContentList) { _statusLog.AddError("Error querying for list of datasources, " + exGetContentList.Message.ToString()); } if (datasourcesList == null) { _statusLog.AddError("Aborting datasources download"); return; } //==================================================================================================== //Apply filters to the list of content to see if we need to reduce the set of content to be downloaded //==================================================================================================== var filteredList = datasourcesList; _statusLog.AddStatus("Download datasources count before filters: " + filteredList.Count.ToString()); //See if we have a PROJECTS filter to apply to the set of content to download filteredList = FilterProjectMembership <SiteDatasource> .KeepOnlyProjectMembers( filteredList, singleProjectIdFilter, true); _statusLog.AddStatus("Download datasources count after projects filter: " + filteredList.Count.ToString()); //See if we have a TAGS filter to apply to the set of content to be downloaded filteredList = FilterTagSet <SiteDatasource> .KeepOnlyTagged( filteredList, exportOnlyWithThisTag, true); _statusLog.AddStatus("Download datasources count after tags filter: " + filteredList.Count.ToString()); ICollection <SiteDatasource> successfullExportSet = null; var datasourcePath = Path.Combine(exportToPath, "datasources"); FileIOHelper.CreatePathIfNeeded(datasourcePath); //----------------------------------------------------------- //Download the data sources //----------------------------------------------------------- try { var datasourceDownloads = new DownloadDatasources( _onlineUrls, onlineLogin, filteredList, datasourcePath, projectsList); successfullExportSet = datasourceDownloads.ExecuteRequest(); } catch (Exception exDatasourceDownload) { _statusLog.AddError("Error during datasource download, " + exDatasourceDownload.ToString()); } //-------------------------------------------------------------------------------- //Do we want to remove tags from successfully downloaded content? //-------------------------------------------------------------------------------- if ((successfullExportSet != null) && (deleteTagAfterExport) && (!string.IsNullOrWhiteSpace(exportOnlyWithThisTag))) { Execute_DeleteTagFromDatasources(onlineLogin, successfullExportSet, exportOnlyWithThisTag); } }