/// <summary> /// Inner wrapper for async operations /// </summary> /// <returns></returns> private async Task Execute_Async() { var azureGraphSession = AzureGetGraphSession(); //=================================================================================== //Get Groups that map to Tableau Site User Roles from Azure //=================================================================================== _statusLogs.AddStatus("Azure: Getting user roles groups"); await GenerateUsersRolesList_FromAzureGroupsSyncList(azureGraphSession, _configSyncGroups.GroupsToRolesSyncList); //=================================================================================== //Get Groups that map to Tableau Site Groups from Azure //=================================================================================== _statusLogs.AddStatus("Azure: Getting user roles groups"); await GenerateGroupsMembersList_FromAzureGroupsSyncList(azureGraphSession, _configSyncGroups.GroupsToGroupsSyncList); //=================================================================================== //Now perform any replace/override operations we need to based on explicit users //=================================================================================== _statusLogs.AddStatus("Replacing any explicit user/role overrides"); foreach (var thisOverrideUser in _configSyncGroups.UserRolesOverrideList) { SetManagerForRoles.AddAndForceReplaceUser(thisOverrideUser); } //Mark the work status as complete IsExecuteComplete.Trigger(); }
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> /// Queries a site for all its users, and for recent content. /// Genterates and sends email to all the users /// </summary> /// <param name="showLogsHere"></param> /// <param name="statusLogs"></param> public void Execute() { //================================================================================== //Get the data we need to run //================================================================================== //Generate the URLs we will need var siteUrlManager = TableauServerUrls.FromContentUrl(_configTableauSecrets.SiteUrl, TaskMasterOptions.RestApiReponsePageSizeDefault); //================================================================================= //Sign in to the site //================================================================================= var siteSignIn = new TableauServerSignIn( siteUrlManager, _configTableauSecrets.SiteClientId, _configTableauSecrets.Secret, _statusLogs, _configTableauSecrets.SiteSignInMode); var signInSuccess = siteSignIn.Execute(); ShowLogs(); //================================================================================= //Get the basic site info //================================================================================= var downloadSiteInfo = new DownloadSiteInfo(siteSignIn); downloadSiteInfo.ExecuteRequest(); var siteInfo = downloadSiteInfo.Site; //=================================================================================== //Get Groups that map to Tableau Site User Roles from Tableau //=================================================================================== _statusLogs.AddStatus("Tableau: Getting user roles groups"); GenerateUsersRolesList_FromTableauSite(siteSignIn); //=================================================================================== //Get Groups that map to Tableau Site Groups from Tableau //=================================================================================== _statusLogs.AddStatus("Tableau: Getting user roles groups"); GenerateGroupsMembersList_FromTableauSite(siteSignIn); }
/// <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); } }
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> /// Called to attempt to execute a custom command (to be run after the user logs in) /// </summary> /// <param name="onlineLogin"></param> /// <param name="customCommand"></param> private void AttemptExecutionOfCustomHttpGet(TableauServerSignIn onlineLogin, string customCommand) { _statusLog.AddStatusHeader("GET request: " + customCommand); var customGetRequest = new SendPostLogInCommand(_onlineUrls, onlineLogin, customCommand); try { var customResult = customGetRequest.ExecuteRequest(); _statusLog.AddStatus("GET result: " + customResult); } catch (Exception exCustomCommand) { _statusLog.AddError("Error during custom GET, " + exCustomCommand.ToString()); } }
/// <summary> /// Executes the authentication request against the Tableau server /// </summary> public bool ExecuteRequest() { var webRequest = WebRequest.Create(_onlineUrls.UrlLogin); string bodyText = xmlLogIn; bodyText = bodyText.Replace("{{iwsUserName}}", _userName); bodyText = bodyText.Replace("{{iwsPassword}}", _password); bodyText = bodyText.Replace("{{iwsSiteUrl}}", _siteUrlSegment); AssertTemplateFullyReplaced(bodyText); //=============================================================================================== //Make the sign in request, trap and note, and rethrow any errors //=============================================================================================== try { SendRequestContents(webRequest, bodyText); } catch (Exception exSendRequest) { StatusLog.AddError("Error sending sign in request: " + exSendRequest); throw; } //=============================================================================================== //Get the web response, trap and note, and rethrow any errors //=============================================================================================== WebResponse response; try { response = webRequest.GetResponse(); } catch (Exception exResponse) { StatusLog.AddError("Error returned from sign in response: " + exResponse); throw; } var allHeaders = response.Headers; var cookies = allHeaders["Set-Cookie"]; _logInCookies = cookies; //Keep any cookies //=============================================================================================== //Get the web response's XML payload, trap and note, and rethrow any errors //=============================================================================================== XmlDocument xmlDoc; try { xmlDoc = GetWebResponseAsXml(response); } catch (Exception exSignInResponse) { StatusLog.AddError("Error returned from sign in xml response: " + exSignInResponse); throw; } var nsManager = XmlHelper.CreateTableauXmlNamespaceManager("iwsOnline"); var credentialNode = xmlDoc.SelectSingleNode("//iwsOnline:credentials", nsManager); var siteNode = xmlDoc.SelectSingleNode("//iwsOnline:site", nsManager); _logInSiteId = siteNode.Attributes["id"].Value; _logInToken = credentialNode.Attributes["token"].Value; //Adding the UserId to the log-in return was a feature that was added late in the product cycle. //For this reason this code is going to defensively look to see if hte attribute is there var userNode = xmlDoc.SelectSingleNode("//iwsOnline:user", nsManager); string userId = null; if (userNode != null) { var userIdAttribute = userNode.Attributes?["id"]; if (userIdAttribute != null) { userId = userIdAttribute.Value; } _logInUserId = userId; } //Output some status text... if (!string.IsNullOrWhiteSpace(userId)) { StatusLog.AddStatus("Log-in returned user id: '" + userId + "'", -10); } else { StatusLog.AddStatus("No User Id returned from log-in request"); return(false); //Failed sign in } return(true); //Success }
/// <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, 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"); 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"); throw new Exception("813-1214: Error parsing sync groups, " + exGroupsMapping.Message); } //=========================================================================================== //Download all the data we need from Azure //=========================================================================================== statusLogs.AddStatus("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"); throw new Exception("813-0148: Error in Azure Download, " + exAzureDownload.Message); } //=========================================================================================== //Write the provisioning manifest out to an intermediary file //=========================================================================================== statusLogs.AddStatus("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"); throw new Exception("813-739: Error writing provisioning manifest, " + exWriteProvisioningManifest.Message); } //=========================================================================================== //Provision the Tableau site using the manifest file we just created //=========================================================================================== statusLogs.AddStatus("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"); throw new Exception("814-353: Error provisioning Tableau Online site, " + exProvisionSite.Message); } }