Open-sourced my personal cloud-gaming scripts

This commit is contained in:
hamilcarBarca17
2023-02-17 13:34:55 -07:00
parent caab29d3c4
commit 87538d511f
17 changed files with 1351 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
!jest.config.js
*.d.ts
*.js
node_modules
# CDK asset staging directory
.cdk.staging
cdk.out
bin/*.js
lib/*.js
+6
View File
@@ -0,0 +1,6 @@
*.ts
!*.d.ts
# CDK asset staging directory
.cdk.staging
cdk.out
+51
View File
@@ -0,0 +1,51 @@
/* tslint:disable:no-import-side-effect no-submodule-imports no-unused-expression */
import "source-map-support/register";
import { G4ADStack } from "../lib/g4ad";
import {App} from "aws-cdk-lib";
import {InstanceSize} from "aws-cdk-lib/aws-ec2";
const app = new App();
const NICE_DCV_DISPLAY_DRIVER_URL = "https://d1uj6qtbmh3dt5.cloudfront.net/Drivers/nice-dcv-virtual-display-x64-Release-34.msi";
const NICE_DCV_SERVER_URL = "https://d1uj6qtbmh3dt5.cloudfront.net/2021.0/Servers/nice-dcv-server-x64-Release-2021.0-10242.msi";
const VOLUME_SIZE_GIB = 150;
const OPEN_PORTS = [3389, 8443];
const ACCOUNT_ID = "PLACEHOLDER"
const REGION = "us-east-1"
const VPC_ID = 'PLACEHOLDER'
const SUBNET_ID = 'PLACEHOLDER'
const user = app.node.tryGetContext("user");
if (!user) {
throw new Error("User is a required parameter. Specify it with `-c user=me`.");
}
const localIp = app.node.tryGetContext("localIp");
if (!localIp) {
throw new Error("Local IP is a required parameter. Specify it with '-c localIp=XXX.XXX.XXX.XXX'.");
}
const sshKeyName = process.env.AWS_CLOUD_GAMING_SSH_KEY;
if (!sshKeyName) {
throw new Error("SSH key name is a required parameter. Specify it by setting the environment variable 'AWS_CLOUD_GAMING_SSH_KEY'.");
}
new G4ADStack(app, `TeamBuildingCloudGaming-${user}`, {
niceDCVDisplayDriverUrl: NICE_DCV_DISPLAY_DRIVER_URL,
niceDCVServerUrl: NICE_DCV_SERVER_URL,
instanceSize: InstanceSize.XLARGE4,
sshKeyName,
volumeSizeGiB: VOLUME_SIZE_GIB,
openPorts: OPEN_PORTS,
allowInboundCidr: `${localIp}/32`,
env: {
account: ACCOUNT_ID,
region: REGION
},
tags: {
"Application": "cloud-gaming"
},
user,
vpcId: VPC_ID,
subnetId: SUBNET_ID
});
+3
View File
@@ -0,0 +1,3 @@
{
"app": "npx ts-node bin/cloud-gaming-on-ec2.ts"
}
+113
View File
@@ -0,0 +1,113 @@
/* tslint:disable:no-submodule-imports quotemark no-unused-expression */
import {
BlockDeviceVolume,
CfnLaunchTemplate,
EbsDeviceVolumeType,
Instance,
InstanceSize,
MachineImage,
Peer,
Port,
SecurityGroup,
Subnet, UserData,
Vpc,
WindowsVersion,
InstanceType
} from "aws-cdk-lib/aws-ec2";
import {App, CfnOutput, Stack, StackProps} from "aws-cdk-lib";
import {ManagedPolicy, Role, ServicePrincipal} from "aws-cdk-lib/aws-iam";
export interface BaseConfig extends StackProps {
readonly instanceSize: InstanceSize;
readonly vpcId: string;
readonly subnetId: string;
readonly sshKeyName: string;
readonly volumeSizeGiB: number;
readonly niceDCVDisplayDriverUrl: string;
readonly niceDCVServerUrl: string;
readonly openPorts: number[];
readonly allowInboundCidr: string;
readonly user: String;
}
export abstract class BaseEc2Stack extends Stack {
protected props: BaseConfig;
constructor(scope: App, id: string, props: BaseConfig) {
super(scope, id, props);
this.props = props;
const { vpcId, subnetId, sshKeyName, volumeSizeGiB, openPorts, allowInboundCidr, user } = props;
const vpc = Vpc.fromLookup(this, "Vpc", { vpcId });
const securityGroup = new SecurityGroup(this, `SecurityGroup-${user}`, {
vpc,
description: `Allow RDP, and NICE DCV access for ${user}`,
securityGroupName: `InboundAccessFromRdpDcvFor${user}`
});
for (const port of openPorts) {
securityGroup.connections.allowFrom(Peer.ipv4(allowInboundCidr), Port.tcp(port));
}
const role = new Role(this, `${id}S3Read-${user}`, {
roleName: `${id}.GraphicsDriverS3Access-${user}`,
assumedBy: new ServicePrincipal('ec2.amazonaws.com'),
managedPolicies: [
ManagedPolicy.fromAwsManagedPolicyName('AmazonS3ReadOnlyAccess')
],
});
const launchTemplate = new CfnLaunchTemplate(this, `TeamBuildingCloudGamingLaunchTemplate-${user}`, {
launchTemplateData: {
keyName: sshKeyName,
instanceType: this.getInstanceType().toString(),
networkInterfaces: [{
subnetId,
deviceIndex: 0,
description: "ENI",
groups: [securityGroup.securityGroupId]
}],
instanceMarketOptions: {
spotOptions: {
blockDurationMinutes: 120,
instanceInterruptionBehavior: "stop",
}
}
},
launchTemplateName: `TeamBuildingCloudGamingInstanceLaunchTemplate-${user}/${this.getInstanceType().toString()}`,
});
const ec2Instance = new Instance(this, `EC2Instance-${user}`, {
instanceType: this.getInstanceType(),
vpc,
securityGroup,
vpcSubnets: vpc.selectSubnets({ subnets: [Subnet.fromSubnetAttributes(this, 'publicSubnet', {subnetId, availabilityZone: "us-east-1a"})] }),
keyName: sshKeyName,
userData: this.getUserdata(),
machineImage: MachineImage.latestWindows(WindowsVersion.WINDOWS_SERVER_2019_ENGLISH_FULL_BASE),
blockDevices: [
{
deviceName: "/dev/sda1",
volume: BlockDeviceVolume.ebs(volumeSizeGiB, {
volumeType: EbsDeviceVolumeType.GP3,
encrypted: true
}),
}
],
role,
instanceName: `TeamBuildingCloudGaming-${user}/${this.getInstanceType().toString()}`
});
new CfnOutput(this, `Public IP`, { value: ec2Instance.instancePublicIp });
new CfnOutput(this, `Credentials`, { value: `https://${this.region}.console.aws.amazon.com/ec2/v2/home?region=${this.region}#ConnectToInstance:instanceId=${ec2Instance.instanceId}` });
new CfnOutput(this, `InstanceId`, { value: ec2Instance.instanceId });
new CfnOutput(this, `KeyName`, { value: sshKeyName });
new CfnOutput(this, `LaunchTemplateId`, { value: launchTemplate.launchTemplateName! });
}
protected abstract getUserdata(): UserData;
protected abstract getInstanceType(): InstanceType;
}
+58
View File
@@ -0,0 +1,58 @@
import { BaseConfig, BaseEc2Stack } from "./base";
import {App} from "aws-cdk-lib";
import {InstanceType, UserData} from "aws-cdk-lib/aws-ec2";
// tslint:disable-next-line:no-empty-interface
export interface G4ADConfig extends BaseConfig {
}
export class G4ADStack extends BaseEc2Stack {
protected props: G4ADConfig;
constructor(scope: App, id: string, props: G4ADConfig) {
super(scope, id, props);
}
protected getUserdata() {
const userData = UserData.forWindows();
const { niceDCVDisplayDriverUrl, niceDCVServerUrl } = this.props;
userData.addCommands(
`$NiceDCVDisplayDrivers = "${niceDCVDisplayDriverUrl}"`,
`$NiceDCVServer = "${niceDCVServerUrl}"`,
'$SteamInstallation = "https://cdn.cloudflare.steamstatic.com/client/installer/SteamSetup.exe"',
'$MicrosoftEdgeInstallation = "https://go.microsoft.com/fwlink/?linkid=2108834&Channel=Stable&language=en"',
`$InstallationFilesFolder = "$home\\Desktop\\InstallationFiles"`,
`$Bucket = "ec2-amd-windows-drivers"`,
`$KeyPrefix = "latest"`,
`$Objects = Get-S3Object -BucketName $Bucket -KeyPrefix $KeyPrefix -Region us-east-1`,
`foreach ($Object in $Objects) {
$LocalFileName = $Object.Key
if ($LocalFileName -ne '' -and $Object.Size -ne 0) {
$LocalFilePath = Join-Path $InstallationFilesFolder $LocalFileName
Copy-S3Object -BucketName $Bucket -Key $Object.Key -LocalFile $LocalFilePath -Region us-east-1
Expand-Archive $LocalFilePath -DestinationPath $InstallationFilesFolder\\1_AMD_driver
}
}`,
'pnputil /add-driver $home\\Desktop\\InstallationFiles\\1_AMD_Driver\\210414a-365562C-Retail_End_User.2\\packages\\Drivers\\Display\\WT6A_INF/*.inf /install',
'Invoke-WebRequest -Uri $NiceDCVServer -OutFile $InstallationFilesFolder\\2_NICEDCV-Server.msi',
'Invoke-WebRequest -Uri $NiceDCVDisplayDrivers -OutFile $InstallationFilesFolder\\3_NICEDCV-DisplayDriver.msi',
'Remove-Item $InstallationFilesFolder\\latest -Recurse',
'Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString(\'https://community.chocolatey.org/install.ps1\'))',
'choco feature enable -n=allowGlobalConfirmation',
'choco install steam-rom-manager',
'choco install steam-client',
'choco install microsoft-edge',
`Start-Process msiexec.exe -Wait -ArgumentList '/I C:\\Users\\Administrator\\Desktop\\InstallationFiles\\2_NICEDCV-Server.msi /QN /L* "C:\\msilog.log"'`,
`Start-Process msiexec.exe -Wait -ArgumentList '/I C:\\Users\\Administrator\\Desktop\\InstallationFiles\\3_NICEDCV-DisplayDriver.msi /QN /L* "C:\\msilog.log"'`,
`'' >> $InstallationFilesFolder\\OK`
);
return userData;
}
protected getInstanceType() {
return new InstanceType(`g4ad.${this.props.instanceSize}`);
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"name": "cloud-gaming-on-ec2-instances",
"version": "1.0.0",
"scripts": {
"build": "tsc",
"watch": "tsc -w",
"cdk": "cdk"
},
"devDependencies": {
"@types/node": "^14.0.0",
"aws-cdk": "^2.41.0",
"ts-node": "^8.1.0",
"tslint": "^6.1.3",
"typescript": "~3.9.7"
},
"dependencies": {
"aws-cdk-lib": "^2.41.0",
"constructs": "^10.1.96",
"source-map-support": "^0.5.16"
}
}
+23
View File
@@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "ES2018",
"module": "commonjs",
"lib": ["es2018"],
"declaration": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": false,
"noUnusedParameters": false,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": false,
"inlineSourceMap": true,
"inlineSources": true,
"experimentalDecorators": true,
"strictPropertyInitialization": false,
"typeRoots": ["./node_modules/@types"]
},
"exclude": ["cdk.out"]
}
+79
View File
@@ -0,0 +1,79 @@
{
"extends": [
"tslint:all"
],
"rules": {
"cyclomatic-complexity": false,
"increment-decrement": false,
"newline-before-return": false,
"newline-per-chained-call": false,
"no-parameter-properties": false,
"no-parameter-reassignment": false,
"no-implicit-dependencies": false,
"no-unnecessary-class": ["allow-empty-class", "allow-constructor-only"],
"file-name-casing": false,
"object-literal-sort-keys": false,
"max-line-length": false,
"whitespace": false,
"no-unused-variable": false,
"no-var-requires": false,
"no-console": false,
"typedef": false,
"unnecessary-else": false,
"trailing-comma": false,
"comment-format": {
"options": [
"check-space"
]
},
"member-access": true,
"only-arrow-functions": {
"options": [
"allow-declarations",
"allow-named-functions"
]
},
"completed-docs": false,
"no-any": false,
"no-magic-numbers": false,
"no-non-null-assertion": false,
"no-null-keyword": false,
"no-require-imports": false,
"no-unbound-method": false,
"no-unnecessary-qualifier": false,
"no-use-before-declare": false,
"no-void-expression": false,
"prefer-function-over-method": false,
"strict-comparisons": false,
"strict-type-predicates": false,
"triple-equals": {
"options": [
"allow-undefined-check"
]
},
"interface-name": false,
"max-classes-per-file": false,
"member-ordering": {
"options": {
"order": "statics-first"
}
},
"no-switch-case-fall-through": true,
"strict-boolean-expressions": {
"options": [
"allow-boolean-or-undefined"
]
},
"switch-default": false,
"variable-name": {
"options": [
"ban-keywords",
"check-format",
"allow-leading-underscore",
"allow-pascal-case"
]
},
"linebreak-style": false
}
}