Skip to content

Instantly share code, notes, and snippets.

@Thong-Tran
Last active May 27, 2022 11:09
Show Gist options
  • Save Thong-Tran/190e01661515476a24c231edb38ef992 to your computer and use it in GitHub Desktop.
Save Thong-Tran/190e01661515476a24c231edb38ef992 to your computer and use it in GitHub Desktop.
Set up ec2 with pm2, nginx

Deploy EC2 windows server core with nginx, nodejs

Note:

  • setup-ec2-windows.ps1 doesn't have SSH's setup script because of some bug. You need to set it up manually after deployment

Deploy stack

  • Create file opts.yml with options:

    • MyIP: ip for access to server
    • KeyName: aws key pair for ec2
  • Run serverless deploy

  • Log of setup-ec2-windows.ps1 script can view in C:\ProgramData\Amazon\EC2-Windows\Launch\Log\UserdataExecution.log

Setup SSH

  • Connect to ec2 via RDC
  • Run these commands
# Instal ssh
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
Set-Service -Name ssh-agent -StartupType 'Automatic'
Set-Service -Name sshd -StartupType 'Automatic'

# check install
Get-WindowsCapability -Online | ? Name -like 'OpenSSH*'
Start-Service sshd
Start-Service ssh-agent

# check firewall
Get-NetFirewallRule -Name *ssh*
  • For connect server without password:

    • Generate new key in your device
    ssh-keygen -t rsa-sha2-512 -f $env:USERPROFILE\.ssh\ec2-windows-key
    • Add your public key (ec2-windows-key.pub) to C:\Users\Administrator\.ssh\authorized_keys
    • Comment out the following lines in C:\ProgramData\ssh\sshd_config file. Then restart the sshd service Restart-Service sshd
    #Match Group administrators
    #       AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
    

Active WSL

Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
Invoke-WebRequest -Uri https://aka.ms/wsl-ubuntu -OutFile Ubuntu.zip -UseBasicParsing
Expand-Archive .\Ubuntu.zip c:\ubuntu
c:\ubuntu\ubuntu.exe

If the pm2 service cannot be restarted or run

  • Remove service pm2-service-uninstall
  • Recheck it wmic service where 'name like "%pm2serv.exe%"' get /value
  • Remove root pm2 folder Remove-Item D:\pm2
  • Reinstall it pm2-service-install -n pm2-serv

Send more metric to CloudWatch

  • Create and attach role have 2 polices: CloudWatchAgentServerPolicy, AmazonSSMManagedInstanceCore to ec2
  • Install cloudwatch agent curl https://s3.amazonaws.com/amazoncloudwatch-agent/windows/amd64/latest/amazon-cloudwatch-agent.msi -OutFile amazon-cloudwatch-agent.msi
  • Install msi file msiexec /i amazon-cloudwatch-agent.msi
  • Move to cloudwatch folder cd 'C:\Program Files\Amazon\AmazonCloudWatchAgent\'
  • Start agent ./amazon-cloudwatch-agent-ctl -a start

Set up certbot SSL

curl https://dl.eff.org/certbot-beta-installer-win32.exe  -OutFile certbot-beta-installer-win32.exe
Start-Process -Wait -FilePath "certbot-beta-installer-win32.exe" -ArgumentList '/S','/v','/qn' -passthru
cd 'C:\Program Files (x86)\Certbot\bin'
.\certbot certonly --webroot

cp C:\Certbot\live\yourwebsite.com\fullchain.pem D:\nginx\nginx-1.18.0\
cp C:\Certbot\live\yourwebsite.com\privkey.pem D:\nginx\nginx-1.18.0\

