I have a portainer on a remote server with containers inside. With following code I can list stacks and the stack details of the containers without problem, when the control of the stacks are "total" and the type is "compose". But for stacks that are built outside of Portainer (control=limited, type=swarm), I get them not listed. I am looking for a code that can list also these.
My current code:
public static async Task Export_Portainer_Main()
{
string baseUrl = "myurl"; //
string username = "myuser";
string password = "mypassword";
string exportPath = @"C:\temp\Portainer\Export"; //
string stacksFilePath = @"C:\temp\Portainer\Export\stacks.txt"; //
try
{
// Authenticate and get token
string token = await Authenticate(baseUrl, username, password);
// List endpoints and stacks
await ListEndpointsAndStacks(baseUrl, token, stacksFilePath);
// Export all stacks
await ExportAllStacks(baseUrl, token, exportPath);
Console.WriteLine("All stacks exported successfully.");
Console.ReadKey();
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
Console.ReadKey();
}
}
private static async Task<string> Authenticate(string baseUrl, string username, string password)
{
using (var client = new HttpClient())
{
var authData = new { Username = username, Password = password };
var content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(authData));
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
var response = await client.PostAsync($"{baseUrl}/api/auth", content);
response.EnsureSuccessStatusCode();
var responseData = await response.Content.ReadAsStringAsync();
return JObject.Parse(responseData)["jwt"].ToString(); // Extract JWT token
}
}
private static async Task ListEndpointsAndStacks(string baseUrl, string token, string filePath)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var sb = new StringBuilder();
// Fetch all endpoints
var endpointsResponse = await client.GetAsync($"{baseUrl}/api/endpoints");
endpointsResponse.EnsureSuccessStatusCode();
var endpoints = JArray.Parse(await endpointsResponse.Content.ReadAsStringAsync());
// Fetch all stacks globally
var stacksResponse = await client.GetAsync($"{baseUrl}/api/stacks");
stacksResponse.EnsureSuccessStatusCode();
var stacks = JArray.Parse(await stacksResponse.Content.ReadAsStringAsync());
sb.AppendLine("Endpoints and Stacks:");
sb.AppendLine("=====================");
foreach (var endpoint in endpoints)
{
string endpointId = endpoint["Id"].ToString();
string endpointName = endpoint["Name"].ToString();
sb.AppendLine($"Endpoint: {endpointName} (ID: {endpointId})");
// Filter stacks for this endpoint
var stacksForEndpoint = stacks.Where(stack => stack["EndpointId"].ToString() == endpointId);
if (!stacksForEndpoint.Any())
{
sb.AppendLine(" No stacks found.");
}
else
{
foreach (var stack in stacksForEndpoint)
{
string stackName = stack["Name"].ToString();
sb.AppendLine($" Stack: {stackName}");
}
}
sb.AppendLine(); // Blank line between endpoints
}
// Write all data to file
File.WriteAllText(filePath, sb.ToString());
Console.WriteLine($"Endpoints and stack names written to: {filePath}");
}
}
private static async Task ExportAllStacks(string baseUrl, string token, string exportPath)
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
// Ensure the export directory exists
Directory.CreateDirectory(exportPath);
try
{
// Fetch all endpoints
var endpointsResponse = await client.GetAsync($"{baseUrl}/api/endpoints");
endpointsResponse.EnsureSuccessStatusCode();
var endpoints = JArray.Parse(await endpointsResponse.Content.ReadAsStringAsync());
// Fetch all stacks globally
var stacksResponse = await client.GetAsync($"{baseUrl}/api/stacks");
stacksResponse.EnsureSuccessStatusCode();
var stacks = JArray.Parse(await stacksResponse.Content.ReadAsStringAsync());
// Group stacks by endpoint
foreach (var endpoint in endpoints)
{
string endpointId = endpoint["Id"].ToString();
string endpointName = endpoint["Name"].ToString();
Console.WriteLine($"Processing Endpoint: {endpointName} (ID: {endpointId})");
var endpointStacks = stacks.Where(stack => stack["EndpointId"].ToString() == endpointId).ToList();
if (!endpointStacks.Any())
{
Console.WriteLine($" No stacks found for endpoint '{endpointName}' (ID: {endpointId})");
continue;
}
foreach (var stack in endpointStacks)
{
string stackId = stack["Id"].ToString();
string stackName = stack["Name"].ToString();
try
{
Console.WriteLine($"Exporting Stack: {stackName} (ID: {stackId}) for Endpoint: {endpointName}");
// Fetch stack file content
var stackFileResponse = await client.GetAsync($"{baseUrl}/api/stacks/{stackId}/file");
if (!stackFileResponse.IsSuccessStatusCode)
{
Console.WriteLine($" Failed to export stack '{stackName}' for endpoint '{endpointName}': {stackFileResponse.ReasonPhrase}");
continue; // Skip this stack and continue with the next one
}
string stackFileContent = await stackFileResponse.Content.ReadAsStringAsync();
// Save to a file, including the endpoint name for uniqueness
string sanitizedFileName = Path.Combine(exportPath, $"{endpointName}_{stackName.Replace(" ", "_")}.txt");
File.WriteAllText(sanitizedFileName, stackFileContent);
Console.WriteLine($" Stack '{stackName}' exported to '{sanitizedFileName}'");
}
catch (Exception ex)
{
Console.WriteLine($" Error exporting stack '{stackName}' for endpoint '{endpointName}': {ex.Message}");
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
}