static void Main(string[] args) { if (args.Length != 7) { Console.WriteLine("Usage: copydashboard {orgUrl} {personalAccessToken} {projectName} {sourceTeamName} {sourceDashboardName} {targetTeamName} {targetDashboardName}"); return; } Uri orgUrl = new Uri(args[0]); // Organization URL, for example: https://dev.azure.com/fabrikam string personalAccessToken = args[1]; // See https://docs.microsoft.com/azure/devops/integrate/get-started/authentication/pats string projectName = args[2]; // Project Name string sourceTeamName = args[3]; // Source Team Name string sourceDashboardName = args[4]; // Source Dashboard to copy string targetTeamName = args[5]; // Target Team Name string targetDashboardName = args[6]; // Target Dashboard name // Create a connection using VssConnection connection = new VssConnection(orgUrl, new VssBasicCredential(string.Empty, personalAccessToken)); try { // Make sure we can connect before running the copy connection.ConnectAsync().SyncResult(); } catch (VssServiceResponseException) { Console.Error.WriteLine("ERROR: Could not connect to {0}", orgUrl); return; } // Get WebApi client using DashboardHttpClient dashboardClient = connection.GetClient <DashboardHttpClient>(); // Set source team context TeamContext sourceTeamContext = new TeamContext(projectName, sourceTeamName); // Get dashboard entries DashboardGroup dashboards; try { dashboards = dashboardClient.GetDashboardsAsync(sourceTeamContext).SyncResult(); } catch (VssUnauthorizedException) { Console.Error.WriteLine("ERROR: Not authorized. Invalid token or invalid scope"); return; } catch (ProjectDoesNotExistWithNameException) { Console.Error.WriteLine("ERROR: Project not found: {0}", projectName); return; } catch (TeamNotFoundException) { Console.Error.WriteLine("ERROR: Team not found: {0}", sourceTeamName); return; } // Get dashboard by name Dashboard sourceDashboardEntry = dashboards.DashboardEntries.Single(d => d.Name == sourceDashboardName); if (sourceDashboardEntry == null) { Console.Error.WriteLine("ERROR: Dashboard not found: {0}", sourceDashboardName); return; } Dashboard sourceDashboard = dashboardClient.GetDashboardAsync(sourceTeamContext, (Guid)sourceDashboardEntry.Id).SyncResult(); // get target team WebApiTeam targetTeam; using TeamHttpClient teamClient = connection.GetClient <TeamHttpClient>(); try { targetTeam = teamClient.GetTeamAsync(projectName, targetTeamName).SyncResult(); } catch (TeamNotFoundException) { Console.Error.WriteLine("ERROR: Team not found: {0}", targetTeamName); return; } // replace source team id with target team id // for widgets like Burndown or Backlog where team id is a parameter foreach (Widget w in sourceDashboard.Widgets) { if (w.Settings != null) { w.Settings = w.Settings.Replace(sourceDashboard.OwnerId.ToString(), targetTeam.Id.ToString()); } } // Set target team context TeamContext targetTeamContext = new TeamContext(projectName, targetTeamName); // Create target dashboard Dashboard targetObj = new Dashboard { Name = targetDashboardName, Description = sourceDashboard.Description, Widgets = sourceDashboard.Widgets }; try { dashboardClient.CreateDashboardAsync(targetObj, targetTeamContext).SyncResult(); } catch (DuplicateDashboardNameException) { Console.Error.WriteLine("ERROR: Dashboard {0} already exists in {1} team", targetDashboardName, targetTeamName); return; } }
private void MigrateTeamDashboardSettings() { Stopwatch stopwatch = Stopwatch.StartNew(); ////////////////////////////////////////////////// List <TeamFoundationTeam> sourceTeams = Source.TfsTeamService.QueryTeams(Source.Project).ToList(); Log.LogInformation("TfsTeamDashboardsProcessor::InternalExecute: Found {0} teams in Source?", sourceTeams.Count); ////////////////////////////////////////////////// List <TeamFoundationTeam> targetTeams = Target.TfsTeamService.QueryTeams(Target.Project).ToList(); Stopwatch witstopwatch = Stopwatch.StartNew(); int current = sourceTeams.Count; int count = 0; long elapsedms = 0; ///////// if (_Options.Teams != null) { sourceTeams = sourceTeams.Where(t => _Options.Teams.Contains(t.Name)).ToList(); } // Create a connection using (VssConnection sourceConnection = new VssConnection(new Uri(Source.Options.Organisation), new VssBasicCredential(string.Empty, Source.Options.AccessToken))) using (VssConnection targetConnection = new VssConnection(new Uri(Target.Options.Organisation), new VssBasicCredential(string.Empty, Target.Options.AccessToken))) { if (!TryConnection(sourceConnection)) { return; } if (!TryConnection(targetConnection)) { return; } // Get WebApi client using (DashboardHttpClient sourceDashboardClient = sourceConnection.GetClient <DashboardHttpClient>()) using (DashboardHttpClient targetDashboardClient = targetConnection.GetClient <DashboardHttpClient>()) using (TeamHttpClient teamClient = targetConnection.GetClient <TeamHttpClient>()) { foreach (TeamFoundationTeam sourceTeam in sourceTeams) { var foundTargetTeam = (from x in targetTeams where x.Name == sourceTeam.Name select x).SingleOrDefault(); if (foundTargetTeam != null) { // Set source team context TeamContext sourceTeamContext = new TeamContext(Source.Options.Project, sourceTeam.Name); // Get source dashboard entries DashboardGroup sourceDashboards; try { sourceDashboards = sourceDashboardClient.GetDashboardsAsync(sourceTeamContext).Result; } catch (VssUnauthorizedException) { Console.Error.WriteLine("ERROR: Not authorized. Invalid token or invalid scope"); return; } catch (ProjectDoesNotExistWithNameException) { Console.Error.WriteLine("ERROR: Project not found: {0}", Source.Options.Project); return; } catch (TeamNotFoundException) { Console.Error.WriteLine("ERROR: Team not found: {0}", sourceTeam.Name); return; } // Iterate over all dashboards foreach (Dashboard sourceDashboardEntry in sourceDashboards.DashboardEntries) { Dashboard sourceDashboard = sourceDashboardClient.GetDashboardAsync(sourceTeamContext, (Guid)sourceDashboardEntry.Id).Result; // get target team WebApiTeam targetTeam; try { targetTeam = teamClient.GetTeamAsync(Target.Options.Project, foundTargetTeam.Name).Result; } catch (TeamNotFoundException) { Console.Error.WriteLine("ERROR: Team not found: {0}", foundTargetTeam.Name); return; } // replace source team id with target team id // for widgets like Burndown or Backlog where team id is a parameter foreach (Widget w in sourceDashboard.Widgets) { if (w.Settings != null) { // Replace source team id with target team id w.Settings = w.Settings.Replace(sourceDashboard.OwnerId.ToString(), targetTeam.Id.ToString()); // Replace source query id with target query id w.Settings = ReplaceQueryIds(w.Settings); } } // Set target team context TeamContext targetTeamContext = new TeamContext(Target.Options.Project, foundTargetTeam.Name); // Create target dashboard Dashboard targetObj = new Dashboard { Name = sourceDashboard.Name, Description = sourceDashboard.Description, Widgets = sourceDashboard.Widgets }; try { _ = targetDashboardClient.CreateDashboardAsync(targetObj, targetTeamContext).Result; } catch (AggregateException ae) { if (ae.InnerExceptions.Any((ie) => ie is DuplicateDashboardNameException)) { Log.LogWarning("Dashboard {0} already exists in {1} team", sourceDashboard.Name, foundTargetTeam.Name); } else { Log.LogWarning(ae.ToString()); } } catch (DuplicateDashboardNameException) { Log.LogWarning("Dashboard {0} already exists in {1} team", sourceDashboard.Name, foundTargetTeam.Name); } catch (Exception ex) { Log.LogWarning(ex.ToString()); } } } } } } witstopwatch.Stop(); elapsedms = elapsedms + witstopwatch.ElapsedMilliseconds; current--; count++; TimeSpan average = new TimeSpan(0, 0, 0, 0, (int)(elapsedms / count)); TimeSpan remaining = new TimeSpan(0, 0, 0, 0, (int)(average.TotalMilliseconds * current)); ////////////////////////////////////////////////// stopwatch.Stop(); Log.LogDebug("DONE in {Elapsed} ", stopwatch.Elapsed.ToString("c")); }