├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── bootstrap ├── Install-DCV.ps1 ├── Install-DCVandSMAgent.ps1 ├── dcv-gateway-installer.sh ├── dcv-session-manager-installer.sh └── linux-config-sessionmgr-agent.sh ├── cdk ├── README.md ├── dcv-access-console │ ├── README.md │ ├── app.py │ ├── cdk.json │ ├── config.json │ ├── documentation │ │ └── images │ │ │ └── SolutionArchitecture.png │ ├── requirements.txt │ ├── scripts │ │ └── access-console-user-data.sh │ └── stacks │ │ └── dcv_ac_infra │ │ └── dcv_ac_infra.py ├── dcv-gw-sm-with-pipelines │ ├── README.md │ ├── app.py │ ├── cdk.json │ ├── components │ │ ├── build-dcv-connection-gwy.yaml │ │ └── build-dcv-session-mgr.yaml │ ├── config.json │ ├── documentation │ │ └── images │ │ │ ├── EC2ImageBuilderRunPipeline.jpg │ │ │ └── SolutionArchitecture.PNG │ ├── requirements.txt │ ├── scripts │ │ ├── connection-gwy-user-data.sh │ │ └── session-mgr-user-data.sh │ └── stacks │ │ ├── dcv_ami │ │ ├── __init__.py │ │ └── dcv_ami.py │ │ └── dcv_infra │ │ ├── __init__.py │ │ └── dcv_infra.py └── dcv-gw-sm-without-pipelines │ ├── README.md │ ├── app.py │ ├── cdk.json │ ├── config.json │ ├── documentation │ └── images │ │ └── SolutionArchitecture.png │ ├── requirements.txt │ ├── scripts │ ├── connection-gwy-user-data.sh │ └── session-mgr-user-data.sh │ └── stacks │ └── dcv_infra │ ├── __init__.py │ └── dcv_infra.py ├── session-resolver └── resolver.py └── usage-reporting ├── Build-DCVUsageReports.ps1 └── Build-WSPUsageReports.ps1 /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT No Attribution 2 | 3 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 13 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 14 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 15 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Amazon DCV Samples 2 | 3 | Welcome to the DCV Samples repository within [AWS Samples](https://github.com/aws-samples). This repository will be used to host samples for Amazon DCV integrated workloads. These workloads include managed services that leverage DCV, such as [Amazon WorkSpaces](https://aws.amazon.com/workspaces/all-inclusive/) that stream with [DCV](https://docs.aws.amazon.com/workspaces/latest/adminguide/amazon-workspaces-protocols.html) (previously known as [WSP](https://aws.amazon.com/about-aws/whats-new/2024/10/nice-dcv-amazon-dcv-20240-ubuntu-2404/)). 4 | 5 | ## Glossary 6 | - [Bootstrap](./bootstrap/) 7 | - [AWS Cloud Development Kit Examples](./cdk/) 8 | - [Session Resolver](./session-resolver/) 9 | - [DCV Usage Reports](./usage-reporting/) 10 | 11 | ## Overview 12 | 13 | ### Bootstrap 14 | The provided script `dcv-gateway-installer.sh` bootstraps the installation and configuration of the DCV Connection Gateway component. This can be injected with [Amazon EC2 user data](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html) or by running the script locally with `sudo`. To read more about the DCV Connection Gateway, see the [DCV Connection Gateway Administrator Guide](https://docs.aws.amazon.com/dcv/latest/gw-admin/what-is-gw.html). 15 | 16 | The provided script `dcv-session-manager-installer.sh` bootstraps the installation and configuration of the DCV Session Manager component. This can be injected with [Amazon EC2 user data](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html) or by running the script locally with `sudo`. To read more about DCV Session Manager, see the [DCV Session Manager Administrator Guide](https://docs.aws.amazon.com/dcv/latest/sm-admin/what-is-sm.html). 17 | 18 | The provided script `Install-DCVandSMAgent.ps1` bootstraps the installation and configuration of DCV server and DCV Session Manager agent. This can be injected with [Amazon EC2 user data](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html) or by running the script locally within an Administrator PowerShell terminal. Updating the `SESSION-MGR-PRIVATE-DNS` placeholder with the private DNS of DCV Session Manager allows the script to configure the host correctly. 19 | 20 | The provided script `Install-DCV.ps1` bootstraps the installation and configuration of only DCV server. This can be invoked by injecting with [Amazon EC2 user data](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html) or by running the script locally within an Administrator PowerShell terminal. Optionally parameters are commented out to [configure automatic session creations](https://docs.aws.amazon.com/dcv/latest/adminguide/managing-sessions-start.html#managing-sessions-start-auto). 21 | 22 | The provided script `linux-config-sessionmgr-agent.sh` bootstraps the configuration of DCV server and DCV Session Manager agent. This can be injected with [Amazon EC2 user data](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html) or by running the script locally with `sudo`. Updating the `SESSION-MGR-PRIVATE-DNS` placeholder with the private DNS of DCV Session Manager allows the script to configure the host correctly. Note, this script will only work on a host that already has DCV server and DCV Session Manager agent installed. It is intended to be used with the Linux-based [AWS Marketplace DCV AMIs](https://aws.amazon.com/marketplace/seller-profile?id=74eff437-1315-4130-8b04-27da3fa01de1). 23 | 24 | 25 | ### CDK 26 | This folder contains several [AWS Cloud Development Kit](https://aws.amazon.com/cdk/) (AWS CDK) examples for deploying DCV workloads as IaaC. For an overview of the current CDK examples, see the [README](/cdk/README.md) in the cdk folder. 27 | 28 | **Current CDK Examples** 29 | - Deploy a DCV Session Manager and DCV Connection Gateway environment 30 | - Deploy a DCV Session Manager and DCV Connection Gateway environment with EC2 Image Builder pipelines for both components 31 | - Deploy DCV Access Console 32 | 33 | ### Session Resolver 34 | The provided script provides logic to run in a AWS Lambda function to resolver DCV sessions streaming through a DCV Connection Gateway. To learn more, see the [Build a serverless session resolver for your Amazon DCV Connection Gateway](https://aws.amazon.com/blogs/desktop-and-application-streaming/build-a-serverless-session-resolver-for-your-nice-dcv-connection-gateway/) AWS blog post. 35 | 36 | ### DCV Usage Reports 37 | The provided scripts to generate DCV usage reports on Windows illustrate how to use DCV calls to find when a user starts and ends their session. This allows administrators to generate granular usage reports that can be further modified to include additional information. For a walkthrough of how to implement these reports on a DCV Amazon WorkSpace, see this [blog post](https://aws.amazon.com/blogs/desktop-and-application-streaming/generate-custom-usage-reports-for-amazon-workspaces/). Note, for WorkSpaces using DCV, utilize the WSP PowerShell script. 38 | 39 | ## Security 40 | 41 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 42 | 43 | ## License 44 | 45 | This library is licensed under the MIT-0 License. See the LICENSE file. 46 | -------------------------------------------------------------------------------- /bootstrap/Install-DCV.ps1: -------------------------------------------------------------------------------- 1 | 2 | <# 3 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | software and associated documentation files (the "Software"), to deal in the Software 7 | without restriction, including without limitation the rights to use, copy, modify, 8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | #> 18 | $token = Invoke-RestMethod -Headers @{'X-aws-ec2-metadata-token-ttl-seconds' = '21600'} -Method PUT -Uri http://169.254.169.254/latest/api/token 19 | $instanceType = Invoke-RestMethod -Headers @{'X-aws-ec2-metadata-token' = $token} -Method GET -Uri http://169.254.169.254/latest/meta-data/instance-type 20 | $OSVersion = ((Get-ItemProperty -Path "Microsoft.PowerShell.Core\Registry::\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName).ProductName) -replace "[^0-9]" , '' 21 | if(($OSVersion -eq "2016") -and (($InstanceType[0] -ne 'g') -or ($InstanceType[0] -ne 'p'))){ 22 | $VirtualDisplayDriverRequired = $true 23 | } 24 | $token = Invoke-RestMethod -Headers @{"X-aws-ec2-metadata-token-ttl-seconds" = "21600"} -Method PUT -Uri http://169.254.169.254/latest/api/token 25 | $instanceType = Invoke-RestMethod -Headers @{"X-aws-ec2-metadata-token" = $token} -Method GET -Uri http://169.254.169.254/latest/meta-data/instance-type 26 | # Check and install Visual C++ prerequisite 27 | $InstalledSoftware = (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*) | Where-Object {$_.DisplayName -like "Microsoft Visual C++ 2022*"} 28 | if($InstalledSoftware.count -eq 0){ 29 | Start-Job -Name VcWebReq -ScriptBlock { Invoke-WebRequest -uri https://aka.ms/vs/17/release/vc_redist.x64.exe -OutFile C:\Windows\Temp\vc_redist_64.exe } 30 | Wait-Job -Name VcWebReq 31 | Invoke-Command -ScriptBlock { . C:\Windows\Temp\vc_redist_64.exe /install /passive /norestart } 32 | } 33 | # Download Package(s) 34 | if($VirtualDisplayDriverRequired){ 35 | Start-Job -Name WebReq -ScriptBlock { Invoke-WebRequest -uri https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-virtual-display-x64-Release.msi -OutFile C:\Windows\Temp\DCVDisplayDriver.msi ; Invoke-WebRequest -uri https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-server-x64-Release.msi -OutFile C:\Windows\Temp\DCVServer.msi } 36 | }else{ 37 | Start-Job -Name WebReq -ScriptBlock { Invoke-WebRequest -uri https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-server-x64-Release.msi -OutFile C:\Windows\Temp\DCVServer.msi } 38 | } 39 | Wait-Job -Name WebReq 40 | # Install Package(s) 41 | if($VirtualDisplayDriverRequired){ 42 | Invoke-Command -ScriptBlock {Start-Process "msiexec.exe" -ArgumentList "/I C:\Windows\Temp\DCVDisplayDriver.msi /quiet /norestart" -Wait} 43 | } 44 | Invoke-Command -ScriptBlock {Start-Process "msiexec.exe" -ArgumentList "/I C:\Windows\Temp\DCVServer.msi ADDLOCAL=ALL /quiet /norestart /l*v dcv_install_msi.log " -Wait} 45 | while (-not(Get-Service dcvserver -ErrorAction SilentlyContinue)) { Start-Sleep -Milliseconds 250 } 46 | 47 | # Automatic DCV Session Creation 48 | # https://docs.aws.amazon.com/dcv/latest/adminguide/managing-sessions-start.html#managing-sessions-start-auto 49 | #$dcvPath = "Microsoft.PowerShell.Core\Registry::\HKEY_USERS\S-1-5-18\Software\GSettings\com\nicesoftware\dcv" 50 | #New-ItemProperty -Path "$dcvPath\session-management" -Name create-session -PropertyType DWORD -Value 1 -force 51 | #New-ItemProperty -Path "$dcvPath\session-management\automatic-console-session" -Name owner -Value "USERNAME" -force 52 | #Restart-Service dcvserver 53 | -------------------------------------------------------------------------------- /bootstrap/Install-DCVandSMAgent.ps1: -------------------------------------------------------------------------------- 1 | 2 | <# 3 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | software and associated documentation files (the "Software"), to deal in the Software 7 | without restriction, including without limitation the rights to use, copy, modify, 8 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | #> 18 | $SessMgrDNS = "SESSION-MGR-PRIVATE-DNS" 19 | $BrokerAgentConnectionPort = "8445" 20 | $token = Invoke-RestMethod -Headers @{'X-aws-ec2-metadata-token-ttl-seconds' = '21600'} -Method PUT -Uri http://169.254.169.254/latest/api/token 21 | $instanceType = Invoke-RestMethod -Headers @{'X-aws-ec2-metadata-token' = $token} -Method GET -Uri http://169.254.169.254/latest/meta-data/instance-type 22 | $OSVersion = ((Get-ItemProperty -Path "Microsoft.PowerShell.Core\Registry::\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion" -Name ProductName).ProductName) -replace "[^0-9]" , '' 23 | if(($OSVersion -eq "2016") -and (($InstanceType[0] -ne 'g') -or ($InstanceType[0] -ne 'p'))){ 24 | $VirtualDisplayDriverRequired = $true 25 | } 26 | $token = Invoke-RestMethod -Headers @{"X-aws-ec2-metadata-token-ttl-seconds" = "21600"} -Method PUT -Uri http://169.254.169.254/latest/api/token 27 | $instanceType = Invoke-RestMethod -Headers @{"X-aws-ec2-metadata-token" = $token} -Method GET -Uri http://169.254.169.254/latest/meta-data/instance-type 28 | # Check and install Visual C++ prerequisite 29 | $InstalledSoftware = (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*) | Where-Object {$_.DisplayName -like "Microsoft Visual C++ 2022*"} 30 | if($InstalledSoftware.count -eq 0){ 31 | Start-Job -Name VcWebReq -ScriptBlock { Invoke-WebRequest -uri https://aka.ms/vs/17/release/vc_redist.x64.exe -OutFile C:\Windows\Temp\vc_redist_64.exe } 32 | Wait-Job -Name VcWebReq 33 | Invoke-Command -ScriptBlock { . C:\Windows\Temp\vc_redist_64.exe /install /passive /norestart } 34 | } 35 | # Download Package(s) 36 | if($VirtualDisplayDriverRequired){ 37 | Start-Job -Name WebReq -ScriptBlock { Invoke-WebRequest -uri https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-virtual-display-x64-Release.msi -OutFile C:\Windows\Temp\DCVDisplayDriver.msi ; Invoke-WebRequest -uri https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-server-x64-Release.msi -OutFile C:\Windows\Temp\DCVServer.msi } 38 | }else{ 39 | Start-Job -Name WebReq -ScriptBlock { Invoke-WebRequest -uri https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-server-x64-Release.msi -OutFile C:\Windows\Temp\DCVServer.msi } 40 | } 41 | Wait-Job -Name WebReq 42 | # Install Package(s) 43 | if($VirtualDisplayDriverRequired){ 44 | Invoke-Command -ScriptBlock {Start-Process "msiexec.exe" -ArgumentList "/I C:\Windows\Temp\DCVDisplayDriver.msi /quiet /norestart" -Wait} 45 | } 46 | Invoke-Command -ScriptBlock {Start-Process "msiexec.exe" -ArgumentList "/I C:\Windows\Temp\DCVServer.msi ADDLOCAL=ALL /quiet /norestart /l*v dcv_install_msi.log " -Wait} 47 | while (-not(Get-Service dcvserver -ErrorAction SilentlyContinue)) { Start-Sleep -Milliseconds 250 } 48 | $dcvPath = "Microsoft.PowerShell.Core\Registry::\HKEY_USERS\S-1-5-18\Software\GSettings\com\nicesoftware\dcv" 49 | Set-ItemProperty -Path "$dcvPath\session-management" -Name create-session -Value 0 -force 50 | New-ItemProperty -Path "$dcvPath\security" -Name "auth-token-verifier" -Value "https://$SessMgrDNS`:$BrokerAgentConnectionPort/agent/validate-authentication-token" -Force 51 | New-ItemProperty -Path "$dcvPath\security" -Name no-tls-strict -PropertyType DWORD -Value 1 -force 52 | Stop-Service dcvserver 53 | 54 | Start-Job -Name SMWebReq -ScriptBlock { Invoke-WebRequest -uri https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-session-manager-agent-x64-Release.msi -OutFile C:\Windows\Temp\DCVSMAgent.msi } 55 | Wait-Job -Name SMWebReq 56 | Invoke-Command -ScriptBlock {Start-Process "msiexec.exe" -ArgumentList "/I C:\Windows\Temp\DCVSMAgent.msi /quiet /norestart " -Wait} 57 | while (-not(Get-Service DcvSessionManagerAgentService -ErrorAction SilentlyContinue)) { Start-Sleep -Milliseconds 250 } 58 | Stop-Service DcvSessionManagerAgentService 59 | 60 | # SET AGENT.CONF 61 | $AgentConfContent = "version = '0.1' 62 | [agent] 63 | broker_host = '$SessMgrDNS' 64 | broker_port = $BrokerAgentConnectionPort 65 | tls_strict = false 66 | broker_update_interval = 15 67 | [log] 68 | level = 'debug' 69 | rotation = 'daily' 70 | " 71 | $AgentConfFolder = "C:\Program Files\NICE\DCVSessionManagerAgent\conf" 72 | New-Item -Path $AgentConfFolder -Name "agent.conf" -ItemType File -Force -Value "$AgentConfContent" 73 | 74 | Set-Service -Name DcvSessionManagerAgentService -StartupType Automatic 75 | Start-Service dcvserver 76 | Start-Service DcvSessionManagerAgentService 77 | -------------------------------------------------------------------------------- /bootstrap/dcv-gateway-installer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | # software and associated documentation files (the "Software"), to deal in the Software 6 | # without restriction, including without limitation the rights to use, copy, modify, 7 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | # NICE DCV Connection Gateway Installer Script 18 | 19 | set -eE 20 | 21 | # Retrieve System Info 22 | read -r system version <<<$(echo $(cat /etc/os-release | grep "^ID=\|^VERSION_ID=" | sort | cut -d"=" -f2 | tr -d "\"" | tr '[:upper:]' '[:lower:]')) 23 | major_version="${version%.*}" 24 | arch="$(arch)" 25 | CLOUDFRONT_PREFIX="https://d1uj6qtbmh3dt5.cloudfront.net" 26 | TMP_DIR="$(mktemp -d /tmp/XXXXXX)" 27 | trap 'rm -rf -- "$TMP_DIR"' ERR 28 | 29 | case $system in 30 | amzn ) 31 | if [ "$major_version" = 2 ]; then 32 | package_type="el7" 33 | package_manager="yum" 34 | package_extension="rpm" 35 | fi 36 | ;; 37 | centos|rhel ) 38 | if [[ "$major_version" =~ ^(7|8|9) ]]; then 39 | package_type="el$major_version" 40 | if [[ "$major_version" =~ ^(8|9) ]]; then 41 | package_manager="dnf" 42 | else 43 | package_manager="yum" 44 | fi 45 | package_extension="rpm" 46 | fi 47 | ;; 48 | ubuntu ) 49 | if [ "$major_version" = 22 ] || [ "$major_version" = 20 ]; then 50 | package_type="ubuntu$(echo $version | tr -d '.')" 51 | package_manager="apt" 52 | package_extension="deb" 53 | fi 54 | ;; 55 | * ) 56 | echo "Error: system '$system' is not supported" 57 | exit 1 58 | ;; 59 | esac 60 | 61 | if [ -z "$package_type" ]; then 62 | echo "Error: system '$system' with version '$version' is not supported for arch '$arch'" 63 | exit 1 64 | fi 65 | 66 | # Download Packages 67 | if [ "$package_manager" = apt ]; then 68 | curl -o "$TMP_DIR/NICE-GPG-KEY" "$CLOUDFRONT_PREFIX/NICE-GPG-KEY" 69 | gpg --import "$TMP_DIR/NICE-GPG-KEY" 70 | if [ $arch != "x86_64" ]; then 71 | deb_arch="arm64" 72 | curl -o "$TMP_DIR/nice-dcv-server.tgz" "$CLOUDFRONT_PREFIX/nice-dcv-ubuntu2204-aarch64.tgz" 73 | else 74 | deb_arch="amd64" 75 | curl -o "$TMP_DIR/nice-dcv-server.tgz" "$CLOUDFRONT_PREFIX/nice-dcv-$package_type-$arch.tgz" 76 | fi 77 | curl -o "$TMP_DIR/nice-dcv-connection-gateway.$package_extension" "$CLOUDFRONT_PREFIX/nice-dcv-connection-gateway_$deb_arch.$package_type.$package_extension" 78 | else 79 | rpm --import "$CLOUDFRONT_PREFIX"/NICE-GPG-KEY 80 | curl -o "$TMP_DIR/nice-dcv-connection-gateway.$package_extension" "$CLOUDFRONT_PREFIX/nice-dcv-connection-gateway-$package_type.$arch.$package_extension" 81 | curl -o "$TMP_DIR/nice-dcv-server.tgz" "$CLOUDFRONT_PREFIX/nice-dcv-$package_type-$arch.tgz" 82 | fi 83 | 84 | # Install Packages 85 | tar -xvzf "$TMP_DIR/nice-dcv-server.tgz" -C "$TMP_DIR" 86 | for package_pattern in "nice-dcv-web-viewer*" "nice-dcv-connection-gateway.$package_extension"; do 87 | package_full_path=$(find "$TMP_DIR" -name "$package_pattern") 88 | "$package_manager" install -y "$package_full_path" 89 | done 90 | 91 | # Configure Gateway 92 | ## Enables Web Access through the Gateway 93 | sed -i --expression 's|url = "https://localhost:8080"|local-resources-path = "/usr/share/dcv/www"|' /etc/dcv-connection-gateway/dcv-connection-gateway.conf 94 | ## Uncomment the line below to add your Session Resolver and replace the placeholder 95 | #sed -i --expression 's|url = "https://localhost:8081"|url = "https://RESOLVER-URL"|' /etc/dcv-connection-gateway/dcv-connection-gateway.conf 96 | 97 | # Enable and start Gateway 98 | systemctl enable dcv-connection-gateway 99 | systemctl start dcv-connection-gateway 100 | 101 | # Clean Up 102 | rm -rf "$TMP_DIR" 103 | -------------------------------------------------------------------------------- /bootstrap/dcv-session-manager-installer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | # software and associated documentation files (the "Software"), to deal in the Software 6 | # without restriction, including without limitation the rights to use, copy, modify, 7 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | # NICE DCV Session Manager Installer Script 18 | 19 | set -eE 20 | 21 | # Retrieve System Info 22 | read -r system version <<<$(echo $(cat /etc/os-release | grep "^ID=\|^VERSION_ID=" | sort | cut -d"=" -f2 | tr -d "\"" | tr '[:upper:]' '[:lower:]')) 23 | major_version="${version%.*}" 24 | CLOUDFRONT_PREFIX="https://d1uj6qtbmh3dt5.cloudfront.net" 25 | TMP_DIR="$(mktemp -d /tmp/XXXXXX)" 26 | trap 'rm -rf -- "$TMP_DIR"' ERR 27 | 28 | case $system in 29 | amzn ) 30 | if [ "$major_version" = 2 ]; then 31 | package_type="el7" 32 | package_manager="yum" 33 | package_extension="rpm" 34 | fi 35 | ;; 36 | centos|rhel ) 37 | if [[ "$major_version" =~ ^(7|8|9) ]]; then 38 | package_type="el$major_version" 39 | if [[ "$major_version" =~ ^(8|9) ]]; then 40 | package_manager="dnf" 41 | else 42 | package_manager="yum" 43 | fi 44 | package_extension="rpm" 45 | fi 46 | ;; 47 | ubuntu ) 48 | if [ "$major_version" = 22 ] || [ "$major_version" = 20 ]; then 49 | package_type="ubuntu$(echo $version | tr -d '.')" 50 | package_manager="apt" 51 | package_extension="deb" 52 | fi 53 | ;; 54 | * ) 55 | echo "Error: system '$system' is not supported" 56 | exit 1 57 | ;; 58 | esac 59 | 60 | 61 | if [ -z "$package_type" ]; then 62 | echo "Error: system '$system' with version '$version' is not supported" 63 | exit 1 64 | fi 65 | 66 | # Download Packages 67 | if [ "$package_manager" = apt ]; then 68 | curl -o "$TMP_DIR/NICE-GPG-KEY" "$CLOUDFRONT_PREFIX/NICE-GPG-KEY" 69 | gpg --import "$TMP_DIR/NICE-GPG-KEY" 70 | curl -o "$TMP_DIR/nice-dcv-session-manager-broker.$package_extension" "$CLOUDFRONT_PREFIX/nice-dcv-session-manager-broker_all.$package_type.$package_extension" 71 | else 72 | rpm --import "$CLOUDFRONT_PREFIX"/NICE-GPG-KEY 73 | curl -o "$TMP_DIR/nice-dcv-session-manager-broker.$package_extension" "$CLOUDFRONT_PREFIX/nice-dcv-session-manager-broker-$package_type.noarch.$package_extension" 74 | fi 75 | 76 | # Install Packages 77 | for package_pattern in "nice-dcv-session-manager-broker.$package_extension"; do 78 | package_full_path=$(find "$TMP_DIR" -name "$package_pattern") 79 | "$package_manager" install -y "$package_full_path" 80 | done 81 | 82 | # Enable and start DCV Session Manager service 83 | systemctl start dcv-session-manager-broker 84 | systemctl enable dcv-session-manager-broker 85 | 86 | # Configure DCV Session Manager 87 | CONFIG_PATH="/etc/dcv-session-manager-broker/session-manager-broker.properties" 88 | TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` 89 | REGION=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/region) 90 | ## Enable the gateway in config 91 | sed -i '/^enable-gateway/s/=.*$/= true/' "$CONFIG_PATH" 92 | ## Uncomment the broker connector host and port in config 93 | sed -i '/gateway-to-broker-connector-https-port/s/^#\s//g' "$CONFIG_PATH" 94 | sed -i '/gateway-to-broker-connector-bind-host/s/^#\s//g' "$CONFIG_PATH" 95 | 96 | ## (Optional) Uncomment the following entries to enable the broker to persist on Amazon DynamoDB in config 97 | #sed -i '/^enable-persistence/s/=.*$/= true/' "$CONFIG_PATH" 98 | #sed -i '/persistence-db/s/^#\s//g' "$CONFIG_PATH" 99 | #sed -i '/dynamodb-region/s/^#\s//g' "$CONFIG_PATH" 100 | #sed -i '/dynamodb-table-rcu/s/^#\s//g' "$CONFIG_PATH" 101 | #sed -i '/dynamodb-table-wcu/s/^#\s//g' "$CONFIG_PATH" 102 | #sed -i '/dynamodb-table-name-prefix/s/^#\s//g' "$CONFIG_PATH" 103 | #sed -i "/^dynamodb-region/s/=.*$/= $REGION/" "$CONFIG_PATH" 104 | 105 | # Restart the broker service 106 | systemctl restart dcv-session-manager-broker.service 107 | 108 | # Clean Up 109 | rm -rf "$TMP_DIR" -------------------------------------------------------------------------------- /bootstrap/linux-config-sessionmgr-agent.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | # software and associated documentation files (the "Software"), to deal in the Software 6 | # without restriction, including without limitation the rights to use, copy, modify, 7 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | # permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | BROKER_PRIVATE_DNS="SESSION-MGR-PRIVATE-DNS" 18 | sed -i --expression "s|#auth-token-verifier=\"https://127.0.0.1:8444\"|auth-token-verifier=\"https://$BROKER_PRIVATE_DNS:8445/agent/validate-authentication-token\"|" /etc/dcv/dcv.conf 19 | sed -i "/\[security\]/a administrators=[\"dcvsmagent\"]\nno-tls-strict=true" /etc/dcv/dcv.conf 20 | sed -i --expression "s|broker_host = ''|broker_host = \"$BROKER_PRIVATE_DNS\"|" /etc/dcv-session-manager-agent/agent.conf 21 | sed -i --expression 's|#tls_strict = false|tls_strict = false|' /etc/dcv-session-manager-agent/agent.conf 22 | systemctl enable dcv-session-manager-agent.service 23 | systemctl start dcv-session-manager-agent 24 | systemctl restart dcvserver -------------------------------------------------------------------------------- /cdk/README.md: -------------------------------------------------------------------------------- 1 | # DCV CDK Samples 2 | 3 | This folder contains sample DCV deployments using the [AWS Cloud Development Kit](https://aws.amazon.com/cdk/) (AWS CDK). These samples are intended to be used so you quickly deploy a DCV environment. The code samples may be modified to meet you requirements. 4 | 5 | ## Glossary 6 | - [dcv-gw-sm-without-pipelines](./dcv-gw-sm-without-pipelines/README.md) 7 | - [dcv-gw-sm-with-pipelines](./dcv-gw-sm-with-pipelines/README.md) 8 | - [dcv-sm-access-console](./dcv-access-console/README.md) 9 | 10 | ## Overview 11 | 12 | ### dcv-gw-sm-without-pipelines 13 | This AWS CDK sample provisions the foundational infrastructure for a [DCV Connection Gateway](https://docs.aws.amazon.com/dcv/latest/gw-admin/what-is-gw.html) with [DCV Session Manager](https://docs.aws.amazon.com/dcv/latest/sm-admin/what-is-sm.html) environment. Both DCV Session Manager and DCV Connection Gateway are configured with bootstrap scripts so that can you utilize base AMIs. This deployment is intended to be deployed with Amazon Linux 2, but you can also deploy using other supported operating systems. For more information, see the associated [README](./dcv-gw-sm-without-pipelines/README.md). 14 | 15 | ![Without Pipelines Architecture](./dcv-gw-sm-without-pipelines/documentation/images/SolutionArchitecture.png) 16 | 17 | ### dcv-gw-sm-with-pipelines 18 | This AWS CDK sample provisions the foundational infrastructure for a [DCV Connection Gateway](https://docs.aws.amazon.com/dcv/latest/gw-admin/what-is-gw.html) with [DCV Session Manager](https://docs.aws.amazon.com/dcv/latest/sm-admin/what-is-sm.html) environment. Both DCV Session Manager and DCV Connection Gateway have EC2 Image Builder pipelines to publish a configured AMI. This deployment is intended to be deployed with ARM-based Amazon Linux 2, but you can also deploy using other supported operating systems. For more information, see the associated [README](./dcv-gw-sm-with-pipelines/README.md). 19 | 20 | ![With Pipelines Architecture](./dcv-gw-sm-with-pipelines/documentation/images/SolutionArchitecture.PNG) 21 | 22 | ### dcv-access-console 23 | This AWS CDK sample provisions a single Amazon EC2 instance that hosts all of the [DCV Access Console](https://docs.aws.amazon.com/dcv/latest/access-console/what-is-access-console.html) components. This deployment depends on an existing DCV Session Manager environment. If you do not have an existing environment, you may deploy *dcv-gw-sm-without-pipelines* or *dcv-gw-sm-with-pipelines* before deploying *dcv-access-console*. This CDK is intended to be deployed with ARM-based Amazon Linux 2023, but you can also deploy using other supported operating systems. The usage of a DCV Connection Gateway is optional. The CDK also supports the DCV Access Console being deployed in a private subnet to be accessed internally. For more information, see the associated [README](./dcv-access-console/README.md). 24 | 25 | ![Access Console Architecture](./dcv-access-console/documentation/images/SolutionArchitecture.png) 26 | 27 | ## Security 28 | 29 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 30 | 31 | ## License 32 | 33 | This library is licensed under the MIT-0 License. See the LICENSE file. 34 | -------------------------------------------------------------------------------- /cdk/dcv-access-console/README.md: -------------------------------------------------------------------------------- 1 | # Amazon DCV Access Console Deployment 2 | 3 | Accelerate [Amazon DCV Access Console](https://docs.aws.amazon.com/dcv/latest/access-console/what-is-access-console.html) deployments with the AWS Cloud Development Kit (AWS CDK). This CDK will create a single Amazon Elastic Compute Cloud (Amazon EC2) instance to host all of the required DCV Access Console components. 4 | 5 | ## Overview 6 | The architecture diagram below depicts the resources that this solution will deploy into your account. This CDK will only deploy the DCV Access Console. An existing DCV Session Manager environment is required. The DCV Connection Gateway in the diagram is optional. 7 | 8 | ![Figure1: Architecture](./documentation/images/SolutionArchitecture.png) 9 | 10 | Figure 1: AWS CDK that will deploy and configure a DCV Access Console on a single instance in an existing DCV Session Manager environment. 11 | 12 | ## Prerequisites 13 | - An AWS account with full permissions on: 14 | - AWS Systems Manager Parameter Store 15 | - AWS Systems Manager Session Manager 16 | - Amazon Elastic Compute Cloud (Amazon EC2) 17 | - Amazon VPC 18 | - A system with the following installed: 19 | - AWS CLI 20 | - AWS CDK 21 | - Node.js 22 | - Python 23 | - Git 24 | - Basic understanding of: 25 | - AWS Cloud 26 | - DCV usage 27 | - Scripting languages (Python, Bash) 28 | 29 | # AWS VPC Infrastructure Creation with CDK 30 | This example creates: 31 | - A single EC2 instance hosting the DCV Access Console 32 | - Systems Manager Parameter Store parameters 33 | - IAM role for Access Console 34 | - Security Group for Access Console 35 | 36 | >Note: An SSH key-pair (.pem) is required to be registered in your AWS account and region. This key-pair name needs to be entered in the `config.json` file before deploying the infra stack in order to allow SSH access into the instance. 37 | 38 | ## Step 1: NPM 39 | The LTS version of [npm](https://www.npmjs.com/) is recommended. If npm is already on your system, upgrade npm to the latest version. 40 | 41 | ```bash 42 | npm install -g npm@latest 43 | ``` 44 | 45 | ## Step 2: AWS CDK 46 | 47 | Ensure that the latest [aws-cdk](https://aws.amazon.com/cdk/) is installed. 48 | 49 | ```bash 50 | npm install -g aws-cdk 51 | ``` 52 | 53 | Once installed, the following command confirms the installation was successful. 54 | 55 | ```bash 56 | cdk --version 57 | ``` 58 | 59 | ## Step 3: AWS CLI 60 | Install [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 61 | 62 | 63 | ## Step 4: Git 64 | Install [Git](https://git-scm.com/) 65 | 66 | ## Step 5: Clone the repository 67 | Visit [clone a repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) for instructions. Clone this repository locally. 68 | 69 | ## Step 6: Create a virtual environment 70 | Open up a command line terminal of your choice. 71 | ### Windows 72 | ```bash 73 | virtualenv --python dcv-env 74 | .\dcv-env\Scripts\activate 75 | ``` 76 | 77 | ### macOS/Linux 78 | ```bash 79 | virtualenv --python dcv-env 80 | source dcv-env/bin/activate 81 | ``` 82 | 83 | ## Step 7: Install Project Dependencies 84 | Change directory into the **cdk** folder of the cloned repository. Install project dependencies. 85 | 86 | ```bash 87 | pip install -r requirements.txt 88 | ``` 89 | 90 | ## Step 8: Assume IAM role 91 | [Use an IAM role in the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html). 92 | > Note: To confirm your credentials are associated with the correct AWS account, you can run the following AWS CLI command - 93 | 94 | ```bash 95 | aws sts get-caller-identity 96 | ``` 97 | (See [get-caller-identity](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/sts/get-caller-identity.html) in the AWS CLI documentation). 98 | 99 | ## Step 9: CDK Bootstrap 100 | [Bootstrap your AWS CDK environment. ](https://docs.aws.amazon.com/cdk/v2/guide/cli.html#cli-bootstrap) 101 | Replace with your desired account number and region. 102 | 103 | ```bash 104 | cdk bootstrap 1234567890/us-east-1 105 | ``` 106 | 107 | ## Step 10: Set AWS Account and Region for Infrastructure 108 | Set the AWS account id and region by specifying in `/config.json` 109 | ```bash 110 | { 111 | ... 112 | "accountId": "xxxxxxxxxxxx", 113 | "region": "xx-xxxx-x", 114 | ... 115 | } 116 | ``` 117 | 118 | Ensure the region is set in your AWS credentials by running the following command by replacing `us-east-1` with your intended region: 119 | 120 | For Windows: 121 | ```bash 122 | set AWS_REGION=us-east-1 123 | ``` 124 | For Mac/Linux: 125 | ```bash 126 | export AWS_REGION=us-east-1 127 | ``` 128 | 129 | ## Step 11: Key Pairs for EC2 130 | Use your own Key Pair or [Create Key Pairs](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/create-key-pairs.html) for accessing the EC2s. 131 | 132 | Use the Key Pair value for *sshKeypairName* in `/config.json` 133 | 134 | ```bash 135 | { 136 | "accountId": "xxxxxxxxxxxx", 137 | ... 138 | "sshKeypairName": "xxxxxxxxxxxxxxx", 139 | ... 140 | } 141 | ``` 142 | 143 | ## Step 12: AWS Key Management Service (KMS) 144 | Choose to use the AWS provide default KMS or [Create Keys](href="https://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html). 145 | 146 | Use the KMS value for kms_key_name in `/config.json` 147 | 148 | ```bash 149 | { 150 | "accountId": "xxxxxxxxxxxx", 151 | ... 152 | "kmsKeyName": "aws/ebs", 153 | ... 154 | } 155 | ``` 156 | 157 | ## Step 13: Networking 158 | Provide the network details for where the DCV Access Console should be provisioned. This includes the VPC ID, Subnet ID, and Availability Zone (ie. us-east-1a). 159 | 160 | **Note:** If you provide a private subnet, it needs to have a route to a NAT Gateway to download components. Private deployments can only be accessed on the private network. 161 | 162 | ```json 163 | { 164 | ... 165 | "network" : { 166 | "vpcId": "vpc-xxxxxxxxxxxxxxxxx", 167 | "accessConsoleSubnetId": "subnet-xxxxxxxxxxxxxxxxx", 168 | "subnetAZ": "xx-xxxx-xx" 169 | }, ... 170 | } 171 | ``` 172 | 173 | 174 | ## Step 14: Update the Access Console parameters 175 | This CDK is intended to deployed in an existing DCV Session Manager environment. Update the `config.json` parameters to align to your existing environment. 176 | 177 | ### amiId 178 | This CDK is intended to be deployed using the ARM-based Amazon Linux 2023 AMI. This can be found within the [AMI Catalog](https://console.aws.amazon.com/ec2/home?#AMICatalog:). 179 | 180 | If you would like to use a different [supported distribution for DCV Access Console](https://docs.aws.amazon.com/dcv/latest/access-console/requirements.html), ensure you meet the following AMI checklist. 181 | 182 | **AMI Checklist** 183 | - The provided AMI is based on a [supported distribution for DCV Access Console](https://docs.aws.amazon.com/dcv/latest/access-console/requirements.html). 184 | - Ensure your provided AMI is up to date and has the [AWS CLI](https://aws.amazon.com/cli/) installed. 185 | - The CDK assumes you are using ARM-based AMIs so you can benefit from [EC2 Graviton](https://aws.amazon.com/ec2/graviton/). If you would like to use an x86-based AMI, you may update the statically configured instance types in *dcv_ac_infra.py*. 186 | - To utilize AWS Systems Manager Session Manager for access, ensure [SSM Agent](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html) is installed within your AMI. 187 | 188 | **Note:** If you plan to use Ubuntu, it is recommended you run the [installation wizard](https://docs.aws.amazon.com/dcv/latest/access-console/using-setup-wizard.html) manually. Modify *access-console-user-data.py* to align to the Ubuntu commented caveats (comment out wizard call and uncomment the TMP_DIR). 189 | 190 | ### sessionMgrDns 191 | This parameter is the private DNS name of your DCV Session Manager. This parameter configures the DCV Access Console to contact DCV Session Manager through the provided DNS name. 192 | 193 | ### DCV Session Manager credentials 194 | To interact with DCV Session Manager, the client needs to have registered credentials. If you do not have registered credentials for DCV Session Manager, see *Prerequisites* in the [DCV Access Console Administrator Guide](https://docs.aws.amazon.com/dcv/latest/access-console/prerequisites.html). These credentials are stored in SSM Parameter Store so the Access Console instance can retrieve them. Users with Identity Access Management permissions that permit access to the parameter will also be able to retrieve the credentials. 195 | 196 | - **smClientId** - Provide your registered Client ID. 197 | - **smClientPsw** - Provide your registered Client Password. 198 | 199 | ### adminUser 200 | This CDK configures the DCV Access Console to use *system-auth*, which redirects the credentials to the PAM module on the DCV Access Console host. On Amazon Linux 2023, the default created user is *ec2-user*. If you choose use this user for testing, connect to the DCV Access Console with SSM Session Manager and set a password for *ec2-user*. For example, `sudo passwd ec2-user`. 201 | 202 | ### gatewayDns (Optional) 203 | If your existing environment utilizes a [DCV Connection Gateway](https://docs.aws.amazon.com/dcv/latest/gw-admin/what-is-gw.html), provide the DNS name for the gateway in this parameter. If you front your gateways with a NLB, provide the DNS name of the NLB. 204 | 205 | **If you do not use a gateway, leave this parameter blank.** 206 | 207 | 208 | ### inboundAccess 209 | This parameter configures the inbound access on the DCV Access Console Security Group. By default, the connection is made on port 443. If you would like to isolate this to a specific IP range, update this parameter. 210 | 211 | **The default parameter allows 443 traffic from anywhere** 212 | 213 | Example config.json 214 | 215 | ```bash 216 | { 217 | ... 218 | "accessConsole" : { 219 | "amiId": "ami-xxxxxxxxxxxxxxxxx", 220 | "sessionMgrDns": "PRIVATE-BROKER-DNS", 221 | "smClientId": "ASIAIOSFODNN7EXAMPLE", 222 | "smClientPsw": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", 223 | "adminUser": "ec2-user", 224 | "gatewayDns": "", 225 | "inboundAccess": "0.0.0.0/0" 226 | } 227 | } 228 | ``` 229 | 230 | ## Step 15: Check Project with Synth 231 | Verify there are no errors with the cloned project with [cdk synth](https://docs.aws.amazon.com/cdk/v2/guide/cli.html#cli-synth) (synthesize). 232 | ```bash 233 | cdk synth --all 234 | ``` 235 | 236 | ## Step 16: Deploy DCV Access Console stack 237 | ```bash 238 | cdk deploy 239 | ``` 240 | 241 | ## Step 17: Testing your Configuration 242 | After your deployment is finished, the CDK the output the URL of your DCV Access Console. Once the underlying EC2 instance is passing health checks, you can access the DCV Access Console through this URL. For usage instructions, see *Getting started with the Amazon DCV Session Manager console* in the [DCV Access Console Administrator Guide](https://docs.aws.amazon.com/dcv/latest/access-console/getting-started.html) 243 | 244 | ## Cleanup your Environment 245 | To destroy all infrastructure contained in the CDK, run the following command: 246 | ```bash 247 | cdk destroy 248 | ``` 249 | 250 | ## Troubleshooting DCV Access Console 251 | See the *Troubleshooting* section of the [DCV Access Console Administrator Guide](https://docs.aws.amazon.com/dcv/latest/access-console/troubleshooting.html). 252 | 253 | The bash bootstrap creates a log file located at `/var/log/dcv-access-console-install.log`. This bootstrap builds your configuration file (`onebox-config-input.json`) and passes it into the [DCV Access Console Setup Wizard](https://docs.aws.amazon.com/dcv/latest/access-console/using-setup-wizard.html). The installation is placed within a temporary directory, which can be found in the bootstrap log. The Setup Wizard created detailed installation logs within the `output` directory. If you find the installation failed on a step, you may run the installation again. This can be performed by connecting to the Access Console instance with AWS SSM Session Manager and running the following command from within the installation directory: 254 | 255 | `sudo python3 wizard.py --is-onebox --input-json onebox-config-input.json --force` 256 | 257 | ## CDK Feedback 258 | If you have issues or feedback, open an issue on the Github repository. Note, this CDK is licensed as MIT-0. As-is examples are not supported by AWS Support. 259 | 260 | ## Authors and acknowledgment 261 | [Andrew Morgan](https://github.com/morgnza)
262 | 263 | ## License 264 | MIT License -------------------------------------------------------------------------------- /cdk/dcv-access-console/app.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | # this software and associated documentation files (the "Software"), to deal in 5 | # the Software without restriction, including without limitation the rights to 6 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | # the Software, and to permit persons to whom the Software is furnished to do so. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import json 17 | import aws_cdk as cdk 18 | from stacks.dcv_ac_infra.dcv_ac_infra import DcvAccessConsole 19 | 20 | app = cdk.App() 21 | 22 | # Load the app configuration from the config.json file 23 | try: 24 | with open("config.json", "r") as config_file: 25 | config_data = json.load(config_file) 26 | except Exception as e: 27 | print(f"Could not read the app configuration file. {e}") 28 | raise e 29 | 30 | 31 | # Set CDK environment variables 32 | environment = cdk.Environment(account=config_data['accountId'], region=config_data['region']) 33 | 34 | # Create the DCV Infrastructure Stack for Session Manager and Connection Gateway 35 | DcvAccessConsole(app, "DcvAccessConsole", 36 | description='(uksb-1tupboc66) (tag:dcv-access-console)', 37 | config_data=config_data, 38 | env=environment) 39 | 40 | app.synth() -------------------------------------------------------------------------------- /cdk/dcv-access-console/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "python app.py", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "requirements*.txt", 11 | "**/__init__.py", 12 | "**/__pycache__" 13 | ] 14 | }, 15 | "context": { 16 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 17 | "@aws-cdk/core:checkSecretUsage": true, 18 | "@aws-cdk/core:target-partitions": [ 19 | "aws", 20 | "aws-cn" 21 | ], 22 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 23 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 24 | "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, 25 | "@aws-cdk/aws-iam:minimizePolicies": true, 26 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 27 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 28 | "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, 29 | "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, 30 | "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, 31 | "@aws-cdk/core:enablePartitionLiterals": true, 32 | "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, 33 | "@aws-cdk/aws-iam:standardizedServicePrincipals": true, 34 | "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, 35 | "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, 36 | "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, 37 | "@aws-cdk/aws-route53-patters:useCertificate": true, 38 | "@aws-cdk/customresources:installLatestAwsSdkDefault": false, 39 | "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, 40 | "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, 41 | "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, 42 | "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, 43 | "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, 44 | "@aws-cdk/aws-redshift:columnId": true, 45 | "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, 46 | "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, 47 | "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, 48 | "@aws-cdk/aws-kms:aliasNameRef": true, 49 | "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, 50 | "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, 51 | "@aws-cdk/aws-efs:denyAnonymousAccess": true, 52 | "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, 53 | "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, 54 | "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, 55 | "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, 56 | "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, 57 | "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, 58 | "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /cdk/dcv-access-console/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "NOTE: BEFORE DEPLOYING SESSION MANAGER AND CONNECTION GATEWAY STACK, UPDATE THE ACCOUNT ID AND REGION BELOW", 3 | "accountId": "xxxxxxxxxxxxx", 4 | "region": "xx-xxxx-x", 5 | "sshKeypairName": "", 6 | "kmsKeyName": "aws/ebs", 7 | "network" : { 8 | "vpcId": "vpc-xxxxxxxxxxxxxxxxx", 9 | "accessConsoleSubnetId": "subnet-xxxxxxxxxxxxxxxxx", 10 | "subnetAZ": "xx-xxxx-xx" 11 | }, 12 | "accessConsole" : { 13 | "amiId": "", 14 | "sessionMgrDns": "https://PRIVATE-BROKER-DNS", 15 | "smClientId": "", 16 | "smClientPsw": "", 17 | "adminUser": "ec2-user", 18 | "gatewayDns": "", 19 | "inboundAccess": "0.0.0.0/0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /cdk/dcv-access-console/documentation/images/SolutionArchitecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/dcv-samples/62554cb1deb94b843a7faceb94ff774b94f73243/cdk/dcv-access-console/documentation/images/SolutionArchitecture.png -------------------------------------------------------------------------------- /cdk/dcv-access-console/requirements.txt: -------------------------------------------------------------------------------- 1 | aws-cdk-lib>=2.141.0 2 | constructs>=10.0.0,<11.0.0 -------------------------------------------------------------------------------- /cdk/dcv-access-console/scripts/access-console-user-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | # this software and associated documentation files (the "Software"), to deal in 6 | # the Software without restriction, including without limitation the rights to 7 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | # the Software, and to permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 12 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 13 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 14 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 15 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | LOG_PATH="/var/log/dcv-access-console-install.log" 18 | echo $(date -u) "*****START USER DATA SCRIPT*****" | tee -a "$LOG_PATH" 19 | TMP_DIR="$(mktemp -d /tmp/XXXXXX)" 20 | echo $(date -u) "Created temp directory: $TMP_DIR" | tee -a "$LOG_PATH" 21 | 22 | # Uncommet the following two lines if launching an Ubuntu based AMI 23 | #TMP_DIR="/etc/dcv-access-console-install" 24 | #mkdir "$TMP_DIR" 25 | 26 | # Retrieve System Info 27 | read -r system version <<<$(echo $(cat /etc/os-release | grep "^ID=\|^VERSION_ID=" | sort | cut -d"=" -f2 | tr -d "\"" | tr '[:upper:]' '[:lower:]')) 28 | major_version="${version%.*}" 29 | arch="$(arch)" 30 | CLOUDFRONT_PREFIX="https://d1uj6qtbmh3dt5.cloudfront.net" 31 | 32 | case $system in 33 | amzn ) 34 | if [ "$major_version" = 2023 ]; then 35 | package_type="el9" 36 | package_manager="yum" 37 | package_extension="rpm" 38 | elif [ "$major_version" = 2 ]; then 39 | package_type="el7" 40 | package_manager="yum" 41 | package_extension="rpm" 42 | fi 43 | ;; 44 | centos|rhel|rocky ) 45 | if [[ "$major_version" =~ ^(7|8|9) ]]; then 46 | package_type="el$major_version" 47 | if [[ "$major_version" =~ ^(8|9) ]]; then 48 | package_manager="dnf" 49 | else 50 | package_manager="yum" 51 | fi 52 | package_extension="rpm" 53 | fi 54 | ;; 55 | ubuntu ) 56 | if [ "$major_version" = 22 ] || [ "$major_version" = 20 ]; then 57 | package_type="ubuntu$(echo $version | tr -d '.')" 58 | package_manager="apt" 59 | package_extension="deb" 60 | fi 61 | ;; 62 | * ) 63 | echo $(date -u) "Error: system '$system' is not supported" | tee -a "$LOG_PATH" 64 | exit 1 65 | ;; 66 | esac 67 | 68 | if [ -z "$package_type" ]; then 69 | echo $(date -u) "Error: system '$system' with version '$version' is not supported for arch '$arch'" | tee -a "$LOG_PATH" 70 | exit 1 71 | fi 72 | 73 | echo $(date -u) "System Info detected:" | tee -a "$LOG_PATH" 74 | echo $(date -u) "OS Type: $package_type" | tee -a "$LOG_PATH" 75 | echo $(date -u) "Package Manager: $package_manager" | tee -a "$LOG_PATH" 76 | 77 | if ! command -v jq &> /dev/null 78 | then 79 | echo $(date -u) "jq dependency not found. Installing" | tee -a "$LOG_PATH" 80 | "$package_manager" install -y jq 81 | fi 82 | 83 | if ! command -v aws &> /dev/null 84 | then 85 | echo $(date -u) "awscli dependency not found. Installing" | tee -a "$LOG_PATH" 86 | "$package_manager" install -y awscli 87 | fi 88 | 89 | # Download Packages 90 | if [ "$package_manager" = apt ]; then 91 | curl -o "$TMP_DIR/NICE-GPG-KEY" "$CLOUDFRONT_PREFIX/NICE-GPG-KEY" 92 | gpg --import "$TMP_DIR/NICE-GPG-KEY" 93 | if [ $arch != "x86_64" ]; then 94 | curl -o "$TMP_DIR/nice-dcv-access-console.tgz" "$CLOUDFRONT_PREFIX/nice-dcv-access-console-$package_type-aarch64.tgz" 95 | else 96 | curl -o "$TMP_DIR/nice-dcv-access-console.tgz" "$CLOUDFRONT_PREFIX/nice-dcv-access-console-$package_type-x86_64.tgz" 97 | fi 98 | else 99 | rpm --import "$CLOUDFRONT_PREFIX"/NICE-GPG-KEY 100 | curl -o "$TMP_DIR/nice-dcv-access-console.tgz" "$CLOUDFRONT_PREFIX/nice-dcv-access-console-$package_type-$arch.tgz" 101 | fi 102 | 103 | echo $(date -u) "DCV Access Console packages downloaded" | tee -a "$LOG_PATH" 104 | 105 | tar -xvzf "$TMP_DIR/nice-dcv-access-console.tgz" -C "$TMP_DIR" 106 | 107 | # Retrieve required setup information 108 | TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` 109 | region=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/region) 110 | metadata=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/) 111 | if grep -q "public-hostname" <<< "$metadata"; then 112 | acDns=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/public-hostname) 113 | else 114 | acDns=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/local-hostname) 115 | fi 116 | smDns=$(aws ssm get-parameter --name dcv-session-manager-dns --region "$region" --with-decryption | grep -Po '"Value": "\K[^"]*') 117 | adminUser=$(aws ssm get-parameter --name dcv-access-console-admin --region "$region" --with-decryption | grep -Po '"Value": "\K[^"]*') 118 | response=$? 119 | if [[ "$response" -ne 0 ]]; then 120 | echo $(date -u) "SSM Parameter for admin not found. Defaulting to ec2-user" | tee -a "$LOG_PATH" 121 | adminUser="ec2-user" 122 | fi 123 | dcvConnGwyCheck=true 124 | dcvConnGwy=$(aws ssm get-parameter --name dcv-connection-gwy-dns --region "$region" --with-decryption | grep -Po '"Value": "\K[^"]*') 125 | response=$? 126 | if [[ "$response" -ne 0 ]]; then 127 | echo $(date -u) "SSM Parameter for DCV Connection Gateway not found. Disabling gateway in configuration." | tee -a "$LOG_PATH" 128 | dcvConnGwyCheck=false 129 | fi 130 | dbPwd=$(openssl rand -base64 12) 131 | pamAuth="system-auth" 132 | CREDS=$(aws ssm get-parameter --name dcv-session-manager-credentials --region "$region" --with-decryption | grep -Po '"Value": "\K[^"]*') 133 | IFS=':' 134 | read -r smClintId smClientPwd <<<"$CREDS" 135 | echo $(date -u) "Configuration discovered:" | tee -a "$LOG_PATH" 136 | echo $(date -u) "AWS Region: $region" | tee -a "$LOG_PATH" 137 | echo $(date -u) "Access Console DNS: $acDns" | tee -a "$LOG_PATH" 138 | echo $(date -u) "Broker DNS: $smDns" | tee -a "$LOG_PATH" 139 | echo $(date -u) "DCV Connection Gateway DNS: $dcvConnGwy" | tee -a "$LOG_PATH" 140 | echo $(date -u) "Admin User: $adminUser" | tee -a $"$LOG_PATH" 141 | 142 | # Set configuration input 143 | jsonPath=$(find "$TMP_DIR" | grep onebox_wizard_input.json) 144 | basePath=$(dirname "$jsonPath") 145 | json=$(cat "$jsonPath") 146 | json=$(jq --arg acDns "$acDns" '."onebox-address" = $acDns' <<<"$json") 147 | json=$(jq --arg smDns "$smDns" '."broker-address" = $smDns' <<<"$json") 148 | json=$(jq --arg smClintId "$smClintId" '."broker-client-id" = $smClintId' <<<"$json") 149 | json=$(jq --arg smClientPwd "$smClientPwd" '."broker-client-password" = $smClientPwd' <<<"$json") 150 | json=$(jq -r '."mariadb-username"="maria"' <<<"$json") 151 | json=$(jq --arg dbPwd "$dbPwd" '."mariadb-password" = $dbPwd' <<<"$json") 152 | json=$(jq --arg adminUser "$adminUser" '."admin-user" = $adminUser' <<<"$json") 153 | if [ $dcvConnGwyCheck ]; then 154 | json=$(jq -r '."enable-connection-gateway"=true' <<<"$json") 155 | json=$(jq --arg dcvConnGwy "$dcvConnGwy" '."connection-gateway-host" = $dcvConnGwy' <<<"$json") 156 | fi 157 | json=$(jq --arg pamAuth $pamAuth '. + {"pam-service-name": $pamAuth}' <<<"$json") 158 | echo "$json" > "$basePath/onebox-config-input.json" 159 | 160 | # Install packages 161 | if [ "$system" = rocky ]; then 162 | sudo setsebool -P httpd_can_network_connect 1 163 | fi 164 | wizardPath=$(find "$TMP_DIR" | grep wizard.py) 165 | cd $basePath 166 | echo $(date -u) "Initiating DCV Access Console installation wizard" | tee -a "$LOG_PATH" 167 | 168 | # Comment out the following command and run manually on Ubuntu 169 | python3 wizard.py --is-onebox --input-json onebox-config-input.json --force 170 | 171 | echo "$json" > /etc/dcv-access-console-auth-server/onebox-config-input-bak.json 172 | echo $(date -u) "Created config backup at /etc/dcv-access-console-auth-server/onebox-config-input-bak.json" | tee -a "$LOG_PATH" 173 | -------------------------------------------------------------------------------- /cdk/dcv-access-console/stacks/dcv_ac_infra/dcv_ac_infra.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | # this software and associated documentation files (the "Software"), to deal in 5 | # the Software without restriction, including without limitation the rights to 6 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | # the Software, and to permit persons to whom the Software is furnished to do so. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import os 17 | from aws_cdk import Stack 18 | import aws_cdk as cdk 19 | import aws_cdk.aws_ec2 as ec2 20 | import aws_cdk.aws_iam as iam 21 | import aws_cdk.aws_kms as kms 22 | import aws_cdk.aws_ssm as ssm 23 | from constructs import Construct 24 | 25 | # The DCV Access Console stack 26 | class DcvAccessConsole(Stack): 27 | """ Class to deploy DCV infrastructure components """ 28 | def __init__(self, scope: Construct, construct_id: str, config_data: dict, **kwargs) -> None: 29 | super().__init__(scope, construct_id, **kwargs) 30 | 31 | # Create reference for VPC 32 | vpc = ec2.Vpc.from_lookup(self, "VPC", vpc_id=config_data['network']['vpcId']) 33 | # Create references for target subnet 34 | # using subnet_ids pulled from config.json 35 | subnet_ref = ec2.Subnet.from_subnet_attributes( 36 | self, "SubnetFromAttributes", 37 | subnet_id=config_data['network']['accessConsoleSubnetId'], 38 | availability_zone=config_data['network']['subnetAZ'] 39 | ) 40 | subnet_target = ec2.SubnetSelection( 41 | subnets=[subnet_ref] 42 | ) 43 | 44 | # IAM Fleet Role Configuration 45 | role_access_console = iam.Role(self, "AccessConsoleRole", 46 | assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"), 47 | role_name="dcv-access-console-role" 48 | ) 49 | 50 | role_access_console.add_managed_policy( 51 | iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore") 52 | ) 53 | 54 | role_access_console.add_to_policy(iam.PolicyStatement( 55 | effect=iam.Effect.ALLOW, 56 | actions=[ 57 | 'ssm:DescribeParameters' 58 | ], 59 | resources=[ 60 | f"arn:aws:ssm:{self.region}:{self.account}:parameter/*", 61 | ] 62 | )) 63 | 64 | role_access_console.add_to_policy(iam.PolicyStatement( 65 | effect=iam.Effect.ALLOW, 66 | actions=[ 67 | 'ssm:GetParameter' 68 | ], 69 | resources=[ 70 | f"arn:aws:ssm:{self.region}:{self.account}:parameter/dcv-*", 71 | ] 72 | )) 73 | 74 | # Access Console Security Group configuration 75 | sg_access_console = ec2.SecurityGroup(self, "AccessConsoleSecurityGroup", 76 | vpc=vpc, 77 | description="Security Group for the DCV Access Console", 78 | allow_all_outbound=True, #Egress all 79 | disable_inline_rules=True 80 | ) 81 | 82 | # Add Ingress for Access Console 83 | sg_access_console.add_ingress_rule( 84 | ec2.Peer.ipv4(f"{config_data['accessConsole']['inboundAccess']}"), ec2.Port.tcp(443), \ 85 | "Allow HTTPS traffic to the DCV Access Console" 86 | ) 87 | 88 | # Get KMS Key from Alias/Key Name 89 | kms_arn = f"arn:aws:kms:{self.region}:{self.account}:alias/{config_data['kmsKeyName']}" 90 | kms_key = kms.Key.from_key_arn(self, "kms-key", kms_arn) 91 | 92 | # SSM parameters to hold config values 93 | ssm.StringParameter(self, "brokerCredentials", 94 | description="Client credentials for DCV Session Manager", 95 | parameter_name="dcv-session-manager-credentials", 96 | string_value=f"{config_data['accessConsole']['smClientId']}:{config_data['accessConsole']['smClientPsw']}", 97 | tier=ssm.ParameterTier.STANDARD 98 | ) 99 | 100 | ssm.StringParameter(self, "brokerDns", 101 | description="Reachable DNS of DCV Session Manager", 102 | parameter_name="dcv-session-manager-dns", 103 | string_value=f"{config_data['accessConsole']['sessionMgrDns']}", 104 | tier=ssm.ParameterTier.STANDARD 105 | ) 106 | 107 | if config_data['accessConsole']['gatewayDns'] != "": 108 | ssm.StringParameter(self, "gwyDns", 109 | description="Reachable DNS of DCV Connection Gateway", 110 | parameter_name="dcv-connection-gwy-dns", 111 | string_value=f"{config_data['accessConsole']['gatewayDns']}", 112 | tier=ssm.ParameterTier.STANDARD 113 | ) 114 | 115 | if config_data['accessConsole']['adminUser'] != "ec2-user": 116 | ssm.StringParameter(self, "accessConsoleAdmin", 117 | description="Admin user to set for Access Console", 118 | parameter_name="dcv-access-console-admin", 119 | string_value=f"{config_data['accessConsole']['gatewayDns']}", 120 | tier=ssm.ParameterTier.STANDARD 121 | ) 122 | 123 | 124 | # DCV Access Console 125 | ### DCV Access Console AMI 126 | session_mgr_ami = ec2.GenericLinuxImage({ 127 | self.region : config_data['accessConsole']['amiId'] 128 | } 129 | ) 130 | 131 | # Add the user data script to custom string 132 | access_console_user_data_file = open(os.path.join(os.path.dirname( __file__ ), 133 | "..", "..", 134 | "scripts", 135 | "access-console-user-data.sh"), 136 | "r", encoding="utf-8") 137 | access_console_user_data_content = access_console_user_data_file.read() 138 | access_console_user_data = ec2.UserData.custom(access_console_user_data_content) 139 | 140 | # Create a reference to the SSH Key Pair name given in the config.json file 141 | key_pair = ec2.KeyPair.from_key_pair_attributes(self, "DCVKeyPair", 142 | key_pair_name=config_data['sshKeypairName'] 143 | ) 144 | 145 | # Create the Session Manager EC2 instance 146 | access_console_instance = ec2.Instance(self, "AccessConsoleInstance", 147 | vpc=vpc, 148 | vpc_subnets=subnet_target, 149 | instance_type=ec2.InstanceType.of( 150 | ec2.InstanceClass.M6G, ec2.InstanceSize.LARGE), 151 | machine_image=session_mgr_ami, 152 | security_group=sg_access_console, 153 | key_pair=key_pair, 154 | user_data=access_console_user_data, 155 | role=role_access_console, 156 | block_devices=[ec2.BlockDevice( 157 | device_name="/dev/xvda", 158 | volume=ec2.BlockDeviceVolume.ebs( 159 | 8, encrypted=True, kms_key=kms_key))] 160 | ) 161 | 162 | cdk.CfnOutput(self, "AccessConsoleURL", \ 163 | value= f"You can access the console at https://{access_console_instance.instance_public_dns_name} or privately at https://{access_console_instance.instance_private_dns_name}") 164 | -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-with-pipelines/README.md: -------------------------------------------------------------------------------- 1 | # Scale Out Deployment of Amazon DCV Connection Gateway, Sessions Manager, and Broker 2 | 3 | Accelerate cloud deployment of foundational autoscaling infrastructure for [Amazon DCV](https://aws.amazon.com/hpc/dcv/) with AWS Cloud Development Kit (AWS CDK). AWS CDK will create EC2 Image builder pipelines to create images to run DCV components on Amazon Elastic Compute Cloud (Amazon EC2). 4 | 5 | ## Overview 6 | The architecture diagram below depicts the resources that this solution will deploy into your account. 7 | >Note: A DCV fleet will need to be spun up in conjunction with this solution. For testing this solution, one will be created near the end of the procedure below. 8 | 9 | ![Figure1: Architecture](./documentation/images/SolutionArchitecture.PNG) 10 | Figure 1: AWS CDK that will setup the image build process and foundational infrastructure for a scale out solution of Amazon DCV Connection Gateway and Session Manager. 11 | 12 | ## Prerequisites 13 | - An AWS account with full permissions on: 14 | - Amazon EC2 Image Builder 15 | - AWS Systems Manager (AWS SSM) Parameter Store 16 | - AWS SSM Session Manager 17 | - Amazon Elastic Compute Cloud (Amazon EC2) 18 | - Amazon Virtual Private Cloud (Amazon VPC) 19 | - A system with the following installed: 20 | - AWS CLI 21 | - AWS CDK 22 | - Node.js 23 | - Python 24 | - Git 25 | - Basic understanding of: 26 | - Image building 27 | - AWS Cloud 28 | - Scripting languages (Python, Bash) 29 | - DCV 30 | 31 | >Note: See Testing Prerequisites for additional items 32 | 33 | # EC2 Image Builder Pipeline 34 | This example will create: 35 | - Two EC2 Image Builder Pipelines to build the DCV images; one for DCV Session Manager and one for DCV Connection Gateway 36 | 37 | There are two image builder pipelines that will be created: 38 | - Session Manager 39 | - Connection Gateway 40 | 41 | >Note: If you do not provide a target VPC, a default VPC is required for the EC2 Image Builder Pipeline. If your environment doesn't have a default VPC, you will receive an error message similar to "...An error occurred (VPCIdNotSpecified) when calling the RunInstances operation: No default VPC for this user..." when invoking the pipeline. 42 | 43 | # AWS VPC Infrastructure Creation with CDK 44 | This example contains: 45 | - Choice of VPC and Subnets 46 | - If you do not specify a VPC, the CDK will create: 47 | - A new VPC with an Internet Gateway 48 | - Public and Private subnets 49 | - Two availability zones 50 | - NAT Gateway 51 | - A security groups for DCV Connection Gateway, DCV Session Manager, and DCV Server 52 | - IAM roles required for configuration 53 | - Network Load Balancer for DCV Connection Gateway 54 | - Autoscaling Groups for the DCV Connection Gateway instances 55 | - This solution will initialize the autoscaling group with one Connection Gateway instance per availability zone 56 | - Autoscaling based off of CPU consumption 57 | - SSM Parameter Store parameter to hold the broker DNS 58 | 59 | >Note: An SSH key-pair (.pem or .ppk) is required to be registered in your AWS account and region. This key-pair name is required within the `config.json` file before deploying the infrastructure stack in order to allow SSH access into the instances. AWS SSM can also be used for terminal access. 60 | 61 | ## Step 1: NPM 62 | The LTS version of [npm](https://www.npmjs.com/) is recommended. If npm is already on your system, upgrade npm to the latest version. 63 | npm install -g npm@latest 64 | 65 | ```bash 66 | npm install -g npm@latest 67 | ``` 68 | 69 | ## Step 2: AWS CDK 70 | 71 | Ensure that the latest [aws-cdk](https://aws.amazon.com/cdk/) is installed. 72 | 73 | ```bash 74 | npm install -g aws-cdk 75 | ``` 76 | 77 | Once installed, the command cdk --version will confirm installation was successful. 78 | 79 | ```bash 80 | cdk --version 81 | ``` 82 | 83 | ## Step 3: AWS CLI 84 | Install [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 85 | 86 | 87 | ## Step 4: Git 88 | Install [Git](https://git-scm.com/) 89 | 90 | ## Step 5: Clone the Repository 91 | Visit [clone a repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) for instructions. Clone this repository locally. 92 | 93 | ## Step 6: Create a virtual environment 94 | Open up a command line terminal of your choice. Change directory into the cloned repository. 95 | ### Windows 96 | ```bash 97 | virtualenv --python dcv-env 98 | .\dcv-env\Scripts\activate 99 | ``` 100 | ### macOS/Linux 101 | ```bash 102 | virtualenv --python dcv-env 103 | source dcv-env/bin/activate 104 | ``` 105 | 106 | ## Step 7: Install Project Dependencies 107 | Install project dependencies by running the following command. 108 | 109 | ```bash 110 | pip install -r requirements.txt 111 | ``` 112 | 113 | ## Step 8: Assume IAM role 114 | [Use an IAM role in the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html). 115 | > Note: To confirm your credentials are associated with the correct AWS account, you can run the following AWS CLI command - 116 | ```bash 117 | aws sts get-caller-identity 118 | ``` 119 | (See [get-caller-identity](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/sts/get-caller-identity.html) in the AWS CLI documentation). 120 | 121 | ## Step 9: CDK Bootstrap 122 | [Bootstrap your AWS CDK environment. ](https://docs.aws.amazon.com/cdk/v2/guide/cli.html#cli-bootstrap) 123 | Replace with your desired account number and region. 124 | ```bash 125 | cdk bootstrap 1234567890/us-east-1 126 | ``` 127 | 128 | > Note: sess_manager_ami and conn_gateway_ami will need to get updated with AMI IDs after deploying SessionMgrAmiStack and ConnectionGwyAmiStack stacks and running their pipelines. 129 | 130 | ## Step 10: Set AWS Account and Region for Infrastructure 131 | Set the AWS account id and region by specifying in `config.json` 132 | ```bash 133 | { 134 | ... 135 | "accountId": "xxxxxxxxxxxx", 136 | "region": "xxxxxxxxxxxxxxx", 137 | ... 138 | } 139 | ``` 140 | Also, be sure the region is set in your AWS credentials by running the following command by replacing `us-east-1` with your intended region: 141 | 142 | For Windows: 143 | ```powershell 144 | set AWS_REGION=us-east-1 145 | ``` 146 | For Mac/Linux: 147 | ```bash 148 | export AWS_REGION=us-east-1 149 | ``` 150 | 151 | ## Step 11: Check Project with Synth 152 | Verify there are no errors with the cloned project with [cdk synth](https://docs.aws.amazon.com/cdk/v2/guide/cli.html#cli-synth) (synthesize). 153 | ```bash 154 | cdk synth --all 155 | ``` 156 | 157 | ## Step 12: Deploy Session Manager stack 158 | The CDK will create the DCV Sessions Manager EC2 Image Builder Pipeline. 159 | ```bash 160 | cdk deploy SessionMgrAmiStack 161 | ``` 162 | 163 | ## Step 13: Deploy Connection Gateway stack 164 | The CDK will create the DCV Connection Gateway EC2 Image Builder Pipeline. 165 | ```bash 166 | cdk deploy ConnectionGwyAmiStack 167 | ``` 168 | 169 | ## Step 14: Run the EC2 Builder Pipelines 170 | Run pipelines to create the AMIs for the Sessions Manager and the Connection Gateway. 171 | 172 | 173 | ### Option 1: Running Pipeline from AWS CLI 174 | 175 | Run the following command to run the pipeline. Replace the ``, ``, and `` with your values. The pipeline names are `dcv-connection-gwy-ami-pipeline` or `dcv-session-mgr-ami-pipeline`. 176 | 177 | ```bash 178 | aws imagebuilder start-image-pipeline-execution \ 179 | --image-pipeline-arn arn:aws:imagebuilder:::image-pipeline/ 180 | ``` 181 |
182 | 183 | The cdk outputs the values to run the pipeline using the AWS CLI. 184 | 185 | - Run the dcv-sess-mgr pipeline first 186 | - If successful, run the dcv-conn-gateway pipeline 187 | - Take note of the AMI IDs created 188 | 189 | **Example:** Using the AWS CLI to run the image pipeline. 190 | ```bash 191 | aws imagebuilder start-image-pipeline-execution --image-pipeline-arn arn:aws:imagebuilder:::image-pipeline/pipeline-dcv-conn-gateway 192 | 193 | ``` 194 | 195 | **Example:** Using the AWS CLI to get the AMI ID from the pipeline build. 196 | 197 | ```bash 198 | aws imagebuilder list-image-build-versions --image-version-arn arn:aws:imagebuilder:::image/recipe-dcv-sess-mgr/1.0.0 | grep ami- 199 | 200 | ``` 201 | 202 | Example sample output 203 | ```bash 204 | "image": "ami-1234567890abcdef0", 205 | ``` 206 | The AMI ID response value will be added in the config.json file in Step 14. 207 | 208 | ### Option 2: Use the **Run pipeline** action in the AWS Management Console. 209 | 210 | For more information, see *Run your image pipeline* in the [EC2 Image Builder User Guide](https://docs.aws.amazon.com/imagebuilder/latest/userguide/pipelines-run.html). 211 | 212 | ![Figure2: EC2 Builder Pipeline](./documentation/images/EC2ImageBuilderRunPipeline.jpg) 213 | 214 | - Run the dcv-sess-mgr pipeline first 215 | - If successful, run the dcv-conn-gateway pipeline 216 | - Take note of the AMI IDs created 217 | 218 | 219 | ## Step 15: Add AMI IDs into the CDK project 220 | 221 | Locate and note the AMI ID for each of your pipelines. Your images can be found on the *Images* page in the [EC2 Image Builder Console](https://console.aws.amazon.com/imagebuilder/home?#/images). For more information, see the *List images and build versions* section of the [EC2 Image Builder User Guide](https://docs.aws.amazon.com/imagebuilder/latest/userguide/image-details-list.html). 222 | 223 | Update `config.json` with the AMI IDs from each of your pipelines. Update sess_manager_ami and conn_gateway_ami values. 224 | 225 | Example config.json 226 | 227 | ```bash 228 | { 229 | "accountId": "xxxxxxxxxxxx", 230 | ... 231 | "connectionGwy" : { 232 | ... 233 | "builderAmiId": "ami-xxxxxxxxxxxxxxxxx" 234 | } 235 | ``` 236 | 237 | ## Step 16: Key Pairs for EC2 238 | Use your own Key Pair or [Create Key Pairs](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/create-key-pairs.html) for accessing the EC2s. 239 | 240 | Use the Key Pair value for ssh_keypair_name in `config.json` 241 | ```bash 242 | { 243 | "accountId": "xxxxxxxxxxxx", 244 | ... 245 | "sshKeypairName": "xxxxxxxxxxxxxxx", 246 | ... 247 | } 248 | ``` 249 | 250 | ## Step 17: AWS Key Management Service (KMS) 251 | Choose to use the AWS provide default KMS or [Create Keys](href="https://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html). 252 | 253 | Use the KMS value for kms_key_name in `config.json` 254 | 255 | ```bash 256 | { 257 | "accountId": "xxxxxxxxxxxx", 258 | ... 259 | "kmsKeyName": "aws/ebs", 260 | ... 261 | } 262 | ``` 263 | 264 | ## Step 18: Deploy Infrastructure stack 265 | ```bash 266 | cdk deploy DcvInfraStack 267 | ``` 268 | 269 | ## Step 19: Testing Your Configuration 270 | Now that your infrastructure is created, you must create a DCV session on a DCV server to connect to. For this step, [DCV Session Manager CLI](https://docs.aws.amazon.com/dcv/latest/sm-cli/sm-cli-reference.html) is required, as well as a [DCV Server](https://docs.aws.amazon.com/dcv/latest/adminguide/servers.html). Alteratively, you may deploy DCV Access Console. Use the instructions below to test your deployment. 271 | 272 | ### Testing Prerequisites 273 | There are several ways testing can be performed, but this guide uses [AWS Systems Manager Session Manager](https://aws.amazon.com/systems-manager/) and the CDK provides IAM permissions to use it. 274 | 275 | ### Launch a DCV Server Fleet Instance 276 | Once your CDK deployed instances are passing EC2 health checks, a DCV fleet server is required to be launched. The DCV server also needs to have the DCV Session Manager agent installed. With both of these components configured, the instance will check in with the DCV Session Manager broker so sessions on the instance can be managed by the broker. 277 | 278 | **Note:** You will need to manage the user accounts on the DCV servers. If you do not have a centralized identity (e.g. Microsoft Active Directory), you need to create a local user and set a password. The default user for Windows is `Administrator` and the default user for Amazon Linux 2 is `ec2-user`. You can create new users or update passwords by using AWS Systems Manager Session Manager for remote access. 279 | 280 | #### Windows 281 | To bootstrap a Windows DCV server, first download the [Install-DCVandSMAgent.ps1](/bootstrap/Install-DCVandSMAgent.ps1) script from the bootstrap folder of dcv-samples. Once downloaded, update the `SESSION-MGR-PRIVATE-DNS` placeholder with the private DNS name of your DCV Session Manager instance. This can be retrieved from the EC2 console. Deploy a new Windows-based instance within a private subnet and inject the script content in the **User data** field. Ensure you have chosen a base image that is [supported by DCV server](https://docs.aws.amazon.com/dcv/latest/adminguide/servers.html). The CDK provisioned a DCV server security group named `DcvInfraStack-DCVServerSecurityGroup*`. The CDK also created an instance profile named `dcv-fleet-role`. 282 | 283 | #### Linux 284 | To bootstrap a Windows DCV server, first download the [linux-config-sessionmgr-agent.sh](/bootstrap/linux-config-sessionmgr-agent.sh) script from the bootstrap folder of dcv-samples. Once downloaded, update the `SESSION-MGR-PRIVATE-DNS` placeholder with the private DNS name of your DCV Session Manager instance. This can be retrieved from the EC2 console. By default, Linux operating systems do not provide a desktop environment. For simplicity, you may use the [AWS Marketplace DCV AMI for Amazon Linux 2](https://aws.amazon.com/marketplace/seller-profile?id=74eff437-1315-4130-8b04-27da3fa01de1). Deploy a new instance based on the Marketplace AMI within a private subnet and inject the script content in the **User data** field. If you use an alternative image, ensure you have chosen a base image that is [supported by DCV server](https://docs.aws.amazon.com/dcv/latest/adminguide/servers.html). The CDK provisioned a DCV server security group named `DcvInfraStack-DCVServerSecurityGroup*`. The CDK also created an instance profile named `dcv-fleet-role`. 285 | 286 | When your newly deployed instance is passing EC2 health checks, proceed to the next step. 287 | 288 | ### Register an API Client on Session Manager Instance 289 | 1. Access your DCV Session Manager instance using Systems Manager Session Manager. 290 | 2. To make API calls to your broker, you will need to [register an API client](https://docs.aws.amazon.com/dcv/latest/sm-admin/register-api-client.html). To generate these credentials, run the following command. 291 | > IMPORTANT: These credentials cannot be retrieved later so take note of the response. You will need to rerun this command if you lose the credentials. 292 | 293 | ```bash 294 | sudo -u root dcv-session-manager-broker register-api-client --client-name client_name 295 | ``` 296 | 3. Record the printed credentials and exit out of Sessions Manager SSH session 297 | 298 | ### Communicating with the broker 299 | 300 | #### Option 1: Deploy the DCV Access Console 301 | The [DCV Access Console](https://docs.aws.amazon.com/dcv/latest/access-console/what-is-access-console.html) is a web application that helps administrators and end users manage their DCV sessions. To deploy the DCV Access Console, see the [CDK README](/cdk/README.md) or [administrator guide](https://docs.aws.amazon.com/dcv/latest/access-console/setup.html). 302 | 303 | #### Option 2: Download and Configure the DCV Session Manager CLI 304 | The [DCV Session Manager Command Line Interface (CLI)](https://docs.aws.amazon.com/dcv/latest/sm-cli/what-is-sm-cli.html) is used to interact with a DCV Session Manager broker using commands in your command-line shell. The CDK allows access from the DCV Connection Gateway instances. Alternatively, you may run the CLI on the Session Manager instance. 305 | 306 | 1. Download the DCV Session Manager CLI with the following command: 307 | ```bash 308 | wget https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-session-manager-cli.zip 309 | ``` 310 | 311 | 2. Unzip the CLI package with the following command: 312 | ```bash 313 | unzip nice-dcv-session-manager-cli.zip 314 | ``` 315 | 316 | 3. Configure the CLI ```dcvsmcli.conf``` file. See the [CLI guide](https://docs.aws.amazon.com/dcv/latest/sm-cli/configuration-file.html) for a reference. You will need to have the following configuration set. 317 | 318 | ```json 319 | [output] 320 | # The formatting style for command output. 321 | output-format = json 322 | 323 | # Turn on debug logging 324 | #debug = true 325 | 326 | [security] 327 | # Disable SSL certificates verification. 328 | no-verify-ssl = true 329 | 330 | # CA certificate bundle to use when verifying SSL certificates. 331 | #ca-bundle = ca-bundle.pem 332 | 333 | [authentication] 334 | # hostname of the authentication server used to request the token 335 | #auth-server-url = https://broker-host:broker-port/oauth2/token?grant_type=client_credentials 336 | 337 | # The client ID 338 | client-id = CLIENT_ID_FROM_REGISTRATION 339 | 340 | # The client password 341 | client-password = CLIENT_PASSWORD_FROM_REGISTRATION 342 | 343 | [broker] 344 | # hostname or IP of the broker 345 | url = https://DNSorIP-DCVSessionMgr:8443 346 | ``` 347 | 348 | 4. Test your CLI install and ensure the DCV server is checked in with the broker by running the following command. This should return the DCV Fleet instance. Run the following commands from within the CLI unzipped folder. 349 | ```bash 350 | python3 dcvsm describe-servers 351 | ``` 352 | - Output should return an available server in the JSON 353 | - If no servers are available, follow the **Troubleshooting DCV Fleet Session Manager Creation Errors** section below. 354 | 355 | 5. Run the following command to have DCV Session Manager create a DCV session on the DCV Server you deployed. Take note of the `"successful_list": "id"` (*not* `request_id`) in the call response. 356 | >Note: Be sure to enter the fleet instance ID in the `requirements` argument. For additional requirements, see [DCV Session Manager Developer Guide](https://docs.aws.amazon.com/dcv/latest/sm-dev/CreateSessions.html#request). The placeholder *LOCALUSER* should be replaced with the user on the instance you plan to login with. 357 | 358 | ```bash 359 | python3 dcvsm create-session --name Testing --owner LOCALUSER --type Console --requirements "server:Host.Aws.Ec2InstanceId = 'i-XXXXXXXXXXXXXXXXX'" 360 | ``` 361 | 6. Run the following command to retrieve the DCV session’s authentication token specific to the connecting user. Take note of the token in the response. 362 | >Note: Be sure to enter the `session-id` below from the previous step. 363 | ```bash 364 | python3 dcvsm get-session-connection-data --session-id --user LOCALUSER 365 | ``` 366 | - Take note the `connection_token` in the response. 367 | 7. You can initiate a connection from a DCV client using the session ID and authentication code. The [connection string](https://docs.aws.amazon.com/dcv/latest/sm-dev/GetSessionConnectionData.html#additional-info) should be formatted as: 368 | - `Connection-Gateway-NLB-DNSorIP:8443/?authToken=TOKEN#SESSION-ID` 369 | - Replace `TOKEN` with the `connection_token` retrieved from the previous call. 370 | - Replace `SESSION-ID` with the session ID returned when you created the session. 371 | - **Note:** you must initiate the call from a [DCV client](https://docs.aws.amazon.com/dcv/latest/userguide/client.html). 372 | 8. Once your connection is established, log in to your DCV Fleet Server with the instance credentials. 373 | 374 | ## Cleanup Your Environment 375 | To destroy all infrastructure contained in the CDK, run the following command: 376 | - Be sure to terminate and remove bastion resources manually created (e.g. instances, security groups, etc.) that were used in testing (any infrastructure outside of the CDK project, but within the VPC) before running the command below. 377 | - You will need to delete the SSM Parameter for the broker private ip named `dcv-broker-private-ip-*`. 378 | ```bash 379 | # Destroy all project resources. 380 | cdk destroy --all 381 | ``` 382 | 383 | ## Troubleshooting DCV Fleet Session Manager Creation Errors 384 | 1. List all sessions. Output should be a list of DCV sessions. 385 | ```bash 386 | python3 dcvsm describe-sessions 387 | ``` 388 | 2. Check the CLI can find DCV servers. Output should be an available server in the JSON. 389 | ```bash 390 | python3 dcvsm describe-servers 391 | ``` 392 | 3. Check the Agent is running. Output should be "Success". 393 | ```bash 394 | grep 'sessionsUpdateResponse' /var/log/dcv-session-manager-agent/agent.log | tail -1 | grep -o success` 395 | ``` 396 | 4. Check the Broker is running. Output should be { "error": "No authorization header"} 397 | >Note: Be sure to fill in the correct private IP address and region from the Session Manager instance below in `X-X-X-X.xx-xxxx-x`. (e.g. `10-0-2-63.us-west-2`) 398 | ```bash 399 | curl -X GET https://BROKER_DNS:8443/sessionConnectionData/aSession/aOwner --insecure 400 | ``` 401 | For more information, see the *Verify the installations* page of the [DCV Session Manager Administrator guide](https://docs.aws.amazon.com/dcv/latest/sm-admin/verify.html). 402 | 403 | ## Authors and acknowledgment 404 | [Andrew Morgan](https://github.com/morgnza)
405 | [Eric Cornwell](https://github.com/Eecornwell)
406 | 407 | ## License 408 | MIT License -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-with-pipelines/app.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | # this software and associated documentation files (the "Software"), to deal in 5 | # the Software without restriction, including without limitation the rights to 6 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | # the Software, and to permit persons to whom the Software is furnished to do so. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import os 17 | import json 18 | import aws_cdk as cdk 19 | from stacks.dcv_ami.dcv_ami import DcvAmi 20 | from stacks.dcv_infra.dcv_infra import DcvInfra 21 | 22 | app = cdk.App() 23 | 24 | # Load the app configuration from the config.json file 25 | try: 26 | with open("config.json", "r", encoding="utf-8") as config_file: 27 | config_data = dict(json.load(config_file)) 28 | except Exception as e: 29 | print(f"Could not read the app configuration file. {e}") 30 | raise e 31 | 32 | # Get the contents of the Session Manager component file 33 | session_mgr_component = open(os.path.join(os.path.dirname( __file__ ), 34 | "components", 35 | "build-dcv-session-mgr.yaml"), 36 | "r", encoding="utf-8").read() 37 | 38 | # Get the contents of the Connection Gateway component file 39 | connection_gwy_component = open(os.path.join(os.path.dirname( __file__ ), 40 | "components", 41 | "build-dcv-connection-gwy.yaml"), 42 | "r", encoding="utf-8").read() 43 | 44 | # Set CDK environment variables 45 | environment = cdk.Environment(account=config_data['accountId'], region=config_data['region']) 46 | 47 | # Create the DCV AMI Builder Stack for Session Manager 48 | session_mgr_ami = DcvAmi(app, "SessionMgrAmiStack", 49 | description='(uksb-1tupboc66) (tag:dcv-session-mgr-pipeline)', 50 | image_pipeline_name="dcv-session-mgr-ami", 51 | component_content=session_mgr_component, 52 | instance_type="m6g.large", 53 | config_data=config_data, 54 | env=environment) 55 | 56 | # Create the DCV AMI Builder Stack for Connection Gateway 57 | connection_gwy_ami= DcvAmi(app, "ConnectionGwyAmiStack", 58 | description='(uksb-1tupboc66) (tag:dcv-connection-gwy-pipeline)', 59 | image_pipeline_name="dcv-connection-gwy-ami", 60 | component_content=connection_gwy_component, 61 | instance_type="c7g.large", 62 | config_data=config_data, 63 | env=environment) 64 | 65 | # Create the DCV Infrastructure Stack for Session Manager and Connection Gateway 66 | DcvInfra(app, "DcvInfraStack", 67 | description='(uksb-1tupboc66) (tag:dcv-gw-sm-with-pipelines)', 68 | config_data=config_data, 69 | env=environment) 70 | 71 | app.synth() 72 | -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-with-pipelines/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "python app.py", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "requirements*.txt", 11 | "**/__init__.py", 12 | "**/__pycache__" 13 | ] 14 | }, 15 | "context": { 16 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 17 | "@aws-cdk/core:checkSecretUsage": true, 18 | "@aws-cdk/core:target-partitions": [ 19 | "aws", 20 | "aws-cn" 21 | ], 22 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 23 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 24 | "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, 25 | "@aws-cdk/aws-iam:minimizePolicies": true, 26 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 27 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 28 | "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, 29 | "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, 30 | "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, 31 | "@aws-cdk/core:enablePartitionLiterals": true, 32 | "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, 33 | "@aws-cdk/aws-iam:standardizedServicePrincipals": true, 34 | "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, 35 | "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, 36 | "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, 37 | "@aws-cdk/aws-route53-patters:useCertificate": true, 38 | "@aws-cdk/customresources:installLatestAwsSdkDefault": false, 39 | "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, 40 | "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, 41 | "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, 42 | "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, 43 | "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, 44 | "@aws-cdk/aws-redshift:columnId": true, 45 | "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, 46 | "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, 47 | "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, 48 | "@aws-cdk/aws-kms:aliasNameRef": true, 49 | "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, 50 | "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, 51 | "@aws-cdk/aws-efs:denyAnonymousAccess": true, 52 | "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, 53 | "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, 54 | "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, 55 | "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, 56 | "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, 57 | "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, 58 | "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-with-pipelines/components/build-dcv-connection-gwy.yaml: -------------------------------------------------------------------------------- 1 | name: DCVConnGatewayInstallBuildDocument 2 | description: This is a build document for NICE DCV Connection Gateway installation. 3 | schemaVersion: 1.0 4 | phases: 5 | - name: build 6 | steps: 7 | - name: UpdateOS 8 | action: UpdateOS 9 | timeoutSeconds: -1 10 | onFailure: Continue 11 | maxAttempts: 3 12 | inputs: 13 | exclude: 14 | - amazon-ssm-agent 15 | - name: CreateLogFile 16 | action: CreateFile 17 | inputs: 18 | - path: /var/log/dcv-connection-gwy-install.log 19 | content: 20 | overwrite: true 21 | - name: CreateTempFolder 22 | action: CreateFolder 23 | inputs: 24 | - path: /tmp/DCVGW/ 25 | - name: DownloadDCVConnGateway 26 | action: WebDownload 27 | maxAttempts: 3 28 | inputs: 29 | - source: https://raw.githubusercontent.com/aws-samples/dcv-samples/main/bootstrap/dcv-gateway-installer.sh 30 | destination: /tmp/DCVGW/ 31 | - name: InstallDCVConnGateway 32 | action: ExecuteBash 33 | inputs: 34 | commands: 35 | - set -eE 36 | - LOG_PATH="/var/log/dcv-connection-gwy-install.log" 37 | - echo $(date -u) "Starting NICE DCV Connection Gateway installation..." | tee -a $LOG_PATH 38 | - cd /tmp/DCVGW/ 39 | - echo $(date -u) "Installing Connection Gateway package..." | tee -a $LOG_PATH 40 | - chmod +x dcv-gateway-installer.sh 41 | - sudo /bin/bash /tmp/DCVGW/dcv-gateway-installer.sh 42 | - echo $(date -u) "Installed Connection Gateway package..." | tee -a $LOG_PATH -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-with-pipelines/components/build-dcv-session-mgr.yaml: -------------------------------------------------------------------------------- 1 | name: DCVSessMgrInstallBuildDocument 2 | description: This is a build document for NICE DCV Session Manager installation. 3 | schemaVersion: 1.0 4 | phases: 5 | - name: build 6 | steps: 7 | - name: UpdateOS 8 | action: UpdateOS 9 | timeoutSeconds: -1 10 | onFailure: Continue 11 | maxAttempts: 3 12 | inputs: 13 | exclude: 14 | - amazon-ssm-agent 15 | - name: CreateLogFile 16 | action: CreateFile 17 | inputs: 18 | - path: /var/log/dcv-session-mgr-install.log 19 | content: 20 | overwrite: true 21 | - name: CreateTempFolder 22 | action: CreateFolder 23 | inputs: 24 | - path: /tmp/DCVSM/ 25 | - name: DownloadDCVSessMgr 26 | action: WebDownload 27 | maxAttempts: 3 28 | inputs: 29 | - source: https://raw.githubusercontent.com/aws-samples/dcv-samples/main/bootstrap/dcv-session-manager-installer.sh 30 | destination: /tmp/DCVSM/ 31 | - name: InstallDCVSessMgr 32 | action: ExecuteBash 33 | inputs: 34 | commands: 35 | # Set the installation log and current directory 36 | - LOG_PATH="/var/log/dcv-session-mgr-install.log" 37 | - echo $(date -u) "Starting NICE DCV Session Manager installation..." | tee -a $LOG_PATH 38 | # Install the broker package 39 | - chmod +x /tmp/DCVSM/dcv-session-manager-installer.sh 40 | - /bin/bash /tmp/DCVSM/dcv-session-manager-installer.sh 41 | - name: ConfigureDCVSessMgr 42 | action: ExecuteBash 43 | inputs: 44 | commands: 45 | - LOG_PATH="/var/log/dcv-session-mgr-install.log" 46 | - echo $(date -u) "Configuring DCV Session Manager..." | tee -a $LOG_PATH 47 | - CONFIG_PATH="/etc/dcv-session-manager-broker/session-manager-broker.properties" 48 | # Get the current region so we know where to put cert content secret 49 | - | 50 | TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` 51 | - | 52 | REGION=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/region) 53 | # Enable the gateway in config 54 | - sed -i '/^enable-gateway/s/=.*$/= true/' $CONFIG_PATH 55 | # Uncomment the broker connector host and port in config 56 | - sed -i '/gateway-to-broker-connector-https-port/s/^#\s//g' $CONFIG_PATH 57 | - sed -i '/gateway-to-broker-connector-bind-host/s/^#\s//g' $CONFIG_PATH 58 | # Enable the broker to persist on DynamoDB in config 59 | - sed -i '/^enable-persistence/s/=.*$/= true/' $CONFIG_PATH 60 | # Uncomment database, region, Read Capacity Units(RCU), Write Capacity Units(WCU), and table name prefix in config 61 | - sed -i '/persistence-db/s/^#\s//g' $CONFIG_PATH 62 | - sed -i '/dynamodb-region/s/^#\s//g' $CONFIG_PATH 63 | - sed -i '/dynamodb-table-rcu/s/^#\s//g' $CONFIG_PATH 64 | - sed -i '/dynamodb-table-wcu/s/^#\s//g' $CONFIG_PATH 65 | - sed -i '/dynamodb-table-name-prefix/s/^#\s//g' $CONFIG_PATH 66 | # Be sure the region is correct and not the default in the file 67 | - sed -i "/^dynamodb-region/s/=.*$/= $REGION/" $CONFIG_PATH 68 | # Restart the broker service 69 | - systemctl restart dcv-session-manager-broker.service 70 | - echo $(date -u) "DCV Session Manager Installation and Configuration complete..." | tee -a $LOG_PATH 71 | -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-with-pipelines/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "NOTE: BEFORE DEPLOYING SESSION MANAGER AND CONNECTION GATEWAY STACK, UPDATE THE ACCOUNT ID AND REGION BELOW", 3 | "accountId": "xxxxxxxxxxxx", 4 | "region": "xx-xxxx-x", 5 | "sshKeypairName": "", 6 | "kmsKeyName": "aws/ebs", 7 | "network" : { 8 | "vpcId": "", 9 | "publicASubnetId": "", 10 | "publicBSubnetId": "", 11 | "privateASubnetId": "", 12 | "privateBSubnetId": "" 13 | }, 14 | "sessionMgr" : { 15 | "baseAmiId": "ami-xxxxxxxxxxxxxxxxx", 16 | "comment": "NOTE: BEFORE DEPLOYING INFRA STACK AND AFTER DEPLOYING THE SESSION MANAGER & CONNECTION GATEWAY STACK, UPDATE THE AMI ID BELOW", 17 | "builderAmiId": "ami-xxxxxxxxxxxxxxxxx" 18 | }, 19 | "connectionGwy" : { 20 | "baseAmiId": "ami-xxxxxxxxxxxxxxxxx", 21 | "comment": "NOTE: BEFORE DEPLOYING INFRA STACK AND AFTER DEPLOYING THE SESSION MANAGER & CONNECTION GATEWAY STACK, UPDATE THE AMI ID BELOW", 22 | "builderAmiId": "ami-xxxxxxxxxxxxxxxxx" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-with-pipelines/documentation/images/EC2ImageBuilderRunPipeline.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/dcv-samples/62554cb1deb94b843a7faceb94ff774b94f73243/cdk/dcv-gw-sm-with-pipelines/documentation/images/EC2ImageBuilderRunPipeline.jpg -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-with-pipelines/documentation/images/SolutionArchitecture.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/dcv-samples/62554cb1deb94b843a7faceb94ff774b94f73243/cdk/dcv-gw-sm-with-pipelines/documentation/images/SolutionArchitecture.PNG -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-with-pipelines/requirements.txt: -------------------------------------------------------------------------------- 1 | aws-cdk-lib>=2.141.0 2 | constructs>=10.0.0,<11.0.0 -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-with-pipelines/scripts/connection-gwy-user-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | # this software and associated documentation files (the "Software"), to deal in 6 | # the Software without restriction, including without limitation the rights to 7 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | # the Software, and to permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 12 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 13 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 14 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 15 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | LOG_PATH="/var/log/dcv-connection-gwy-install.log" 18 | echo $(date -u) "*****START USER DATA SCRIPT*****" | tee -a "$LOG_PATH" 19 | 20 | # Get current region 21 | TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` 22 | REGION=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/region) 23 | echo $(date -u) "Current Region: $REGION" | tee -a "$LOG_PATH" 24 | 25 | # Retrieve Broker Private DNS from SSM Parameter Store 26 | echo $(date -u) "Retrieving broker private DNS from SSM Parameter Store" | tee -a "$LOG_PATH" 27 | BROKER_PRIVATE_DNS=$(aws ssm get-parameter --name dcv-broker-private-dns --region "$REGION" --with-decryption | grep -Po '"Value": "\K[^"]*') 28 | timeout 1 bash -c "/dev/tcp/$BROKER_PRIVATE_DNS/8447" 29 | RESPONSE="$?" 30 | while [ "$RESPONSE" != 0 ]; do 31 | echo $(date -u) "Unable to reach broker. Waiting before retry." | tee -a "$LOG_PATH" 32 | sleep 15s 33 | BROKER_PRIVATE_DNS=$(aws ssm get-parameter --name dcv-broker-private-dns --region "$REGION" --with-decryption | grep -Po '"Value": "\K[^"]*') 34 | timeout 1 bash -c "cat < /dev/null > /dev/tcp/$BROKER_PRIVATE_DNS/8447" 35 | RESPONSE="$?" 36 | done 37 | echo $(date -u) "Broker private DNS found $BROKER_PRIVATE_DNS" | tee -a "$LOG_PATH" 38 | 39 | # Port Configuration. Using hex to escape double quotes: \x22 = " 40 | echo $(date -u) "Configuring Connection Gateway..." | tee -a "$LOG_PATH" 41 | sed -i "s/^#\[health-check\]/\[health-check\]/g" /etc/dcv-connection-gateway/dcv-connection-gateway.conf 42 | sed -i --expression 's|#bind-addr = "::"|bind-addr = "::"|' /etc/dcv-connection-gateway/dcv-connection-gateway.conf 43 | sed -i --expression 's|#tls-strict = false|tls-strict = false|' /etc/dcv-connection-gateway/dcv-connection-gateway.conf 44 | sed -i "/\[resolver\]/a tls-strict = false" /etc/dcv-connection-gateway/dcv-connection-gateway.conf 45 | sed -i "/bind-addr = \"::\"/a port = 8989" /etc/dcv-connection-gateway/dcv-connection-gateway.conf 46 | sed -i "s|url = \"https://localhost:8081\"|url = \"https://$BROKER_PRIVATE_DNS:8447\"|" /etc/dcv-connection-gateway/dcv-connection-gateway.conf 47 | 48 | # Start DCV Connection Gateway Service 49 | echo $(date -u) "Starting and Enabling Connection Gateway service..." | tee -a "$LOG_PATH" 50 | systemctl restart dcv-connection-gateway.service 51 | 52 | # Log if successful installation 53 | if [[ $? -eq 0 ]]; then 54 | echo $(date -u) "Successfully installed DCV Connection Gateway" | tee -a "$LOG_PATH" 55 | else 56 | echo $(date -u) "There was an error during DCV Connection Gateway installation" | tee -a "$LOG_PATH" 57 | fi -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-with-pipelines/scripts/session-mgr-user-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | # this software and associated documentation files (the "Software"), to deal in 6 | # the Software without restriction, including without limitation the rights to 7 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | # the Software, and to permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 12 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 13 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 14 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 15 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | LOG_PATH="/var/log/dcv-session-mgr-install.log" 18 | echo $(date -u) "*****START USER DATA SCRIPT*****" | tee -a "$LOG_PATH" 19 | 20 | # Get the private IP for connection gateway to use during configuration 21 | echo $(date -u) "Retrieving private broker DNS..." | tee -a "$LOG_PATH" 22 | TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` 23 | MAC=`curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/network/interfaces/macs/` 24 | PRIVATE_DNS=`curl -H "X-aws-ec2-metadata-token: $TOKEN" "http://169.254.169.254/latest/meta-data/network/interfaces/macs/${MAC}local-hostname"` 25 | echo $(date -u) "Using private DNS $PRIVATE_DNS" | tee -a "$LOG_PATH" 26 | 27 | # Get current region 28 | REGION=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/region) 29 | echo $(date -u) "Current Region: $REGION" | tee -a "$LOG_PATH" 30 | 31 | # Store the private private DNS name in SSM Parameter Store 32 | echo $(date -u) "Storing broker private DNS name in AWS SSM Parameter Store..." | tee -a "$LOG_PATH" 33 | aws ssm put-parameter --name dcv-broker-private-dns --value "$PRIVATE_DNS" --type String --overwrite --region "$REGION" 34 | # Log if successful installation 35 | if [[ $? -eq 0 ]]; then 36 | echo $(date -u) "Stored private DNS name $PRIVATE_DNS in Parameter Store" | tee -a "$LOG_PATH" 37 | # Restart the broker service for the new config to get pulled 38 | systemctl restart dcv-session-manager-broker.service 39 | echo $(date -u) "Successfully installed DCV Session Manager" | tee -a "$LOG_PATH" 40 | else 41 | echo $(date -u) "There was an error during DCV Session Manager installation" | tee -a "$LOG_PATH" 42 | fi -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-with-pipelines/stacks/dcv_ami/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/dcv-samples/62554cb1deb94b843a7faceb94ff774b94f73243/cdk/dcv-gw-sm-with-pipelines/stacks/dcv_ami/__init__.py -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-with-pipelines/stacks/dcv_ami/dcv_ami.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | # this software and associated documentation files (the "Software"), to deal in 5 | # the Software without restriction, including without limitation the rights to 6 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | # the Software, and to permit persons to whom the Software is furnished to do so. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | """ 16 | 17 | from aws_cdk import ( 18 | Stack, 19 | aws_imagebuilder as imagebuilder, 20 | aws_iam as iam, 21 | aws_ec2 as ec2 22 | ) 23 | from constructs import Construct 24 | 25 | # The NICE DCV AMI stack 26 | class DcvAmi(Stack): 27 | def __init__(self, scope: Construct, construct_id: str, image_pipeline_name: str, 28 | component_content: str, instance_type: str, config_data: dict, **kwargs) -> None: 29 | super().__init__(scope, construct_id, **kwargs) 30 | 31 | # Component to install for Session Manager or Connection Gateway 32 | component = imagebuilder.CfnComponent(self, 33 | image_pipeline_name, 34 | name=f"{image_pipeline_name}-component", 35 | platform="Linux", 36 | version="1.0.0", 37 | data=component_content 38 | ) 39 | 40 | # Recipe that installs all of above components together with a base image 41 | recipe = imagebuilder.CfnImageRecipe(self, 42 | f"{image_pipeline_name}-recipe", 43 | name=f"{image_pipeline_name}-recipe", 44 | version="1.0.0", 45 | components=[ 46 | {"componentArn": component.attr_arn}, 47 | {"componentArn": "arn:aws:imagebuilder:us-east-1:aws:component/aws-cli-version-2-linux/1.0.4/1"} 48 | ], 49 | parent_image=config_data['sessionMgr']['baseAmiId'] 50 | ) 51 | 52 | # Distribution to specified accounts and regions 53 | imagebuilder.CfnDistributionConfiguration(self, 54 | f"{image_pipeline_name}-dist-config", 55 | name=f"{image_pipeline_name}-dist-config", 56 | distributions=[{"region": self.region, 57 | "amiDistributionConfiguration": { 58 | "name": image_pipeline_name + "-{{ imagebuilder:buildDate }}", 59 | "description": "NICE DCV AMI", 60 | "targetAccountIds": [ self.account ] 61 | } 62 | }] 63 | ) 64 | 65 | # Role for pipeline and instance (ImageBuilder, Logs, and SSM) 66 | role = iam.Role(self, f"{image_pipeline_name}-role", 67 | role_name=f"{image_pipeline_name}-role", 68 | assumed_by=iam.ServicePrincipal("ec2.amazonaws.com") 69 | ) 70 | role.add_managed_policy( 71 | iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore") 72 | ) 73 | role.add_managed_policy( 74 | iam.ManagedPolicy.from_aws_managed_policy_name("EC2InstanceProfileForImageBuilder") 75 | ) 76 | 77 | # Create an instance profile for infrastructure config 78 | instance_profile = iam.CfnInstanceProfile(self, 79 | f"{image_pipeline_name}-instance-profile", 80 | instance_profile_name=f"{image_pipeline_name}-instance-profile", 81 | roles=[role.role_name] 82 | ) 83 | 84 | # Select between using the default VPC or use a specific subnet from config.json 85 | if config_data['network']['privateASubnetId'] and config_data['network']['vpcId']: 86 | # Create a reference to the VPC given in the config.json file 87 | vpc = ec2.Vpc.from_lookup(self, "VPC", vpc_id=config_data['network']['vpcId']) 88 | 89 | # Create an egress only security group for the instance during build ami 90 | security_group = ec2.SecurityGroup(self, "DCVAmiCreationSecurityGroup", 91 | vpc=vpc, 92 | description="SG default ports for DCV", 93 | allow_all_outbound=True, #Egress all 94 | disable_inline_rules=True 95 | ) 96 | 97 | # Create infrastructure configuration to supply instance type 98 | infra_config = imagebuilder.CfnInfrastructureConfiguration(self, 99 | f"{image_pipeline_name}-infra-config", 100 | name=f"{image_pipeline_name}-infra-config", 101 | instance_types=[instance_type], 102 | instance_profile_name=instance_profile.instance_profile_name, 103 | subnet_id=config_data['network']['privateASubnetId'], 104 | security_group_ids=[security_group.security_group_id] 105 | ) 106 | else: # Use default VPC 107 | # Create infrastructure configuration to supply instance type 108 | infra_config = imagebuilder.CfnInfrastructureConfiguration(self, 109 | f"{image_pipeline_name}-infra-config", 110 | name=f"{image_pipeline_name}-infra-config", 111 | instance_types=[instance_type], 112 | instance_profile_name=instance_profile.instance_profile_name, 113 | ) 114 | 115 | # Infrastructure config depends on instance profile to complete before beginning deployment. 116 | instance_profile.add_dependency(role.node.default_child) 117 | infra_config.add_dependency(instance_profile) 118 | 119 | # The imagebuilder pipeline 120 | imagebuilder.CfnImagePipeline(self, 121 | f"{image_pipeline_name}-pipeline", 122 | name=image_pipeline_name, 123 | image_recipe_arn=recipe.attr_arn, 124 | infrastructure_configuration_arn=infra_config.attr_arn 125 | ) 126 | -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-with-pipelines/stacks/dcv_infra/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/dcv-samples/62554cb1deb94b843a7faceb94ff774b94f73243/cdk/dcv-gw-sm-with-pipelines/stacks/dcv_infra/__init__.py -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-with-pipelines/stacks/dcv_infra/dcv_infra.py: -------------------------------------------------------------------------------- 1 | """ 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | # this software and associated documentation files (the "Software"), to deal in 5 | # the Software without restriction, including without limitation the rights to 6 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | # the Software, and to permit persons to whom the Software is furnished to do so. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | """ 16 | 17 | import os 18 | from aws_cdk import Stack 19 | import aws_cdk as cdk 20 | import aws_cdk.aws_ec2 as ec2 21 | import aws_cdk.aws_iam as iam 22 | import aws_cdk.aws_kms as kms 23 | import aws_cdk.aws_autoscaling as autoscaling 24 | import aws_cdk.aws_elasticloadbalancingv2 as elbv2 25 | from constructs import Construct 26 | 27 | # The NICE DCV INFRA stack 28 | class DcvInfra(Stack): 29 | """ Class to deploy DCV infrastructure components """ 30 | def __init__(self, scope: Construct, construct_id: str, config_data: dict, **kwargs) -> None: 31 | super().__init__(scope, construct_id, **kwargs) 32 | 33 | # Check if a VPC and subnets were provided in the config.json file 34 | if config_data['network']['vpcId'] and config_data['network']['publicASubnetId'] \ 35 | and config_data['network']['publicBSubnetId'] \ 36 | and config_data['network']['privateASubnetId'] \ 37 | and config_data['network']['privateBSubnetId']: 38 | # Create reference for VPC 39 | vpc = ec2.Vpc.from_lookup(self, "VPC", vpc_id=config_data['network']['vpcId']) 40 | # Create references for private and public subnets 41 | # using subnet_ids pulled from config.json 42 | subnets_public_ref_a = ec2.Subnet.from_subnet_attributes( 43 | self, "PublicSubnetAFromAttributes", 44 | subnet_id=config_data['network']['publicASubnetId'], 45 | availability_zone=f"{self.region}a" 46 | ) 47 | subnets_public_ref_b = ec2.Subnet.from_subnet_attributes( 48 | self, "PublicSubnetBFromAttributes", 49 | subnet_id=config_data['network']['publicBSubnetId'], 50 | availability_zone=f"{self.region}b" 51 | ) 52 | subnets_private_ref_a = ec2.Subnet.from_subnet_attributes( 53 | self, "PrivateSubnetAFromAttributes", 54 | subnet_id=config_data['network']['privateASubnetId'], 55 | availability_zone=f"{self.region}a" 56 | ) 57 | subnets_private_ref_b = ec2.Subnet.from_subnet_attributes( 58 | self, "PrivateSubnetBFromAttributes", 59 | subnet_id=config_data['network']['privateBSubnetId'], 60 | availability_zone=f"{self.region}b" 61 | ) 62 | subnets_public = ec2.SubnetSelection( 63 | subnets=[subnets_public_ref_a, subnets_public_ref_b] 64 | ) 65 | subnets_private = ec2.SubnetSelection( 66 | subnets=[subnets_private_ref_a, subnets_private_ref_b] 67 | ) 68 | else: 69 | # Create new VPC, subnets, and NAT Gateway 70 | vpc = ec2.Vpc(self, "VPC", 71 | nat_gateways = 1, 72 | max_azs = 2, 73 | subnet_configuration=[ 74 | ec2.SubnetConfiguration( 75 | name = "public-subnet", 76 | subnet_type = ec2.SubnetType.PUBLIC, 77 | cidr_mask = 24, 78 | ), 79 | ec2.SubnetConfiguration( 80 | name = "private-subnet", 81 | subnet_type = ec2.SubnetType.PRIVATE_WITH_EGRESS, 82 | cidr_mask = 24 83 | ) 84 | ], 85 | ) 86 | # Create references for private and public subnets 87 | # using availability zones described above 88 | subnets_public = ec2.SubnetSelection( 89 | subnet_type=ec2.SubnetType.PUBLIC 90 | ) 91 | subnets_private = ec2.SubnetSelection( 92 | subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS 93 | ) 94 | 95 | # IAM Fleet Role Configuration 96 | role_fleet = iam.Role(self, "FleetRole", 97 | assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"), 98 | role_name="dcv-fleet-role" 99 | ) 100 | 101 | # IAM Session Manager Role Configuration 102 | role_session_mgr = iam.Role(self, "SessionMgrRole", 103 | assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"), 104 | role_name="dcv-session-mgr-role" 105 | ) 106 | 107 | # IAM Connection Gateway Role Configuration 108 | role_connection_gwy = iam.Role(self, "ConnectionGwyRole", 109 | assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"), 110 | role_name="dcv-connection-gwy-role" 111 | ) 112 | 113 | # Permission to get DCV License for fleet role 114 | role_fleet.add_to_policy(iam.PolicyStatement( 115 | effect=iam.Effect.ALLOW, 116 | actions=["s3:GetObject"], 117 | resources=[f"arn:aws:s3:::dcv-license.{self.region}/*"] 118 | )) 119 | 120 | role_fleet.add_managed_policy( 121 | iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore") 122 | ) 123 | 124 | iam.CfnInstanceProfile(self, "DCVServerInstanceProfile", 125 | instance_profile_name="dcv-server-profile", 126 | roles=[role_fleet.role_name] 127 | ) 128 | 129 | # Permission for Broker Persistence on Session Manager instance 130 | role_session_mgr.add_to_policy(iam.PolicyStatement( 131 | effect=iam.Effect.ALLOW, 132 | actions=[ 133 | 'dynamodb:BatchGetItem', 134 | 'dynamodb:BatchWriteItem', 135 | 'dynamodb:ConditionCheckItem', 136 | 'dynamodb:PutItem', 137 | 'dynamodb:DescribeTable', 138 | 'dynamodb:DeleteItem', 139 | 'dynamodb:GetItem', 140 | 'dynamodb:Scan', 141 | 'dynamodb:Query', 142 | 'dynamodb:UpdateItem', 143 | 'dynamodb:CreateTable' 144 | ], 145 | resources=[ 146 | f"arn:aws:dynamodb:{self.region}:{self.account}:*", 147 | f"arn:aws:dynamodb:{self.region}:{self.account}:*/*", 148 | f"arn:aws:dynamodb:{self.region}:{self.account}:*/*/index/*" 149 | ] 150 | )) 151 | 152 | # Permission for SSM Parameter for Session Manager instance 153 | role_session_mgr.add_to_policy(iam.PolicyStatement( 154 | effect=iam.Effect.ALLOW, 155 | actions=[ 156 | 'ssm:DescribeParameters' 157 | ], 158 | resources=[ 159 | f"arn:aws:ssm:{self.region}:{self.account}:parameter/*", 160 | ] 161 | )) 162 | 163 | role_session_mgr.add_to_policy(iam.PolicyStatement( 164 | effect=iam.Effect.ALLOW, 165 | actions=[ 166 | 'ssm:PutParameter', 167 | 'ssm:GetParameter' 168 | ], 169 | resources=[ 170 | f"arn:aws:ssm:{self.region}:{self.account}:parameter/dcv-broker-private-dns", 171 | ] 172 | )) 173 | 174 | role_session_mgr.add_managed_policy( 175 | iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore") 176 | ) 177 | 178 | # Permission for SSM Parameter for Session Manager instance 179 | role_connection_gwy.add_to_policy(iam.PolicyStatement( 180 | effect=iam.Effect.ALLOW, 181 | actions=[ 182 | 'ssm:DescribeParameters' 183 | ], 184 | resources=[ 185 | f"arn:aws:ssm:{self.region}:{self.account}:parameter/*", 186 | ] 187 | )) 188 | 189 | # Permission to read the SSM Parameter for Connection Gateway instance 190 | role_connection_gwy.add_to_policy(iam.PolicyStatement( 191 | effect=iam.Effect.ALLOW, 192 | actions=[ 193 | 'ssm:GetParameter' 194 | ], 195 | resources=[ 196 | f"arn:aws:ssm:{self.region}:{self.account}:parameter/dcv-broker-private-dns" 197 | ] 198 | )) 199 | 200 | role_connection_gwy.add_managed_policy( 201 | iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore") 202 | ) 203 | 204 | # Security Group Configuration 205 | sg_session_mgr = ec2.SecurityGroup(self, "SMSecurityGroup", 206 | vpc=vpc, 207 | description="Default ports for DCV Gateway, DCV Servers and Broker", 208 | allow_all_outbound=True, #Egress all 209 | disable_inline_rules=True 210 | ) 211 | 212 | # Add Ingress for DCV TCP/UDP Ports 213 | sg_session_mgr.add_ingress_rule( 214 | ec2.Peer.any_ipv4(), ec2.Port.tcp(8443), "allow CLI to Broker communication" 215 | ) 216 | sg_session_mgr.add_ingress_rule( 217 | ec2.Peer.any_ipv4(), ec2.Port.tcp(8445), "allow Agent to Broker communication" 218 | ) 219 | sg_session_mgr.add_ingress_rule( 220 | sg_session_mgr, ec2.Port.all_traffic(), "allow Broker to Broker communication" 221 | ) 222 | 223 | sg_connection_gwy = ec2.SecurityGroup(self, "CGSecurityGroup", 224 | vpc=vpc, 225 | description="SG default ports for DCV Connection Gateway", 226 | allow_all_outbound=True, #Egress all 227 | disable_inline_rules=True 228 | ) 229 | 230 | sg_connection_gwy.add_ingress_rule( 231 | ec2.Peer.any_ipv4(), ec2.Port.tcp(8443), "allow TCP DCV access from public internet" 232 | ) 233 | sg_connection_gwy.add_ingress_rule( 234 | ec2.Peer.any_ipv4(), ec2.Port.udp(8443), "allow UDP DCV access from public internet" 235 | ) 236 | sg_connection_gwy.add_ingress_rule( 237 | ec2.Peer.any_ipv4(), ec2.Port.tcp(8989), "allow health check for NLB targets" 238 | ) 239 | 240 | # Gateway to Session Manager resolver communication 241 | sg_session_mgr.add_ingress_rule( 242 | sg_connection_gwy, ec2.Port.tcp(8447), "allow Gateway to Broker resolver communication" 243 | ) 244 | 245 | # DCV server fleet Security Group configuration 246 | sg_dcv_server = ec2.SecurityGroup(self, "DCVServerSecurityGroup", 247 | vpc=vpc, 248 | description="SG default ports for DCV Connection Gateway", 249 | allow_all_outbound=True, #Egress all 250 | disable_inline_rules=True 251 | ) 252 | 253 | sg_dcv_server.add_ingress_rule( 254 | sg_connection_gwy, ec2.Port.tcp(8443), "allow DCV streaming traffic from Gateway" 255 | ) 256 | sg_dcv_server.add_ingress_rule( 257 | sg_connection_gwy, ec2.Port.udp(8443), "allow DCV streaming traffic from Gateway" 258 | ) 259 | 260 | # Get KMS Key from Alias/Key Name 261 | kms_arn = f"arn:aws:kms:{self.region}:{self.account}:alias/{config_data['kmsKeyName']}" 262 | kms_key = kms.Key.from_key_arn(self, "kms-key", kms_arn) 263 | 264 | # Session Manager 265 | ### Session Manager AMI 266 | session_mgr_ami = ec2.GenericLinuxImage({ 267 | self.region : config_data['sessionMgr']['builderAmiId'] 268 | } 269 | ) 270 | 271 | # Add the user data script to custom string 272 | session_mgr_user_data_file = open(os.path.join(os.path.dirname( __file__ ), 273 | "..", "..", 274 | "scripts", 275 | "session-mgr-user-data.sh"), 276 | "r", encoding="utf-8") 277 | 278 | session_mgr_user_data_content = session_mgr_user_data_file.read() 279 | session_mgr_user_data = ec2.UserData.custom(session_mgr_user_data_content) 280 | 281 | # Create a reference to the SSH Key Pair name given in the config.json file 282 | key_pair = ec2.KeyPair.from_key_pair_attributes(self, "DCVKeyPair", 283 | key_pair_name=config_data['sshKeypairName'] 284 | ) 285 | 286 | # Create the Session Manager EC2 instance 287 | session_mgr_instance = ec2.Instance(self, "SessionMgrInstance", 288 | vpc=vpc, 289 | vpc_subnets=subnets_private, 290 | instance_type=ec2.InstanceType.of( 291 | ec2.InstanceClass.M6G, ec2.InstanceSize.LARGE), 292 | machine_image=session_mgr_ami, 293 | security_group=sg_session_mgr, 294 | key_pair=key_pair, 295 | user_data=session_mgr_user_data, 296 | role=role_session_mgr, 297 | block_devices=[ec2.BlockDevice( 298 | device_name="/dev/xvda", 299 | volume=ec2.BlockDeviceVolume.ebs( 300 | 8, encrypted=True, kms_key=kms_key))] 301 | ) 302 | 303 | # Connection Gateway 304 | ### Connection Gateway AMI 305 | connection_gwy_ami = ec2.GenericLinuxImage({ 306 | self.region : config_data['connectionGwy']['builderAmiId'] 307 | }) 308 | 309 | # Add the user data script to custom string 310 | connection_gwy_user_data_file = open(os.path.join(os.path.dirname( __file__ ), 311 | "..", "..", 312 | "scripts", 313 | "connection-gwy-user-data.sh"), 314 | "r", encoding="utf-8") 315 | 316 | connection_gwy_user_data_content = connection_gwy_user_data_file.read() 317 | connection_gwy_session_mgr_user_data = ec2.UserData.custom(connection_gwy_user_data_content) 318 | 319 | # Create an EC2 launch template with the encrypted volume for Connection Gateway 320 | connection_gwy_launch_template = ec2.LaunchTemplate(self, "ConnectionGwyLaunchTemplate", 321 | machine_image=connection_gwy_ami, 322 | security_group=sg_connection_gwy, 323 | instance_type=ec2.InstanceType.of( 324 | ec2.InstanceClass.C7G, ec2.InstanceSize.LARGE), 325 | key_pair=key_pair, 326 | user_data=connection_gwy_session_mgr_user_data, 327 | role=role_connection_gwy, 328 | block_devices=[ec2.BlockDevice( 329 | device_name="/dev/xvda", 330 | volume=ec2.BlockDeviceVolume.ebs( 331 | 8, encrypted=True, kms_key=kms_key))], 332 | ) 333 | 334 | # Create a Connection Gateway Auto Scaling Group 335 | connection_gwy_asg = autoscaling.AutoScalingGroup(self, "ConnectionGwyASG", 336 | vpc=vpc, 337 | launch_template=connection_gwy_launch_template, 338 | min_capacity=1, 339 | max_capacity=5, 340 | desired_capacity=1, 341 | vpc_subnets=subnets_private 342 | ) 343 | 344 | # Ensure Session Manager instance is created before Connection Gateway targets 345 | connection_gwy_asg.node.add_dependency(session_mgr_instance) 346 | 347 | # Connection Gateway Auto Scaling Group CPU Utilization to scale 348 | connection_gwy_asg.scale_on_cpu_utilization( 349 | "ConnectionGwyASGCPUUtilization", target_utilization_percent=75 350 | ) 351 | 352 | # Create a Connection Gateway Network Load Balancer 353 | connection_gwy_nlb = elbv2.NetworkLoadBalancer(self, "ConnectionGwyNLB", 354 | vpc=vpc, 355 | internet_facing=True, 356 | vpc_subnets=subnets_public, 357 | load_balancer_name="connection-gwy-nlb", 358 | cross_zone_enabled=True 359 | ) 360 | 361 | # Connection Gateway Listener to Target Setup for NLB 362 | connection_gwy_listener = connection_gwy_nlb.add_listener( 363 | "ConnectionGwyNLBListener", port=8443, protocol=elbv2.Protocol.TCP_UDP 364 | ) 365 | 366 | # Route the Connection Gateway targets 367 | connection_gwy_listener.add_targets("ConnectionGwyNLBTarget", 368 | port=8443, 369 | protocol=elbv2.Protocol.TCP_UDP, 370 | health_check=elbv2.HealthCheck( 371 | port="8989", 372 | protocol=elbv2.Protocol.TCP, 373 | unhealthy_threshold_count=5, 374 | healthy_threshold_count=5, 375 | interval=cdk.Duration.seconds(30) 376 | ), 377 | targets=[connection_gwy_asg] 378 | ) 379 | -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-without-pipelines/README.md: -------------------------------------------------------------------------------- 1 | # Deployment of Amazon DCV Connection Gateway and DCV Sessions Manager broker 2 | 3 | Accelerate cloud deployment of foundational autoscaling infrastructure for [Amazon DCV](https://aws.amazon.com/hpc/dcv/) with AWS Cloud Development Kit (AWS CDK). 4 | 5 | ## Overview 6 | The architecture diagram below illustrates the resources that this sample deploys into your account. This CDK deploys a single DCV Session Manager, a DCV Connection Gateway Auto Scaling group, an AWS Network Load Balancer forwarding traffic to the gateways, the required IAM roles for the architecture (gateway, broker, and DCV server), and configured Security Groups. The CDK provides the choice of deploying in an existing VPC or creating a new one. 7 | >Note: A DCV server fleet is not automatically deployed with this CDK. Instructions for deploying your DCV server fleet are within **Step 17: Testing Your Configuration**. 8 | 9 | ![Figure1: Architecture](.//documentation/images/SolutionArchitecture.png) 10 | 11 | 12 | Figure 1: AWS CDK that will setup the foundational infrastructure for a scale out solution of Amazon DCV Connection Gateway and Session Manager. 13 | 14 | ## Prerequisites 15 | - An AWS account with full permissions on: 16 | - AWS Systems Manager (AWS SSM) Parameter Store 17 | - AWS SSM Session Manager 18 | - Amazon Elastic Compute Cloud (Amazon EC2) 19 | - Amazon Virtual Private Cloud (Amazon VPC) 20 | - A system with the following installed: 21 | - AWS Command Line Interface (AWS CLI) 22 | - AWS CDK 23 | - Node.js 24 | - Python 25 | - Git 26 | - Basic understanding of: 27 | - AWS Cloud 28 | - Scripting languages (Python, Bash) 29 | - DCV 30 | 31 | # AWS VPC Infrastructure Creation with CDK 32 | This example contains: 33 | - Choice of VPC and Subnets 34 | - If you do not specify a VPC, the CDK will create: 35 | - A new VPC with an Internet Gateway 36 | - Public and Private subnets 37 | - Two availability zones 38 | - NAT Gateway 39 | - A security groups for DCV Connection Gateway, DCV Session Manager, and DCV Server 40 | - IAM roles required for configuration 41 | - Network Load Balancer for DCV Connection Gateway 42 | - Autoscaling Groups for the DCV Connection Gateway instances 43 | - This solution will initialize the autoscaling group with one Connection Gateway instance per availability zone 44 | - Autoscaling based off of CPU consumption 45 | - SSM Parameter Store parameter to hold the broker DNS 46 | 47 | >Note: An SSH key-pair (.pem or .ppk) is required to be registered in your AWS account and region. This key-pair name is required within the `config.json` file before deploying the infrastructure stack in order to allow SSH access into the instances. AWS SSM can also be used for terminal access. 48 | 49 | ## Step 1: NPM 50 | The LTS version of [npm](https://www.npmjs.com/) is recommended. If npm is already on your system, upgrade npm to the latest version. 51 | npm install -g npm@latest 52 | 53 | ```bash 54 | npm install -g npm@latest 55 | ``` 56 | 57 | ## Step 2: AWS CDK 58 | 59 | Ensure that the latest [aws-cdk](https://aws.amazon.com/cdk/) is installed. 60 | 61 | ```bash 62 | npm install -g aws-cdk 63 | ``` 64 | 65 | Once installed, the command cdk --version will confirm installation was successful. 66 | 67 | ```bash 68 | cdk --version 69 | ``` 70 | 71 | ## Step 3: AWS CLI 72 | Install [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) 73 | 74 | 75 | ## Step 4: Git 76 | Install [Git](https://git-scm.com/) 77 | 78 | ## Step 5: Clone the Repository 79 | Visit [clone a repository](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) for instructions. Clone this repository locally. 80 | 81 | ## Step 6: Create a virtual environment 82 | Open up a command line terminal of your choice. Change directory into the cloned repository. 83 | ### Windows 84 | ```bash 85 | virtualenv --python dcv-env 86 | .\dcv-env\Scripts\activate 87 | ``` 88 | ### macOS/Linux 89 | ```bash 90 | virtualenv --python dcv-env 91 | source dcv-env/bin/activate 92 | ``` 93 | 94 | ## Step 7: Install Project Dependencies 95 | Install project dependencies by running the following command. 96 | 97 | ```bash 98 | pip install -r requirements.txt 99 | ``` 100 | 101 | ## Step 8: Assume IAM role 102 | [Use an IAM role in the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-role.html). 103 | > Note: To confirm your credentials are associated with the correct AWS account, you can run the following AWS CLI command - 104 | ```bash 105 | aws sts get-caller-identity 106 | ``` 107 | (See [get-caller-identity](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/sts/get-caller-identity.html) in the AWS CLI documentation). 108 | 109 | ## Step 9: CDK Bootstrap 110 | [Bootstrap your AWS CDK environment. ](https://docs.aws.amazon.com/cdk/v2/guide/cli.html#cli-bootstrap) 111 | Replace with your desired account number and region. 112 | ```bash 113 | cdk bootstrap 111122223333/us-east-1 114 | ``` 115 | 116 | ## Step 10: Set AWS Account and Region for Infrastructure 117 | Set the AWS account id and region by specifying in `/config.json` 118 | ```json 119 | { 120 | ... 121 | "accountId": "xxxxxxxxxxxx", 122 | "region": "xxxxxxxxxxxxxxx", 123 | ... 124 | } 125 | ``` 126 | Also, be sure the region is set in your AWS credentials by running the following command by replacing `us-east-1` with your intended region: 127 | 128 | For Windows: 129 | ```powershell 130 | set AWS_REGION=us-east-1 131 | ``` 132 | For Mac/Linux: 133 | ```bash 134 | export AWS_REGION=us-east-1 135 | ``` 136 | 137 | ## Step 11: Check Project with Synth 138 | Verify there are no errors with the cloned project with [cdk synth](https://docs.aws.amazon.com/cdk/v2/guide/cli.html#cli-synth) (synthesize). 139 | ```bash 140 | cdk synth --all 141 | ``` 142 | 143 | ## Step 12: Config Network Settings 144 | If you do not provide a VPC and subnets within *config.json*, the CDK will create a new VPC. 145 | 146 | **Provisioning** 147 | - Network Load Balancer provisioned across public subnets 148 | - DCV Connection Gateway Auto Scaling group provisioned across private subnets 149 | - A single DCV Session Manager host provisioned in private subnet 150 | 151 | **Note:** The subnets assume you are targeting A and B Availability Zones (AZ). For example, ```publicASubnetId``` is aligned to the *region-a* AZ. If you would like to change your AZ, you can update the static assignments in *dcv_infra.py*. 152 | 153 | ```json 154 | "network" : { 155 | "vpcId": "", 156 | "publicASubnetId": "", 157 | "publicBSubnetId": "", 158 | "privateASubnetId": "", 159 | "privateBSubnetId": "" 160 | } ... 161 | ``` 162 | 163 | 164 | ## Step 13: Add AMI IDs into the CDK project 165 | 166 | This CDK will bootstrap the provided AMIs to install the required components. For more information, see the **AMI Checklist** below. 167 | 168 | ### AMI Checklist 169 | - Provide AMI IDs that are based on supported operating systems for [DCV Session Manager broker](https://docs.aws.amazon.com/dcv/latest/sm-admin/requirements.html) and [DCV Connection Gateway](https://docs.aws.amazon.com/dcv/latest/gw-admin/system-requirements.html). 170 | - Ensure your provided AMI is up to date and has the [AWS CLI](https://aws.amazon.com/cli/) installed. 171 | - The CDK assumes you are using ARM-based AMIs so you can benefit from [EC2 Graviton](https://aws.amazon.com/ec2/graviton/). If you would like to use an x86-based AMI, you may update the statically configured instance types in *dcv_infra.py*. 172 | - If you do not have an AMI to provide, use the latest ARM-based Amazon Linux 2 AMI in your target region. 173 | - The procedure in this README will use AWS Systems Manager Session Manager for access. To follow this procedure, ensure [SSM Agent](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html) is installed on your AMI. 174 | 175 | ```json 176 | { 177 | ... 178 | "sessionMgr" : { 179 | "baseAmiId": "ami-xxxxxxxxxxxxxxxxx" 180 | }, 181 | "connectionGwy" : { 182 | "baseAmiId": "ami-xxxxxxxxxxxxxxxxx" 183 | } 184 | } 185 | ``` 186 | 187 | 188 | ## Step 14: Key Pairs for EC2 189 | Use your own Key Pair or [Create Key Pairs](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/create-key-pairs.html) for accessing the EC2s. 190 | 191 | Use the Key Pair value for ssh_keypair_name in `/config.json` 192 | ```json 193 | { 194 | ... 195 | "sshKeypairName": "xxxxxxxxxxxxxxx", 196 | ... 197 | } 198 | ``` 199 | 200 | ## Step 15: AWS Key Management Service (KMS) 201 | Choose to use the AWS provide default KMS or [Create Keys](href="https://docs.aws.amazon.com/kms/latest/developerguide/create-keys.html). 202 | 203 | Use the KMS value for kms_key_name in `/config.json` 204 | 205 | ```json 206 | { 207 | ... 208 | "kmsKeyName": "aws/ebs", 209 | ... 210 | } 211 | ``` 212 | 213 | ## Step 16: Deploy Infrastructure stack 214 | ```bash 215 | cdk deploy 216 | ``` 217 | 218 | ## Step 17: Testing Your Configuration 219 | Now that your infrastructure is created, you must create a DCV session on a DCV server to connect to. For this step, [DCV Session Manager CLI](https://docs.aws.amazon.com/dcv/latest/sm-cli/sm-cli-reference.html) is required, as well as a [DCV Server](https://docs.aws.amazon.com/dcv/latest/adminguide/servers.html). Alteratively, you may deploy DCV Access Console. Use the instructions below to test your deployment. 220 | 221 | ### Testing Prerequisites 222 | There are several ways testing can be performed, but this guide uses [AWS Systems Manager Session Manager](https://aws.amazon.com/systems-manager/) and the CDK provides IAM permissions to use it. 223 | 224 | ### Launch a DCV Server Fleet Instance 225 | Once your CDK deployed instances are passing EC2 health checks, a DCV fleet server is required to be launched. The DCV server also needs to have the DCV Session Manager agent installed. With both of these components configured, the instance will check in with the DCV Session Manager broker so sessions on the instance can be managed by the broker. 226 | 227 | **Note:** You will need to manage the user accounts on the DCV servers. If you do not have a centralized identity (e.g. Microsoft Active Directory), you need to create a local user and set a password. The default user for Windows is `Administrator` and the default user for Amazon Linux 2 is `ec2-user`. You can create new users or update passwords by using AWS Systems Manager Session Manager for remote access. 228 | 229 | #### Windows 230 | To bootstrap a Windows DCV server, first download the [Install-DCVandSMAgent.ps1](/bootstrap/Install-DCVandSMAgent.ps1) script from the bootstrap folder of dcv-samples. Once downloaded, update the `SESSION-MGR-PRIVATE-DNS` placeholder with the private DNS name of your DCV Session Manager instance. This can be retrieved from the EC2 console. Deploy a new Windows-based instance within a private subnet and inject the script content in the **User data** field. Ensure you have chosen a base image that is [supported by DCV server](https://docs.aws.amazon.com/dcv/latest/adminguide/servers.html). The CDK provisioned a DCV server security group named `DcvInfraStack-DCVServerSecurityGroup*`. The CDK also created an instance profile named `dcv-fleet-role`. 231 | 232 | #### Linux 233 | To bootstrap a Windows DCV server, first download the [linux-config-sessionmgr-agent.sh](/bootstrap/linux-config-sessionmgr-agent.sh) script from the bootstrap folder of dcv-samples. Once downloaded, update the `SESSION-MGR-PRIVATE-DNS` placeholder with the private DNS name of your DCV Session Manager instance. This can be retrieved from the EC2 console. By default, Linux operating systems do not provide a desktop environment. For simplicity, you may use the [AWS Marketplace DCV AMI for Amazon Linux 2](https://aws.amazon.com/marketplace/seller-profile?id=74eff437-1315-4130-8b04-27da3fa01de1). Deploy a new instance based on the Marketplace AMI within a private subnet and inject the script content in the **User data** field. If you use an alternative image, ensure you have chosen a base image that is [supported by DCV server](https://docs.aws.amazon.com/dcv/latest/adminguide/servers.html). The CDK provisioned a DCV server security group named `DcvInfraStack-DCVServerSecurityGroup*`. The CDK also created an instance profile named `dcv-fleet-role`. 234 | 235 | When your newly deployed instance is passing EC2 health checks, proceed to the next step. 236 | 237 | ### Register an API Client on Session Manager Instance 238 | 1. Access your DCV Session Manager instance using Systems Manager Session Manager. 239 | 2. To make API calls to your broker, you will need to [register an API client](https://docs.aws.amazon.com/dcv/latest/sm-admin/register-api-client.html). To generate these credentials, run the following command. 240 | > IMPORTANT: These credentials cannot be retrieved later so take note of the response. You will need to rerun this command if you lose the credentials. 241 | 242 | ```bash 243 | sudo -u root dcv-session-manager-broker register-api-client --client-name client_name 244 | ``` 245 | 3. Record the printed credentials and exit out of Sessions Manager SSH session 246 | 247 | ### Communicating with the broker 248 | 249 | #### Option 1: Deploy the DCV Access Console 250 | The [DCV Access Console](https://docs.aws.amazon.com/dcv/latest/access-console/what-is-access-console.html) is a web application that helps administrators and end users manage their DCV sessions. To deploy the DCV Access Console, see the [CDK README]() or [administrator guide](https://docs.aws.amazon.com/dcv/latest/access-console/setup.html). 251 | 252 | #### Option 2: Download and Configure the DCV Session Manager CLI 253 | The [DCV Session Manager Command Line Interface (CLI)](https://docs.aws.amazon.com/dcv/latest/sm-cli/what-is-sm-cli.html) is used to interact with a DCV Session Manager broker using commands in your command-line shell. The CDK allows access from the DCV Connection Gateway instances. Alternatively, you may run the CLI on the Session Manager instance. 254 | 255 | 1. Download the DCV Session Manager CLI with the following command: 256 | ```bash 257 | wget https://d1uj6qtbmh3dt5.cloudfront.net/nice-dcv-session-manager-cli.zip 258 | ``` 259 | 260 | 2. Unzip the CLI package with the following command: 261 | ```bash 262 | unzip nice-dcv-session-manager-cli.zip 263 | ``` 264 | 265 | 3. Configure the CLI ```dcvsmcli.conf``` file. See the [CLI guide](https://docs.aws.amazon.com/dcv/latest/sm-cli/configuration-file.html) for a reference. You will need to have the following configuration set. 266 | ```bash 267 | 268 | [output] 269 | # The formatting style for command output. 270 | output-format = json 271 | 272 | # Turn on debug logging 273 | #debug = true 274 | 275 | [security] 276 | # Disable SSL certificates verification. 277 | no-verify-ssl = true 278 | 279 | # CA certificate bundle to use when verifying SSL certificates. 280 | #ca-bundle = ca-bundle.pem 281 | 282 | [authentication] 283 | # hostname of the authentication server used to request the token 284 | #auth-server-url = https://broker-host:broker-port/oauth2/token?grant_type=client_credentials 285 | 286 | # The client ID 287 | client-id = CLIENT_ID_FROM_REGISTRATION 288 | 289 | # The client password 290 | client-password = CLIENT_PASSWORD_FROM_REGISTRATION 291 | 292 | [broker] 293 | # hostname or IP of the broker 294 | url = https://DNSorIP-DCVSessionMgr:8443 295 | 296 | ``` 297 | 298 | 4. Test your CLI install and ensure the DCV server is checked in with the broker by running the following command. This should return the DCV Fleet instance. Run the following commands from within the CLI unzipped folder. 299 | ```bash 300 | python3 dcvsm describe-servers 301 | ``` 302 | - Output should return an available server in the JSON 303 | - If no servers are available, follow the **Troubleshooting DCV Fleet Session Manager Creation Errors** section below. 304 | 305 | 5. Run the following command to have DCV Session Manager create a DCV session on the DCV Server you deployed. Take note of the `"successful_list": "id"` (*not* `request_id`) in the call response. 306 | >Note: Be sure to enter the fleet instance ID in the `requirements` argument. For additional requirements, see [DCV Session Manager Developer Guide](https://docs.aws.amazon.com/dcv/latest/sm-dev/CreateSessions.html#request). The placeholder *LOCALUSER* should be replaced with the user on the instance you plan to login with. 307 | 308 | ```bash 309 | python3 dcvsm create-session --name Testing --owner LOCALUSER --type Console --requirements "server:Host.Aws.Ec2InstanceId = 'i-XXXXXXXXXXXXXXXXX'" 310 | ``` 311 | 6. Run the following command to retrieve the DCV session’s authentication token specific to the connecting user. Take note of the token in the response. 312 | >Note: Be sure to enter the `session-id` below from the previous step. 313 | ```bash 314 | python3 dcvsm get-session-connection-data --session-id --user LOCALUSER 315 | ``` 316 | - Take note the `connection_token` in the response. 317 | 7. You can initiate a connection from a DCV client using the session ID and authentication code. The [connection string](https://docs.aws.amazon.com/dcv/latest/sm-dev/GetSessionConnectionData.html#additional-info) should be formatted as: 318 | - `Connection-Gateway-NLB-DNSorIP:8443/?authToken=TOKEN#SESSION-ID` 319 | - Replace `TOKEN` with the `connection_token` retrieved from the previous call. 320 | - Replace `SESSION-ID` with the session ID returned when you created the session. 321 | - **Note:** you must initiate the call from a [DCV client](https://docs.aws.amazon.com/dcv/latest/userguide/client.html). 322 | 8. Once your connection is established, log in to your DCV Fleet Server with the instance credentials. 323 | 324 | ## Cleanup Your Environment 325 | To destroy all infrastructure contained in the CDK, run the following command: 326 | - Be sure to terminate and remove bastion resources manually created (e.g. instances, security groups, etc.) that were used in testing (any infrastructure outside of the CDK project, but within the VPC) before running the command below. 327 | - You will need to delete the SSM Parameter for the broker private ip named `dcv-broker-private-ip-*`. 328 | ```bash 329 | # Destroy all project resources. 330 | cdk destroy 331 | ``` 332 | 333 | ## Troubleshooting DCV Fleet Session Manager Creation Errors 334 | 1. List all sessions. Output should be a list of DCV sessions. 335 | ```bash 336 | python3 dcvsm describe-sessions 337 | ``` 338 | 2. Check the CLI can find DCV servers. Output should be an available server in the JSON. 339 | ```bash 340 | python3 dcvsm describe-servers 341 | ``` 342 | 3. Check the Agent is running. Output should be "Success". 343 | ```bash 344 | grep 'sessionsUpdateResponse' /var/log/dcv-session-manager-agent/agent.log | tail -1 | grep -o success` 345 | ``` 346 | 4. Check the Broker is running. Output should be { "error": "No authorization header"} 347 | >Note: Be sure to fill in the correct private IP address and region from the Session Manager instance below in `X-X-X-X.xx-xxxx-x`. (e.g. `10-0-2-63.us-west-2`) 348 | ```bash 349 | curl -X GET https://BROKER_DNS:8443/sessionConnectionData/aSession/aOwner --insecure 350 | ``` 351 | For more information, see the *Verify the installations* page of the [DCV Session Manager Administrator guide](https://docs.aws.amazon.com/dcv/latest/sm-admin/verify.html). 352 | 353 | ## Authors and acknowledgment 354 | [Andrew Morgan](https://github.com/morgnza)
355 | [Eric Cornwell](https://github.com/Eecornwell)
356 | 357 | ## License 358 | MIT License -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-without-pipelines/app.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | # this software and associated documentation files (the "Software"), to deal in 5 | # the Software without restriction, including without limitation the rights to 6 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | # the Software, and to permit persons to whom the Software is furnished to do so. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import json 17 | import aws_cdk as cdk 18 | from stacks.dcv_infra.dcv_infra import DcvInfra 19 | 20 | app = cdk.App() 21 | 22 | # Load the app configuration from the config.json file 23 | try: 24 | with open("config.json", "r") as config_file: 25 | config_data = json.load(config_file) 26 | except Exception as e: 27 | print(f"Could not read the app configuration file. {e}") 28 | raise e 29 | 30 | 31 | # Set CDK environment variables 32 | environment = cdk.Environment(account=config_data['accountId'], region=config_data['region']) 33 | 34 | # Create the DCV Infrastructure Stack for Session Manager and Connection Gateway 35 | DcvInfra(app, "DcvInfraStack", 36 | description='(uksb-1tupboc66) (tag:dcv-gw-sm-without-pipelines)', 37 | config_data=config_data, 38 | env=environment) 39 | 40 | app.synth() -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-without-pipelines/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "python app.py", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "requirements*.txt", 11 | "**/__init__.py", 12 | "**/__pycache__" 13 | ] 14 | }, 15 | "context": { 16 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 17 | "@aws-cdk/core:checkSecretUsage": true, 18 | "@aws-cdk/core:target-partitions": [ 19 | "aws", 20 | "aws-cn" 21 | ], 22 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 23 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 24 | "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, 25 | "@aws-cdk/aws-iam:minimizePolicies": true, 26 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 27 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 28 | "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, 29 | "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, 30 | "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, 31 | "@aws-cdk/core:enablePartitionLiterals": true, 32 | "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, 33 | "@aws-cdk/aws-iam:standardizedServicePrincipals": true, 34 | "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, 35 | "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, 36 | "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, 37 | "@aws-cdk/aws-route53-patters:useCertificate": true, 38 | "@aws-cdk/customresources:installLatestAwsSdkDefault": false, 39 | "@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true, 40 | "@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true, 41 | "@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true, 42 | "@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true, 43 | "@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true, 44 | "@aws-cdk/aws-redshift:columnId": true, 45 | "@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true, 46 | "@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true, 47 | "@aws-cdk/aws-apigateway:requestValidatorUniqueId": true, 48 | "@aws-cdk/aws-kms:aliasNameRef": true, 49 | "@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true, 50 | "@aws-cdk/core:includePrefixInUniqueNameGeneration": true, 51 | "@aws-cdk/aws-efs:denyAnonymousAccess": true, 52 | "@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true, 53 | "@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true, 54 | "@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true, 55 | "@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true, 56 | "@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true, 57 | "@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true, 58 | "@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-without-pipelines/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "comment": "NOTE: BEFORE DEPLOYING SESSION MANAGER AND CONNECTION GATEWAY STACK, UPDATE THE ACCOUNT ID AND REGION BELOW", 3 | "accountId": "xxxxxxxxxxxx", 4 | "region": "xx-xxxx-x", 5 | "sshKeypairName": "", 6 | "kmsKeyName": "aws/ebs", 7 | "network" : { 8 | "vpcId": "", 9 | "publicASubnetId": "", 10 | "publicBSubnetId": "", 11 | "privateASubnetId": "", 12 | "privateBSubnetId": "" 13 | }, 14 | "sessionMgr" : { 15 | "baseAmiId": "ami-xxxxxxxxxxxxxxxxx" 16 | }, 17 | "connectionGwy" : { 18 | "baseAmiId": "ami-xxxxxxxxxxxxxxxxx" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-without-pipelines/documentation/images/SolutionArchitecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/dcv-samples/62554cb1deb94b843a7faceb94ff774b94f73243/cdk/dcv-gw-sm-without-pipelines/documentation/images/SolutionArchitecture.png -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-without-pipelines/requirements.txt: -------------------------------------------------------------------------------- 1 | aws-cdk-lib>=2.141.0 2 | constructs>=10.0.0,<11.0.0 -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-without-pipelines/scripts/connection-gwy-user-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | # this software and associated documentation files (the "Software"), to deal in 6 | # the Software without restriction, including without limitation the rights to 7 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | # the Software, and to permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 12 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 13 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 14 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 15 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | LOG_PATH="/var/log/dcv-connection-gwy-install.log" 18 | echo $(date -u) "*****START USER DATA SCRIPT*****" | tee -a "$LOG_PATH" 19 | 20 | # Retrieve System Info 21 | echo $(date -u) "Discovering OS Info" | tee -a "$LOG_PATH" 22 | read -r system version <<<$(echo $(cat /etc/os-release | grep "^ID=\|^VERSION_ID=" | sort | cut -d"=" -f2 | tr -d "\"" | tr '[:upper:]' '[:lower:]')) 23 | major_version="${version%.*}" 24 | arch="$(arch)" 25 | CLOUDFRONT_PREFIX="https://d1uj6qtbmh3dt5.cloudfront.net" 26 | TMP_DIR="$(mktemp -d /tmp/XXXXXX)" 27 | trap 'rm -rf -- "$TMP_DIR"' ERR 28 | 29 | case $system in 30 | amzn ) 31 | if [ "$major_version" = 2 ]; then 32 | package_type="el7" 33 | package_manager="yum" 34 | package_extension="rpm" 35 | fi 36 | ;; 37 | centos|rhel ) 38 | if [[ "$major_version" =~ ^(7|8|9) ]]; then 39 | package_type="el$major_version" 40 | if [[ "$major_version" =~ ^(8|9) ]]; then 41 | package_manager="dnf" 42 | else 43 | package_manager="yum" 44 | fi 45 | package_extension="rpm" 46 | fi 47 | ;; 48 | ubuntu ) 49 | if [ "$major_version" = 22 ] || [ "$major_version" = 20 ]; then 50 | package_type="ubuntu$(echo $version | tr -d '.')" 51 | package_manager="apt" 52 | package_extension="deb" 53 | fi 54 | ;; 55 | * ) 56 | echo "Error: system '$system' is not supported" 57 | exit 1 58 | ;; 59 | esac 60 | 61 | if [ -z "$package_type" ]; then 62 | echo "Error: system '$system' with version '$version' is not supported for arch '$arch'" 63 | exit 1 64 | fi 65 | echo $(date -u) "Discovered OS:" | tee -a "$LOG_PATH" 66 | echo $(date -u) "Arch: $arch" | tee -a "$LOG_PATH" 67 | echo $(date -u) "OS Type: $package_type" | tee -a "$LOG_PATH" 68 | echo $(date -u) "Package Manager: $package_manager" | tee -a "$LOG_PATH" 69 | echo $(date -u) "Package Extension: $package_extension" | tee -a "$LOG_PATH" 70 | 71 | # Download Packages 72 | echo $(date -u) "Downloading DCV Connection Gateway Packages" | tee -a "$LOG_PATH" 73 | if [ "$package_manager" = apt ]; then 74 | curl -o "$TMP_DIR/NICE-GPG-KEY" "$CLOUDFRONT_PREFIX/NICE-GPG-KEY" 75 | gpg --import "$TMP_DIR/NICE-GPG-KEY" 76 | if [ $arch != "x86_64" ]; then 77 | deb_arch="arm64" 78 | curl -o "$TMP_DIR/nice-dcv-server.tgz" "$CLOUDFRONT_PREFIX/nice-dcv-ubuntu2204-aarch64.tgz" 79 | else 80 | deb_arch="amd64" 81 | curl -o "$TMP_DIR/nice-dcv-server.tgz" "$CLOUDFRONT_PREFIX/nice-dcv-$package_type-$arch.tgz" 82 | fi 83 | curl -o "$TMP_DIR/nice-dcv-connection-gateway.$package_extension" "$CLOUDFRONT_PREFIX/nice-dcv-connection-gateway_$deb_arch.$package_type.$package_extension" 84 | else 85 | rpm --import "$CLOUDFRONT_PREFIX"/NICE-GPG-KEY 86 | curl -o "$TMP_DIR/nice-dcv-connection-gateway.$package_extension" "$CLOUDFRONT_PREFIX/nice-dcv-connection-gateway-$package_type.$arch.$package_extension" 87 | curl -o "$TMP_DIR/nice-dcv-server.tgz" "$CLOUDFRONT_PREFIX/nice-dcv-$package_type-$arch.tgz" 88 | fi 89 | 90 | # Install Packages 91 | echo $(date -u) "Installing DCV Connection Gateway" | tee -a "$LOG_PATH" 92 | tar -xvzf "$TMP_DIR/nice-dcv-server.tgz" -C "$TMP_DIR" 93 | for package_pattern in "nice-dcv-web-viewer*" "nice-dcv-connection-gateway.$package_extension"; do 94 | package_full_path=$(find "$TMP_DIR" -name "$package_pattern") 95 | "$package_manager" install -y "$package_full_path" 96 | done 97 | 98 | # Enables Web Access through the Gateway 99 | sed -i --expression 's|url = "https://localhost:8080"|local-resources-path = "/usr/share/dcv/www"|' /etc/dcv-connection-gateway/dcv-connection-gateway.conf 100 | 101 | # Enable and start Gateway 102 | systemctl enable dcv-connection-gateway 103 | systemctl start dcv-connection-gateway 104 | 105 | # Clean Up 106 | rm -rf "$TMP_DIR" 107 | 108 | # Get current region 109 | TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` 110 | REGION=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/region) 111 | echo $(date -u) "Current Region: $REGION" | tee -a "$LOG_PATH" 112 | 113 | # Retrieve Broker Private DNS from SSM Parameter Store 114 | echo $(date -u) "Retrieving broker private DNS from SSM Parameter Store" | tee -a "$LOG_PATH" 115 | BROKER_PRIVATE_DNS=$(aws ssm get-parameter --name dcv-broker-private-dns --region "$REGION" --with-decryption | grep -Po '"Value": "\K[^"]*') 116 | timeout 1 bash -c "/dev/tcp/$BROKER_PRIVATE_DNS/8447" 117 | RESPONSE="$?" 118 | while [ "$RESPONSE" != 0 ]; do 119 | echo $(date -u) "Unable to reach broker. Waiting before retry." | tee -a "$LOG_PATH" 120 | sleep 15s 121 | BROKER_PRIVATE_DNS=$(aws ssm get-parameter --name dcv-broker-private-dns --region "$REGION" --with-decryption | grep -Po '"Value": "\K[^"]*') 122 | timeout 1 bash -c "cat < /dev/null > /dev/tcp/$BROKER_PRIVATE_DNS/8447" 123 | RESPONSE="$?" 124 | done 125 | echo $(date -u) "Broker private DNS found $BROKER_PRIVATE_DNS" | tee -a "$LOG_PATH" 126 | 127 | # Port Configuration. Using hex to escape double quotes: \x22 = " 128 | echo $(date -u) "Configuring Connection Gateway..." | tee -a "$LOG_PATH" 129 | sed -i "s/^#\[health-check\]/\[health-check\]/g" /etc/dcv-connection-gateway/dcv-connection-gateway.conf 130 | sed -i --expression 's|#bind-addr = "::"|bind-addr = "::"|' /etc/dcv-connection-gateway/dcv-connection-gateway.conf 131 | sed -i --expression 's|#tls-strict = false|tls-strict = false|' /etc/dcv-connection-gateway/dcv-connection-gateway.conf 132 | sed -i "/\[resolver\]/a tls-strict = false" /etc/dcv-connection-gateway/dcv-connection-gateway.conf 133 | sed -i "/bind-addr = \"::\"/a port = 8989" /etc/dcv-connection-gateway/dcv-connection-gateway.conf 134 | sed -i "s|url = \"https://localhost:8081\"|url = \"https://$BROKER_PRIVATE_DNS:8447\"|" /etc/dcv-connection-gateway/dcv-connection-gateway.conf 135 | 136 | # Start DCV Connection Gateway Service 137 | echo $(date -u) "Starting and Enabling Connection Gateway service..." | tee -a "$LOG_PATH" 138 | systemctl restart dcv-connection-gateway.service 139 | 140 | # Log if successful installation 141 | if [[ $? -eq 0 ]]; then 142 | echo $(date -u) "Successfully installed DCV Connection Gateway" | tee -a "$LOG_PATH" 143 | else 144 | echo $(date -u) "There was an error during DCV Connection Gateway installation" | tee -a "$LOG_PATH" 145 | fi 146 | -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-without-pipelines/scripts/session-mgr-user-data.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | # this software and associated documentation files (the "Software"), to deal in 6 | # the Software without restriction, including without limitation the rights to 7 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | # the Software, and to permit persons to whom the Software is furnished to do so. 9 | # 10 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 11 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 12 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 13 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 14 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 15 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | set -eE 18 | LOG_PATH="/var/log/dcv-session-mgr-install.log" 19 | echo $(date -u) "*****START USER DATA SCRIPT*****" | tee -a $LOG_PATH 20 | 21 | # Retrieve System Info 22 | read -r system version <<<$(echo $(cat /etc/os-release | grep "^ID=\|^VERSION_ID=" | sort | cut -d"=" -f2 | tr -d "\"" | tr '[:upper:]' '[:lower:]')) 23 | major_version="${version%.*}" 24 | CLOUDFRONT_PREFIX="https://d1uj6qtbmh3dt5.cloudfront.net" 25 | TMP_DIR="$(mktemp -d /tmp/XXXXXX)" 26 | trap 'rm -rf -- "$TMP_DIR"' ERR 27 | 28 | case $system in 29 | amzn ) 30 | if [ "$major_version" = 2 ]; then 31 | package_type="el7" 32 | package_manager="yum" 33 | package_extension="rpm" 34 | fi 35 | ;; 36 | centos|rhel ) 37 | if [[ "$major_version" =~ ^(7|8|9) ]]; then 38 | package_type="el$major_version" 39 | if [[ "$major_version" =~ ^(8|9) ]]; then 40 | package_manager="dnf" 41 | else 42 | package_manager="yum" 43 | fi 44 | package_extension="rpm" 45 | fi 46 | ;; 47 | ubuntu ) 48 | if [ "$major_version" = 22 ] || [ "$major_version" = 20 ]; then 49 | package_type="ubuntu$(echo $version | tr -d '.')" 50 | package_manager="apt" 51 | package_extension="deb" 52 | fi 53 | ;; 54 | * ) 55 | echo "Error: system '$system' is not supported" 56 | exit 1 57 | ;; 58 | esac 59 | 60 | 61 | if [ -z "$package_type" ]; then 62 | echo "Error: system '$system' with version '$version' is not supported for arch '$arch'" 63 | exit 1 64 | fi 65 | 66 | # Download Packages 67 | if [ "$package_manager" = apt ]; then 68 | curl -o "$TMP_DIR/NICE-GPG-KEY" "$CLOUDFRONT_PREFIX/NICE-GPG-KEY" 69 | gpg --import "$TMP_DIR/NICE-GPG-KEY" 70 | curl -o "$TMP_DIR/nice-dcv-session-manager-broker.$package_extension" "$CLOUDFRONT_PREFIX/nice-dcv-session-manager-broker_all.$package_type.$package_extension" 71 | else 72 | rpm --import "$CLOUDFRONT_PREFIX"/NICE-GPG-KEY 73 | curl -o "$TMP_DIR/nice-dcv-session-manager-broker.$package_extension" "$CLOUDFRONT_PREFIX/nice-dcv-session-manager-broker-$package_type.noarch.$package_extension" 74 | fi 75 | 76 | # Install Packages 77 | for package_pattern in "nice-dcv-session-manager-broker.$package_extension"; do 78 | package_full_path=$(find "$TMP_DIR" -name "$package_pattern") 79 | "$package_manager" install -y "$package_full_path" 80 | done 81 | 82 | # Enable and start DCV Session Manager service 83 | systemctl start dcv-session-manager-broker 84 | systemctl enable dcv-session-manager-broker 85 | 86 | # Configure DCV Session Manager 87 | CONFIG_PATH="/etc/dcv-session-manager-broker/session-manager-broker.properties" 88 | ## Enable the gateway in config 89 | sed -i '/^enable-gateway/s/=.*$/= true/' "$CONFIG_PATH" 90 | ## Uncomment the broker connector host and port in config 91 | sed -i '/gateway-to-broker-connector-https-port/s/^#\s//g' "$CONFIG_PATH" 92 | sed -i '/gateway-to-broker-connector-bind-host/s/^#\s//g' "$CONFIG_PATH" 93 | ## (Optional) Enable the broker to persist on DynamoDB in config 94 | #sed -i '/^enable-persistence/s/=.*$/= true/' "$CONFIG_PATH" 95 | ### Uncomment database, region, Read Capacity Units(RCU), Write Capacity Units(WCU), and table name prefix in config 96 | #sed -i '/persistence-db/s/^#\s//g' "$CONFIG_PATH" 97 | #sed -i '/dynamodb-region/s/^#\s//g' "$CONFIG_PATH" 98 | #sed -i '/dynamodb-table-rcu/s/^#\s//g' "$CONFIG_PATH" 99 | #sed -i '/dynamodb-table-wcu/s/^#\s//g' "$CONFIG_PATH" 100 | #sed -i '/dynamodb-table-name-prefix/s/^#\s//g' "$CONFIG_PATH" 101 | #sed -i "/^dynamodb-region/s/=.*$/= $REGION/" "$CONFIG_PATH" 102 | 103 | # Restart the broker service 104 | systemctl restart dcv-session-manager-broker.service 105 | 106 | # Clean Up 107 | rm -rf "$TMP_DIR" 108 | 109 | # Get the private IP for connection gateway to use during configuration 110 | echo $(date -u) "Retrieving private broker DNS..." | tee -a "$LOG_PATH" 111 | TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` 112 | MAC=`curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/network/interfaces/macs/` 113 | PRIVATE_DNS=`curl -H "X-aws-ec2-metadata-token: $TOKEN" "http://169.254.169.254/latest/meta-data/network/interfaces/macs/${MAC}local-hostname"` 114 | echo $(date -u) "Using private DNS $PRIVATE_DNS" | tee -a "$LOG_PATH" 115 | 116 | # Get current region 117 | REGION=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/placement/region) 118 | echo $(date -u) "Current Region: $REGION" | tee -a "$LOG_PATH" 119 | 120 | # Store the private private DNS name in SSM Parameter Store 121 | echo $(date -u) "Storing broker private DNS name in AWS SSM Parameter Store..." | tee -a "$LOG_PATH" 122 | aws ssm put-parameter --name dcv-broker-private-dns --value "$PRIVATE_DNS" --type String --overwrite --region "$REGION" 123 | # Log if successful installation 124 | if [[ $? -eq 0 ]]; then 125 | echo $(date -u) "Stored private DNS name $PRIVATE_DNS in Parameter Store" | tee -a "$LOG_PATH" 126 | else 127 | echo $(date -u) "There was an error during DCV Session Manager installation" | tee -a "$LOG_PATH" 128 | fi -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-without-pipelines/stacks/dcv_infra/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/dcv-samples/62554cb1deb94b843a7faceb94ff774b94f73243/cdk/dcv-gw-sm-without-pipelines/stacks/dcv_infra/__init__.py -------------------------------------------------------------------------------- /cdk/dcv-gw-sm-without-pipelines/stacks/dcv_infra/dcv_infra.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | # this software and associated documentation files (the "Software"), to deal in 5 | # the Software without restriction, including without limitation the rights to 6 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | # the Software, and to permit persons to whom the Software is furnished to do so. 8 | # 9 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | import os 17 | from aws_cdk import Stack 18 | import aws_cdk as cdk 19 | import aws_cdk.aws_ec2 as ec2 20 | import aws_cdk.aws_iam as iam 21 | import aws_cdk.aws_kms as kms 22 | import aws_cdk.aws_autoscaling as autoscaling 23 | import aws_cdk.aws_elasticloadbalancingv2 as elbv2 24 | from constructs import Construct 25 | 26 | # The NICE DCV INFRA stack 27 | class DcvInfra(Stack): 28 | """ Class to deploy DCV infrastructure components """ 29 | def __init__(self, scope: Construct, construct_id: str, config_data: dict, **kwargs) -> None: 30 | super().__init__(scope, construct_id, **kwargs) 31 | 32 | # Check if a VPC and subnets were provided in the config.json file 33 | if config_data['network']['vpcId'] and config_data['network']['publicASubnetId'] \ 34 | and config_data['network']['publicBSubnetId'] \ 35 | and config_data['network']['privateASubnetId'] \ 36 | and config_data['network']['privateBSubnetId']: 37 | # Create reference for VPC 38 | vpc = ec2.Vpc.from_lookup(self, "VPC", vpc_id=config_data['network']['vpcId']) 39 | # Create references for private and public subnets 40 | # using subnet_ids pulled from config.json 41 | subnets_public_ref_a = ec2.Subnet.from_subnet_attributes( 42 | self, "PublicSubnetAFromAttributes", 43 | subnet_id=config_data['network']['publicASubnetId'], 44 | availability_zone=f"{self.region}a" 45 | ) 46 | subnets_public_ref_b = ec2.Subnet.from_subnet_attributes( 47 | self, "PublicSubnetBFromAttributes", 48 | subnet_id=config_data['network']['publicBSubnetId'], 49 | availability_zone=f"{self.region}b" 50 | ) 51 | subnets_private_ref_a = ec2.Subnet.from_subnet_attributes( 52 | self, "PrivateSubnetAFromAttributes", 53 | subnet_id=config_data['network']['privateASubnetId'], 54 | availability_zone=f"{self.region}a" 55 | ) 56 | subnets_private_ref_b = ec2.Subnet.from_subnet_attributes( 57 | self, "PrivateSubnetBFromAttributes", 58 | subnet_id=config_data['network']['privateBSubnetId'], 59 | availability_zone=f"{self.region}b" 60 | ) 61 | subnets_public = ec2.SubnetSelection( 62 | subnets=[subnets_public_ref_a, subnets_public_ref_b] 63 | ) 64 | subnets_private = ec2.SubnetSelection( 65 | subnets=[subnets_private_ref_a, subnets_private_ref_b] 66 | ) 67 | else: 68 | # Create new VPC, subnets, and NAT Gateway 69 | vpc = ec2.Vpc(self, "VPC", 70 | nat_gateways = 1, 71 | max_azs = 2, 72 | subnet_configuration=[ 73 | ec2.SubnetConfiguration( 74 | name = "public-subnet", 75 | subnet_type = ec2.SubnetType.PUBLIC, 76 | cidr_mask = 24, 77 | ), 78 | ec2.SubnetConfiguration( 79 | name = "private-subnet", 80 | subnet_type = ec2.SubnetType.PRIVATE_WITH_EGRESS, 81 | cidr_mask = 24 82 | ) 83 | ], 84 | ) 85 | # Create references for private and public subnets 86 | # using availability zones described above 87 | subnets_public = ec2.SubnetSelection( 88 | subnet_type=ec2.SubnetType.PUBLIC 89 | ) 90 | subnets_private = ec2.SubnetSelection( 91 | subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS 92 | ) 93 | 94 | 95 | # IAM Fleet Role Configuration 96 | role_fleet = iam.Role(self, "DCVFleetRole", 97 | assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"), 98 | role_name="dcv-fleet-role" 99 | ) 100 | 101 | # IAM Session Manager Role Configuration 102 | role_session_mgr = iam.Role(self, "DCVSessionMgrRole", 103 | assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"), 104 | role_name="dcv-session-mgr-role" 105 | ) 106 | 107 | # IAM Connection Gateway Role Configuration 108 | role_connection_gwy = iam.Role(self, "DCVConnectionGwyRole", 109 | assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"), 110 | role_name="dcv-connection-gwy-role" 111 | ) 112 | 113 | # Permission to get NICE DCV License for fleet role 114 | role_fleet.add_to_policy(iam.PolicyStatement( 115 | effect=iam.Effect.ALLOW, 116 | actions=["s3:GetObject"], 117 | resources=[f"arn:aws:s3:::dcv-license.{self.region}/*"] 118 | )) 119 | 120 | role_fleet.add_managed_policy( 121 | iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore") 122 | ) 123 | 124 | iam.CfnInstanceProfile(self, "DCVServerInstanceProfile", 125 | instance_profile_name="dcv-server-profile", 126 | roles=[role_fleet.role_name] 127 | ) 128 | 129 | # Permission for SSM Parameter for Session Manager instance 130 | role_session_mgr.add_to_policy(iam.PolicyStatement( 131 | effect=iam.Effect.ALLOW, 132 | actions=[ 133 | 'ssm:DescribeParameters' 134 | ], 135 | resources=[ 136 | f"arn:aws:ssm:{self.region}:{self.account}:parameter/*", 137 | ] 138 | )) 139 | 140 | role_session_mgr.add_to_policy(iam.PolicyStatement( 141 | effect=iam.Effect.ALLOW, 142 | actions=[ 143 | 'ssm:PutParameter', 144 | 'ssm:GetParameter' 145 | ], 146 | resources=[ 147 | f"arn:aws:ssm:{self.region}:{self.account}:parameter/dcv-broker-private-dns", 148 | ] 149 | )) 150 | 151 | role_session_mgr.add_managed_policy( 152 | iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore") 153 | ) 154 | 155 | # Permission for SSM Parameter for Session Manager instance 156 | role_connection_gwy.add_to_policy(iam.PolicyStatement( 157 | effect=iam.Effect.ALLOW, 158 | actions=[ 159 | 'ssm:DescribeParameters' 160 | ], 161 | resources=[ 162 | f"arn:aws:ssm:{self.region}:{self.account}:parameter/*", 163 | ] 164 | )) 165 | 166 | # Permission to read the SSM Parameter for Connection Gateway instance 167 | role_connection_gwy.add_to_policy(iam.PolicyStatement( 168 | effect=iam.Effect.ALLOW, 169 | actions=[ 170 | 'ssm:GetParameter' 171 | ], 172 | resources=[ 173 | f"arn:aws:ssm:{self.region}:{self.account}:parameter/dcv-broker-private-dns" 174 | ] 175 | )) 176 | 177 | # Add managed policy to allow CloudWatch logs and SSM 178 | role_connection_gwy.add_managed_policy( 179 | iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore")) 180 | 181 | # Session Manager Security Group configuration 182 | sg_session_mgr = ec2.SecurityGroup(self, "SMSecurityGroup", 183 | vpc=vpc, 184 | description="Default ports for DCV Session, Gateway, and Broker", 185 | allow_all_outbound=True, #Egress all 186 | disable_inline_rules=True 187 | ) 188 | 189 | # Add Ingress for Nice DCV TCP/UDP Ports 190 | sg_session_mgr.add_ingress_rule( 191 | ec2.Peer.any_ipv4(), ec2.Port.tcp(8443), "allow CLI to Broker communication" 192 | ) 193 | sg_session_mgr.add_ingress_rule( 194 | ec2.Peer.any_ipv4(), ec2.Port.tcp(8445), "allow Agent to Broker communication" 195 | ) 196 | sg_session_mgr.add_ingress_rule( 197 | sg_session_mgr, ec2.Port.all_traffic(), "allow Broker to Broker communication" 198 | ) 199 | 200 | # Connection Gateway Security Group configuration 201 | sg_connection_gwy = ec2.SecurityGroup(self, "CGSecurityGroup", 202 | vpc=vpc, 203 | description="SG default ports for DCV Connection Gateway", 204 | allow_all_outbound=True, #Egress all 205 | disable_inline_rules=True 206 | ) 207 | 208 | sg_connection_gwy.add_ingress_rule( 209 | ec2.Peer.any_ipv4(), ec2.Port.tcp(8443), "allow TCP DCV access from public internet" 210 | ) 211 | sg_connection_gwy.add_ingress_rule( 212 | ec2.Peer.any_ipv4(), ec2.Port.udp(8443), "allow UDP DCV access from public internet" 213 | ) 214 | sg_connection_gwy.add_ingress_rule( 215 | ec2.Peer.any_ipv4(), ec2.Port.tcp(8989), "allow health check for NLB targets" 216 | ) 217 | 218 | # Gateway to Session Manager resolver communication 219 | sg_session_mgr.add_ingress_rule( 220 | sg_connection_gwy, ec2.Port.tcp(8447), "allow Gateway to Broker resolver communication" 221 | ) 222 | 223 | # DCV server fleet Security Group configuration 224 | sg_dcv_server = ec2.SecurityGroup(self, "DCVServerSecurityGroup", 225 | vpc=vpc, 226 | description="SG default ports for DCV Connection Gateway", 227 | allow_all_outbound=True, #Egress all 228 | disable_inline_rules=True 229 | ) 230 | 231 | sg_dcv_server.add_ingress_rule( 232 | sg_connection_gwy, ec2.Port.tcp(8443), "allow DCV streaming traffic from Gateway" 233 | ) 234 | sg_dcv_server.add_ingress_rule( 235 | sg_connection_gwy, ec2.Port.udp(8443), "allow DCV streaming traffic from Gateway" 236 | ) 237 | 238 | # Get KMS Key from Alias/Key Name 239 | kms_arn = f"arn:aws:kms:{self.region}:{self.account}:alias/{config_data['kmsKeyName']}" 240 | kms_key = kms.Key.from_key_arn(self, "kms-key", kms_arn) 241 | 242 | # Session Manager 243 | ### Session Manager AMI 244 | session_mgr_ami = ec2.GenericLinuxImage({ 245 | self.region : config_data['sessionMgr']['baseAmiId'] 246 | } 247 | ) 248 | 249 | # Add the user data script to custom string 250 | session_mgr_user_data_file = open(os.path.join(os.path.dirname( __file__ ), 251 | "..", "..", 252 | "scripts", 253 | "session-mgr-user-data.sh"), 254 | "r", encoding="utf-8") 255 | session_mgr_user_data_content = session_mgr_user_data_file.read() 256 | session_mgr_user_data = ec2.UserData.custom(session_mgr_user_data_content) 257 | 258 | # Create a reference to the SSH Key Pair name given in the config.json file 259 | key_pair = ec2.KeyPair.from_key_pair_attributes(self, "DCVKeyPair", 260 | key_pair_name=config_data['sshKeypairName'] 261 | ) 262 | 263 | # Create the Session Manager EC2 instance 264 | session_mgr_instance = ec2.Instance(self, "SessionMgrInstance", 265 | vpc=vpc, 266 | vpc_subnets=subnets_private, 267 | instance_type=ec2.InstanceType.of( 268 | ec2.InstanceClass.M6G, ec2.InstanceSize.LARGE), 269 | machine_image=session_mgr_ami, 270 | security_group=sg_session_mgr, 271 | key_pair=key_pair, 272 | user_data=session_mgr_user_data, 273 | role=role_session_mgr, 274 | block_devices=[ec2.BlockDevice( 275 | device_name="/dev/xvda", 276 | volume=ec2.BlockDeviceVolume.ebs( 277 | 8, encrypted=True, kms_key=kms_key))] 278 | ) 279 | 280 | # Connection Gateway 281 | ### Connection Gateway AMI 282 | connection_gwy_ami = ec2.GenericLinuxImage({ 283 | self.region : config_data['connectionGwy']['baseAmiId'] 284 | }) 285 | 286 | # Add the user data script to custom string 287 | connection_gwy_user_data_file = open(os.path.join(os.path.dirname( __file__ ), 288 | "..", "..", 289 | "scripts", 290 | "connection-gwy-user-data.sh"), 291 | "r", encoding="utf-8") 292 | 293 | connection_gwy_user_data_content = connection_gwy_user_data_file.read() 294 | connection_gwy_session_mgr_user_data = ec2.UserData.custom(connection_gwy_user_data_content) 295 | 296 | # Create an EC2 launch template with the encrypted volume for Connection Gateway 297 | connection_gwy_launch_template = ec2.LaunchTemplate(self, "ConnectionGwyLaunchTemplate", 298 | machine_image=connection_gwy_ami, 299 | security_group=sg_connection_gwy, 300 | instance_type=ec2.InstanceType.of( 301 | ec2.InstanceClass.C7G, ec2.InstanceSize.LARGE), 302 | key_pair=key_pair, 303 | user_data=connection_gwy_session_mgr_user_data, 304 | role=role_connection_gwy, 305 | block_devices=[ec2.BlockDevice( 306 | device_name="/dev/xvda", 307 | volume=ec2.BlockDeviceVolume.ebs( 308 | 8, encrypted=True, kms_key=kms_key))], 309 | ) 310 | 311 | # Create a Connection Gateway Auto Scaling Group 312 | connection_gwy_asg = autoscaling.AutoScalingGroup(self, "ConnectionGwyASG", 313 | vpc=vpc, 314 | launch_template=connection_gwy_launch_template, 315 | min_capacity=1, 316 | max_capacity=5, 317 | desired_capacity=1, 318 | vpc_subnets=subnets_private 319 | ) 320 | 321 | # Ensure Session Manager instance is created before Connection Gateway targets 322 | connection_gwy_asg.node.add_dependency(session_mgr_instance) 323 | 324 | # Connection Gateway Auto Scaling Group CPU Utilization to scale 325 | connection_gwy_asg.scale_on_cpu_utilization( 326 | "ConnectionGwyASGCPUUtilization", target_utilization_percent=75 327 | ) 328 | 329 | # Create a Connection Gateway Network Load Balancer 330 | connection_gwy_nlb = elbv2.NetworkLoadBalancer(self, "ConnectionGwyNLB", 331 | vpc=vpc, 332 | internet_facing=True, 333 | vpc_subnets=subnets_public, 334 | load_balancer_name="connection-gwy-nlb", 335 | cross_zone_enabled=True 336 | ) 337 | 338 | # Connection Gateway Listener to Target Setup for NLB 339 | connection_gwy_listener = connection_gwy_nlb.add_listener( 340 | "ConnectionGwyNLBListener", port=8443, protocol=elbv2.Protocol.TCP_UDP 341 | ) 342 | 343 | # Route the Connection Gateway targets 344 | connection_gwy_listener.add_targets("ConnectionGwyNLBTarget", 345 | port=8443, 346 | protocol=elbv2.Protocol.TCP_UDP, 347 | health_check=elbv2.HealthCheck( 348 | port="8989", 349 | protocol=elbv2.Protocol.TCP, 350 | unhealthy_threshold_count=5, 351 | healthy_threshold_count=5, 352 | interval=cdk.Duration.seconds(30) 353 | ), 354 | targets=[connection_gwy_asg] 355 | ) 356 | -------------------------------------------------------------------------------- /session-resolver/resolver.py: -------------------------------------------------------------------------------- 1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | # SPDX-License-Identifier: MIT-0 3 | 4 | import json 5 | import boto3 6 | from botocore.exceptions import ClientError 7 | 8 | ec2 = boto3.client('ec2') 9 | 10 | TCP_PORT = 8443 11 | UDP_PORT = 8443 12 | 13 | def get_instance_ip(instance_id): 14 | """ Given an instance ID this returns the private Ip address corresponding to it """ 15 | try: 16 | response = ec2.describe_instances(InstanceIds=[instance_id]) 17 | private_ip_addr = response['Reservations'][0]['Instances'][0]['PrivateIpAddress'] 18 | return private_ip_addr 19 | except ClientError: 20 | return { 21 | 'statusCode': 404, 22 | 'body': f"Invalid session ID '{instance_id}'." 23 | } 24 | 25 | 26 | # https://docs.aws.amazon.com/dcv/latest/gw-admin/session-resolver.html#implementing-session-resolver 27 | def lambda_handler(event, context): 28 | # Gateway POST - sessionId=session_id&transport=transport&clientIpAddress=clientIpAddress 29 | session_id = event['queryStringParameters']['sessionId'] 30 | transport = event['queryStringParameters']['transport'] 31 | 32 | if session_id is None: 33 | return { 34 | 'statusCode': 400, 35 | 'body': "Missing sessionId parameter" 36 | } 37 | 38 | if transport not in ["HTTP", "QUIC"]: 39 | return { 40 | 'statusCode': 400, 41 | 'body': "Invalid transport parameter: " + transport 42 | } 43 | 44 | server_endpoint = get_instance_ip(session_id) 45 | port = int(TCP_PORT if transport == 'HTTP' else UDP_PORT) 46 | session_details = { 47 | 'SessionId': "console", 48 | 'DcvServerEndpoint': server_endpoint, 49 | 'Port': port, 50 | 'WebUrlPath': '/', 51 | 'TransportProtocol':transport 52 | } 53 | if 'statusCode' in server_endpoint: 54 | return server_endpoint 55 | if 'statusCode' not in server_endpoint: 56 | return { 57 | 'statusCode': 200, 58 | 'body': json.dumps(session_details) 59 | } 60 | -------------------------------------------------------------------------------- /usage-reporting/Build-DCVUsageReports.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | software and associated documentation files (the "Software"), to deal in the Software 6 | without restriction, including without limitation the rights to use, copy, modify, 7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | .SYNOPSIS 18 | This script is intended to generate usage reports for DCV Amazon WorkSpaces. 19 | .DESCRIPTION 20 | A scheduled task should be pushed via Group Policy that will invoke the local script every minute 21 | on a WSP WorkSpace. The script will create a CSV for each day and records hostname, sessions start time, 22 | session end time, username, and total session duration. These reports can be further analyzed by ingesting 23 | into a visualization logging mechanism. To deploy this task, it is recommended to use a Group Policy Object, 24 | as WorkSpaces require AD DS. Alternatively, the scheduled task can be created locally. 25 | .EXAMPLE 26 | Scheduled task argument to execute local script. Path can be modified to your script location. 27 | -noprofile -ExecutionPolicy Unrestricted -file "C:\Program Files\Amazon\Build-DCVUsageReports.ps1" 28 | Scheduled task argument to execute local script with a provided path. The path must exist locally. 29 | -noprofile -ExecutionPolicy Unrestricted -file "C:\Program Files\Amazon\Build-DCVUsageReports.ps1" -usageReportsFolder "C:\path" 30 | #> 31 | [CmdletBinding()] 32 | Param ( 33 | [Parameter(Mandatory=$false)] 34 | [string]$usageReportsFolder 35 | ) 36 | 37 | $today = Get-Date 38 | $reportId = $today.Month.ToString() + "-" + $today.Day.ToString() + "-" + $today.Year.ToString() 39 | if($usageReportsFolder -eq ''){ 40 | $usageReportsFolder = "C:\Windows\Temp\DCVUsageReports" 41 | }else{ 42 | if(-not(Test-Path -Path $usageReportsFolder)){ 43 | write-host "Provided path not found. Reverting to default path." 44 | $usageReportsFolder = "C:\Windows\Temp\DCVUsageReports" 45 | } 46 | } 47 | $outfile = "$usageReportsFolder\$reportId.csv" 48 | if(-not(Test-Path -Path $usageReportsFolder)){ 49 | New-Item $usageReportsFolder -ItemType Directory -ea 0 50 | } 51 | if(-not(Test-Path -Path $outfile)){ 52 | {} | Select "Hostname","LogonTime","LogoffTime","User","SessionTime" | Export-Csv $outfile -NoTypeInformation 53 | } 54 | $hostname = $env:computername 55 | $sessionInfo = .'C:\Program Files\NICE\DCV\Server\bin\dcv.exe' list-sessions console -j | ConvertFrom-Json 56 | if ($sessionInfo.'num-of-connections' -ne 0){ 57 | $connectionInfo = .'C:\Program Files\NICE\DCV\Server\bin\dcv.exe' list-connections console -j | ConvertFrom-Json 58 | if ($connectionInfo.count -eq 1){ 59 | $user = $connectionInfo.username 60 | }else{ 61 | $user = $connectionInfo[0].username 62 | } 63 | $sessionStart = '{0:HH:mm:ss}' -f ([DateTime](($connectionInfo.'connection-time').Split('T')).Split('.')[1]) 64 | $importReport = Import-CSV -Path $outfile 65 | if (-not($importReport.LogonTime.Contains($sessionStart))){ 66 | $inputLogonTime = [PSCustomObject]@{ 67 | Hostname = $hostname 68 | LogonTime = $sessionStart 69 | LogoffTime = '' 70 | User = $user 71 | SessionTime = '00:00:00' 72 | } 73 | $inputLogonTime | Export-Csv $outfile -Append 74 | } 75 | } 76 | if ($sessionInfo.'last-disconnection-time' -ne ''){ 77 | if (([DateTime]($sessionInfo.'last-disconnection-time').Split('T')[0]).Day -eq $today.Day){ 78 | $disconnectedTime = '{0:HH:mm:ss}' -f ([DateTime]((($sessionInfo.'last-disconnection-time')).Split('T')).Split('.')[1]) 79 | }else{ 80 | $disconnectedTime = '' 81 | } 82 | }else{ 83 | $disconnectedTime = '' 84 | } 85 | $importReport = Import-CSV -Path $outfile 86 | $updateReport = foreach ($line in $importReport){ 87 | if (($line.LogoffTime -eq '') -and ($line.LogonTime -ne '') -and ($disconnectedTime -ne '')){ 88 | if ((New-TimeSpan -Start $disconnectedTime -End $today) -lt (New-TimeSpan -Start $line.LogonTime -End $today)){ 89 | $line.LogoffTime = $disconnectedTime 90 | $sessionTime = New-TimeSpan -Start $line.LogonTime -End $line.LogoffTime 91 | $sessionTime = $sessionTime.Hours.ToString() + ':' + $sessionTime.Minutes.ToString() + ':' +$sessionTime.Seconds.ToString() 92 | $line.SessionTime = $sessionTime 93 | } 94 | $line 95 | } elseif ($line.LogonTime -ne ''){ 96 | $line 97 | } 98 | } 99 | $updateReport | Export-Csv $outfile -NoTypeInformation 100 | $verifyFile = Get-Content $outfile 101 | if ($null -eq $verifyFile){ 102 | {} | Select "Hostname","LogonTime","LogoffTime","User","SessionTime" | Export-Csv $outfile -NoTypeInformation 103 | } -------------------------------------------------------------------------------- /usage-reporting/Build-WSPUsageReports.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 5 | software and associated documentation files (the "Software"), to deal in the Software 6 | without restriction, including without limitation the rights to use, copy, modify, 7 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 11 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 12 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 13 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 14 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 15 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 16 | 17 | .SYNOPSIS 18 | This script is intended to generate usage reports for WSP Amazon WorkSpaces. 19 | .DESCRIPTION 20 | A scheduled task should be pushed via Group Policy that will invoke the local script every minute 21 | on a WSP WorkSpace. The script will create a CSV for each day and records hostname, sessions start time, 22 | session end time, username, and total session duration. These reports can be further analyzed by ingesting 23 | into a visualization logging mechanism. To deploy this task, it is recommended to use a Group Policy Object, 24 | as WorkSpaces require AD DS. Alternatively, the scheduled task can be created locally. 25 | .EXAMPLE 26 | Scheduled task argument to execute local script. Path can be modified to your script location. 27 | -noprofile -ExecutionPolicy Unrestricted -file "C:\Program Files\Amazon\Build-WSPUsageReports.ps1" 28 | Scheduled task argument to execute local script with a provided path. The path must exist locally. 29 | -noprofile -ExecutionPolicy Unrestricted -file "C:\Program Files\Amazon\Build-WSPUsageReports.ps1" -usageReportsFolder "C:\path" 30 | #> 31 | [CmdletBinding()] 32 | Param ( 33 | [Parameter(Mandatory=$false)] 34 | [string]$usageReportsFolder 35 | ) 36 | 37 | $today = Get-Date 38 | $reportId = $today.Month.ToString() + "-" + $today.Day.ToString() + "-" + $today.Year.ToString() 39 | if($usageReportsFolder -eq ''){ 40 | $usageReportsFolder = "C:\Windows\Temp\WSPUsageReports" 41 | }else{ 42 | if(-not(Test-Path -Path $usageReportsFolder)){ 43 | write-host "Provided path not found. Reverting to default path." 44 | $usageReportsFolder = "C:\Windows\Temp\WSPUsageReports" 45 | } 46 | } 47 | $outfile = "$usageReportsFolder\$reportId.csv" 48 | if(-not(Test-Path -Path $usageReportsFolder)){ 49 | New-Item $usageReportsFolder -ItemType Directory -ea 0 50 | } 51 | if(-not(Test-Path -Path $outfile)){ 52 | {} | Select "Hostname","LogonTime","LogoffTime","User","SessionTime" | Export-Csv $outfile -NoTypeInformation 53 | } 54 | $hostname = $env:computername 55 | $user = (Get-ChildItem D:\Users\)[0].Name 56 | $sessionInfo = .'C:\Program Files\NICE\DCV\Server\bin\dcv.exe' list-sessions console -j | ConvertFrom-Json 57 | if ($sessionInfo.'num-of-connections' -ne 0){ 58 | $connectionInfo = .'C:\Program Files\NICE\DCV\Server\bin\dcv.exe' list-connections console -j | ConvertFrom-Json 59 | $sessionStart = '{0:HH:mm:ss}' -f ([DateTime](($connectionInfo.'connection-time').Split('T')).Split('.')[1]) 60 | $importReport = Import-CSV -Path $outfile 61 | if (-not($importReport.LogonTime.Contains($sessionStart))){ 62 | $inputLogonTime = [PSCustomObject]@{ 63 | Hostname = $hostname 64 | LogonTime = $sessionStart 65 | LogoffTime = '' 66 | User = $user 67 | SessionTime = '00:00:00' 68 | } 69 | $inputLogonTime | Export-Csv $outfile -Append 70 | } 71 | } 72 | if ($sessionInfo.'last-disconnection-time' -ne ''){ 73 | if (([DateTime]($sessionInfo.'last-disconnection-time').Split('T')[0]).Day -eq $today.Day){ 74 | $disconnectedTime = '{0:HH:mm:ss}' -f ([DateTime]((($sessionInfo.'last-disconnection-time')).Split('T')).Split('.')[1]) 75 | }else{ 76 | $disconnectedTime = '' 77 | } 78 | }else{ 79 | $disconnectedTime = '' 80 | } 81 | $importReport = Import-CSV -Path $outfile 82 | $updateReport = foreach ($line in $importReport){ 83 | if (($line.LogoffTime -eq '') -and ($line.LogonTime -ne '') -and ($disconnectedTime -ne '')){ 84 | if ((New-TimeSpan -Start $disconnectedTime -End $today) -lt (New-TimeSpan -Start $line.LogonTime -End $today)){ 85 | $line.LogoffTime = $disconnectedTime 86 | $sessionTime = New-TimeSpan -Start $line.LogonTime -End $line.LogoffTime 87 | $sessionTime = $sessionTime.Hours.ToString() + ':' + $sessionTime.Minutes.ToString() + ':' +$sessionTime.Seconds.ToString() 88 | $line.SessionTime = $sessionTime 89 | } 90 | $line 91 | } elseif ($line.LogonTime -ne ''){ 92 | $line 93 | } 94 | } 95 | $updateReport | Export-Csv $outfile -NoTypeInformation 96 | $verifyFile = Get-Content $outfile 97 | if ($null -eq $verifyFile){ 98 | {} | Select "Hostname","LogonTime","LogoffTime","User","SessionTime" | Export-Csv $outfile -NoTypeInformation 99 | } --------------------------------------------------------------------------------