diff --git a/Ansible/01-Basics/lab1.yml b/Ansible/01-Basics/Code/lab1.yml similarity index 100% rename from Ansible/01-Basics/lab1.yml rename to Ansible/01-Basics/Code/lab1.yml diff --git a/Ansible/01-Basics/Guide.md b/Ansible/01-Basics/Guide.md index 1845650..b6a0132 100644 --- a/Ansible/01-Basics/Guide.md +++ b/Ansible/01-Basics/Guide.md @@ -153,7 +153,7 @@ localhost : ok=3 changed=1 unreachable=0 failed=0 s Congratulations, you have created your first playbook. Go to [Azure portal](https://portal.azure.com) to verify that you have created the resources. -> **CODE**: You can refer to [lab1.yml](lab1.yml) for a complete sample playbook. +> **CODE**: You can refer to [lab1.yml](Code/lab1.yml) for a complete sample playbook. ## Further readings diff --git a/Ansible/02-Variables/lab2.yml b/Ansible/02-Variables/Code/lab2.yml similarity index 100% rename from Ansible/02-Variables/lab2.yml rename to Ansible/02-Variables/Code/lab2.yml diff --git a/Ansible/02-Variables/Guide.md b/Ansible/02-Variables/Guide.md index 3ab51b6..55e871f 100644 --- a/Ansible/02-Variables/Guide.md +++ b/Ansible/02-Variables/Guide.md @@ -144,13 +144,11 @@ Expand to see how you can create a VM using SSH key to access the host. version: latest ``` - - You can go to [Azure portal](https://portal.azure.com) to verify that you have created the resources. -> **CODE**: To view all of the completed code for this part of the lab, go [here](lab2.yml). +> **CODE**: To view all of the completed code for this part of the lab, go [here](code/lab2.yml). ## Registering variables and working with conditionals @@ -227,6 +225,6 @@ mySubNet: "{{ myVnet }}Subnet" > - right clicking vars.yml and select **"Upload to Cloud Shell"**. > - in Cloud Shell terminal, move the file to ./clouddrive/ansible-playbooks/ by doing `mv vars.yml ./clouddrive/ansible-playbooks/` -> **CODE**: The [Code](../03-Helpers/Lab3/Code) folder in **Lab #3 - 03-Helpers** contains the sample playbook and vars.yml you can refer to. +> **CODE**: The [/Code/Code_vars_files](../03-Helpers/Code/Code-vars_files) folder in **Lab #3 - 03-Helpers** contains the sample playbook and vars.yml you can refer to. - Refer to [Ansible documentation](https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html) for more details and ways to use variables. \ No newline at end of file diff --git a/Ansible/03-Helpers/Lab3/Code/lab3.yml b/Ansible/03-Helpers/Code/Code-vars_files/lab3.yml similarity index 100% rename from Ansible/03-Helpers/Lab3/Code/lab3.yml rename to Ansible/03-Helpers/Code/Code-vars_files/lab3.yml diff --git a/Ansible/03-Helpers/Lab3/Code/vars.yml b/Ansible/03-Helpers/Code/Code-vars_files/vars.yml similarity index 100% rename from Ansible/03-Helpers/Lab3/Code/vars.yml rename to Ansible/03-Helpers/Code/Code-vars_files/vars.yml diff --git a/Ansible/03-Helpers/lab3.yml b/Ansible/03-Helpers/Code/lab3.yml similarity index 100% rename from Ansible/03-Helpers/lab3.yml rename to Ansible/03-Helpers/Code/lab3.yml diff --git a/Ansible/03-Helpers/Guide.md b/Ansible/03-Helpers/Guide.md index 01ce5a8..000cab4 100644 --- a/Ansible/03-Helpers/Guide.md +++ b/Ansible/03-Helpers/Guide.md @@ -144,6 +144,4 @@ Expand to see how you use loop to create the NSG rules ``` -> **CODE**: To view all of the completed codes, go to [lab3.yml](lab3.yml). - -**** \ No newline at end of file +> **CODE**: To view all of the completed codes, go to [lab3.yml](code/lab3.yml). \ No newline at end of file diff --git a/Ansible/04-Security/lab4-keyvault.yml b/Ansible/04-Security/Code/lab4-keyvault.yml similarity index 91% rename from Ansible/04-Security/lab4-keyvault.yml rename to Ansible/04-Security/Code/lab4-keyvault.yml index a5f8306..197be62 100644 --- a/Ansible/04-Security/lab4-keyvault.yml +++ b/Ansible/04-Security/Code/lab4-keyvault.yml @@ -5,7 +5,7 @@ --- - hosts: localhost vars: - keyvault_name: myKeyVault + keyvault_name: mykeyvaul secret_name: myVMSecret roles: - ./modules diff --git a/Ansible/04-Security/lab4.yml b/Ansible/04-Security/Code/lab4.yml similarity index 98% rename from Ansible/04-Security/lab4.yml rename to Ansible/04-Security/Code/lab4.yml index fe19c51..4f0de31 100644 --- a/Ansible/04-Security/lab4.yml +++ b/Ansible/04-Security/Code/lab4.yml @@ -7,7 +7,7 @@ myNetworkSecurityGroup: myNSG myNIC: myNIC myVM: myVM - mylist: + NSGlist: - name: Allow-SSH access: Allow protocol: Tcp @@ -67,7 +67,7 @@ destination_port_range: "{{ item.port }}" priority: "{{ item.priority }}" source_address_prefix: "{{ item.source_address_prefix }}" - loop: "{{ mylist }}" + loop: "{{ NSGlist }}" - name: Create virtual network interface card(NIC) azure_rm_networkinterface: diff --git a/Ansible/04-Security/modules/library/azure_rm_keyvaultsecret_info.py b/Ansible/04-Security/Code/modules/library/azure_rm_keyvaultsecret_info.py similarity index 100% rename from Ansible/04-Security/modules/library/azure_rm_keyvaultsecret_info.py rename to Ansible/04-Security/Code/modules/library/azure_rm_keyvaultsecret_info.py diff --git a/Ansible/04-Security/Guide.md b/Ansible/04-Security/Guide.md index c19e891..dfbe3f2 100644 --- a/Ansible/04-Security/Guide.md +++ b/Ansible/04-Security/Guide.md @@ -37,7 +37,7 @@ While preparing for this workshop, we discovered we do not have a module for ret We will be adding a new module called azure_rm_keyvaultsecret_info in future release. For the purpose of this lab, we will embed the new module using the `roles` keyword. (Roles will be explained in Lab 5.) -1. Copy `azure_rm_keyvaultsecret_info.py` in `modules/library`(/modules/library) to `clouddrive/ansible-playbooks/modules/library` +1. You will perform an extra step in this lab to copy the preview version of azure_rm_keyvaultsecret_info to your environment. Copy `azure_rm_keyvaultsecret_info.py` in `modules/library`(code/modules/library) to `clouddrive/ansible-playbooks/modules/library`. Make sure you keep the same folder structure `modules/library` 2. In the header, add: ```yml @@ -61,7 +61,7 @@ We will be adding a new module called azure_rm_keyvaultsecret_info in future rel version: 12345 ``` -#### Cheat Sheet: get secret +### Cheat Sheet: get secret
Expand to see how you can get a key vault secret @@ -80,19 +80,21 @@ Expand to see how you can get a key vault secret
-> **CODE**: To view all of the completed codes, go to [lab4-keyvault.yml](lab4-keyvault.yml). +> **CODE**: To view all of the completed codes, go to [lab4-keyvault.yml](Code/lab4-keyvault.yml). ## Replace hard coded password in previous lab Next, instead of hard coding the password and exposing a major security risk when provisioning the VM, you can now: -1. Make sure you have already copied `azure_rm_keyvaultsecret_info.py` in `modules/library`(/modules/library) to `clouddrive/ansible-playbooks/modules/library` -2. Run a task to retrieve the secret from Azure Key Vault. **Hint**: use `register output`. + +1. Make sure you have already copied `azure_rm_keyvaultsecret_info.py` in `modules/library`(/modules/library) to `clouddrive/ansible-playbooks/modules/library` +2. Run a task to retrieve the secret from Azure Key Vault. **Hint**: use `register output` 3. Use the value retrieved back from key vault to configure the admin password for the VM. **Hint**: use `output.secret.value` -#### Cheat Sheet: get secret +### Cheat Sheet: pass secret retrieved from Key Vault to the next task +
-Expand to see how you can get a key vault secret +Expand to see how you can pass secret retrieved from Key Vault to the next task ```yml @@ -119,7 +121,7 @@ Expand to see how you can get a key vault secret
-> **CODE:** To view all of the completed codes, go to [lab4.yml](lab4.yml). +> **CODE:** To view all of the completed codes, go to [lab4.yml](Code/lab4.yml). ## Further readings diff --git a/Ansible/05-Reusability/lab5/confignetwork.yml b/Ansible/05-Reusability/Code/confignetwork.yml similarity index 96% rename from Ansible/05-Reusability/lab5/confignetwork.yml rename to Ansible/05-Reusability/Code/confignetwork.yml index d15bf44..3d76279 100644 --- a/Ansible/05-Reusability/lab5/confignetwork.yml +++ b/Ansible/05-Reusability/Code/confignetwork.yml @@ -23,7 +23,7 @@ protocol: "{{ item.protocol }}" destination_port_range: "{{ item.port }}" priority: "{{ item.priority }}" - direction: " {{ item.direction }}" + direction: "{{ item.direction }}" source_address_prefix: "{{ item.source_address_prefix }}" loop: "{{ NSGlist }}" register: NSG diff --git a/Ansible/05-Reusability/lab5/createvm.yml b/Ansible/05-Reusability/Code/createvm.yml similarity index 77% rename from Ansible/05-Reusability/lab5/createvm.yml rename to Ansible/05-Reusability/Code/createvm.yml index 068c14a..6e9ad52 100644 --- a/Ansible/05-Reusability/lab5/createvm.yml +++ b/Ansible/05-Reusability/Code/createvm.yml @@ -10,10 +10,6 @@ name: "{{ myVM }}" admin_username: "testadmin" admin_password: "{{ output.secret.value }}" -# ssh_password_enabled: false -# ssh_public_keys: -# - path: /home/testadmin/.ssh/authorized_keys -# key_data: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" vm_size: Standard_B1ms network_interfaces: "{{ NIC.state.name }}" image: diff --git a/Ansible/05-Reusability/lab5/main.yml b/Ansible/05-Reusability/Code/main.yml similarity index 60% rename from Ansible/05-Reusability/lab5/main.yml rename to Ansible/05-Reusability/Code/main.yml index 266684a..9c8ca47 100644 --- a/Ansible/05-Reusability/lab5/main.yml +++ b/Ansible/05-Reusability/Code/main.yml @@ -1,6 +1,6 @@ - hosts: localhost vars_files: - - ./vars.yml + - vars/vars.yml roles: - ./modules gather_facts: no @@ -28,7 +28,21 @@ subnetAddPrefix: "172.16.10.0/24" myNetworkSecurityGroup: myNSG myNIC: myNIC - NSGlist: [{ name: 'Allow-SSH', access: 'Allow', protocol: 'Tcp', direction: 'Inbound', priority: '300', port: '22', source_address_prefix: 'Internet'},{ name: 'Allow-HTTP', access: 'Allow', protocol: 'Tcp', direction: 'Inbound', priority: '100', port: '80', source_address_prefix: 'Internet'}] + NSGlist: + - name: Allow-SSH + access: Allow + protocol: Tcp + direction: Inbound + priority: 300 + port: 22 + source_address_prefix: Internet + - name: Allow-HTTP + access: Allow + protocol: Tcp + direction: Inbound + priority: 100 + port: 80 + source_address_prefix: Internet include_tasks: ./confignetwork.yml # - name: Show NIC details @@ -48,7 +62,28 @@ subnetAddPrefix: "172.16.20.0/24" myNetworkSecurityGroup: myNSG-BE myNIC: myNIC-BE - NSGlist: [{ name: 'Allow-SSH', access: 'Allow', protocol: 'Tcp', direction: 'Inbound', priority: '200', port: '22', source_address_prefix: 'Internet'},{ name: 'Allow-MySQL-FE', access: 'Allow', protocol: 'Tcp', direction: 'Inbound', priority: '100', port: '3306', source_address_prefix: '172.16.10.0/24'}, { name: 'Deny-internet-all', access: 'Deny', protocol: 'Tcp', direction: 'Outbound', priority: '300', port: '*', source_address_prefix: '*'}] + NSGlist: + - name: Allow-SSH + access: Allow + protocol: Tcp + direction: Inbound + priority: 200 + port: 22 + source_address_prefix: Internet + - name: Allow-MySQL-FE + access: Allow + protocol: Tcp + direction: Inbound + priority: 100 + port: 3306 + source_address_prefix: 172.16.10.0/24 + - name: Deny-internet-all + access: Allow + protocol: Tcp + direction: Outbound + priority: 300 + port: "*" + source_address_prefix: "*" include_tasks: ./confignetwork.yml # - name: Show NIC details diff --git a/Ansible/05-Reusability/Code/modules/library/azure_rm_keyvaultsecret_info.py b/Ansible/05-Reusability/Code/modules/library/azure_rm_keyvaultsecret_info.py new file mode 100644 index 0000000..3e5fe7b --- /dev/null +++ b/Ansible/05-Reusability/Code/modules/library/azure_rm_keyvaultsecret_info.py @@ -0,0 +1,202 @@ +#!/usr/bin/python +# +# +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: azure_rm_keyvaultkey_info +version_added: "2.9" +short_description: Get Azure Key Vault secret facts. +description: + - Get facts of Azure Key Vault secret. + +options: + vault_uri: + description: + - Vault uri where the secret stored in. + required: True + type: str + name: + description: + - secret name. + required: True + type: str + version: + description: + - secret version. + default: current (latest) + type: str + + +extends_documentation_fragment: + - azure + +''' + +EXAMPLES = ''' + - name: Get latest version of a secret + azure_rm_keyvaultsecret_info:: + vault_uri: "https://myVault.vault.azure.net" + name: mysecret + + - name: Get a specific version of a secret + azure_rm_keyvaultsecret_info: + vault_uri: "https://myVault.vault.azure.net" + name: mysecret + version: 12345 +''' + + +from ansible.module_utils.azure_rm_common import AzureRMModuleBase + +try: + from azure.keyvault import KeyVaultClient, KeyVaultId, KeyVaultAuthentication, KeyId + from azure.keyvault.models import KeyAttributes, JsonWebKey + from azure.common.credentials import ServicePrincipalCredentials + from azure.keyvault.models.key_vault_error import KeyVaultErrorException + from msrestazure.azure_active_directory import MSIAuthentication +except ImportError: + # This is handled in azure_rm_common + pass + +def keyitem_to_dict(keyitem): + return dict( + id=keyitem.id, + value=keyitem.value, + version=KeyVaultId.parse_secret_id(keyitem.id).version, + manged=keyitem.managed, + attributes=dict( + enabled=keyitem.attributes.enabled, + not_before=keyitem.attributes.not_before, + expires=keyitem.attributes.expires, + created=keyitem.attributes.created, + updated=keyitem.attributes.updated, + recovery_level=keyitem.attributes.recovery_level + ) + ) + + +class AzureRMKeyVaultSecretInfo(AzureRMModuleBase): + + def __init__(self): + self.module_arg_spec = dict( + version=dict(type='str', default='current'), + name=dict(type='str', required=True), + vault_uri=dict(type='str', required=True), + tags=dict(type='list') + ) + + self.vault_uri = None + self.name = None + self.version = None + self.tags = None + + self.results = dict(changed=False) + self._client = None + + super(AzureRMKeyVaultSecretInfo, self).__init__(derived_arg_spec=self.module_arg_spec, + supports_check_mode=False, + supports_tags=False) + + def exec_module(self, **kwargs): + """Main module execution method""" + + for key in list(self.module_arg_spec.keys()): + if hasattr(self, key): + setattr(self, key, kwargs[key]) + + self._client = self.get_keyvault_client() + + versions = self.get_secret_versions() + # self.results['versions'] = versions + + if self.version == 'current': + self.version = versions[-1] + + secret = self.get_secret() + self.results['secret'] = keyitem_to_dict(secret) + return self.results + + def get_keyvault_client(self): + try: + self.log("Get KeyVaultClient from MSI") + credentials = MSIAuthentication(resource='https://vault.azure.net') + return KeyVaultClient(credentials) + except Exception: + self.log("Get KeyVaultClient from service principal") + + # Create KeyVault Client using KeyVault auth class and auth_callback + def auth_callback(server, resource, scope): + if self.credentials['client_id'] is None or self.credentials['secret'] is None: + self.fail('Please specify client_id, secret and tenant to access azure Key Vault.') + + tenant = self.credentials.get('tenant') + if not self.credentials['tenant']: + tenant = "common" + + authcredential = ServicePrincipalCredentials( + client_id=self.credentials['client_id'], + secret=self.credentials['secret'], + tenant=tenant, + cloud_environment=self._cloud_environment, + resource="https://vault.azure.net") + + token = authcredential.token + return token['token_type'], token['access_token'] + + return KeyVaultClient(KeyVaultAuthentication(auth_callback)) + + def get_secret(self): + ''' + Gets the properties of the specified key in key vault. + + :return: deserialized key state dictionary + ''' + self.log("Get the key {0}".format(self.name)) + + response = None + try: + response = self._client.get_secret(vault_base_url=self.vault_uri, secret_name=self.name, secret_version=self.version) + except KeyVaultErrorException as e: + self.log("Did not find the key vault secret {0}: {1}".format(self.name, str(e))) + return response + + def get_secret_versions(self): + ''' + Lists secret versions. + + :return: deserialized versions of secrets, includes key identifier, attributes and tags + ''' + self.log("Get the secret versions {0}".format(self.name)) + + versions = [] + try: + response = self._client.get_secret_versions(vault_base_url=self.vault_uri, secret_name=self.name) + + self.log("Response : {0}".format(response)) + + if response: + for item in response: + version = KeyVaultId.parse_secret_id(item.id).version + versions.append(version) + except KeyVaultErrorException as e: + self.log("Did not find secret versions {0} : {1}.".format(self.name, str(e))) + return versions + +def main(): + """Main execution""" + AzureRMKeyVaultSecretInfo() + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/Ansible/05-Reusability/lab5/myazure_rm.yml b/Ansible/05-Reusability/Code/myazure_rm.yml similarity index 100% rename from Ansible/05-Reusability/lab5/myazure_rm.yml rename to Ansible/05-Reusability/Code/myazure_rm.yml diff --git a/Ansible/05-Reusability/Code/vars/vars.yml b/Ansible/05-Reusability/Code/vars/vars.yml new file mode 100644 index 0000000..e86565b --- /dev/null +++ b/Ansible/05-Reusability/Code/vars/vars.yml @@ -0,0 +1,5 @@ +myResource_group: myResource_group +myVnet: myVnet +keyvault_name: mykeyvault +secret_name: myVMSecret +myVM: myVM \ No newline at end of file diff --git a/Ansible/05-Reusability/Guide.md b/Ansible/05-Reusability/Guide.md index 1a50713..55c9502 100644 --- a/Ansible/05-Reusability/Guide.md +++ b/Ansible/05-Reusability/Guide.md @@ -15,86 +15,167 @@ Running playbooks in Cloud Shell presents a couple of limitations: - you can only upload file to Cloud Shell one by one - after uploading to Cloud Shell, you need to perform an additional step to move files uploaded to the right location. -It can become cumbersome to continue running your playbook in Cloud Shell. +It can become cumbersome to continue running your playbooks in Cloud Shell. The VS Code Ansible extension provides better integration experience when you need to work with multiple files so we recommend running Ansible in the remote host via SSH. You can hit `F1` to copy files to remote host. 1. Hit `F1`; type "Ansible: copy folder to Remote Host" 1. Follow the prompt to provide the source directory -1. Select "Set up host" if this is the first time. Else select your remote host -1. Next specify the target folder +1. Select "Set up host" if this is the first time. Else select your remote host from the list of remote hosts. +1. Next, specify the target folder ## Reusing task(s) in your playbook You can use the [include](https://docs.ansible.com/ansible/latest/modules/include_module.html) and [include_tasks](https://docs.ansible.com/ansible/latest/modules/include_tasks_module.html#include-tasks-module) modules to dynamically include a task or a list of tasks. -Let's modify what you have done so far to create a multi-tier application. +Let's modify what you have done so far to create a network of multi-tier applications based on this [Azure CLI sample](https://docs.microsoft.com/en-us/azure/virtual-network/scripts/virtual-network-cli-sample-multi-tier-application). -Based on the Azure CLI sample, let's build a [network of multi-tier applications](https://docs.microsoft.com/en-us/azure/virtual-network/scripts/virtual-network-cli-sample-multi-tier-application). +- a virtual network (172.16.0.0/16) with front-end and back-end subnets +- two virtual machines: web and MySQL; one in each subnet -- a virtual network (172.16.0.0/16) with front-end (172.16.10.0/24) and back-end (176.16.20.0/24) subnets: - - Traffic to the front-end subnet is limited to HTTP and SSH - - Traffic to the back-end subnet is limited to MySQL, port 3306 and SSH. All outbound traffic from the back-end subnet should be blocked. -- two virtual machines, one in each subnet +![Sample multi-tier applications](../../Terraform/05%20-%20Reusability/Assets/labnet.png "Azure resources to be provisioned.") What you have done so far: - A VNet 172.16.0.0/16 -- A front-end subnet - 172.16.10.0/24 with NSG rules that allow: - - SSH traffic from the Internet to the front-end subnetSSH on port 22 - - HTTP traffic in from the Internet to the front-end subnet on port 80 -- And a VM in the front-end subnet that can act as the front-end web server +- A front-end subnet - 172.16.10.0/24 with NSG rules that: + - allows SSH traffic from the Internet to the front-end subnet on port 22 + - allows HTTP traffic in from the Internet to the front-end subnet on port 80 +- And a VM in the front-end subnet that acts as the front-end web server You need to add: - A backend subnet 172.16.20/24 with NSG rules that: - allows SSH traffic from the Internet to the front-end subnet on port 22 - allows MySQL traffic from the front-end subnet to the back-end subnet on port 3306 - blocks all outbound traffic from the back-end subnet to the Internet -- A VM in the back-end subnet. - -![Sample multi-tier applications](../../Terraform/05%20-%20Reusability/Assets/labnet.png "Azure resources to be provisioned.") +- A VM (MySQL) in the back-end subnet. Let's start by restructuring your playbook and break the tasks out into a few playbooks. -1. Move all common tasks related to subnet configuration to one YAML file say configurenetwork.yml so that you can use the same list of tasks to provision and configure the subnet, public IP etc. networking related tasks. +1. Move all common tasks related to network configuration to one YAML file say `configurenetwork.yml` so that you can use the same list of tasks to provision and configure the subnet, public IP etc. networking related tasks. + + ```yml + - name: Create a subset within the virtual network + azure_rm_subnet: + ... + register: subnet + + - name: Create public IP address + azure_rm_publicipaddress: + ... + register: publicIP + + - name: Create Network Security Group + azure_rm_securitygroup: + ... + loop: "{{ NSGlist }}" + register: NSG + + - name: Create virtual network interface card(NIC) with public IP + azure_rm_networkinterface: + ... + register: NIC + ``` + + >**Note**: you can register a variable after each task and use "debug" to show the output. By doing so, you can also refer to value in the Azure resource by using e.g., `"{{ NIC.state.name }}"` in subsequent task. + +For example: + +```yml +- name: Show NIC details + debug: + var: NIC +``` + +### Cheat Sheet: confignetwork.yml + +
+ +Expand to see confignetwork.yml + ```yml - name: Create a subset within the virtual network azure_rm_subnet: - ... + resource_group: "{{ myResource_group }}" + virtual_network_name: "{{ myVnet }}" + name: "{{ myVnetSubNet }}" + address_prefix_cidr: "{{ subnetAddPrefix }}" register: subnet - + - name: Create public IP address azure_rm_publicipaddress: - ... + resource_group: "{{ myResource_group }}" + allocation_method: Static + name: "{{ myPublicIP }}" register: publicIP - name: Create Network Security Group azure_rm_securitygroup: - ... + resource_group: "{{ myResource_group }}" + name: "{{ myNetworkSecurityGroup}}" + rules: + - name: "Allow-{{ item.name }}" + access: "{{ item.access }}" + protocol: "{{ item.protocol }}" + destination_port_range: "{{ item.port }}" + priority: "{{ item.priority }}" + direction: " {{ item.direction }}" + source_address_prefix: "{{ item.source_address_prefix }}" loop: "{{ NSGlist }}" register: NSG - name: Create virtual network interface card(NIC) with public IP azure_rm_networkinterface: - ... + resource_group: "{{ myResource_group }}" + name: "{{ myNIC }}" + virtual_network: "{{ myVnet }}" + subnet: "{{ myVnetSubNet }}" + ip_configurations: + - name: ipconfig + public_ip_address_name: "{{ myPublicIP }}" + security_group: "{{ myNetworkSecurityGroup }}" register: NIC ``` ->**Note**: you can register a variable after each task and use "debug" to show the output. By doing so, you can also refer to value in the Azure resource by using e.g., `"{{ NIC.state.name }}"` in subsequent task. - -For example: - -```yml - - name: Show NIC details - debug: - var: NIC -``` +
2. Move the task to create the VM to a file called `createvm.yml`. Let's add an additional configuration (a tag) to each VM. - For the front-end, add a tag by setting `tags: "Ansible=web"` - For the back-end, add a tag by setting `tags: "Ansible=MySQL"` +### Cheat Sheet: createvm.yml + +
+ +Expand to see createvm.yml + + +```yml +- name: Get latest version of a secret + azure_rm_keyvaultsecret_info: + vault_uri: "https://{{ keyvault_name }}.vault.azure.net" + name: "{{ secret_name }}" + register: output + +- name: Create a virtual machines + azure_rm_virtualmachine: + resource_group: "{{ myResource_group }}" + name: "{{ myVM }}" + admin_username: "testadmin" + admin_password: "{{ output.secret.value }}" + vm_size: Standard_B1ms + network_interfaces: "{{ NIC.state.name }}" + image: + offer: UbuntuServer + publisher: Canonical + sku: 16.04-LTS + version: latest + tags: "{{ myTags }}" +``` + +
+ 3. main.yml will be your main playbook. The structure is similar to this: ```yml @@ -133,7 +214,108 @@ For example: ``` -> **CODE**: To view all of the completed codes, go to [lab5](/lab5). +### Cheat Sheet: main.yml + +
+ +Expand to see a sample of main.yml + + +```yml +- hosts: localhost + vars_files: + - ./vars.yml + roles: + - ./modules + gather_facts: no + + tasks: +# ---------------------------------------------------------------------------------- +# Start with a resource group so that clean up is easy. This tasks is commented out +# since you cannot create resource group in this workshop. +# ---------------------------------------------------------------------------------- +# - name: Create a resource group +# azure_rm_resourcegroup: +# name: "{{ myResource_group }}" +# location: eastus2 + + - name: Create a virtual network. + azure_rm_virtualnetwork: + resource_group: "{{ myResource_group }}" + name: "{{ myVnet }}" + address_prefixes: "172.16.0.0/16" + + - name: Create front-end subnet and NSG rules + vars: + myVnetSubNet: myVnetSubNet + myPublicIP: myPublicIP + subnetAddPrefix: "172.16.10.0/24" + myNetworkSecurityGroup: myNSG + myNIC: myNIC + NSGlist: + - name: Allow-SSH + access: Allow + protocol: Tcp + direction: Inbound + priority: 300 + port: 22 + source_address_prefix: Internet + - name: Allow-HTTP + access: Allow + protocol: Tcp + direction: Inbound + priority: 100 + port: 80 + source_address_prefix: Internet + include_tasks: ./confignetwork.yml + + - name: Create a front-end virtual machines + vars: + myVM: myVM + myTags: "Ansible=web" + include: ./createvm.yml + + - name: Create back-end subnet and NSG rules + vars: + myVnetSubNet: myVnetSubNet-BE + myPublicIP: myPublicIP-BE + subnetAddPrefix: "172.16.20.0/24" + myNetworkSecurityGroup: myNSG-BE + myNIC: myNIC-BE + NSGlist: + - name: Allow-SSH + access: Allow + protocol: Tcp + direction: Inbound + priority: 200 + port: 22 + source_address_prefix: Internet + - name: Allow-MySQL-FE + access: Allow + protocol: Tcp + direction: Inbound + priority: 100 + port: 3306 + source_address_prefix: 172.16.10.0/24 + - name: Deny-internet-all + access: Allow + protocol: Tcp + direction: Outbound + priority: 300 + port: "*" + source_address_prefix: "*" + include_tasks: ./confignetwork.yml + + - name: Create a back-end virtual machines + vars: + myVM: myVM-BE + myTags: "Ansible=MySQL" + include: ./createvm.yml +``` + +
+ +> **CODE**: To view all of the completed codes, go to [lab5](Code). ## Dynamic inventory @@ -153,10 +335,11 @@ include_vm_resource_groups: keyed_groups: - prefix: tag key: tags - ``` -1. Run the following command to view the populated inventory: +> **CODE**: Go [here](Code/myazure_rm.yml) to see the code. + +2. Run the following command to view the populated inventory: ```bash ansible-inventory -i myazure_rm.yml --graph @@ -164,7 +347,8 @@ ansible-inventory -i myazure_rm.yml --graph You should see something like this: -``` +```output + @all: |--@tag_Ansible_SQL: | |--myVM-BE_7ac4 @@ -184,15 +368,13 @@ You can test connection to myVM-BE by doing: ansible -u testadmin -i myazure_rm.yml -m ping tag_Ansible_web -k ``` - - Likewise, for myVM-FE, run: ```bash ansible -u testadmin -i myazure_rm.yml -m ping tag_Ansible_MySQL -k ``` ->**_Note_**: since the VMs are using userid/password for authentication, you need to add `-k` to the command and provide password used to SSH into the VMs. +>**Note**: since the VMs are using userid/password for authentication, you need to add `-k` to the command and provide password used to SSH into the VMs. With dynamic inventory, you can run playbook by targeting vm(s) with the specific tag. For instance, if you wish to apply `XXX.yml` to the back-end VM, you can do so by running the command: @@ -211,23 +393,26 @@ To take it one step further, you can create Roles so that you can reuse and furt Example of project structure: -```yml - site.yml - webservers.yml - fooservers.yml - roles/ - common/ - tasks/ - handlers/ - files/ - templates/ - vars/ - defaults/ - meta/ - webservers/ - tasks/ - defaults/ - meta/ +```output +testrole +├── defaults +│   └── main.yml +├── files +├── handlers +│   └── main.yml +├── meta +│   └── main.yml +├── README.md +├── tasks +│   └── main.yml +├── templates +├── tests +│   ├── inventory +│   └── test.yml +└── vars + └── main.yml + +8 directories, 8 files ``` [Ansible Galaxy or Galaxy](https://docs.ansible.com/ansible/latest/reference_appendices/galaxy.html), is a free site for finding, downloading, and sharing community developed roles. Downloading roles from Galaxy is a great way to jumpstart your automation projects. @@ -242,8 +427,36 @@ Roles may also include modules and other plugin types. In Lab 4, you saw how we For further exploration, the [AKS role](https://galaxy.ansible.com/azure/aks) we shared in Ansible Galaxy is a good example on how you can reuse and share common configurations and tasks to provision AKS cluster in your organization. +To create an AKS with monitoring enabled: + +1. Install AKS role by doing in your Ansible host + + >**Note**: you cannot install roles in CloudShell + + ```output + ansible-galaxy install azure.aks + ``` + +2. Run this playbook: + + ```yml + - hosts: localhost + tasks: + - include_role: + name: azure.aks + vars: + monitoring: yes + name: akscluster + resource_group: aksroletest + ``` + ## Further readings +### Roles - [Ansible Galaxy](https://galaxy.ansible.com) - [Ansible Roles Explained | Cheat Sheet](https://linuxacademy.com/blog/linux-academy/ansible-roles-explained/) - [Ansible roles tutorialspoint](https://www.tutorialspoint.com/ansible/ansible_roles.htm) + +### Sample playbooks and labs: +- [Ansible playbooks for Azure](https://github.com/Azure-Samples/ansible-playbooks) +- [Ansible Deployment Labs for Microsoft Azure](https://github.com/Microsoft/Ansiblelabs) diff --git a/Ansible/05-Reusability/lab5/vars.yml b/Ansible/05-Reusability/lab5/vars.yml deleted file mode 100644 index f09df34..0000000 --- a/Ansible/05-Reusability/lab5/vars.yml +++ /dev/null @@ -1,5 +0,0 @@ -myResource_group: myResource_group_pc10 -myVnet: myVnet -keyvault_name: pckeyvault10 -secret_name: myVMSecret -myVM: myVM \ No newline at end of file