├── .gitignore ├── .tmux.conf ├── .vimrc ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── aml2userdata.sh ├── config └── deploy.ini ├── deploy.sh ├── images ├── screenshot.png ├── step_1_specify_template.png ├── step_2_specify_stack_details.png ├── step_3_configure_stack_options.png └── step_4_review.png ├── stm.sh └── templates └── ec2.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /.tmux.conf: -------------------------------------------------------------------------------- 1 | # Change prefix key (also useful to map control to caps-lock) 2 | set -g prefix C-a 3 | 4 | # update window splitting to something more intuitive 5 | bind / split-window -h -c '#{pane_current_path}' 6 | bind - split-window -v -c '#{pane_current_path}' 7 | 8 | # After updates simply prefix-r to update tmux config 9 | bind r source-file ${HOME}/.tmux.conf \; display 'Reloaded!' 10 | 11 | # Set scroll-back buffer options 12 | set-option -g history-limit 10000 13 | setw -g mode-keys vi 14 | unbind-key [ 15 | bind-key Escape copy-mode 16 | 17 | # Increase time for pressing correct key after prefix 18 | set -s escape-time 1 19 | 20 | # resize panes with vim movement keys 21 | bind -r H resize-pane -L 5 22 | bind -r J resize-pane -D 5 23 | bind -r K resize-pane -U 5 24 | bind -r L resize-pane -R 5 25 | 26 | # Smart pane switching with awareness of Vim splits. 27 | # See: https://github.com/christoomey/vim-tmux-navigator 28 | is_vim="ps -o state= -o comm= -t '#{pane_tty}' \ 29 | | grep -iqE '^[^TXZ ]+ +(\\S+\\/)?g?(view|n?vim?x?)(diff)?$'" 30 | bind-key -n 'C-h' if-shell "$is_vim" 'send-keys C-h' 'select-pane -L' 31 | bind-key -n 'C-j' if-shell "$is_vim" 'send-keys C-j' 'select-pane -D' 32 | bind-key -n 'C-k' if-shell "$is_vim" 'send-keys C-k' 'select-pane -U' 33 | bind-key -n 'C-l' if-shell "$is_vim" 'send-keys C-l' 'select-pane -R' 34 | tmux_version='$(tmux -V | sed -En "s/^tmux ([0-9]+(.[0-9]+)?).*/\1/p")' 35 | if-shell -b '[ "$(echo "$tmux_version < 3.0" | bc)" = 1 ]' \ 36 | "bind-key -n 'C-\\' if-shell \"$is_vim\" 'send-keys C-\\' 'select-pane -l'" 37 | if-shell -b '[ "$(echo "$tmux_version >= 3.0" | bc)" = 1 ]' \ 38 | "bind-key -n 'C-\\' if-shell \"$is_vim\" 'send-keys C-\\\\' 'select-pane -l'" 39 | 40 | bind-key -T copy-mode-vi 'C-h' select-pane -L 41 | bind-key -T copy-mode-vi 'C-j' select-pane -D 42 | bind-key -T copy-mode-vi 'C-k' select-pane -U 43 | bind-key -T copy-mode-vi 'C-l' select-pane -R 44 | bind-key -T copy-mode-vi 'C-\' select-pane -l 45 | 46 | -------------------------------------------------------------------------------- /.vimrc: -------------------------------------------------------------------------------- 1 | call plug#begin('~/.vim/plugged') 2 | 3 | Plug 'Chiel92/vim-autoformat' 4 | Plug 'tmhedberg/SimpylFold' 5 | Plug 'preservim/nerdtree' 6 | Plug 'itchyny/lightline.vim' 7 | Plug 'zxqfl/tabnine-vim' 8 | Plug 'tpope/vim-commentary' 9 | Plug 'christoomey/vim-tmux-navigator' 10 | Plug 'jiangmiao/auto-pairs' 11 | Plug 'machakann/vim-highlightedyank' 12 | Plug 'morhetz/gruvbox' 13 | 14 | call plug#end() 15 | 16 | set shell=/bin/zsh 17 | 18 | " Setting the leader 19 | let mapleader="\" 20 | 21 | " Easier writing/quitting 22 | nnoremap w :w 23 | nnoremap x :x 24 | nnoremap q :q 25 | 26 | " set the clipboard 27 | set clipboard=unnamed 28 | 29 | " Copy to the system clipboard 30 | vnoremap y "+y 31 | 32 | " Code formatting with black 33 | " Apply formatter on save 34 | au BufWrite * :Autoformat 35 | 36 | " Disable fallback to vim's indent file, retabbing and removing trailing whitespace 37 | let g:autoformat_autoindent = 0 38 | let g:autoformat_retab = 0 39 | let g:autoformat_remove_trailing_spaces = 0 40 | 41 | " Use better code folding 42 | let g:SimpylFold_docstring_preview = 1 43 | 44 | " Use black formatter 45 | let g:formatters_python=['black'] 46 | 47 | " Vim pane switching 48 | map j 49 | map k 50 | map h 51 | map l 52 | 53 | " Hybrid line numbers 54 | :set number relativenumber 55 | 56 | :augroup numbertoggle 57 | : autocmd! 58 | : autocmd BufEnter,FocusGained,InsertLeave * set relativenumber 59 | : autocmd BufLeave,FocusLost,InsertEnter * set norelativenumber 60 | :augroup END 61 | 62 | " Searching within a file 63 | set hlsearch " highlight search results 64 | set incsearch " show search results as you type 65 | nnoremap :nohlsearch:echo " Clear search results with enter 66 | 67 | " Nerdtree open/close toggle 68 | map :NERDTreeToggle 69 | 70 | 71 | " Tab spacing 72 | set tabstop=4 73 | set softtabstop=4 74 | set shiftwidth=4 75 | set smarttab 76 | set expandtab 77 | 78 | " Color 79 | let g:gruvbox_contrast_dark = 'hard' 80 | if exists('+termguicolors') 81 | let &t_8f = "\[38;2;%lu;%lu;%lum" 82 | let &t_8b = "\[48;2;%lu;%lu;%lum" 83 | endif 84 | let g:gruvbox_invert_selection='0' 85 | syntax enable 86 | silent! colorscheme gruvbox 87 | set background=dark 88 | 89 | " Additional vim options 90 | set encoding=utf-8 " Set encoding 91 | set ruler " Show line, column number 92 | set viminfo='20,<1000 " Increase the copy/paste-buffer 93 | set noerrorbells visualbell t_vb= " Get rid of bell sound on error 94 | set nowrap " Turn off text wrapping 95 | set colorcolumn=88 " Column number for vertical line 96 | set cursorline " Highlight the line of the cursor 97 | set t_Co=256 " Required for vim colorscheme show up in tmux 98 | -------------------------------------------------------------------------------- /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 | 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple setup Data Science development with tmux, zsh, and vim 2 | 3 | A simple guide to setting up basic Vim, Tmux, Zsh for the Deep Learning AMI 4 | Amazon Linux 2 for data scientists. 5 | 6 | After setting up the environment it should look something like this. 7 | 8 | ![images/screenshot.png](images/screenshot.png) 9 | 10 | There are three aspects to this setup: 11 | - Edit/navigate and run code in the same window simultaneously using 12 | [Tmux](https://github.com/tmux/tmux/wiki). 13 | - Develop code with [Vim](https://www.vim.org/), configured with popular 14 | plugins and sensible defaults. 15 | - Enable directory navigation and display git branch/status with [Oh My 16 | Zsh](https://ohmyz.sh/). 17 | 18 | ## Getting started 19 | 20 | ### Commandline Deployment 21 | > Need modify config/deploy.ini before running your commands 22 | 23 | git clone https://github.com/aws-samples/ec2-data-science-vim-tmux-zsh.git 24 | cd ec2-data-science-vim-tmux-zsh 25 | ./deploy.sh 26 | 27 | ### One click Deployment 28 | [![Launch Stack](https://s3.amazonaws.com/cloudformation-examples/cloudformation-launch-stack.png)](https://console.aws.amazon.com/cloudformation/home?#/stacks/new?stackName=data-science-dev-env&templateURL=https://raw.githubusercontent.com/aws-samples/ec2-data-science-vim-tmux-zsh/main/templates/ec2.yml) 29 | 30 | Upload the cloudformation template file at https://github.com/aws-samples/ec2-data-science-vim-tmux-zsh/blob/main/templates/ec2.yml 31 | 32 | ![images/step_1_specify_template.png](images/step_1_specify_template.png) 33 | 34 | Input your own parameters 35 | 36 | ![images/step_2_specify_stack_details.png](images/step_2_specify_stack_details.png) 37 | 38 | Proceed to create a stack 39 | 40 | ![images/step_3_configure_stack_options.png](images/step_3_configure_stack_options.png) 41 | 42 | ![images/step_4_review .png](images/step_4_review.png) 43 | -------------------------------------------------------------------------------- /aml2userdata.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Setting up an Amazon Linux 2 Instance with the Deep Learning AMI 3 | set -e -x 4 | 5 | get_dotfiles () { 6 | 7 | echo "(1/4): GETTING DOTFILES..." 8 | local DIR=/home/ec2-user 9 | git clone https://github.com/aws-samples/ec2-data-science-vim-tmux-zsh.git $DIR/dotfiles 10 | ln -s $DIR/dotfiles/.tmux.conf $DIR/.tmux.conf 11 | ln -s $DIR/dotfiles/.vimrc $DIR/.vimrc 12 | chown -R ec2-user:ec2-user $DIR/dotfiles $DIR/.vimrc $DIR/.tmux.conf 13 | 14 | } 15 | 16 | setup_vim () { 17 | 18 | echo "(2/4) SETTING UP VIM..." 19 | local DIR=/home/ec2-user 20 | # Install black for formatting 21 | pip3 install black 22 | 23 | # Install vim plug for package management 24 | curl -fLo $DIR/.vim/autoload/plug.vim --create-dirs \ 25 | https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim 26 | chown -R ec2-user:ec2-user $DIR/.vim 27 | # Install packages 28 | runuser -l ec2-user -c 'vim +PlugInstall +qall' 29 | 30 | } 31 | 32 | setup_tmux () { 33 | 34 | echo "(3/4) SETTING UP TMUX..." 35 | # Install tmux dependencies 36 | yum -y install ncurses-devel 37 | yum -y install libevent-devel 38 | 39 | # Get the latest version 40 | git clone https://github.com/tmux/tmux.git 41 | cd tmux 42 | sh autogen.sh 43 | ./configure && make install 44 | cd .. 45 | # Get a simple startup script 46 | mv /home/ec2-user/dotfiles/stm.sh /bin/stm 47 | chmod +x /bin/stm 48 | # Install htop 49 | yum -y install htop 50 | } 51 | 52 | setup_zsh () { 53 | 54 | echo "(4/4) SETTING UP ZSH..." 55 | local DIR=/home/ec2-user 56 | yum -y update && yum -y install zsh 57 | # Install oh-my-zsh 58 | wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh -O $DIR/install.sh 59 | chown -R ec2-user:ec2-user $DIR/install.sh 60 | cd $DIR 61 | echo pwd 62 | runuser -l ec2-user 'install.sh' 63 | # Change the default shell to zsh 64 | yum -y install util-linux-user 65 | chsh -s /bin/zsh ec2-user 66 | # Add conda to end of zshrc 67 | echo "source ~/.dlamirc" >> $DIR/.zshrc 68 | 69 | } 70 | 71 | get_dotfiles 72 | setup_vim 73 | setup_tmux 74 | setup_zsh 75 | -------------------------------------------------------------------------------- /config/deploy.ini: -------------------------------------------------------------------------------- 1 | SecurityGroups=sg-e88cea99 2 | Region=ap-southeast-1 3 | SubnetId=subnet-a9a058cf 4 | InstanceType=t2.large 5 | KeyName=mlmax-ap-southeast-1-davjosia 6 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -ex 2 | 3 | STACK_NAME=${1:-data-science-dev-env} 4 | REGION=${2:-ap-southeast-1} 5 | PROFILE=${3:-default} 6 | 7 | deploy () { 8 | 9 | local CMD="aws cloudformation --region=${REGION} --profile=${PROFILE}" 10 | 11 | ${CMD} deploy \ 12 | --stack-name ${STACK_NAME} \ 13 | --template-file ./templates/ec2.yml \ 14 | --parameter-overrides $(cat config/deploy.ini) \ 15 | --capabilities CAPABILITY_NAMED_IAM CAPABILITY_IAM CAPABILITY_AUTO_EXPAND 16 | } 17 | 18 | deploy 19 | -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/ec2-data-science-vim-tmux-zsh/75725833541e56cb7fd74c85067f69de578545b4/images/screenshot.png -------------------------------------------------------------------------------- /images/step_1_specify_template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/ec2-data-science-vim-tmux-zsh/75725833541e56cb7fd74c85067f69de578545b4/images/step_1_specify_template.png -------------------------------------------------------------------------------- /images/step_2_specify_stack_details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/ec2-data-science-vim-tmux-zsh/75725833541e56cb7fd74c85067f69de578545b4/images/step_2_specify_stack_details.png -------------------------------------------------------------------------------- /images/step_3_configure_stack_options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/ec2-data-science-vim-tmux-zsh/75725833541e56cb7fd74c85067f69de578545b4/images/step_3_configure_stack_options.png -------------------------------------------------------------------------------- /images/step_4_review.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/ec2-data-science-vim-tmux-zsh/75725833541e56cb7fd74c85067f69de578545b4/images/step_4_review.png -------------------------------------------------------------------------------- /stm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Simple script to start a tmux session. 3 | # 4 | # Functionality: Checks whether a tmux session is present. 5 | # If not, it creates one for you with the options provided. 6 | # 7 | # Example usage: 8 | # ./stm.sh # single window with two panes, one for editing code, one for execution. 9 | # ./stm.sh -j # two windows, the second one for a jupyter notebook. 10 | # ./stm.sh -m # two windows, the second one for monitoring with htop. 11 | # ./stm.sh -n # two windows, the second one for monitoring with htop and nvidia-smi 12 | 13 | 14 | CENV='python3' 15 | 16 | tmux has-session -t DEV 2>/dev/null 17 | if [ "$?" -eq 1 ] ; then 18 | # No session found 19 | echo "Starting tmux session $@" 20 | i=1 21 | tmux new-session -s DEV -n DEV -d 22 | tmux send-keys -t DEV:0 "source activate ${CENV}; clear" C-m 23 | tmux split-window -v -p 30 -t DEV:0 24 | tmux send-keys -t DEV:0.1 "source activate ${CENV}; clear" C-m 25 | 26 | for ARG in "$@"; do 27 | case $ARG in 28 | "-j" ) 29 | tmux new-window -n JUPYTER -t DEV:$i 30 | tmux send-keys -t DEV:$i "source activate ${CENV}; jupyter notebook" C-m 31 | ;; 32 | "-m" ) 33 | tmux new-window -n MONITOR -t DEV:$i 34 | tmux send-keys -t DEV:$i 'htop' C-m 35 | ;; 36 | "-n" ) 37 | tmux new-window -n MONITOR -t DEV:$i 38 | tmux send-keys -t DEV:$i 'watch -n0.5 nvidia-smi' C-m 39 | tmux split-window -v -p 60 -t DEV:$i 40 | tmux send-keys -t DEV:${i}.1 'htop' C-m 41 | ;; 42 | *) echo "The option $i is not supported" 43 | esac 44 | let i=$i+1 45 | done 46 | fi 47 | tmux attach -t DEV:0 48 | -------------------------------------------------------------------------------- /templates/ec2.yml: -------------------------------------------------------------------------------- 1 | --- 2 | AWSTemplateFormatVersion: "2010-09-09" 3 | Description: "This is a cloudformation template 4 | for defining a data science dev environment on EC2." 5 | Parameters: 6 | UserDataURL: 7 | Type: String 8 | Default: "https://raw.githubusercontent.com/aws-samples/ec2-data-science-vim-tmux-zsh/main/aml2userdata.sh" 9 | SecurityGroups: 10 | Type: CommaDelimitedList 11 | Default: sg- 12 | Region: 13 | Type: String 14 | Default: ap-southeast-1 15 | SubnetId: 16 | Type: String 17 | Default: subnet- 18 | InstanceType: 19 | Type: String 20 | Default: t2.large 21 | KeyName: 22 | Type: String 23 | Default: 24 | Mappings: 25 | RegionMap: 26 | us-east-1: 27 | AMI: ami-0f840415174c4a8e0 28 | us-east-2: 29 | AMI: ami-0a714e270d06489a9 30 | us-west-1: 31 | AMI: ami-09d540cb66f1315ee 32 | us-west-2: 33 | AMI: ami-0a20a878a1c1e5477 34 | ap-south-1: 35 | AMI: ami-094e7d6f3991224c6 36 | ap-northeast-1: 37 | AMI: ami-027229ac89dd9eb5d 38 | ap-northeast-2: 39 | AMI: ami-07d76242912d104e8 40 | ap-northeast-3: 41 | AMI: ami-0dde2f013096d1b2e 42 | ap-southeast-1: 43 | AMI: ami-0a6358c1d24b5b3a4 44 | ap-southeast-2: 45 | AMI: ami-00728fac07787e1c5 46 | ca-central-1: 47 | AMI: ami-069eac258dd27215c 48 | eu-central-1: 49 | AMI: ami-000d90ab744d79dbb 50 | eu-west-1: 51 | AMI: ami-0e032abfb10b0b80a 52 | eu-west-2: 53 | AMI: ami-09fb6eee0f4854e36 54 | eu-west-3: 55 | AMI: ami-01a63ba58ba1de1bc 56 | eu-north-1: 57 | AMI: ami-0fd52c77a8572ecfe 58 | sa-east-1: 59 | AMI: ami-093e3f53198605e7d 60 | Resources: 61 | MyEC2Instance: 62 | Type: "AWS::EC2::Instance" 63 | Properties: 64 | ImageId: !FindInMap [RegionMap, !Ref "AWS::Region", AMI] 65 | KeyName: !Ref KeyName 66 | SubnetId: !Ref SubnetId 67 | SecurityGroupIds: !Ref SecurityGroups 68 | Tags: 69 | - Key: Name 70 | Value: !Ref "AWS::StackName" 71 | InstanceType: !Ref InstanceType 72 | # lets add an ebs device when we create the instance 73 | BlockDeviceMappings: 74 | - DeviceName: /dev/xvdb 75 | Ebs: 76 | VolumeSize: 8 77 | VolumeType: "gp2" 78 | DeleteOnTermination: "true" 79 | UserData: 80 | Fn::Base64: 81 | !Sub | 82 | #!/bin/bash -xe 83 | curl -o aml2userdata.sh ${UserDataURL} 84 | chmod +x aml2userdata.sh 85 | ./aml2userdata.sh 86 | rm aml2userdata.sh 87 | 88 | 89 | Outputs: 90 | MyEC2InstanceSSH: 91 | Value: !Sub "ssh -i /${KeyName}.pem ec2-user@${MyEC2Instance.PublicIp}" 92 | MyEC2InstancePublicIP: 93 | Value: !GetAtt MyEC2Instance.PublicIp 94 | MyEC2InstancePrivateIP: 95 | Value: !GetAtt MyEC2Instance.PrivateIp 96 | MyEC2InstanceID: 97 | Value: !Ref MyEC2Instance 98 | --------------------------------------------------------------------------------