Skip to content
Permalink
main
Switch branches/tags
Go to file
Latest commit f9ec7a4 Feb 10, 2022 History
2 contributors

Users who have contributed to this file

@davmayd @MarciaRieferJohnston
AWSTemplateFormatVersion: 2010-09-09
Description: >-
This template deploys a .NET development environment. (qs-1sctmf6e2)
Parameters:
AllowedCIDR:
AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
ConstraintDescription: The CIDR block parameter must be in the form x.x.x.x/x
Description: "Block of CIDR IP addresses that can access the EC2 instance. A value of 0.0.0.0/0 allows access from any IP address."
Type: String
Default: 0.0.0.0/0
VPCId:
Description: (Optional) ID of the existing VPC that you're deploying into. Keep blank if you're creating a new VPC.
Type: String #AWS::EC2::VPC::Id doesn't allow 'null' values.
Default: ""
SubnetId:
Description: (Optional) ID of your existing subnet. Keep blank if you're creating a new VPC.
Type: String #AWS::EC2::Subnet::Id doesn't allow 'null' values.
Default: ""
WindowsAMIId:
Description: ID of the Windows EC2 AMI.
Type: 'AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>'
Default: '/aws/service/ami-windows-latest/Windows_Server-2019-English-Full-ContainersLatest'
KeyName:
Description: Name of the key pair to be used to connect to your EC2 instance by using SSH.
Type: AWS::EC2::KeyPair::KeyName
AllowedPattern: \S+
MinLength: 1
ConstraintDescription: Please provide the name of an EC2 keypair that has been registered in your account.
InstanceType:
AllowedValues:
- r4.xlarge
- r4.2xlarge
- r4.4xlarge
- r5.large
- r5.xlarge
- r5.2xlarge
- r5.4xlarge
- m5.large
- m5.2xlarge
- m5.4xlarge
Default: m5.large
Description: Type of EC2 instance to use for the development-tools instance.
Type: String
QSS3BucketName:
AllowedPattern: '^[0-9a-zA-Z]+([0-9a-zA-Z-\.]*[0-9a-zA-Z])*$'
ConstraintDescription:
The Quick Start bucket name can include numbers, lowercase
letters, uppercase letters, periods, and hyphens (-). It
cannot start or end with a hyphen (-).
Default: aws-quickstart
Description:
'Name of the S3 bucket for your copy of the Quick Start assets.
Keep the default name unless you are customizing the template.
Changing the name updates code references to point to a new Quick
Start location. This name can include numbers, lowercase letters,
uppercase letters, and hyphens, but do not start or end with a hyphen (-).
See https://aws-quickstart.github.io/option1.html.'
Type: String
QSS3BucketRegion:
Default: us-east-1
Description: 'AWS Region where the Quick Start S3 bucket (QSS3BucketName) is
hosted. Keep the default Region unless you are customizing the template.
Changing this Region updates code references to point to a new Quick Start location.
When using your own bucket, specify the Region.
See https://aws-quickstart.github.io/option1.html.'
Type: String
QSS3KeyPrefix:
AllowedPattern: '^[0-9a-zA-Z-/]*$'
ConstraintDescription:
The Quick Start S3 key prefix can include numbers, lowercase letters,
uppercase letters, hyphens (-), and forward slashes (/).
Default: quickstart-dotnet-devenvironment-setup/
Description:
'S3 key prefix that is used to simulate a directory for your copy of the
Quick Start assets. Keep the default prefix unless you are customizing
the template. Changing this prefix updates code references to point to
a new Quick Start location. This prefix can include numbers, lowercase
letters, uppercase letters, hyphens (-), and forward slashes (/).
See https://docs.aws.amazon.com/AmazonS3/latest/dev/UsingMetadata.html
and https://aws-quickstart.github.io/option1.html.'
Type: String
Metadata:
cfn-lint:
config:
ignore_checks:
- W9006
QuickStartDocumentation:
EntrypointName: "Parameters for deploying into a new or existing VPC"
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Network configuration
Parameters:
- VPCId
- SubnetId
- AllowedCIDR
- Label:
default: EC2 configuration
Parameters:
- KeyName
- InstanceType
- WindowsAMIId
- Label:
default: Quick Start parameters
Parameters:
- QSS3BucketName
- QSS3KeyPrefix
- QSS3BucketRegion
ParameterLabels:
KeyName:
default: EC2 key pair
VPCId:
default: VPC ID
AllowedCIDR:
default: Allowed CIDR block
SubnetId:
default: Subnet ID
WindowsAMIId:
default: Windows EC2 AMI ID
InstanceType:
default: EC2 instance type
QSS3BucketName:
default: "S3 bucket name"
QSS3KeyPrefix:
default: "S3 key prefix"
QSS3BucketRegion:
default: "S3 bucket Region"
Conditions:
CreateVPC:
!Equals [ !Ref VPCId, "" ]
Resources:
VPC:
Condition: CreateVPC
Type: AWS::EC2::VPC
Properties:
CidrBlock: '10.0.0.0/16'
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} VPC
InternetGW:
Condition: CreateVPC
Type: AWS::EC2::InternetGateway
# Route tables & routes (public/private)
InternetGWAttachment:
Condition: CreateVPC
Type: AWS::EC2::VPCGatewayAttachment
Properties:
InternetGatewayId: !Ref InternetGW
VpcId: !Ref VPC
InternetRouteTable:
Condition: CreateVPC
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
InternetRoute:
Condition: CreateVPC
Type: AWS::EC2::Route
DependsOn: InternetGWAttachment
Properties:
RouteTableId: !Ref InternetRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGW
# Subnets public/private
PublicSubnet:
Condition: CreateVPC
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.0.0/24
MapPublicIpOnLaunch: false
VpcId: !Ref VPC
PublicRouteAssociation:
Condition: CreateVPC
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref InternetRouteTable
SubnetId: !Ref PublicSubnet
VSInstanceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: ec2.amazonaws.com
Sid: ''
Path: /
ManagedPolicyArns:
- !Sub 'arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess'
VSInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Roles: [ !Ref VSInstanceRole ]
RDPAccess:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow RDP connections from the outside world
VpcId: !If [CreateVPC, !Ref VPC, !Ref VPCId]
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3389
ToPort: 3389
CidrIp: !Ref AllowedCIDR
Description: AllowRDP
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 0
ToPort: 65535
CidrIp: 0.0.0.0/0
Description: AllowAll
VSInstance:
Type: AWS::EC2::Instance
DependsOn:
- VSInstanceWaitHandle
- VSCodeWaitHandle
- PowershellWaitHandle
- RiderWaitHandle
- PAWaitHandle
- A2CWaitHandle
- SCTWaitHandle
Properties:
InstanceType: !Ref InstanceType
ImageId: !Ref WindowsAMIId
IamInstanceProfile: !Ref VSInstanceProfile
KeyName: !Ref KeyName
NetworkInterfaces:
- AssociatePublicIpAddress: true
DeviceIndex: '0'
GroupSet: [ !GetAtt RDPAccess.GroupId ]
SubnetId: !If [CreateVPC, !Ref PublicSubnet, !Ref SubnetId]
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: 80
VolumeType: gp3
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-DevBox
UserData:
Fn::Base64:
!Sub |
<powershell>
$VerbosePreference = "Continue"
try {
$logPath = Join-Path "C:\ProgramData\Amazon\EC2-Windows\Launch\Log" UserDataTranscript.log
Start-Transcript -Path $logPath -IncludeInvocationHeader
Set-ExecutionPolicy Bypass -Scope Process -Force;
Write-Output "WaitHandles:"
Write-Output "RiderWaitHandle: ${ RiderWaitHandle }"
Write-Output "VSCodeWaitHandle: ${ VSCodeWaitHandle }"
Write-Output "PowershellWaitHandle: ${ PowershellWaitHandle }"
Write-Output "PAWaitHandle: ${ PAWaitHandle }"
Write-Output "A2CWaitHandle: ${ A2CWaitHandle }"
Write-Output "SCTWaitHandle: ${ SCTWaitHandle }"
Write-Output "VSInstanceWaitHandle: ${ VSInstanceWaitHandle }"
Write-Output "Setting default region"
if (-not $(Test-Path C:\Users\Administrator\.aws)) { mkdir C:\Users\Administrator\.aws }
$configPath = Join-Path C:\Users\Administrator\.aws config
Add-Content $configPath "[default]"
Add-Content $configPath "region = ${AWS::Region}"
Add-Content $configPath "output = json"
Write-Output "Installing Chocolatey"
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
Import-Module "$env:ChocolateyInstall\helpers\chocolateyInstaller.psm1"
choco feature enable -n allowGlobalConfirmation
# Git
Write-Output "Installing Git"
choco install git
# Microsoft Edge
Write-Output "Installing Microsoft Edge Browser"
choco install microsoft-edge
# Corretto JDK
choco install correttojdk
Write-Output "Installing .NET 3.1 SDK"
choco install dotnetcore-3.1-sdk
Write-Output "Installing hosting support for ASP.NET 3.1"
choco install dotnetcore-3.1-windowshosting
Write-Output "Installing .NET 5.0 SDK"
choco install dotnet-5.0-sdk
Write-Output "Installing hosting support for ASP.NET 5.0"
choco install dotnet-5.0-windowshosting
Write-Output "Installing .NET 6.0 SDK"
choco install dotnet-6.0-sdk
Write-Output "Installing hosting support for ASP.NET 6.0"
choco install dotnet-6.0-windowshosting
Write-Output "Installing .NET 4.7.1/4.7.2/4.8 support."
choco install netfx-4.7.1-devpack
choco install netfx-4.7.2-devpack
choco install netfx-4.8-devpack
# JetBrains Rider
Write-Output "Installing JetBrains Rider"
choco install jetbrains-rider
# Signal Rider Installation Completed
Invoke-WebRequest -Method PUT -Body $( ConvertTo-Json @{ "Status" = "SUCCESS"; "UniqueId"=[System.Guid]::NewGuid(); "Data"="Rider Installed." } ) -Uri "${ RiderWaitHandle }"
# AWS CLI
choco install awscli
# VS Code
Write-Output "Installing Visual Studio Code"
choco install vscode
# Signal VSCode Installation Completed
Invoke-WebRequest -Method PUT -Body $( ConvertTo-Json @{ "Status" = "SUCCESS"; "UniqueId"=[System.Guid]::NewGuid(); "Data"="VSCode Installed." } ) -Uri "${ VSCodeWaitHandle }"
# PowerShell Core
Write-Output "Installing latest version of PowerShell Core"
choco upgrade powershell-core
Write-Output "Creating PowerShell 7 shortcut"
$TargetFile = "`"${!env:ProgramFiles}\PowerShell\7\pwsh.exe`" -WorkingDirectory ~"
$ShortcutFile = "$env:Public\Desktop\PowerShell 7.lnk"
$WScriptShell = New-Object -ComObject WScript.Shell
$Shortcut = $WScriptShell.CreateShortcut($ShortcutFile)
$Shortcut.TargetPath = "${!env:ProgramFiles}\PowerShell\7\pwsh.exe"
$Shortcut.Description = "PowerShell 7"
$Shortcut.Save()
Write-Output "Refreshing environment variables"
refreshenv
# Install .NET Deployment Tool
Start-Process -Wait -FilePath "C:\Program Files\dotnet\dotnet.exe" -ArgumentList "tool","install","--global","aws.deploy.cli"
# Helper scripts
Write-Output "Downloading helper scripts"
if (-not $(Test-Path C:\AWS)) { mkdir C:\AWS }
Read-S3Object -Region ${QSS3BucketRegion} -BucketName ${QSS3BucketName} -Key ${QSS3KeyPrefix}docs/images/Background-Dark.bmp -File C:\Temp\Background-Dark.bmp
# Install IIS & ASP.NET Support
Install-WindowsFeature -name Web-Server -IncludeManagementTools
# Install ASP.NET 4.6
Install-WindowsFeature Web-Asp-Net45
# There is no PSDrive for HKU
reg.exe add "HKU\.DEFAULT\Control Panel" /v Wallpaper /t REG_SZ /d "C:\Temp\Background-Dark.bmp"
Read-S3Object -Region ${QSS3BucketRegion} -BucketName ${QSS3BucketName} -Key ${QSS3KeyPrefix}scripts/UpdateProfileCredentials.ps1 -File C:\AWS\UpdateProfileCredentials.ps1
Read-S3Object -Region ${QSS3BucketRegion} -BucketName ${QSS3BucketName} -Key ${QSS3KeyPrefix}scripts/SetupPwsh.ps1 -File C:\AWS\SetupPwsh.ps1
Write-Output "Installing AWS PowerShell Modules"
pwsh -File C:\AWS\SetupPwsh.ps1
Invoke-WebRequest -Method PUT -Body $( ConvertTo-Json @{ "Status" = "SUCCESS"; "UniqueId"=[System.Guid]::NewGuid(); "Data"="Downloaded PowerShell scripts." } ) -Uri "${ PowershellWaitHandle }"
# Install AWS Porting Assistant for .NET
Invoke-WebRequest -Method GET -Uri https://s3-us-west-2.amazonaws.com/aws.portingassistant.dotnet.download/latest/windows/Porting-Assistant-Dotnet.exe -OutFile C:\AWS\Porting-Assistant-Dotnet.exe
Start-Process -Wait -FilePath 'C:\AWS\Porting-Assistant-Dotnet.exe' -ArgumentList "/S"
# Signal PA Installation Completed
Invoke-WebRequest -Method PUT -Body $( ConvertTo-Json @{ "Status" = "SUCCESS"; "UniqueId"=[System.Guid]::NewGuid(); "Data"="VSCode Installed." } ) -Uri "${ PAWaitHandle }"
# Install App2Container
Invoke-WebRequest -Method GET -Uri https://app2container-release-us-east-1.s3.us-east-1.amazonaws.com/latest/windows/AWSApp2Container-installer-windows.zip -OutFile C:\AWS\AWSApp2Container-installer-windows.zip
Expand-Archive -Path C:\AWS\AWSApp2Container-installer-windows.zip -DestinationPath C:\AWS\App2Container\
C:\AWS\App2Container\install.ps1 -acceptEula $true
# Signal A2C Installation Completed
Invoke-WebRequest -Method PUT -Body $( ConvertTo-Json @{ "Status" = "SUCCESS"; "UniqueId"=[System.Guid]::NewGuid(); "Data"="VSCode Installed." } ) -Uri "${ A2CWaitHandle }"
# Install the Schema Conversion Tool
Invoke-WebRequest -Method GET -Uri https://s3.amazonaws.com/publicsctdownload/Windows/aws-schema-conversion-tool-1.0.latest.zip -OutFile C:\AWS\aws-schema-conversion-tool-1.0.latest.zip
Expand-Archive -Path C:\AWS\aws-schema-conversion-tool-1.0.latest.zip -DestinationPath C:\AWS\SCT\
Start-Process -Wait -FilePath msiexec.exe -ArgumentList "/i`"$(gci C:\AWS\SCT\*.msi|select -First 1 -ExpandProperty FullName)`"","/q","/lvx","SCT.log"
# Signal SCT Installation Completed
Invoke-WebRequest -Method PUT -Body $( ConvertTo-Json @{ "Status" = "SUCCESS"; "UniqueId"=[System.Guid]::NewGuid(); "Data"="VSCode Installed." } ) -Uri "${ SCTWaitHandle }"
# AWS Credentials
Write-Output "Creating task to update credentials from EC2 profile"
schtasks.exe /create /tn "Refresh AWS Credentials" /sc ONEVENT /ec "Microsoft-Windows-TerminalServices-LocalSessionManager/Operational" /MO "*[System[Provider[@Name='Microsoft-Windows-TerminalServices-LocalSessionManager'] and EventID=25]]" /tr "pwsh -WindowStyle Hidden -file C:\AWS\UpdateProfileCredentials.ps1"
$task = Get-ScheduledTask -TaskPath "\" -TaskName "Refresh AWS Credentials"
$triggers = $($( Get-ScheduledTask -TaskPath "\" -TaskName "Refresh AWS Credentials" ).Triggers + @( $(New-ScheduledTaskTrigger -AtLogOn)))
Set-ScheduledTask -TaskPath "\" -TaskName "Refresh AWS Credentials" -Trigger $triggers
# We need to restart after installing IIS
Write-Output "Completed! Restarting computer in 120 seconds..."
shutdown /r /t 120
Write-Output "Signaling successful completion to VSInstanceWaitHandle"
Invoke-WebRequest -Method PUT -Body $( ConvertTo-Json @{ "Status" = "SUCCESS"; "UniqueId"=[System.Guid]::NewGuid(); "Data"="Instance initialized successfully." } ) -Uri "${ VSInstanceWaitHandle }"
}
catch {
$itemName = $_.Exception.ItemName;
$errorMessage = $_.Exception.Message;
Write-Output "ERROR: $itemName - $errorMessage`nSignaling failure."
Invoke-WebRequest -Method PUT -Body $( ConvertTo-Json @{ "Status" = "FAILURE"; "UniqueId"=[System.Guid]::NewGuid(); "Data"=$errorMessage } ) -Uri "${ VSInstanceWaitHandle }"
}
</powershell>
VSInstanceWaitHandle:
Type: AWS::CloudFormation::WaitConditionHandle
PowershellWaitHandle:
Type: AWS::CloudFormation::WaitConditionHandle
VSCodeWaitHandle:
Type: AWS::CloudFormation::WaitConditionHandle
RiderWaitHandle:
Type: AWS::CloudFormation::WaitConditionHandle
PAWaitHandle:
Type: AWS::CloudFormation::WaitConditionHandle
A2CWaitHandle:
Type: AWS::CloudFormation::WaitConditionHandle
SCTWaitHandle:
Type: AWS::CloudFormation::WaitConditionHandle
InstanceWaitCondition:
Type: AWS::CloudFormation::WaitCondition
Properties:
Handle: !Ref VSInstanceWaitHandle
Timeout: '7200'
Count: 1
PSWaitCondition:
Type: AWS::CloudFormation::WaitCondition
Properties:
Handle: !Ref PowershellWaitHandle
Timeout: '3600'
Count: 1
VSCodeWaitCondition:
Type: AWS::CloudFormation::WaitCondition
Properties:
Handle: !Ref VSCodeWaitHandle
Timeout: '3600'
Count: 1
RiderWaitCondition:
Type: AWS::CloudFormation::WaitCondition
Properties:
Handle: !Ref RiderWaitHandle
Timeout: '3600'
Count: 1
PAWaitCondition:
Type: AWS::CloudFormation::WaitCondition
Properties:
Handle: !Ref PAWaitHandle
Timeout: '4500'
Count: 1
A2CWaitCondition:
Type: AWS::CloudFormation::WaitCondition
Properties:
Handle: !Ref A2CWaitHandle
Timeout: '4500'
Count: 1
SCTWaitCondition:
Type: AWS::CloudFormation::WaitCondition
Properties:
Handle: !Ref SCTWaitHandle
Timeout: '5400'
Count: 1
Outputs:
VSInstanceId:
Description: VS Instance ID
Value: !Ref VSInstance
VSInstanceAddress:
Description: VS Instance DNS Name
Value: !GetAtt VSInstance.PublicDnsName