NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name AWS console - copy multi-session page link
// @description Adds a button which copies a multi-session page link (which works across accounts/sessions) to the clipboard.
// @namespace https://jonathanhult.com
// @author Jonathan Hult
// @copyright 2025, jhult (https://openuserjs.org/users/jhult)
// @version 1.0.4
// @updateURL https://openuserjs.org/meta/jhult/AWS_Console_-_Share_Link_Button.meta.js
// @downloadURL https://openuserjs.org/install/jhult/AWS_Console_-_Share_Link_Button.user.js
// @license MIT
// @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI2NCIgaGVpZ2h0PSI2NCIgdmlld0JveD0iMCAwIDMyIDMyIj48cGF0aCBkPSJNMTUuNjMgMzEuMzg4bC03LjEzNS0yLjU2VjE4LjM3M2w3LjEzNSAyLjQzem0xLjMgMGw3LjEzNS0yLjU2VjE4LjM3M2wtNy4xMzUgMi40MzJ6bS03LjctMTMuOGw3LjItMi4wMzMgNi42OTYgMi4xNi02LjY5NiAyLjI3M3ptLTIuMDkyLS44TDAgMTQuMjJWMy43NWw3LjEzNSAyLjQzem0xLjMwNyAwbDcuMTM1LTIuNTZWMy43NUw4LjQ0MyA2LjE5MnptLTcuNy0xMy44bDcuMi0yLjA0MyA2LjY5NiAyLjE2LTYuNjk2IDIuMjczem0yMy4wNTIgMTMuOGwtNy4xMzUtMi41NlYzLjc1bDcuMTM1IDIuNDN6bTEuMyAwbDcuMTM1LTIuNTZWMy43NWwtNy4xMzUgMi40M3ptLTcuNy0xMy44bDcuMi0yLjAzMyA2LjY5NiAyLjE2LTYuNjk2IDIuMjczeiIgZmlsbD0iI2Y5MCIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9zdmc+
// @match https://*.*.*.console.aws.amazon.com/*
// @match https://console.aws.amazon.com/*
// @grant GM_setClipboard
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
// Function to create the share button
function createShareButton() {
// Check if button already exists
if (document.getElementById('aws-share-button')) {
return;
}
// Find the scallop icon container
const scallop = document.getElementById('awsc-nav-scallop-icon-container');
if (!scallop) {
// If we can't find it, try again in a moment
setTimeout(createShareButton, 500);
return;
}
// Create share button container with similar styling
const shareContainer = document.createElement('div');
shareContainer.id = 'aws-share-button-container';
shareContainer.className = '_scallop-icon-container_1s953_21';
shareContainer.style.marginLeft = '10px';
shareContainer.style.marginTop = '15px';
// Create the button with similar styling to CloudShell icon
const shareButton = document.createElement('a');
shareButton.id = 'aws-share-button';
shareButton.className = 'globalNav-42226 globalNav-4211 globalNav-4212 _icon-color_1s953_5';
shareButton.href = 'javascript:void(0);';
shareButton.title = 'Share Link';
shareButton.style.display = 'flex';
shareButton.style.alignItems = 'center';
shareButton.style.justifyContent = 'center';
// Create SVG icon for share
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('width', '16');
svg.setAttribute('height', '16');
svg.setAttribute('viewBox', '0 0 24 24');
svg.setAttribute('fill', 'none');
svg.setAttribute('stroke', 'currentColor');
svg.setAttribute('stroke-width', '2');
svg.setAttribute('stroke-linecap', 'round');
svg.setAttribute('stroke-linejoin', 'round');
// Share icon paths
const path1 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path1.setAttribute('d', 'M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8');
svg.appendChild(path1);
const path2 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path2.setAttribute('d', 'M16 6l-4-4-4 4');
svg.appendChild(path2);
const path3 = document.createElementNS('http://www.w3.org/2000/svg', 'path');
path3.setAttribute('d', 'M12 2v13');
svg.appendChild(path3);
// Add SVG to button
shareButton.appendChild(svg);
// Add click event
shareButton.onclick = function (e) {
e.preventDefault();
generateShareableLink();
};
// Add button to container
shareContainer.appendChild(shareButton);
// Insert after CloudShell icon
scallop.parentNode.insertBefore(shareContainer, scallop.nextSibling);
// Create notification element (hidden initially)
const notification = document.createElement('div');
notification.id = 'aws-share-notification';
notification.style.position = 'fixed';
notification.style.top = '50px';
notification.style.right = '20px';
notification.style.backgroundColor = '#37475A';
notification.style.color = 'white';
notification.style.padding = '10px';
notification.style.borderRadius = '3px';
notification.style.zIndex = '9999';
notification.style.display = 'none';
notification.style.maxWidth = '300px';
notification.style.boxShadow = '0 2px 5px rgba(0,0,0,0.2)';
document.body.appendChild(notification);
}
// Function to generate a shareable link
function generateShareableLink() {
// Get current URL
const currentUrl = window.location.href;
// Extract the path and query parameters
const urlParts = currentUrl.match(/https:\/\/([^.]+)[.]([^.]+)[.]console[.]aws[.]amazon[.]com\/([^?]+)(\?[^#]*)?(?:#(.*))?/);
if (!urlParts) {
showNotification('Unable to parse the current URL.', 'error');
return;
}
// Extract components
const accountId = urlParts[1].split('-')[0]; // Extract account ID
const region = urlParts[2];
const service = urlParts[3];
const queryParams = urlParts[4] || '';
const hash = urlParts[5] ? '#' + urlParts[5] : '';
// Build generic URL
let genericUrl = `https://console.aws.amazon.com/${service}`;
// Add region to query params if not already there
let updatedQueryParams = queryParams;
if (!queryParams.includes('region=')) {
updatedQueryParams = queryParams ? queryParams + '®ion=' + region : '?region=' + region;
}
genericUrl += updatedQueryParams + hash;
// Copy to clipboard
copyToClipboard(genericUrl);
// Show notification
showNotification('Shareable link copied to clipboard!', 'success');
}
// Function to copy text to clipboard
function copyToClipboard(text) {
if (typeof GM_setClipboard !== 'undefined') {
GM_setClipboard(text);
}
else {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
}
}
// Function to show notification
function showNotification(message, type) {
const notification = document.getElementById('aws-share-notification');
if (!notification) return;
notification.textContent = message;
notification.style.backgroundColor = type === 'error' ? '#D13212' : '#1E8900';
notification.style.display = 'block';
// Hide after 3 seconds
setTimeout(() => {
notification.style.display = 'none';
}, 3000);
}
// Initialize after a short delay to ensure the page is loaded
setTimeout(createShareButton, 1500);
// Re-initialize when URL changes (for single-page applications)
let lastUrl = location.href;
new MutationObserver(() => {
if (location.href !== lastUrl) {
lastUrl = location.href;
setTimeout(createShareButton, 1500);
}
}).observe(document, {
subtree: true,
childList: true
});
})();