# test re-new SSL
.\certbot renew --dry-run
service: windows-server
provider:
name: aws
stackName: ${self:service}-${self:provider.stage}
stage: ${opt:stage, 'prod'}
region: ap-northeast-1
custom:
opts: ${file(./opts.yml)}
resources:
Resources:
WebServerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Enable HTTP access via port 80, SSH access, remote connection RDC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp: ${self:custom.opts.MyIP}
- IpProtocol: tcp
FromPort: '3389'
ToPort: '3389'
CidrIp: ${self:custom.opts.MyIP}
WebServer:
Type: AWS::EC2::Instance
Properties:
ImageId: "ami-0e36ba79dbd0cbe89" # Windows Server 1909 English-Core-Base
KeyName: ${self:custom.opts.KeyName}
InstanceType: t2.small
SecurityGroups:
- Ref: WebServerSecurityGroup
BlockDeviceMappings:
- DeviceName: "/dev/sdm"
Ebs:
VolumeType: "gp2"
DeleteOnTermination: "false"
VolumeSize: "40"
# Add auto setup script
UserData:
Fn::Base64: |
<powershell>
${file(./scripts/setup-ec2-windows.ps1)}
</powershell>
Outputs:
ServerId:
Description: InstanceId of the newly created EC2 instance
Value: !Ref 'WebServer'
ServerURL:
Value: !GetAtt WebServer.PublicDnsName
# Stop script on the first error
$ErrorActionPreference = "Stop"
echo 'Mount EBS'
$Disk = Get-Disk 1
$Disk | Initialize-Disk -PartitionStyle MBR
$Disk | New-Partition -UseMaximumSize -MbrType IFS
$Partition = Get-Partition -DiskNumber $Disk.Number
$Partition | Format-Volume -FileSystem NTFS -Confirm:$false
$Partition | Add-PartitionAccessPath -AccessPath "D:\"
echo 'Make powershell is default shell'
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" -Name DefaultShell -Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -PropertyType String -Force
echo 'Remove Windows-Defender'
Remove-WindowsFeature Windows-Defender
echo 'Install chocolatey'
Set-ExecutionPolicy Bypass -Scope Process -Force;
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072;
iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))
echo 'Install git, nodejs'
choco install -y git
choco install -y nodejs
# Reset $env:Path
$env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User")
echo 'Install Process Manager'
npm install -g pm2@latest
npm install -g git+https://github.com/jon-hall/pm2-windows-service.git
echo 'Install nginx'
netsh advfirewall firewall add rule name="http for server" dir=in action=allow protocol=TCP localport=80
netsh advfirewall firewall add rule name="https for server" dir=in action=allow protocol=TCP localport=443
Invoke-WebRequest http://nginx.org/download/nginx-1.18.0.zip -OutFile nginx-1.18.0.zip
Expand-Archive .\nginx-1.18.0.zip d:\nginx
echo 'setup pm2'
[System.Environment]::SetEnvironmentVariable('PM2_HOME', 'D:\pm2', [System.EnvironmentVariableTarget]::Machine)
$env:PM2_HOME = 'D:\pm2'
function Set-ServiceAcctCreds
{
param([string] $serviceName, [string] $newAcct, [string] $newPass)
$filter = "Name='$serviceName'"
$tries = 0
while (($service -eq $null -and $tries -le 3)) {
if ($tries -ne 0) {
sleep 2
}
$service = Get-WMIObject -namespace "root\cimv2" -class Win32_Service -Filter $filter
$tries = $tries + 1
}
if ($service -eq $null) {
throw "Could not find '$serviceName' service"
}
$service.Change($null,$null,$null,$null,$null,$null,$newAcct,$newPass)
$service.StopService()
while ($service.Started) {
sleep 2
$service = Get-WMIObject -namespace "root\cimv2" -class Win32_Service -Filter $filter
}
$service.StartService()
}
pm2-service-install -n pm2-serv --unattended
# Changing PM2 to run as LOCAL SERVICE
# Cause the error could not be logged
# Set-ServiceAcctCreds -serviceName "pm2.exe" -newAcct "NT AUTHORITY\LocalService" -newPass ""
# Rotate logs
pm2 install pm2-logrotate
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment