diff --git a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/appstream/appstream-sc-service.js b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/appstream/appstream-sc-service.js
index f24937a422..811b2a5dfd 100644
--- a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/appstream/appstream-sc-service.js
+++ b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/appstream/appstream-sc-service.js
@@ -18,6 +18,7 @@ const Service = require('@aws-ee/base-services-container/lib/service');
const settingKeys = {
appStreamImageName: 'appStreamImageName',
+ isAppStreamEnabled: 'isAppStreamEnabled',
};
class AppStreamScService extends Service {
@@ -76,6 +77,7 @@ class AppStreamScService extends Service {
if (!stackName) {
throw this.boom.badRequest(
`Expected stack ${stackName} to be associated with the account ${accountId} but found`,
+ true,
);
}
@@ -88,7 +90,10 @@ class AppStreamScService extends Service {
const { Names: fleetNames } = await appStream.listAssociatedFleets({ StackName: stackName }).promise();
if (!_.includes(fleetNames, fleetName)) {
- throw this.boom.badRequest(`Expected fleet ${fleetName} to be associated with the AppStream stack but found`);
+ throw this.boom.badRequest(
+ `Expected fleet ${fleetName} to be associated with the AppStream stack but found`,
+ true,
+ );
}
return { stackName, fleetName };
@@ -110,7 +115,10 @@ class AppStreamScService extends Service {
return `${uid}-${sessionSuffix}`.replace(/[^\w+=,.@-]+/g, '').slice(0, 32);
}
- async urlForFirefox(requestContext, { environmentId }) {
+ async getStreamingUrl(requestContext, { environmentId, applicationId }) {
+ const isAppStreamEnabled = this.settings.get(settingKeys.isAppStreamEnabled);
+ if (isAppStreamEnabled !== 'true') return undefined;
+
const environmentScService = await this.service('environmentScService');
const appStream = await environmentScService.getClientSdkWithEnvMgmtRole(
@@ -131,7 +139,7 @@ class AppStreamScService extends Service {
FleetName: fleetName,
StackName: stackName,
UserId: this.generateUserId(requestContext, environment),
- ApplicationId: 'Firefox',
+ ApplicationId: applicationId,
})
.promise();
@@ -142,8 +150,10 @@ class AppStreamScService extends Service {
}
async urlForRemoteDesktop(requestContext, { environmentId, instanceId }) {
- const environmentScService = await this.service('environmentScService');
+ const isAppStreamEnabled = this.settings.get(settingKeys.isAppStreamEnabled);
+ if (isAppStreamEnabled !== 'true') return undefined;
+ const environmentScService = await this.service('environmentScService');
const environment = await environmentScService.mustFind(requestContext, { id: environmentId });
// Get stack and fleet
diff --git a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js
index bbe8ffbf90..e99ab4139e 100644
--- a/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js
+++ b/addons/addon-base-raas-appstream/packages/base-raas-appstream-services/lib/plugins/env-sc-connection-url-plugin.js
@@ -19,24 +19,33 @@ async function createConnectionUrl({ envId, connection }, { requestContext, cont
const log = await container.find('log');
// Only wraps web urls via app stream (i.e., scheme = 'http' or 'https' or no scheme)
const isHttp = connection.scheme === 'http' || connection.scheme === 'https' || _.isEmpty(connection.scheme);
+ const appStreamScService = await container.find('appStreamScService');
// Only wrap via AppStream if the connection.url exists
let appStreamUrl;
if (isHttp && connection.url) {
log.debug({
- msg: `Target connection URL ${connection.url} will be made available for pasting into AppStream`,
+ msg: `Target connection URL ${connection.url} will be accessible via AppStream URL`,
connection,
});
- const appStreamScService = await container.find('appStreamScService');
- appStreamUrl = await appStreamScService.urlForFirefox(requestContext, {
+ appStreamUrl = await appStreamScService.getStreamingUrl(requestContext, {
environmentId: envId,
+ applicationId: 'Firefox',
+ });
+ } else if (connection.scheme === 'ssh') {
+ log.debug({
+ msg: `Target instance ${connection.instanceId} will be available for SSH connection via AppStream URL`,
+ connection,
+ });
+ appStreamUrl = await appStreamScService.getStreamingUrl(requestContext, {
+ environmentId: envId,
+ applicationId: 'EC2Linux',
});
} else if (connection.scheme === 'rdp') {
log.debug({
msg: `Will stream target RDP connection for instance ${connection.instanceId} via AppStream`,
connection,
});
- const appStreamScService = await container.find('appStreamScService');
appStreamUrl = await appStreamScService.urlForRemoteDesktop(requestContext, {
environmentId: envId,
instanceId: connection.instanceId,
@@ -47,7 +56,7 @@ async function createConnectionUrl({ envId, connection }, { requestContext, cont
// Retain the original destination URL so we don't have to trigger another API call
connection.appstreamDestinationUrl = connection.url;
- // Now rewrite connection.url to the AppStream streaming URL so it can opened in a new tab
+ // Now rewrite connection.url to the AppStream streaming URL so it can be opened in a new tab
connection.url = appStreamUrl;
log.debug({ msg: `Modified connection to use AppStream streaming URL ${connection.url}`, connection });
}
diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvHttpConnectionExpanded.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvHttpConnectionExpanded.js
deleted file mode 100644
index 72b91bd934..0000000000
--- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvHttpConnectionExpanded.js
+++ /dev/null
@@ -1,141 +0,0 @@
-import _ from 'lodash';
-import React from 'react';
-import { decorate, action, runInAction, observable, computed } from 'mobx';
-import { observer, inject } from 'mobx-react';
-import { withRouter } from 'react-router-dom';
-import { Table, Segment, Grid } from 'semantic-ui-react';
-
-import CopyToClipboard from '../../helpers/CopyToClipboard';
-
-// expected props
-// destinationUrl (via props)
-// keyName (via props)
-// connectionId (via props)
-// timeout (via props)
-class ScEnvHttpConnectionExpanded extends React.Component {
- constructor(props) {
- super(props);
- runInAction(() => {
- // The count down value
- this.countDown = undefined;
- this.intervalId = undefined;
- this.expired = false;
- });
- }
-
- get destinationUrl() {
- return this.props.destinationUrl;
- }
-
- get timeout() {
- return this.props.timeout;
- }
-
- get keyName() {
- return this.props.keyName;
- }
-
- get connectionId() {
- return this.props.connectionId;
- }
-
- componentDidMount() {
- this.startCountDown();
- }
-
- componentWillUnmount() {
- this.clearInterval();
- }
-
- clearInterval() {
- if (!_.isUndefined(this.intervalId)) {
- clearInterval(this.intervalId);
- this.intervalId = undefined;
- }
- this.countDown = undefined;
- this.expired = false;
- }
-
- startCountDown = () => {
- if (!_.isUndefined(this.intervalId)) return;
- this.countDown = this.timeout;
-
- this.intervalId = setInterval(async () => {
- // eslint-disable-next-line consistent-return
- runInAction(() => {
- if (this.countDown <= 0) {
- this.clearInterval();
- this.expired = true;
- return;
- }
- this.countDown -= 1;
- });
- }, 1000);
- };
-
- render() {
- const connectionId = this.connectionId;
-
- return (
-
-
-
-
- {this.renderInfo()}
-
-
- {this.renderCountDown()}
-
-
-
-
-
-
- );
- }
-
- renderInfo() {
- const destinationUrl = this.destinationUrl;
-
- return (
- <>
-
- Click on this icon to copy the workspace destination URL:
-
-
- (You can come back to this page to copy the destination URL)
- >
- );
- }
-
- renderCountDown() {
- const countDown = this.countDown;
- const expired = this.expired;
-
- if (expired) {
- return AppStream session has been opened in a new tab
;
- }
-
- return (
-
-
- Secure AppStream tab opens in {countDown} seconds
-
-
- );
- }
-}
-
-// see https://medium.com/@mweststrate/mobx-4-better-simpler-faster-smaller-c1fbc08008da
-decorate(ScEnvHttpConnectionExpanded, {
- destinationUrl: computed,
- keyName: computed,
- connectionId: computed,
- intervalId: observable,
- countDown: observable,
- expired: observable,
- startCountDown: action,
- clearInterval: action,
-});
-
-export default inject()(withRouter(observer(ScEnvHttpConnectionExpanded)));
diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js
index 706c7ec2c7..62602caaf3 100644
--- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js
+++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvSshConnRowExpanded.js
@@ -3,46 +3,72 @@ import React from 'react';
import { decorate, action, runInAction, observable, computed } from 'mobx';
import { observer, inject } from 'mobx-react';
import { withRouter } from 'react-router-dom';
-import { Table, List, Segment, Label, Grid } from 'semantic-ui-react';
+import { Table, List, Segment, Label, Grid, Button } from 'semantic-ui-react';
+
+import { displayError } from '@aws-ee/base-ui/dist/helpers/notification';
import CopyToClipboard from '../../helpers/CopyToClipboard';
+const openWindow = (url, windowFeatures) => {
+ return window.open(url, '_blank', windowFeatures);
+};
+
// expected props
// networkInterfaces (via props)
// keyName (via props)
// connectionId (via props)
+// environment (via props)
class ScEnvSshConnRowExpanded extends React.Component {
constructor(props) {
super(props);
+
runInAction(() => {
// The count down value
this.countDown = undefined;
this.intervalId = undefined;
this.expired = false;
+ this.processingId = undefined;
});
}
+ get isAppStreamEnabled() {
+ return process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true';
+ }
+
get networkInterfaces() {
const entries = this.props.networkInterfaces;
if (_.isEmpty(entries)) return [];
const result = [];
_.forEach(entries, item => {
- if (item.publicDnsName) result.push({ value: item.publicDnsName, type: 'dns', scope: 'public', info: 'Public' });
+ if (item.publicDnsName && !this.isAppStreamEnabled)
+ result.push({ value: item.publicDnsName, type: 'dns', scope: 'public', info: 'Public' });
if (item.privateIp) result.push({ value: item.privateIp, type: 'ip', scope: 'private', info: 'Private' });
});
return result;
}
+ get environment() {
+ return this.props.scEnvironment;
+ }
+
get keyName() {
return this.props.keyName;
}
+ get envsStore() {
+ return this.props.scEnvironmentsStore;
+ }
+
get connectionId() {
return this.props.connectionId;
}
+ getConnectionStore() {
+ return this.envsStore.getScEnvConnectionStore(this.environment.id);
+ }
+
componentDidMount() {
this.startCountDown();
}
@@ -60,6 +86,33 @@ class ScEnvSshConnRowExpanded extends React.Component {
this.expired = false;
}
+ handleConnect = id =>
+ action(async () => {
+ try {
+ runInAction(() => {
+ this.processingId = id;
+ });
+ const store = this.getConnectionStore();
+ const urlObj = await store.createConnectionUrl(id);
+ const appStreamUrl = urlObj.url;
+ if (appStreamUrl) {
+ const newTab = openWindow('about:blank');
+ newTab.location = appStreamUrl;
+ } else {
+ throw Error('AppStream URL was not returned by the API');
+ }
+ runInAction(() => {
+ this.processingId = id;
+ });
+ } catch (error) {
+ displayError(error);
+ } finally {
+ runInAction(() => {
+ this.processingId = undefined;
+ });
+ }
+ });
+
startCountDown = () => {
if (!_.isUndefined(this.intervalId)) return;
this.countDown = 60;
@@ -88,25 +141,65 @@ class ScEnvSshConnRowExpanded extends React.Component {
- {this.renderInfo()}
+ {this.isAppStreamEnabled ? (
+ {this.renderAppStreamInfo()}
+ ) : (
+ {this.renderInfo()}
+ )}
- {this.renderCountDown()}
+ {this.renderCountDown()}
-
- Example:
-
- {example}
-
-
+ {!this.isAppStreamEnabled && (
+
+ Example:
+
+ {example}
+
+
+ )}
);
}
+ renderAppStreamInfo() {
+ const interfaces = this.networkInterfaces;
+ const network = interfaces[0];
+ const connectionId = this.connectionId;
+
+ return (
+
+
+
+ The Private IP Address to be used:{' '}
+
+
+ {this.renderHostLabel(network)}
+
+
+
+
+
+ {this.isAppStreamEnabled && (
+
+ Connect
+
+ )}
+
+ );
+ }
+
renderInfo() {
const interfaces = this.networkInterfaces;
const moreThanOne = _.size(interfaces) > 1;
@@ -189,10 +282,12 @@ class ScEnvSshConnRowExpanded extends React.Component {
decorate(ScEnvSshConnRowExpanded, {
networkInterfaces: computed,
keyName: computed,
+ envsStore: computed,
connectionId: computed,
intervalId: observable,
countDown: observable,
expired: observable,
+ processingId: observable,
startCountDown: action,
clearInterval: action,
});
diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js
index 77e07257e5..b6af9f8d57 100644
--- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js
+++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentHttpConnections.js
@@ -3,11 +3,11 @@ import React from 'react';
import { decorate, computed, action, runInAction, observable } from 'mobx';
import { observer, inject } from 'mobx-react';
import { withRouter } from 'react-router-dom';
-import { Segment, Icon, Button, Header, Table, List } from 'semantic-ui-react';
+import { Segment, Icon, Button, Header, Grid, Table, List } from 'semantic-ui-react';
import { displayError } from '@aws-ee/base-ui/dist/helpers/notification';
-import ScEnvHttpConnectionExpanded from './ScEnvHttpConnectionExpanded';
+import CopyToClipboard from '../../helpers/CopyToClipboard';
const openWindow = (url, windowFeatures) => {
return window.open(url, '_blank', windowFeatures);
@@ -22,11 +22,18 @@ class ScEnvironmentHttpConnections extends React.Component {
runInAction(() => {
// The id of the connection that is being processed
this.processingId = '';
+ this.appStreamGeneratingId = '';
+ this.appStreamConnectingId = '';
this.destinationUrl = undefined;
+ this.streamingUrl = undefined;
this.timeout = 10;
});
}
+ get isAppStreamEnabled() {
+ return process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true';
+ }
+
get environment() {
return this.props.scEnvironment;
}
@@ -55,7 +62,10 @@ class ScEnvironmentHttpConnections extends React.Component {
const connectInfo = _.find(connections, ['id', id]) || {};
let url = connectInfo.url;
- this.processingId = id;
+ runInAction(() => {
+ this.processingId = id;
+ });
+
try {
if (url) {
// We use noopener and noreferrer for good practices https://developer.mozilla.org/en-US/docs/Web/API/Window/open#noopener
@@ -64,17 +74,7 @@ class ScEnvironmentHttpConnections extends React.Component {
const urlObj = await store.createConnectionUrl(id);
url = urlObj.url;
- // If AppStream is enabled, copy destination URL to clipboard before new tab loads
- if (process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true') {
- runInAction(() => {
- this.destinationUrl = urlObj.appstreamDestinationUrl;
- });
- // Allow users time to copy the destination URL
- setTimeout(() => {
- const newTab = openWindow('about:blank');
- newTab.location = url;
- }, this.timeout * 1000);
- } else {
+ if (url) {
const newTab = openWindow('about:blank');
newTab.location = url;
}
@@ -88,84 +88,177 @@ class ScEnvironmentHttpConnections extends React.Component {
}
});
+ handleAppStreamConnect = (url, id) =>
+ action(async () => {
+ try {
+ runInAction(() => {
+ this.appStreamConnectingId = id;
+ });
+ if (url) {
+ const newTab = openWindow('about:blank');
+ newTab.location = url;
+ }
+ } catch (error) {
+ displayError(error);
+ } finally {
+ runInAction(() => {
+ this.appStreamConnectingId = '';
+ });
+ }
+ });
+
+ handleGenerateAppStreamUrl = id =>
+ action(async () => {
+ const store = this.getConnectionStore();
+ runInAction(() => {
+ this.appStreamGeneratingId = id;
+ });
+ try {
+ const urlObj = await store.createConnectionUrl(id);
+ runInAction(() => {
+ this.destinationUrl = urlObj.appstreamDestinationUrl;
+ this.streamingUrl = urlObj.url;
+ });
+ } catch (error) {
+ displayError(error);
+ } finally {
+ runInAction(() => {
+ this.appStreamGeneratingId = '';
+ });
+ }
+ });
+
render() {
const env = this.environment;
const state = env.state;
const canConnect = state.canConnect;
const connections = this.connections;
- const destinationUrl = this.destinationUrl;
- const processingId = this.processingId;
- const isDisabled = id => processingId !== id && !_.isEmpty(processingId);
- const isLoading = id => processingId === id;
if (!canConnect) return null;
return (
-
+
HTTP Connections
-
- {_.map(connections, item => (
+ {this.renderBody(connections)}
+
+
+ );
+ }
+
+ renderAppStreamBody(connections) {
+ const appStreamGeneratingId = this.appStreamGeneratingId;
+ const streamingUrl = this.streamingUrl;
+ const destinationUrl = this.destinationUrl;
+ const isDisabled = id => appStreamGeneratingId !== id && !_.isEmpty(appStreamGeneratingId);
+ const isLoading = id => appStreamGeneratingId === id;
+
+ return (
+ <>
+ {_.map(connections, item => (
+ <>
+ {this.renderAppstreamInstructions(item)}
+
+
+
+ Generate URL
+
+
+ {item.name || 'Generate'}
+
+
+
+ {destinationUrl && (
<>
- {process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true' ? (
- this.renderAppstreamInstructions(item)
- ) : (
- <>>
- )}
-
-
+
+
+
+
+
+
+ Click on this icon to copy the workspace destination URL:
+
+
+
+
+
+
+
+
+
+
Connect
-
- {item.name || 'Connect'}
- {destinationUrl ? (
-
- ) : (
- <>>
- )}
>
- ))}
-
-
-
+ )}
+ >
+ ))}
+ >
+ );
+ }
+
+ renderBody(connections) {
+ if (this.isAppStreamEnabled) {
+ return this.renderAppStreamBody(connections);
+ }
+
+ const processingId = this.processingId;
+ const isDisabled = id => processingId !== id && !_.isEmpty(processingId);
+ const isLoading = id => processingId === id;
+ return (
+ <>
+ {_.map(connections, item => (
+ <>
+
+
+
+ Connect
+
+
+ {item.name || 'Connect'}
+
+
+ >
+ ))}
+ >
);
}
renderAppstreamInstructions(item) {
return (
<>
-
+
- Connection instructions for your {item.id} AppStream workspace:
+ Connection instructions for your AppStream workspace:
-
- Step 1: Click the Connect button to start an AppStream session
-
-
- Step 2: Copy the destination URL that becomes available below
-
-
- Step 3: Paste (Ctrl + V) this destination URL in the new AppStream FireFox tab
-
+ Click the 'Generate URL' button to start an AppStream Firefox session
+ Copy the destination URL that becomes available below
+ Paste (Ctrl + V) this destination URL in the new AppStream FireFox tab
@@ -192,7 +285,10 @@ decorate(ScEnvironmentHttpConnections, {
environment: computed,
connections: computed,
processingId: observable,
+ appStreamGeneratingId: observable,
+ appStreamConnectingId: observable,
destinationUrl: observable,
+ streamingUrl: observable,
});
export default inject('scEnvironmentsStore')(withRouter(observer(ScEnvironmentHttpConnections)));
diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js
index 7ab15cf560..a07c416d0b 100644
--- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js
+++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentRdpConnectionRow.js
@@ -9,6 +9,10 @@ import { displayError } from '@aws-ee/base-ui/dist/helpers/notification';
import CopyToClipboard from '../../helpers/CopyToClipboard';
+const openWindow = (url, windowFeatures) => {
+ return window.open(url, '_blank', windowFeatures);
+};
+
// expected props
// - scEnvironment (via prop)
// - connectionId (via prop)
@@ -24,9 +28,14 @@ class ScEnvironmentRdpConnectionRow extends React.Component {
this.processingGetInfo = false;
// Should the password be shown
this.showPassword = false;
+ this.processingId = undefined;
});
}
+ get isAppStreamEnabled() {
+ return process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true';
+ }
+
get environment() {
return this.props.scEnvironment;
}
@@ -71,6 +80,30 @@ class ScEnvironmentRdpConnectionRow extends React.Component {
return result;
}
+ handleConnect = id =>
+ action(async () => {
+ try {
+ const store = this.getConnectionStore();
+ const urlObj = await store.createConnectionUrl(id);
+ const appStreamUrl = urlObj.url;
+ if (appStreamUrl) {
+ const newTab = openWindow('about:blank');
+ newTab.location = appStreamUrl;
+ } else {
+ throw Error('AppStream URL was not returned by the API');
+ }
+ runInAction(() => {
+ this.processingId = id;
+ });
+ } catch (error) {
+ displayError(error);
+ } finally {
+ runInAction(() => {
+ this.processingId = '';
+ });
+ }
+ });
+
handleGetInfo = async () => {
const store = this.getConnectionStore();
const connectionId = this.connectionId;
@@ -128,59 +161,79 @@ class ScEnvironmentRdpConnectionRow extends React.Component {
const username = 'Administrator';
const password = windowsRdpInfo.password;
const showPassword = this.showPassword;
+ const connectionId = this.connectionId;
const moreThanOne = _.size(interfaces) > 1;
return (
-
-
-
- Your Windows workspace can be accessed via an RDP client by using the DNS host name and credentials defined
- below.
-
-
-
- The IP Address or DNS of the instance.{' '}
- {moreThanOne ? 'Ask your administrator if you are not sure which one to use:' : ''}
-
- {_.map(interfaces, network => (
-
- {this.renderHostLabel(network)}
-
-
- ))}
-
-
-
- The username and password:
-
-
- {this.renderUsernameLabel(username)}
-
+ <>
+
+
+
+ Your Windows workspace can be accessed via an RDP client by using the DNS host name and credentials
+ defined below.
+
+
+ {this.isAppStreamEnabled ? (
+ <>
+ Click the 'Connect' button to navigate to the AppStream instance
+ >
+ ) : (
+
+ The IP Address or DNS of the instance.{' '}
+ {moreThanOne ? 'Ask your administrator if you are not sure which one to use:' : ''}
+
+ {_.map(interfaces, network => (
+
+ {this.renderHostLabel(network)}
+
+
+ ))}
+
-
- {this.renderPasswordLabel(password)}
-
- {showPassword ? 'Hide' : 'Show'}
-
-
-
-
-
-
-
- Additional information about connecting via RDP can be found in the documentation below:
-
-
-
- Connect to Your Windows Instance
-
-
-
-
+ )}
+
+
+ The username and password:
+
+
+ {this.renderUsernameLabel(username)}
+
+
+
+ {this.renderPasswordLabel(password)}
+
+ {showPassword ? 'Hide' : 'Show'}
+
+
+
+
+
+
+
+ Additional information about connecting via RDP can be found in the documentation below:
+
+
+
+ Connect to Your Windows Instance
+
+
+
+
+
+ {this.isAppStreamEnabled && windowsRdpInfo && (
+
+
+
+ Connect
+
+
+
+ )}
+ >
);
}
@@ -223,6 +276,7 @@ decorate(ScEnvironmentRdpConnectionRow, {
connection: computed,
connectionId: computed,
networkInterfaces: computed,
+ processingId: observable,
windowsRdpInfo: observable,
processingGetInfo: observable,
showPassword: observable,
diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js
index 077a858ee7..59dc5ecade 100644
--- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js
+++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnectionRow.js
@@ -86,6 +86,7 @@ class ScEnvironmentSshConnectionRow extends React.Component {
this.processingSendKey = true;
try {
const result = await store.sendSshKey(connectionId, keyId);
+
runInAction(() => {
this.networkInterfaces = _.get(result, 'networkInterfaces');
});
@@ -114,6 +115,7 @@ class ScEnvironmentSshConnectionRow extends React.Component {
const options = this.keyPairOptions;
const selectedKeyId = this.selectedKeyId;
const selectedKeyName = this.selectedKeyName;
+
const rows = [
@@ -155,6 +157,8 @@ class ScEnvironmentSshConnectionRow extends React.Component {
networkInterfaces={networkInterfaces}
keyName={selectedKeyName}
connectionId={item.id}
+ scEnvironmentsStore={this.envsStore}
+ scEnvironment={this.environment}
/>,
);
}
diff --git a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js
index fb5dd5f00e..1bbbdf828c 100644
--- a/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js
+++ b/addons/addon-base-raas-ui/packages/base-raas-ui/src/parts/environments-sc/parts/ScEnvironmentSshConnections.js
@@ -3,7 +3,7 @@ import React from 'react';
import { decorate, computed, action, runInAction, observable } from 'mobx';
import { observer, inject } from 'mobx-react';
import { withRouter } from 'react-router-dom';
-import { Segment, Icon, Button, Header, Table, Message } from 'semantic-ui-react';
+import { Segment, Icon, Button, Header, Table, Message, List } from 'semantic-ui-react';
import { swallowError } from '@aws-ee/base-ui/dist/helpers/utils';
import { isStoreLoading, isStoreError, isStoreReady } from '@aws-ee/base-ui/dist/models/BaseStore';
@@ -32,6 +32,10 @@ class ScEnvironmentSshConnections extends React.Component {
}
}
+ get isAppStreamEnabled() {
+ return process.env.REACT_APP_IS_APP_STREAM_ENABLED === 'true';
+ }
+
get environment() {
return this.props.scEnvironment;
}
@@ -75,6 +79,34 @@ class ScEnvironmentSshConnections extends React.Component {
return {content}
;
}
+ renderAppStreamInfo() {
+ return (
+
+ Connection instructions for your AppStream workspace:
+
+ Select your SSH key below. You must have downloaded this already.
+ Paste the key's contents into AppStream Notepad in .PEM format
+
+ Save the file in the Downloads folder in .PEM format named like "KeyName.pem" (with quotes)
+
+ Open PuttyGen in AppStream and convert your private PEM key to PPK format
+ Enter the PPK file and private IP address in Putty to SSH into EC2
+ Delete this file once EC2 connection is established
+
+ More information on connecting to your Linux instance from Windows OS:
+
+
+ Connecting from Windows via Putty
+
+
+
+ );
+ }
+
renderConnections() {
const env = this.environment;
const showCreateKey = this.showCreateKey;
@@ -86,6 +118,7 @@ class ScEnvironmentSshConnections extends React.Component {
return (
+ {this.isAppStreamEnabled && this.renderAppStreamInfo()}
{empty && (
Attention!
@@ -114,7 +147,9 @@ class ScEnvironmentSshConnections extends React.Component {
{_.map(connections, item => (
-
+ <>
+
+ >
))}
diff --git a/main/solution/backend/src/lambdas/workflow-loop-runner/plugins/plugin-registry.js b/main/solution/backend/src/lambdas/workflow-loop-runner/plugins/plugin-registry.js
index 8e7e09ca37..8a76de6bed 100644
--- a/main/solution/backend/src/lambdas/workflow-loop-runner/plugins/plugin-registry.js
+++ b/main/solution/backend/src/lambdas/workflow-loop-runner/plugins/plugin-registry.js
@@ -30,6 +30,8 @@ const environmentScWfPlugin = require('@aws-ee/environment-sc-workflows/lib/plug
const bassRaasEnvTypeVarsPlugin = require('@aws-ee/base-raas-services/lib/plugins/env-provisioning-plugin');
const rolesOnlyStrategyPlugin = require('@aws-ee/base-raas-services/lib/plugins/roles-only-strategy-plugin');
const legacyStrategyPlugin = require('@aws-ee/base-raas-services/lib/plugins/legacy-strategy-plugin');
+const baseRaasAppstreamServicesPlugin = require('@aws-ee/base-raas-appstream-rest-api/lib/plugins/services-plugin');
+const baseRaasAppstreamEnvTypeVarsPlugin = require('@aws-ee/base-raas-appstream-services/lib/plugins/env-sc-provisioning-plugin');
const servicesPlugin = require('services/lib/plugins/services-plugin');
@@ -38,6 +40,7 @@ const extensionPoints = {
baseServicesPlugin,
baseWfServicesPlugin,
baseRaasServicesPlugin,
+ baseRaasAppstreamServicesPlugin,
environmentTypeServicesPlugin,
keyPairServicesPlugin,
servicesPlugin,
@@ -48,7 +51,7 @@ const extensionPoints = {
'workflows': [baseRaasWorkflowsPlugin, environmentScWfPlugin],
'workflow-assignments': [],
'cfn-templates': [baseRaasCfnTemplatesPlugin],
- 'env-provisioning': [bassRaasEnvTypeVarsPlugin], // Plugins to participate in resolving list of "Environment Type Configuration Variables". See "addons/addon-environment-sc-api/README.md" to understand what "Environment Type Configuration Variables" are
+ 'env-provisioning': [bassRaasEnvTypeVarsPlugin, baseRaasAppstreamEnvTypeVarsPlugin], // Plugins to participate in resolving list of "Environment Type Configuration Variables". See "addons/addon-environment-sc-api/README.md" to understand what "Environment Type Configuration Variables" are
// --- Authorization Plugins ---/
diff --git a/scripts/app-stream/SETUP.md b/scripts/app-stream/SETUP.md
index 9dd8c0b03d..e61132198b 100644
--- a/scripts/app-stream/SETUP.md
+++ b/scripts/app-stream/SETUP.md
@@ -29,7 +29,7 @@ Note: Please set up your [AWS Profile](https://docs.aws.amazon.com/cli/latest/us
cd ~\Documents
# Pull the Image Builder script from Github
-Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/feat-secure-workspace-egress/scripts/app-stream/buildImage.ps1 -OutFile buildImage.ps1
+Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/mainline/scripts/app-stream/buildImage.ps1 -OutFile buildImage.ps1
# Execute Image builder script
.\buildImage.ps1
diff --git a/scripts/app-stream/buildImage.ps1 b/scripts/app-stream/buildImage.ps1
index 19435846ee..f0f8ef30a1 100644
--- a/scripts/app-stream/buildImage.ps1
+++ b/scripts/app-stream/buildImage.ps1
@@ -3,6 +3,8 @@ Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManage
choco install -y putty.install
+Invoke-WebRequest -Uri https://raw.githubusercontent.com/awslabs/service-workbench-on-aws/mainline/scripts/app-stream/ec2linux.ps1 -OutFile 'C:\Users\Public\Documents\EC2Linux.ps1'
+
# Prepare remote desktop script with arguments
Set-Content C:\Users\public\Documents\MicrosoftRemoteDesktop.ps1 "mstsc /f /v:`"`$env:APPSTREAM_SESSION_CONTEXT`":3389"
@@ -18,6 +20,10 @@ cd "C:\Program Files\Amazon\Photon\ConsoleImageBuilder"
.\image-assistant.exe add-application --absolute-app-path "C:\Program Files (x86)\Mozilla Firefox\firefox.exe" --display-name Firefox --name Firefox
+.\image-assistant.exe add-application --absolute-app-path "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" `
+--display-name EC2Linux --name EC2Linux `
+--launch-parameters '`"-File "C:\Users\Public\Documents\EC2Linux.ps1" -ExecutionPolicy Bypass`"'
+
.\image-assistant.exe add-application --absolute-app-path "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" `
--display-name MicrosoftRemoteDesktop --name MicrosoftRemoteDesktop `
--launch-parameters '`"-File "C:\Users\Public\Documents\MicrosoftRemoteDesktop.ps1" -ExecutionPolicy Bypass`"' `
diff --git a/scripts/app-stream/ec2linux.ps1 b/scripts/app-stream/ec2linux.ps1
new file mode 100644
index 0000000000..c0ee86766c
--- /dev/null
+++ b/scripts/app-stream/ec2linux.ps1
@@ -0,0 +1,3 @@
+Start-Process -FilePath "C:\Program Files\PuTTY\putty.exe"
+Start-Process -FilePath "C:\Program Files\PuTTY\puttygen.exe"
+Start-Process -FilePath "C:\Windows\System32\notepad.exe"
\ No newline at end of file