├── templates ├── ingress_security_rules.yaml.j2 └── egress_security_rules.yaml.j2 ├── teardown.yaml ├── README.md ├── setup.yaml └── sample.yaml /templates/ingress_security_rules.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # Copyright (c) 2020, 2023 Oracle and/or its affiliates. 3 | # This software is made available to you under the terms of the GPL 3.0 license or the Apache 2.0 license. 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | # Apache License v2.0 6 | # See LICENSE.TXT for details. 7 | instance_ingress_security_rules: 8 | # Allow incoming SSH connections 9 | - source: "{{ quad_zero_route }}" 10 | protocol: "{{ TCP_protocol }}" 11 | tcp_options: 12 | destination_port_range: 13 | min: {{ SSH_port }} 14 | max: {{ SSH_port }} 15 | 16 | -------------------------------------------------------------------------------- /templates/egress_security_rules.yaml.j2: -------------------------------------------------------------------------------- 1 | --- 2 | # Copyright (c) 2020, 2023 Oracle and/or its affiliates. 3 | # This software is made available to you under the terms of the GPL 3.0 license or the Apache 2.0 license. 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | # Apache License v2.0 6 | # See LICENSE.TXT for details. 7 | instance_egress_security_rules: 8 | # Allow ssh connections outside 9 | - destination: "{{ quad_zero_route }}" 10 | protocol: "{{ TCP_protocol }}" 11 | tcp_options: 12 | destination_port_range: 13 | min: {{ SSH_port }} 14 | max: {{ SSH_port }} 15 | 16 | -------------------------------------------------------------------------------- /teardown.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Copyright (c) 2020, 2023 Oracle and/or its affiliates. 3 | # This software is made available to you under the terms of the GPL 3.0 license or the Apache 2.0 license. 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | # Apache License v2.0 6 | # See LICENSE.TXT for details. 7 | 8 | - name: Terminate the instance 9 | oci_compute_instance: 10 | id: "{{ instance_id }}" 11 | state: absent 12 | 13 | - name: Delete the subnet 14 | oci_network_subnet: 15 | id: "{{ instance_subnet_id }}" 16 | state: absent 17 | 18 | - name: Delete the security list 19 | oci_network_security_list: 20 | id: "{{ instance_security_list_ocid }}" 21 | state: absent 22 | 23 | - name: Delete the route table 24 | oci_network_route_table: 25 | id: "{{ rt_id }}" 26 | state: absent 27 | 28 | - name: Delete the Internet Gateway 29 | oci_network_internet_gateway: 30 | id: "{{ ig_id }}" 31 | state: absent 32 | 33 | - name: Delete the VCN 34 | oci_network_vcn: 35 | vcn_id: "{{ vcn_id }}" 36 | state: absent 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | 3 | This sample shows how a public compute instance can be [launched](https://docs.us-phoenix-1.oraclecloud.com/Content/Compute/Tasks/launchinginstance.htm) and [accessed](https://docs.us-phoenix-1.oraclecloud.com/Content/Compute/Tasks/accessinginstance.htm) from the internet using SSH, through OCI ansible cloud modules. 4 | 5 | The sample 6 | - generates a temporary host-specific SSH key-pair 7 | - specifies the public key from that key-pair to connect to the instance during instance launch and 8 | - demonstrates how the newly launched instance can be connected to using SSH. 9 | 10 | # Instructions 11 | 12 | To run the sample, after ensuring that you have the pre-requisites for OCI ansible cloud modules, please provide values (that are specific to your tenancy) for the following variables in the `vars` section of `sample.yaml`: 13 | - instance_ad 14 | - instance_compartment 15 | - instance_image # provide an OL image 16 | 17 | Additionally, you can set the `SAMPLE_PUBLIC_SSH_KEY` env variable 18 | to set the public ssh key for the compute instance. 19 | For example: 20 | > export SAMPLE_PUBLIC_SSH_KEY="ssh-rsa AAA.................................xyz" 21 | 22 | This must be the default ssh key on your system. 23 | You can run the following command to set the environment variable: 24 | > export SAMPLE_PUBLIC_SSH_KEY=\`cat ~/.ssh/id_rsa.pub\` 25 | 26 | 27 | If this is not set, the sample will generate a new key-pair every run anyway. 28 | -------------------------------------------------------------------------------- /setup.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Copyright (c) 2020, 2023 Oracle and/or its affiliates. 3 | # This software is made available to you under the terms of the GPL 3.0 license or the Apache 2.0 license. 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | # Apache License v2.0 6 | # See LICENSE.TXT for details. 7 | 8 | - name: Check pre-requisites 9 | fail: 10 | msg: "Environment variable {{item}} not set. Please declare an environment variable with an appropriate value for the sample to work." 11 | when: item not in ansible_env 12 | with_items: 13 | - "SAMPLE_COMPARTMENT_OCID" 14 | - "SAMPLE_IMAGE_OCID" 15 | - "SAMPLE_AD_NAME" 16 | 17 | - block: 18 | - name: Create a temporary directory to house a temporary SSH keypair we will later use to connect to instance 19 | tempfile: 20 | state: directory 21 | suffix: cert 22 | register: result 23 | - set_fact: 24 | temp_certificates_path: "{{ result.path }}" 25 | - name: Generate a Private Key 26 | openssl_privatekey: 27 | path: "{{ temp_certificates_path }}/private_key.pem" 28 | type: RSA 29 | size: 2048 30 | - set_fact: 31 | my_test_public_key: "{{ temp_certificates_path }}/public_key.pem" 32 | - name: Generate a Public Key 33 | openssl_publickey: 34 | path: "{{ my_test_public_key }}" 35 | privatekey_path: "{{ temp_certificates_path }}/private_key.pem" 36 | format: OpenSSH 37 | when: "'SAMPLE_PUBLIC_SSH_KEY' not in ansible_env" 38 | 39 | - name: Create a VCN 40 | oci_network_vcn: 41 | compartment_id: "{{ instance_compartment }}" 42 | display_name: "{{ vcn_name }}" 43 | cidr_block: "{{ vcn_cidr_block }}" 44 | dns_label: "{{ vcn_dns_label }}" 45 | register: result 46 | - set_fact: 47 | vcn_id: "{{ result.vcn.id }}" 48 | 49 | - name: Create a new Internet Gateway 50 | oci_network_internet_gateway: 51 | compartment_id: "{{ instance_compartment }}" 52 | vcn_id: "{{ vcn_id }}" 53 | name: "{{ ig_name }}" 54 | is_enabled: 'yes' 55 | state: 'present' 56 | register: result 57 | - set_fact: 58 | ig_id: "{{ result.internet_gateway.id }}" 59 | 60 | - name: Create route table to connect internet gateway to the VCN 61 | oci_network_route_table: 62 | compartment_id: "{{ instance_compartment }}" 63 | vcn_id: "{{ vcn_id }}" 64 | name: "{{ route_table_name }}" 65 | route_rules: "{{ route_table_rules }}" 66 | state: 'present' 67 | register: result 68 | - set_fact: 69 | rt_id: "{{ result.route_table.id }}" 70 | 71 | # Create a security list for allowing access to public instance 72 | # Use a jinja2 template of the ingress and egress security rules to generate 73 | # a templated version of the final rules. 74 | - name: create ingress rules yaml body 75 | template: src=./templates/ingress_security_rules.yaml.j2 dest=/tmp/instance_ingress_security_rules.yaml 76 | - name: create egress yaml body 77 | template: src=./templates/egress_security_rules.yaml.j2 dest=/tmp/instance_egress_security_rules.yaml 78 | # Load the variables defined in the generated files 79 | - name: load the variables defined in the ingress rules yaml body 80 | include_vars: 81 | file: /tmp/instance_ingress_security_rules.yaml 82 | name: loaded_ingress 83 | - name: print loaded_ingress 84 | debug: 85 | msg: "loaded ingress is {{loaded_ingress}}" 86 | - name: load the variables defined in the egress rules yaml body 87 | include_vars: 88 | file: /tmp/instance_egress_security_rules.yaml 89 | name: loaded_egress 90 | - name: print loaded_egress 91 | debug: 92 | msg: "loaded egress is {{loaded_egress}}" 93 | - name: Create a security list for allowing access to public instance 94 | oci_network_security_list: 95 | name: "{{ securitylist_name }}" 96 | compartment_id: "{{ instance_compartment }}" 97 | vcn_id: '{{ vcn_id }}' 98 | ingress_security_rules: "{{ loaded_ingress.instance_ingress_security_rules }}" 99 | egress_security_rules: "{{ loaded_egress.instance_egress_security_rules }}" 100 | register: result 101 | - set_fact: 102 | instance_security_list_ocid: "{{ result.security_list.id }}" 103 | 104 | - name: Create a subnet to host the public instance. Link security_list and route_table. 105 | oci_network_subnet: 106 | availability_domain: "{{ instance_ad }}" 107 | cidr_block: "{{ subnet_cidr }}" 108 | compartment_id: "{{ instance_compartment }}" 109 | display_name: "{{ subnet_name }}" 110 | prohibit_public_ip_on_vnic: false 111 | route_table_id: "{{ rt_id }}" 112 | security_list_ids: [ "{{ instance_security_list_ocid }}" ] 113 | vcn_id: '{{ vcn_id }}' 114 | dns_label: "{{ subnet_dns_label }}" 115 | register: result 116 | - set_fact: 117 | instance_subnet_id: "{{ result.subnet.id }}" 118 | -------------------------------------------------------------------------------- /sample.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Copyright (c) 2020, 2023 Oracle and/or its affiliates. 3 | # This software is made available to you under the terms of the GPL 3.0 license or the Apache 2.0 license. 4 | # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 | # Apache License v2.0 6 | # See LICENSE.TXT for details. 7 | 8 | - name: Launch a compute instance and connect to it using SSH 9 | hosts: localhost 10 | collections: 11 | - oracle.oci 12 | vars: 13 | # common networking definitions 14 | quad_zero_route: "0.0.0.0/0" 15 | TCP_protocol: "6" 16 | SSH_port: "22" 17 | 18 | vcn_name: "ansible-sample-launch-compute-instance-vcn" 19 | vcn_cidr_block: "10.0.0.0/16" 20 | vcn_dns_label: "samplelcs" 21 | 22 | ig_name: "myinternetgatewayformytestvcn" 23 | 24 | route_table_name: "myroutetable" 25 | # route all internet access to our Internet Gateway 26 | route_table_rules: 27 | - cidr_block: "{{ quad_zero_route }}" 28 | network_entity_id: "{{ ig_id }}" 29 | 30 | 31 | subnet_cidr: "10.0.0.48/28" 32 | subnet_name: "mytestsubnet" 33 | subnet_dns_label: "mytestsubnet" 34 | 35 | securitylist_name: "mysecuritylist" 36 | 37 | instance_shape: "{{ lookup('env', 'SAMPLE_INSTANCE_SHAPE') | default('VM.Standard.A1.Flex', true) }}" 38 | instance_hostname: "mytestinstance" 39 | 40 | ######################################### 41 | # Tenancy specific configuration 42 | # *Note* - Override the following variables based on your tenancy 43 | # or set a valid value for the corresponding environment variable 44 | ######################################### 45 | instance_ad: "{{ lookup('env', 'SAMPLE_AD_NAME') }}" 46 | instance_compartment: "{{ lookup('env', 'SAMPLE_COMPARTMENT_OCID') }}" 47 | # provide an "OL" image 48 | # find OL image ocids per region here: https://docs.cloud.oracle.com/iaas/images/image/501c6e22-4dc6-4e99-b045-cae47aae343f/ 49 | instance_image: "{{ lookup('env', 'SAMPLE_IMAGE_OCID') }}" 50 | 51 | tasks: 52 | - block: 53 | - import_tasks: setup.yaml 54 | 55 | - set_fact: 56 | SSH_AUTHORIZED_KEYS: "{{ lookup('env', 'SAMPLE_PUBLIC_SSH_KEY') if 'SAMPLE_PUBLIC_SSH_KEY' in ansible_env else lookup('file', my_test_public_key ) }}" 57 | 58 | - name: Launch an instance 59 | oci_compute_instance: 60 | availability_domain: "{{ instance_ad }}" 61 | compartment_id: "{{ instance_compartment }}" 62 | name: "my_test_instance" 63 | image_id: "{{ instance_image }}" 64 | shape: "{{ instance_shape }}" 65 | shape_config: 66 | ocpus: 1 67 | memory_in_gbs: 6 68 | create_vnic_details: 69 | assign_public_ip: True 70 | hostname_label: "{{ instance_hostname }}" 71 | subnet_id: "{{ instance_subnet_id }}" 72 | metadata: 73 | ssh_authorized_keys: "{{ SSH_AUTHORIZED_KEYS }}" 74 | register: result 75 | 76 | - name: Print instance details 77 | debug: 78 | msg: "Launched a new instance {{ result }}" 79 | - set_fact: 80 | instance_id: "{{result.instance.id }}" 81 | 82 | - name: Get the VNIC attachment details of instance 83 | oci_compute_vnic_attachment_facts: 84 | compartment_id: "{{ instance_compartment }}" 85 | instance_id: "{{ instance_id }}" 86 | register: result 87 | 88 | - name: Get details of the VNIC 89 | oci_network_vnic_facts: 90 | id: "{{ result.vnic_attachments[0].vnic_id }}" 91 | register: result 92 | - set_fact: 93 | instance_public_ip: "{{result.vnic.public_ip}}" 94 | 95 | - name: Print the public ip of the newly launched instance 96 | debug: 97 | msg: "Public IP of launched instance {{ instance_public_ip }}" 98 | 99 | - name: Wait (upto 10 minutes) for port 22 to become open 100 | wait_for: 101 | port: 22 102 | host: '{{ instance_public_ip }}' 103 | state: started 104 | delay: 10 105 | timeout: 600 106 | vars: 107 | ansible_connection: local 108 | 109 | - set_fact: 110 | # Use "ubuntu" user as this is an OL image 111 | # Disable SSH's strict host key checking just for this one command invocation 112 | ssh_command_with_generated_key: ssh -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking=no" -i {{ temp_certificates_path }}/private_key.pem ubuntu@{{ instance_public_ip }} uname -a 113 | when: "'SAMPLE_PUBLIC_SSH_KEY' not in ansible_env" 114 | 115 | - set_fact: 116 | # Use "ubuntu" user as this is an OL image 117 | # Disable SSH's strict host key checking just for this one command invocation 118 | ssh_command_with_custom_key: ssh -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking=no" ubuntu@{{ instance_public_ip }} uname -a 119 | when: "'SAMPLE_PUBLIC_SSH_KEY' in ansible_env" 120 | 121 | - name: Attempt a ssh connection to the newly launched instance 122 | command: "{{ ssh_command_with_custom_key if 'SAMPLE_PUBLIC_SSH_KEY' in ansible_env else ssh_command_with_generated_key }}" 123 | retries: 3 124 | delay: 5 125 | register: result 126 | until: result.rc == 0 127 | 128 | - name: Print SSH response from launched instance 129 | debug: 130 | msg: "SSH response from instance -> {{ result.stdout_lines }}" 131 | 132 | - import_tasks: teardown.yaml 133 | 134 | rescue: 135 | 136 | - import_tasks: teardown.yaml 137 | --------------------------------------------------------------------------------