├── .README.html ├── .ansible-lint ├── .codespell_ignores ├── .codespellrc ├── .collection ├── README.md └── galaxy.yml ├── .commitlintrc.js ├── .fmf └── version ├── .github ├── CODEOWNERS ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── ansible-lint.yml │ ├── ansible-managed-var-comment.yml │ ├── ansible-test.yml │ ├── build_docs.yml │ ├── changelog_to_tag.yml │ ├── codespell.yml │ ├── markdownlint.yml │ ├── pr-title-lint.yml │ ├── qemu-kvm-integration-tests.yml │ ├── test_converting_readme.yml │ ├── tft.yml │ ├── tft_citest_bad.yml │ ├── weekly_ci.yml │ └── woke.yml ├── .gitignore ├── .markdownlint.yaml ├── .ostree ├── README.md └── get_ostree_data.sh ├── .packit.yaml ├── .pandoc_template.html5 ├── .yamllint.yml ├── CHANGELOG.md ├── LICENSE ├── README-ansible.md ├── README-ostree.md ├── README.md ├── ansible_pytest_extra_requirements.txt ├── contributing.md ├── custom_requirements.txt ├── defaults └── main.yml ├── files └── ha_is_primary.sql ├── handlers └── main.yml ├── meta ├── collection-requirements.yml └── main.yml ├── molecule └── default │ ├── Dockerfile.j2 │ └── molecule.yml ├── molecule_extra_requirements.txt ├── plans ├── README-plans.md ├── mssql_ha.fmf └── test_playbooks_parallel.fmf ├── pylint_extra_requirements.txt ├── pytest_extra_requirements.txt ├── tasks ├── configure_storage_paths.yml ├── input_sql_files.yml ├── main.yml ├── mssql_conf_setting.yml ├── set_vars.yml ├── sqlcmd_input_content.yml ├── sqlcmd_input_file.yml └── verify_password.yml ├── templates ├── configure_ag.j2 ├── configure_endpoint.j2 ├── configure_listener.j2 ├── create_and_back_up_cert.j2 ├── create_ha_login.j2 ├── create_master_key_encryption.j2 ├── drop_cert.j2 ├── enable_alwayson.j2 ├── get_ansible_managed.j2 ├── grant_permissions_to_ha_login.j2 ├── join_to_ag.j2 ├── replicate_db.j2 └── restore_cert.j2 ├── tests ├── .fmf │ └── version ├── collection-requirements.yml ├── files │ ├── create_ExampleDB1.sql │ ├── create_ExampleDB2.sql │ ├── sql_script.sql │ └── verify_fts.sql ├── no-vault-variables.txt ├── playbooks │ ├── tests_ad_integration.yml │ ├── tests_ad_integration_join_false.yml │ └── tests_ad_integration_w_keytab.yml ├── provision.fmf ├── requirements.txt ├── roles │ ├── caller │ │ ├── tasks │ │ │ └── main.yml │ │ └── vars │ │ │ └── main.yml │ └── linux-system-roles.mssql │ │ ├── defaults │ │ ├── files │ │ ├── handlers │ │ ├── meta │ │ ├── tasks │ │ ├── templates │ │ └── vars ├── setup-snapshot.yml ├── tasks │ ├── assert_fail_on_unsupported_ver.yml │ ├── check_header.yml │ ├── cleanup.yml │ ├── mssql-sever-increase-start-limit.yml │ ├── mssql_conf_verify.yml │ ├── snapshot_install_packages.yml │ ├── tests_idempotency.yml │ ├── tests_input_sql_file.yml │ ├── tests_password.yml │ ├── tests_tcp_firewall.yml │ ├── tests_tls.yml │ ├── upgrade_and_assert.yml │ ├── verify_ad_auth.yml │ ├── verify_package.yml │ ├── verify_settings.yml │ └── verify_tcp_port.yml ├── templates │ ├── add_test_listener.j2 │ ├── alter_ag.j2 │ ├── alter_endpoint.j2 │ ├── configure_ag_cluster_type_none.j2 │ ├── create_example_db.j2 │ ├── disable_alwayson.j2 │ ├── drop_ag.j2 │ ├── drop_endpoint.j2 │ ├── get_ansible_managed.j2 │ ├── remove_listener.j2 │ ├── remove_sql_cert.j2 │ └── separate_from_ag.j2 ├── tests_2019_upgrade.yml ├── tests_2022_upgrade.yml ├── tests_accept_eula.yml ├── tests_configure_ha_cluster_external.yml ├── tests_configure_ha_cluster_read_scale.yml ├── tests_default_2019.yml ├── tests_idempotency_2017.yml ├── tests_idempotency_2019.yml ├── tests_idempotency_2022.yml ├── tests_include_vars_from_parent.yml ├── tests_input_sql_file_2017.yml ├── tests_input_sql_file_2019.yml ├── tests_input_sql_file_2022.yml ├── tests_password_2017.yml ├── tests_password_2019.yml ├── tests_password_2022.yml ├── tests_selinux_enforcing_2022.yml ├── tests_tcp_firewall_2017.yml ├── tests_tcp_firewall_2019.yml ├── tests_tcp_firewall_2022.yml ├── tests_tls_2017.yml ├── tests_tls_2019.yml ├── tests_tls_2022.yml ├── vars │ ├── rh_distros_vars.yml │ └── vault-variables.yml └── vault_pwd ├── tox.ini └── vars ├── AlmaLinux_8.yml ├── CentOS_7.yml ├── CentOS_8.yml ├── RedHat.yml ├── RedHat_7.yml ├── RedHat_8.yml ├── Rocky_8.yml ├── Suse.yml └── main.yml /.ansible-lint: -------------------------------------------------------------------------------- 1 | --- 2 | profile: production 3 | kinds: 4 | - yaml: "**/meta/collection-requirements.yml" 5 | - playbook: "**/tests/get_coverage.yml" 6 | - yaml: "**/tests/collection-requirements.yml" 7 | - playbook: "**/tests/tests_*.yml" 8 | - playbook: "**/tests/setup-snapshot.yml" 9 | - tasks: "**/tests/*.yml" 10 | - playbook: "**/tests/playbooks/*.yml" 11 | - tasks: "**/tests/tasks/*.yml" 12 | - tasks: "**/tests/tasks/*/*.yml" 13 | - vars: "**/tests/vars/*.yml" 14 | - playbook: "**/examples/*.yml" 15 | skip_list: 16 | - fqcn-builtins 17 | - var-naming[no-role-prefix] 18 | - galaxy[no-changelog] 19 | - galaxy[no-runtime] 20 | exclude_paths: 21 | - tests/roles/ 22 | - .github/ 23 | - .markdownlint.yaml 24 | - examples/roles/ 25 | mock_roles: 26 | - linux-system-roles.mssql 27 | supported_ansible_also: 28 | - "2.14.0" 29 | -------------------------------------------------------------------------------- /.codespell_ignores: -------------------------------------------------------------------------------- 1 | passt 2 | -------------------------------------------------------------------------------- /.codespellrc: -------------------------------------------------------------------------------- 1 | [codespell] 2 | check-hidden = true 3 | # Note that `-w` doesn't work when ignore-multiline-regex is set 4 | # https://github.com/codespell-project/codespell/issues/3642 5 | ignore-multiline-regex = codespell:ignore-begin.*codespell:ignore-end 6 | ignore-words = .codespell_ignores 7 | # skip-file is not available https://github.com/codespell-project/codespell/pull/2759 8 | # .pandoc_template.html5 contains a typo in Licence that we shouldn't edit 9 | # .README.html is generated from README.md automatically - no need to check spelling 10 | skip = .pandoc_template.html5,.README.html 11 | -------------------------------------------------------------------------------- /.collection/README.md: -------------------------------------------------------------------------------- 1 | # Microsoft SQL Ansible Collection 2 | 3 | ## Description 4 | 5 | This collection contains a role for managing Microsoft SQL Server. 6 | 7 | ## Installation 8 | 9 | There are currently two ways to install this collection, using `ansible-galaxy` or RPM package. 10 | 11 | ### Installing with ansible-galaxy 12 | 13 | You can install the collection with `ansible-galaxy` by entering the following command: 14 | 15 | ```shell 16 | ansible-galaxy collection install microsoft.sql 17 | ``` 18 | 19 | You can also include it in a requirements.yml file and install it with ansible-galaxy collection install -r requirements.yml, using the format: 20 | 21 | ```yaml 22 | collections: 23 | - name: microsoft.sql 24 | ``` 25 | 26 | Note that if you install any collections from Ansible Galaxy, they will not be upgraded automatically when you upgrade the Ansible package. 27 | To upgrade the collection to the latest available version, run the following command: 28 | 29 | ```shell 30 | ansible-galaxy collection install microsoft.sql --upgrade 31 | ``` 32 | 33 | You can also install a specific version of the collection, for example, if you need to downgrade when something is broken in the latest version (please report an issue in this repository). Use the following syntax to install version 1.0.0: 34 | 35 | ```shell 36 | ansible-galaxy collection install microsoft.sql:==1.0.0 37 | ``` 38 | 39 | See [using Ansible collections](https://docs.ansible.com/ansible/devel/user_guide/collections_using.html) for more details. 40 | 41 | After the installation, you can call the server role from playbooks with `microsoft.sql.server`. 42 | When installing using `ansible-galaxy`, by default, you can find the role documentation at `~/.ansible/collections/ansible_collections/microsoft/sql/roles/server/README.md`. 43 | 44 | ### Installing using RPM package 45 | 46 | You can install the collection with the software package management tool `dnf` by running the following command: 47 | 48 | ```bash 49 | dnf install ansible-collection-microsoft-sql 50 | ``` 51 | 52 | When installing using `dnf`, you can find the role documentation in markdown format at `/usr/share/doc/ansible-collection-microsoft-sql/microsoft.sql-server/README.md` and in HTML format at `/usr/share/doc/ansible-collection-microsoft-sql/microsoft.sql-server/README.html`. 53 | 54 | ## Use Cases 55 | 56 | The common use cases are the following 57 | 58 | * I want to install, configure, and manage SQL Server on one or more systems 59 | * I want to configure several systems with Always On Availability Groups 60 | * I want to configure SQL Server to authenticate with Active Directory Server 61 | 62 | For more scenarios and examples, see role's documentation. 63 | 64 | ## Contributing (Optional) 65 | 66 | If you wish to contribute to roles within this collection, feel free to open a pull request for the role's upstream repository at https://github.com/linux-system-roles/mssql. 67 | 68 | We recommend that prior to submitting a PR, you familiarize yourself with our [Contribution Guidelines](https://linux-system-roles.github.io/contribute.html). 69 | 70 | ## Support 71 | 72 | * Red Hat Enterprise Linux 7 (RHEL 7+) 73 | * Red Hat Enterprise Linux 8 (RHEL 8+) 74 | * Red Hat Enterprise Linux 9 (RHEL 9+) 75 | * Fedora 76 | * RHEL derivatives such as CentOS 77 | * Suse Linux Enterprise Server 15 (SLES 15) 78 | * OpenSUSE 15 79 | 80 | ## Release Notes and Roadmap 81 | 82 | For the list of versions and their changelog, see CHANGELOG.md within this collection. 83 | 84 | ## Related Information 85 | 86 | Where available, link to general how to use collections or other related documentation applicable to the technology/product this collection works with. Useful materials such as demos, case studies, etc. may be linked here as well. 87 | 88 | ## License Information 89 | 90 | - MIT -------------------------------------------------------------------------------- /.collection/galaxy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | namespace: "microsoft" 3 | name: "sql" 4 | version: "1.0.11" 5 | description: "The Ansible role that manages Microsoft SQL Server" 6 | 7 | authors: 8 | - "Sergei Petrosian " 9 | 10 | repository: "https://github.com/linux-system-roles/mssql" 11 | # yamllint disable-line rule:line-length 12 | documentation: "https://github.com/linux-system-roles/mssql/blob/main/README.md" 13 | issues: "https://github.com/linux-system-roles/mssql/issues" 14 | 15 | readme: "README.md" 16 | 17 | license: 18 | - MIT 19 | 20 | dependencies: 21 | "fedora.linux_system_roles": "*" 22 | 23 | tags: 24 | - "mssql" 25 | - "microsoft" 26 | - "sql" 27 | - "database" 28 | -------------------------------------------------------------------------------- /.commitlintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parserPreset: 'conventional-changelog-conventionalcommits', 3 | rules: { 4 | 'body-leading-blank': [1, 'always'], 5 | 'body-max-line-length': [2, 'always', 100], 6 | 'footer-leading-blank': [1, 'always'], 7 | 'footer-max-line-length': [2, 'always', 100], 8 | 'header-max-length': [2, 'always', 100], 9 | 'subject-case': [ 10 | 2, 11 | 'never', 12 | ['start-case', 'pascal-case', 'upper-case'], 13 | ], 14 | 'subject-empty': [2, 'never'], 15 | 'subject-full-stop': [2, 'never', '.'], 16 | 'type-case': [2, 'always', 'lower-case'], 17 | 'type-empty': [2, 'never'], 18 | 'type-enum': [ 19 | 2, 20 | 'always', 21 | [ 22 | 'build', 23 | 'chore', 24 | 'ci', 25 | 'docs', 26 | 'feat', 27 | 'fix', 28 | 'perf', 29 | 'refactor', 30 | 'revert', 31 | 'style', 32 | 'test', 33 | 'tests', 34 | ], 35 | ], 36 | }, 37 | prompt: { 38 | questions: { 39 | type: { 40 | description: "Select the type of change that you're committing", 41 | enum: { 42 | feat: { 43 | description: 'A new feature', 44 | title: 'Features', 45 | emoji: '✨', 46 | }, 47 | fix: { 48 | description: 'A bug fix', 49 | title: 'Bug Fixes', 50 | emoji: '🐛', 51 | }, 52 | docs: { 53 | description: 'Documentation only changes', 54 | title: 'Documentation', 55 | emoji: '📚', 56 | }, 57 | style: { 58 | description: 59 | 'Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)', 60 | title: 'Styles', 61 | emoji: '💎', 62 | }, 63 | refactor: { 64 | description: 65 | 'A code change that neither fixes a bug nor adds a feature', 66 | title: 'Code Refactoring', 67 | emoji: '📦', 68 | }, 69 | perf: { 70 | description: 'A code change that improves performance', 71 | title: 'Performance Improvements', 72 | emoji: '🚀', 73 | }, 74 | test: { 75 | description: 'Adding missing tests or correcting existing tests', 76 | title: 'Tests', 77 | emoji: '🚨', 78 | }, 79 | tests: { 80 | description: 'Adding missing tests or correcting existing tests', 81 | title: 'Tests', 82 | emoji: '🚨', 83 | }, 84 | build: { 85 | description: 86 | 'Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)', 87 | title: 'Builds', 88 | emoji: '🛠', 89 | }, 90 | ci: { 91 | description: 92 | 'Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)', 93 | title: 'Continuous Integrations', 94 | emoji: '⚙️', 95 | }, 96 | chore: { 97 | description: "Other changes that don't modify src or test files", 98 | title: 'Chores', 99 | emoji: '♻️', 100 | }, 101 | revert: { 102 | description: 'Reverts a previous commit', 103 | title: 'Reverts', 104 | emoji: '🗑', 105 | }, 106 | }, 107 | }, 108 | scope: { 109 | description: 110 | 'What is the scope of this change (e.g. component or file name)', 111 | }, 112 | subject: { 113 | description: 114 | 'Write a short, imperative tense description of the change', 115 | }, 116 | body: { 117 | description: 'Provide a longer description of the change', 118 | }, 119 | isBreaking: { 120 | description: 'Are there any breaking changes?', 121 | }, 122 | breakingBody: { 123 | description: 124 | 'A BREAKING CHANGE commit requires a body. Please enter a longer description of the commit itself', 125 | }, 126 | breaking: { 127 | description: 'Describe the breaking changes', 128 | }, 129 | isIssueAffected: { 130 | description: 'Does this change affect any open issues?', 131 | }, 132 | issuesBody: { 133 | description: 134 | 'If issues are closed, the commit requires a body. Please enter a longer description of the commit itself', 135 | }, 136 | issues: { 137 | description: 'Add issue references (e.g. "fix #123", "re #123".)', 138 | }, 139 | }, 140 | }, 141 | }; 142 | -------------------------------------------------------------------------------- /.fmf/version: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # https://help.github.com/en/articles/about-code-owners 2 | # Default reviewers for everything 3 | * @spetrosi @richm 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 2 3 | updates: 4 | - package-ecosystem: github-actions 5 | directory: / 6 | schedule: 7 | interval: monthly 8 | commit-message: 9 | prefix: ci 10 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Enhancement: 2 | 3 | Reason: 4 | 5 | Result: 6 | 7 | Issue Tracker Tickets (Jira or BZ if any): 8 | -------------------------------------------------------------------------------- /.github/workflows/ansible-lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ansible Lint 3 | on: # yamllint disable-line rule:truthy 4 | pull_request: 5 | merge_group: 6 | branches: 7 | - main 8 | types: 9 | - checks_requested 10 | push: 11 | branches: 12 | - main 13 | workflow_dispatch: 14 | env: 15 | LSR_ROLE2COLL_NAMESPACE: fedora 16 | LSR_ROLE2COLL_NAME: linux_system_roles 17 | permissions: 18 | contents: read 19 | jobs: 20 | ansible_lint: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Update pip, git 24 | run: | 25 | set -euxo pipefail 26 | sudo apt update 27 | sudo apt install -y git 28 | 29 | - name: Checkout repo 30 | uses: actions/checkout@v4 31 | 32 | - name: Install tox, tox-lsr 33 | run: | 34 | set -euxo pipefail 35 | pip3 install "git+https://github.com/linux-system-roles/tox-lsr@3.10.0" 36 | 37 | - name: Convert role to collection format 38 | id: collection 39 | run: | 40 | set -euxo pipefail 41 | TOXENV=collection lsr_ci_runtox 42 | coll_dir=".tox/ansible_collections/$LSR_ROLE2COLL_NAMESPACE/$LSR_ROLE2COLL_NAME" 43 | # cleanup after collection conversion 44 | rm -rf "$coll_dir/.ansible" .tox/ansible-plugin-scan 45 | # ansible-lint action requires a .git directory??? 46 | # https://github.com/ansible/ansible-lint/blob/main/action.yml#L45 47 | mkdir -p "$coll_dir/.git" 48 | meta_req_file="${{ github.workspace }}/meta/collection-requirements.yml" 49 | test_req_file="${{ github.workspace }}/tests/collection-requirements.yml" 50 | if [ -f "$meta_req_file" ] && [ -f "$test_req_file" ]; then 51 | coll_req_file="${{ github.workspace }}/req.yml" 52 | python -c 'import sys; import yaml 53 | hsh1 = yaml.safe_load(open(sys.argv[1])) 54 | hsh2 = yaml.safe_load(open(sys.argv[2])) 55 | coll = {} 56 | for item in hsh1["collections"] + hsh2["collections"]: 57 | if isinstance(item, dict): 58 | name = item["name"] 59 | rec = item 60 | else: 61 | name = item # assume string 62 | rec = {"name": name} 63 | if name not in coll: 64 | coll[name] = rec 65 | hsh1["collections"] = list(coll.values()) 66 | yaml.safe_dump(hsh1, open(sys.argv[3], "w"))' "$meta_req_file" "$test_req_file" "$coll_req_file" 67 | echo merged "$coll_req_file" 68 | cat "$coll_req_file" 69 | elif [ -f "$meta_req_file" ]; then 70 | coll_req_file="$meta_req_file" 71 | elif [ -f "$test_req_file" ]; then 72 | coll_req_file="$test_req_file" 73 | else 74 | coll_req_file="" 75 | fi 76 | echo "coll_req_file=$coll_req_file" >> $GITHUB_OUTPUT 77 | 78 | - name: Run ansible-lint 79 | uses: ansible/ansible-lint@v25 80 | with: 81 | working_directory: ${{ github.workspace }}/.tox/ansible_collections/${{ env.LSR_ROLE2COLL_NAMESPACE }}/${{ env.LSR_ROLE2COLL_NAME }} 82 | requirements_file: ${{ steps.collection.outputs.coll_req_file }} 83 | env: 84 | ANSIBLE_COLLECTIONS_PATH: ${{ github.workspace }}/.tox 85 | -------------------------------------------------------------------------------- /.github/workflows/ansible-managed-var-comment.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Check for ansible_managed variable use in comments 3 | on: # yamllint disable-line rule:truthy 4 | pull_request: 5 | merge_group: 6 | branches: 7 | - main 8 | types: 9 | - checks_requested 10 | push: 11 | branches: 12 | - main 13 | workflow_dispatch: 14 | permissions: 15 | contents: read 16 | jobs: 17 | ansible_managed_var_comment: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Update pip, git 21 | run: | 22 | set -euxo pipefail 23 | python3 -m pip install --upgrade pip 24 | sudo apt update 25 | sudo apt install -y git 26 | 27 | - name: Checkout repo 28 | uses: actions/checkout@v4 29 | 30 | - name: Install tox, tox-lsr 31 | run: | 32 | set -euxo pipefail 33 | pip3 install "git+https://github.com/linux-system-roles/tox-lsr@3.10.0" 34 | 35 | - name: Run ansible-plugin-scan 36 | run: | 37 | set -euxo pipefail 38 | TOXENV=ansible-managed-var-comment lsr_ci_runtox 39 | -------------------------------------------------------------------------------- /.github/workflows/ansible-test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Ansible Test 3 | on: # yamllint disable-line rule:truthy 4 | pull_request: 5 | merge_group: 6 | branches: 7 | - main 8 | types: 9 | - checks_requested 10 | push: 11 | branches: 12 | - main 13 | workflow_dispatch: 14 | env: 15 | LSR_ROLE2COLL_NAMESPACE: fedora 16 | LSR_ROLE2COLL_NAME: linux_system_roles 17 | permissions: 18 | contents: read 19 | jobs: 20 | ansible_test: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Update pip, git 24 | run: | 25 | set -euxo pipefail 26 | python3 -m pip install --upgrade pip 27 | sudo apt update 28 | sudo apt install -y git 29 | 30 | - name: Checkout repo 31 | uses: actions/checkout@v4 32 | 33 | - name: Install tox, tox-lsr 34 | run: | 35 | set -euxo pipefail 36 | pip3 install "git+https://github.com/linux-system-roles/tox-lsr@3.10.0" 37 | 38 | - name: Convert role to collection format 39 | run: | 40 | set -euxo pipefail 41 | TOXENV=collection lsr_ci_runtox 42 | 43 | - name: Run ansible-test 44 | uses: ansible-community/ansible-test-gh-action@release/v1 45 | with: 46 | testing-type: sanity # wokeignore:rule=sanity 47 | ansible-core-version: stable-2.17 48 | collection-src-directory: ${{ github.workspace }}/.tox/ansible_collections/${{ env.LSR_ROLE2COLL_NAMESPACE }}/${{ env.LSR_ROLE2COLL_NAME }} 49 | -------------------------------------------------------------------------------- /.github/workflows/build_docs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # yamllint disable rule:line-length 3 | name: Convert README.md to HTML and push to docs branch 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | branches: 7 | - main 8 | paths: 9 | - README.md 10 | release: 11 | types: 12 | - published 13 | permissions: 14 | contents: read 15 | jobs: 16 | build_docs: 17 | runs-on: ubuntu-latest 18 | permissions: 19 | contents: write 20 | steps: 21 | - name: Update pip, git 22 | run: | 23 | set -euxo pipefail 24 | sudo apt update 25 | sudo apt install -y git 26 | 27 | - name: Check out code 28 | uses: actions/checkout@v4 29 | with: 30 | fetch-depth: 0 31 | - name: Ensure the docs branch 32 | run: | 33 | set -euxo pipefail 34 | branch=docs 35 | existed_in_remote=$(git ls-remote --heads origin $branch) 36 | 37 | if [ -z "${existed_in_remote}" ]; then 38 | echo "Creating $branch branch" 39 | git config --global user.name "${{ github.actor }}" 40 | git config --global user.email "${{ github.actor }}@users.noreply.github.com" 41 | git checkout --orphan $branch 42 | git reset --hard 43 | git commit --allow-empty -m "Initializing $branch branch" 44 | git push origin $branch 45 | echo "Created $branch branch" 46 | else 47 | echo "Branch $branch already exists" 48 | fi 49 | 50 | - name: Checkout the docs branch 51 | uses: actions/checkout@v4 52 | with: 53 | ref: docs 54 | 55 | - name: Fetch README.md and .pandoc_template.html5 template from the workflow branch 56 | uses: actions/checkout@v4 57 | with: 58 | sparse-checkout: | 59 | README.md 60 | .pandoc_template.html5 61 | sparse-checkout-cone-mode: false 62 | path: ref_branch 63 | - name: Set RELEASE_VERSION based on whether run on release or on push 64 | run: | 65 | set -euxo pipefail 66 | if [ ${{ github.event_name }} = release ]; then 67 | echo "RELEASE_VERSION=${{ github.event.release.tag_name }}" >> $GITHUB_ENV 68 | elif [ ${{ github.event_name }} = push ]; then 69 | echo "RELEASE_VERSION=latest" >> $GITHUB_ENV 70 | else 71 | echo Unsupported event 72 | exit 1 73 | fi 74 | 75 | - name: Ensure that version and docs directories exist 76 | run: mkdir -p ${{ env.RELEASE_VERSION }} docs 77 | 78 | - name: Remove badges from README.md prior to converting to HTML 79 | run: sed -i '1,8 {/^\[\!.*actions\/workflows/d}' ref_branch/README.md 80 | 81 | - name: Convert README.md to HTML and save to the version directory 82 | uses: docker://pandoc/core:latest 83 | with: 84 | args: >- 85 | --from gfm --to html5 --toc --shift-heading-level-by=-1 86 | --template ref_branch/.pandoc_template.html5 87 | --output ${{ env.RELEASE_VERSION }}/README.html ref_branch/README.md 88 | 89 | - name: Copy latest README.html to docs/index.html for GitHub pages 90 | if: env.RELEASE_VERSION == 'latest' 91 | run: cp ${{ env.RELEASE_VERSION }}/README.html docs/index.html 92 | 93 | - name: Upload README.html as an artifact 94 | uses: actions/upload-artifact@master 95 | with: 96 | name: README.html 97 | path: ${{ env.RELEASE_VERSION }}/README.html 98 | 99 | - name: Commit changes 100 | run: | 101 | git config --global user.name "${{ github.actor }}" 102 | git config --global user.email "${{ github.actor }}@users.noreply.github.com" 103 | git add ${{ env.RELEASE_VERSION }}/README.html docs/index.html 104 | git commit -m "Update README.html for ${{ env.RELEASE_VERSION }}" 105 | 106 | - name: Push changes 107 | uses: ad-m/github-push-action@master 108 | with: 109 | github_token: ${{ secrets.GITHUB_TOKEN }} 110 | branch: docs 111 | -------------------------------------------------------------------------------- /.github/workflows/changelog_to_tag.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # yamllint disable rule:line-length 3 | name: Tag, release, and publish role based on CHANGELOG.md push 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | branches: 7 | - main 8 | paths: 9 | - CHANGELOG.md 10 | permissions: 11 | contents: read 12 | jobs: 13 | tag_release_publish: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: write 17 | steps: 18 | - name: Update pip, git 19 | run: | 20 | set -euxo pipefail 21 | sudo apt update 22 | sudo apt install -y git 23 | 24 | - name: checkout PR 25 | uses: actions/checkout@v4 26 | 27 | - name: Get tag and message from the latest CHANGELOG.md commit 28 | id: tag 29 | run: | 30 | set -euxo pipefail 31 | print=false 32 | while read -r line; do 33 | if [[ "$line" =~ ^\[([0-9]+\.[0-9]+\.[0-9]+)\]\ -\ [0-9-]+ ]]; then 34 | if [ "$print" = false ]; then 35 | _tagname="${BASH_REMATCH[1]}" 36 | echo "$line" 37 | print=true 38 | else 39 | break 40 | fi 41 | elif [ "$print" = true ]; then 42 | echo "$line" 43 | fi 44 | done < CHANGELOG.md > ./.tagmsg.txt 45 | git fetch --all --tags 46 | for t in $( git tag -l ); do 47 | if [ "$t" = "$_tagname" ]; then 48 | echo INFO: tag "$t" already exists 49 | exit 1 50 | fi 51 | done 52 | # Get name of the branch that the change was pushed to 53 | _branch="${GITHUB_REF_NAME:-}" 54 | if [ "$_branch" = master ] || [ "$_branch" = main ]; then 55 | echo Using branch name ["$_branch"] as push branch 56 | else 57 | echo WARNING: GITHUB_REF_NAME ["$_branch"] is not main or master 58 | _branch=$( git branch -r | grep -o 'origin/HEAD -> origin/.*$' | \ 59 | awk -F'/' '{print $3}' || : ) 60 | fi 61 | if [ -z "$_branch" ]; then 62 | _branch=$( git branch --points-at HEAD --no-color --format='%(refname:short)' ) 63 | fi 64 | if [ -z "$_branch" ]; then 65 | echo ERROR: unable to determine push branch 66 | git branch -a 67 | exit 1 68 | fi 69 | echo "tagname=$_tagname" >> "$GITHUB_OUTPUT" 70 | echo "branch=$_branch" >> "$GITHUB_OUTPUT" 71 | 72 | - name: Install dependencies 73 | run: | 74 | set -euxo pipefail 75 | sudo apt-get update 76 | sudo apt install -y git 77 | pip install --upgrade pip galaxy-importer ansible-core 'pyyaml<6,>=5.4.1' ruamel_yaml 78 | 79 | - name: Setup Python 80 | uses: actions/setup-python@v5 81 | 82 | - name: checkout auto-maintenance 83 | uses: actions/checkout@v4 84 | with: 85 | repository: linux-system-roles/auto-maintenance 86 | path: auto-maintenance 87 | 88 | - name: checkout mssql into a separate directory 89 | uses: actions/checkout@v4 90 | with: 91 | path: mssql 92 | - name: Build and publish the collection 93 | shell: bash 94 | id: build 95 | run: | 96 | set -euxo pipefail 97 | tagname=${{ steps.tag.outputs.tagname }} 98 | rolename=mssql 99 | collection_namespace=microsoft 100 | collection_name=sql 101 | collection_rolename=server 102 | dest_path=/var/tmp/collection 103 | coll_path="$dest_path"/ansible_collections/"$collection_namespace"/"$collection_name" 104 | 105 | echo ::group::Update galaxy.yml 106 | sed -i "s/version: .*/version: \"$tagname\"/g" "$rolename"/.collection/galaxy.yml 107 | echo ::endgroup:: 108 | 109 | echo ::group::Remove symlinks in tests/roles 110 | if [ -d "$rolename"/tests/roles ]; then 111 | find "$rolename"/tests/roles -type l -exec rm {} \; 112 | if [ -d "$rolename"/tests/roles/linux-system-roles."$rolename" ]; then 113 | rm -r "$rolename"/tests/roles/linux-system-roles."$rolename" 114 | fi 115 | fi 116 | echo ::endgroup:: 117 | 118 | echo ::group::Build Collection 119 | # Ensure there is no dest_path before running release_collection.py 120 | rm -rf "$dest_path" 121 | python3 auto-maintenance/lsr_role2collection.py --role "$rolename" \ 122 | --src-path "$rolename" \ 123 | --src-owner linux-system-roles \ 124 | --dest-path "$dest_path" \ 125 | --readme "$rolename"/.collection/README.md \ 126 | --namespace microsoft \ 127 | --collection sql \ 128 | --new-role server \ 129 | --meta-runtime auto-maintenance/lsr_role2collection/runtime.yml 130 | 131 | # Replace remnants of "linux-system-roles.mssql" with collection FQDN 132 | find "$coll_path"/ -type f -exec \ 133 | sed -e "s/linux-system-roles[.]%$rolename\\>/$collection_namespace.$collection_name.$collection_rolename/g" \ 134 | -i {} \; 135 | 136 | # removing dot files/dirs 137 | rm -r "$coll_path"/.[A-Za-z]* 138 | rm -r "$coll_path"/tests/"$collection_rolename"/.[A-Za-z]* 139 | 140 | # Copy .ansible-lint to collection dir 141 | cp "$coll_path"/roles/"$collection_rolename"/.ansible-lint "$coll_path" 142 | 143 | # Copy CHANGELOG.md from collection role to parent collection dir 144 | cp "$coll_path"/roles/"$collection_rolename"/CHANGELOG.md \ 145 | "$coll_path" 146 | 147 | # Copy galaxy.yml to the collection directory 148 | cp -p "$rolename"/.collection/galaxy.yml "$coll_path" 149 | 150 | pushd "$coll_path" 151 | ansible-galaxy collection build 152 | popd 153 | echo ::endgroup:: 154 | _tarball="$coll_path"/microsoft-sql-$tagname.tar.gz 155 | if [ ! -f "${_tarball}" ]; then 156 | echo ::error::"Did not find tarball to publish: ${_tarball}" 157 | exit 1 158 | fi 159 | 160 | echo ::group::Run galaxy-importer against collection tarball 161 | GALAXY_IMPORTER_CONFIG=auto-maintenance/lsr_role2collection/galaxy-importer.cfg \ 162 | python3 -m galaxy_importer.main \ 163 | "$_tarball" 164 | echo ::endgroup:: 165 | 166 | echo ::group::Publish Collection to Galaxy 167 | ansible-galaxy collection publish -vv --token "${{ secrets.GALAXY_API_KEY }}" "$_tarball" 168 | echo ::endgroup:: 169 | echo ::info Done 170 | 171 | - name: Create tag 172 | uses: mathieudutour/github-tag-action@v6.2 173 | with: 174 | github_token: ${{ secrets.GITHUB_TOKEN }} 175 | custom_tag: ${{ steps.tag.outputs.tagname }} 176 | tag_prefix: '' 177 | 178 | - name: Create Release 179 | id: create_release 180 | uses: ncipollo/release-action@v1 181 | with: 182 | tag: ${{ steps.tag.outputs.tagname }} 183 | name: Version ${{ steps.tag.outputs.tagname }} 184 | bodyFile: ./.tagmsg.txt 185 | makeLatest: true 186 | -------------------------------------------------------------------------------- /.github/workflows/codespell.yml: -------------------------------------------------------------------------------- 1 | # Codespell configuration is within .codespellrc 2 | --- 3 | name: Codespell 4 | on: # yamllint disable-line rule:truthy 5 | - pull_request 6 | permissions: 7 | contents: read 8 | jobs: 9 | codespell: 10 | name: Check for spelling errors 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout 14 | uses: actions/checkout@v4 15 | 16 | - name: Codespell 17 | uses: codespell-project/actions-codespell@v2 18 | -------------------------------------------------------------------------------- /.github/workflows/markdownlint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # yamllint disable rule:line-length 3 | name: Markdown Lint 4 | on: # yamllint disable-line rule:truthy 5 | pull_request: 6 | merge_group: 7 | branches: 8 | - main 9 | types: 10 | - checks_requested 11 | push: 12 | branches: 13 | - main 14 | workflow_dispatch: 15 | permissions: 16 | contents: read 17 | jobs: 18 | markdownlint: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Update pip, git 22 | run: | 23 | set -euxo pipefail 24 | sudo apt update 25 | sudo apt install -y git 26 | 27 | - name: Check out code 28 | uses: actions/checkout@v4 29 | 30 | # CHANGELOG.md is generated automatically from PR titles and descriptions 31 | # It might have issues but they are not critical 32 | - name: Lint all markdown files except for CHANGELOG.md 33 | uses: docker://avtodev/markdown-lint:master 34 | with: 35 | args: >- 36 | --ignore=CHANGELOG.md 37 | **/*.md 38 | config: .markdownlint.yaml 39 | -------------------------------------------------------------------------------- /.github/workflows/pr-title-lint.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: PR Title Lint 3 | on: # yamllint disable-line rule:truthy 4 | pull_request: 5 | types: 6 | - opened 7 | - synchronize 8 | - reopened 9 | - edited 10 | merge_group: 11 | branches: 12 | - main 13 | types: 14 | - checks_requested 15 | permissions: 16 | contents: read 17 | jobs: 18 | commit-checks: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | fetch-depth: 0 24 | 25 | - name: Install conventional-commit linter 26 | run: npm install @commitlint/config-conventional @commitlint/cli 27 | 28 | - name: Run commitlint on PR title 29 | env: 30 | PR_TITLE: ${{ github.event.pull_request.title }} 31 | # Echo from env variable to avoid bash errors with extra characters 32 | run: echo "$PR_TITLE" | npx commitlint --verbose 33 | -------------------------------------------------------------------------------- /.github/workflows/test_converting_readme.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # yamllint disable rule:line-length 3 | name: Test converting README.md to README.html 4 | on: # yamllint disable-line rule:truthy 5 | pull_request: 6 | merge_group: 7 | branches: 8 | - main 9 | types: 10 | - checks_requested 11 | push: 12 | branches: 13 | - main 14 | permissions: 15 | contents: read 16 | jobs: 17 | test_converting_readme: 18 | runs-on: ubuntu-latest 19 | permissions: 20 | contents: write 21 | steps: 22 | - name: Update pip, git 23 | run: | 24 | set -euxo pipefail 25 | sudo apt update 26 | sudo apt install -y git 27 | 28 | - name: Check out code 29 | uses: actions/checkout@v4 30 | 31 | - name: Remove badges from README.md prior to converting to HTML 32 | run: sed -i '1,8 {/^\[\!.*actions\/workflows/d}' README.md 33 | 34 | - name: Convert README.md to HTML 35 | uses: docker://pandoc/core:latest 36 | with: 37 | args: >- 38 | --from gfm --to html5 --toc --shift-heading-level-by=-1 39 | --template .pandoc_template.html5 40 | --output README.html README.md 41 | 42 | - name: Upload README.html as an artifact 43 | uses: actions/upload-artifact@master 44 | with: 45 | name: README.html 46 | path: README.html 47 | -------------------------------------------------------------------------------- /.github/workflows/tft_citest_bad.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Re-run failed testing farm tests 3 | on: 4 | issue_comment: 5 | types: 6 | - created 7 | permissions: 8 | contents: read 9 | jobs: 10 | citest_bad_rerun: 11 | if: | 12 | github.event.issue.pull_request 13 | && contains(fromJson('["[citest_bad]", "[citest-bad]", "[citest bad]"]'), github.event.comment.body) 14 | && contains(fromJson('["OWNER", "MEMBER", "COLLABORATOR"]'), github.event.comment.author_association) 15 | permissions: 16 | actions: write # for re-running failed jobs: https://docs.github.com/en/rest/actions/workflow-runs?apiVersion=2022-11-28#re-run-a-job-from-a-workflow-run 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Wait 10s until tft.yml workflow is created and skipped because new comment don't match [citest] 20 | run: sleep 10s 21 | 22 | - name: Re-run failed jobs for this PR 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | REPO: ${{ github.repository }} 26 | PR_TITLE: ${{ github.event.issue.title }} 27 | run: | 28 | PENDING_RUN=$(gh api "repos/$REPO/actions/workflows/tft.yml/runs?event=issue_comment" \ 29 | | jq -r "[.workflow_runs[] | select( .display_title == \"$PR_TITLE\") | \ 30 | select(.status == \"pending\" or .status == \"queued\" or .status == \"in_progress\") | .id][0]") 31 | # if pending run don't exist, take the last run with failure state 32 | if [ "$PENDING_RUN" != "null" ]; then 33 | echo "The workflow $PENDING_RUN is still running, wait for it to finish to re-run" 34 | exit 1 35 | fi 36 | RUN_ID=$(gh api "repos/$REPO/actions/workflows/tft.yml/runs?event=issue_comment" \ 37 | | jq -r "[.workflow_runs[] | select( .display_title == \"$PR_TITLE\" ) | select( .conclusion == \"failure\" ) | .id][0]") 38 | if [ "$RUN_ID" = "null" ]; then 39 | echo "Failed workflow not found, exiting" 40 | exit 1 41 | fi 42 | echo "Re-running workflow $RUN_ID" 43 | gh api --method POST repos/$REPO/actions/runs/$RUN_ID/rerun-failed-jobs 44 | -------------------------------------------------------------------------------- /.github/workflows/weekly_ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # yamllint disable rule:line-length 3 | name: Weekly CI trigger 4 | on: # yamllint disable-line rule:truthy 5 | workflow_dispatch: 6 | schedule: 7 | - cron: 0 0 * * 6 8 | env: 9 | BRANCH_NAME: weekly-ci 10 | COMMIT_MESSAGE: "ci: This PR is to trigger periodic CI testing" 11 | BODY_MESSAGE: >- 12 | This PR is for the purpose of triggering periodic CI testing. 13 | We don't currently have a way to trigger CI without a PR, 14 | so this PR serves that purpose. 15 | COMMENT: "[citest]" 16 | permissions: 17 | contents: read 18 | jobs: 19 | weekly_ci: 20 | runs-on: ubuntu-latest 21 | permissions: 22 | issues: write 23 | pull-requests: write 24 | contents: write 25 | steps: 26 | - name: Update pip, git 27 | run: | 28 | set -euxo pipefail 29 | sudo apt update 30 | sudo apt install -y git 31 | 32 | - name: Checkout latest code 33 | uses: actions/checkout@v4 34 | with: 35 | fetch-depth: 0 36 | - name: Create or rebase commit, add dump_packages callback 37 | run: | 38 | set -euxo pipefail 39 | 40 | git config --global user.name "github-actions[bot]" 41 | git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com" 42 | git checkout ${{ env.BRANCH_NAME }} || git checkout -b ${{ env.BRANCH_NAME }} 43 | git rebase main 44 | if [ ! -d tests/callback_plugins ]; then 45 | mkdir -p tests/callback_plugins 46 | fi 47 | curl -L -s -o tests/callback_plugins/dump_packages.py https://raw.githubusercontent.com/linux-system-roles/auto-maintenance/main/callback_plugins/dump_packages.py 48 | git add tests/callback_plugins 49 | git commit --allow-empty -m "${{ env.COMMIT_MESSAGE }}" 50 | git push -f --set-upstream origin ${{ env.BRANCH_NAME }} 51 | 52 | - name: Create and comment pull request 53 | uses: actions/github-script@v7 54 | with: 55 | github-token: ${{ secrets.GH_PUSH_TOKEN }} 56 | script: | 57 | const head = [context.repo.owner, ":", "${{ env.BRANCH_NAME }}"].join(""); 58 | const response = await github.rest.pulls.list({ 59 | owner: context.repo.owner, 60 | repo: context.repo.repo, 61 | head: head, 62 | base: context.ref, 63 | state: "open" 64 | }); 65 | let pr_number = ''; 66 | if (response.data.length === 0) { 67 | pr_number = (await github.rest.pulls.create({ 68 | owner: context.repo.owner, 69 | repo: context.repo.repo, 70 | title: "${{ env.COMMIT_MESSAGE }}", 71 | body: "${{ env.BODY_MESSAGE }}", 72 | head: "${{ env.BRANCH_NAME }}", 73 | base: context.ref, 74 | draft: true 75 | })).data.number; 76 | } else { 77 | pr_number = response.data[0].number; 78 | } 79 | github.rest.issues.createComment({ 80 | owner: context.repo.owner, 81 | repo: context.repo.repo, 82 | issue_number: pr_number, 83 | body: "${{ env.COMMENT }}", 84 | }); 85 | -------------------------------------------------------------------------------- /.github/workflows/woke.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # yamllint disable rule:line-length 3 | name: Woke 4 | on: # yamllint disable-line rule:truthy 5 | - pull_request 6 | jobs: 7 | woke: 8 | name: Detect non-inclusive language 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v4 13 | 14 | - name: Run lsr-woke-action 15 | # Originally, uses: get-woke/woke-action@v0 16 | uses: linux-system-roles/lsr-woke-action@main 17 | with: 18 | woke-args: "-c https://raw.githubusercontent.com/linux-system-roles/tox-lsr/main/src/tox_lsr/config_files/woke.yml --count-only-error-for-failure" 19 | # Cause the check to fail on any broke rules 20 | fail-on-error: true 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | passes.yml 2 | vault.yml 3 | *.pyc 4 | *.retry 5 | /tests/.coverage 6 | /tests/htmlcov* 7 | /.tox 8 | /venv*/ 9 | /.venv/ 10 | .vscode/ 11 | artifacts/ 12 | __pycache__/ 13 | *~ 14 | .pytest_cache/ 15 | -------------------------------------------------------------------------------- /.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Default state for all rules 3 | default: true 4 | 5 | # Path to configuration file to extend 6 | extends: null 7 | 8 | # MD001/heading-increment/header-increment - Heading levels should only increment by one level at a time 9 | MD001: true 10 | 11 | # MD002/first-heading-h1/first-header-h1 - First heading should be a top-level heading 12 | MD002: 13 | # Heading level 14 | level: 1 15 | 16 | # MD003/heading-style/header-style - Heading style 17 | MD003: 18 | # Heading style 19 | style: "consistent" 20 | 21 | # MD004/ul-style - Unordered list style 22 | MD004: 23 | # List style 24 | style: "consistent" 25 | 26 | # MD005/list-indent - Inconsistent indentation for list items at the same level 27 | MD005: true 28 | 29 | # MD006/ul-start-left - Consider starting bulleted lists at the beginning of the line 30 | MD006: true 31 | 32 | # MD007/ul-indent - Unordered list indentation 33 | MD007: 34 | # Spaces for indent 35 | indent: 2 36 | # Whether to indent the first level of the list 37 | start_indented: false 38 | # Spaces for first level indent (when start_indented is set) 39 | start_indent: 2 40 | 41 | # MD009/no-trailing-spaces - Trailing spaces 42 | MD009: 43 | # Spaces for line break 44 | br_spaces: 2 45 | # Allow spaces for empty lines in list items 46 | list_item_empty_lines: false 47 | # Include unnecessary breaks 48 | strict: false 49 | 50 | # MD010/no-hard-tabs - Hard tabs 51 | MD010: 52 | # Include code blocks 53 | code_blocks: true 54 | # Fenced code languages to ignore 55 | ignore_code_languages: [] 56 | # Number of spaces for each hard tab 57 | spaces_per_tab: 1 58 | 59 | # MD011/no-reversed-links - Reversed link syntax 60 | MD011: true 61 | 62 | # MD012/no-multiple-blanks - Multiple consecutive blank lines 63 | MD012: 64 | # Consecutive blank lines 65 | maximum: 1 66 | 67 | # Modified for LSR 68 | # GFM does not limit line length 69 | # MD013/line-length - Line length 70 | MD013: false 71 | # # Number of characters 72 | # # line_length: 80 73 | # line_length: 999 74 | # # Number of characters for headings 75 | # heading_line_length: 80 76 | # # Number of characters for code blocks 77 | # code_block_line_length: 80 78 | # # Include code blocks 79 | # code_blocks: true 80 | # # Include tables 81 | # tables: true 82 | # # Include headings 83 | # headings: true 84 | # # Include headings 85 | # headers: true 86 | # # Strict length checking 87 | # strict: false 88 | # # Stern length checking 89 | # stern: false 90 | 91 | # MD014/commands-show-output - Dollar signs used before commands without showing output 92 | MD014: true 93 | 94 | # MD018/no-missing-space-atx - No space after hash on atx style heading 95 | MD018: true 96 | 97 | # MD019/no-multiple-space-atx - Multiple spaces after hash on atx style heading 98 | MD019: true 99 | 100 | # MD020/no-missing-space-closed-atx - No space inside hashes on closed atx style heading 101 | MD020: true 102 | 103 | # MD021/no-multiple-space-closed-atx - Multiple spaces inside hashes on closed atx style heading 104 | MD021: true 105 | 106 | # MD022/blanks-around-headings/blanks-around-headers - Headings should be surrounded by blank lines 107 | MD022: 108 | # Blank lines above heading 109 | lines_above: 1 110 | # Blank lines below heading 111 | lines_below: 1 112 | 113 | # MD023/heading-start-left/header-start-left - Headings must start at the beginning of the line 114 | MD023: true 115 | 116 | # MD024/no-duplicate-heading/no-duplicate-header - Multiple headings with the same content 117 | MD024: true 118 | 119 | # MD025/single-title/single-h1 - Multiple top-level headings in the same document 120 | MD025: 121 | # Heading level 122 | level: 1 123 | # RegExp for matching title in front matter 124 | front_matter_title: "^\\s*title\\s*[:=]" 125 | 126 | # MD026/no-trailing-punctuation - Trailing punctuation in heading 127 | MD026: 128 | # Punctuation characters not allowed at end of headings 129 | punctuation: ".,;:!。,;:!" 130 | 131 | # MD027/no-multiple-space-blockquote - Multiple spaces after blockquote symbol 132 | MD027: true 133 | 134 | # MD028/no-blanks-blockquote - Blank line inside blockquote 135 | MD028: true 136 | 137 | # MD029/ol-prefix - Ordered list item prefix 138 | MD029: 139 | # List style 140 | style: "one_or_ordered" 141 | 142 | # MD030/list-marker-space - Spaces after list markers 143 | MD030: 144 | # Spaces for single-line unordered list items 145 | ul_single: 1 146 | # Spaces for single-line ordered list items 147 | ol_single: 1 148 | # Spaces for multi-line unordered list items 149 | ul_multi: 1 150 | # Spaces for multi-line ordered list items 151 | ol_multi: 1 152 | 153 | # MD031/blanks-around-fences - Fenced code blocks should be surrounded by blank lines 154 | MD031: 155 | # Include list items 156 | list_items: true 157 | 158 | # MD032/blanks-around-lists - Lists should be surrounded by blank lines 159 | MD032: true 160 | 161 | # MD033/no-inline-html - Inline HTML 162 | MD033: 163 | # Allowed elements 164 | allowed_elements: [] 165 | 166 | # MD034/no-bare-urls - Bare URL used 167 | MD034: true 168 | 169 | # MD035/hr-style - Horizontal rule style 170 | MD035: 171 | # Horizontal rule style 172 | style: "consistent" 173 | 174 | # MD036/no-emphasis-as-heading/no-emphasis-as-header - Emphasis used instead of a heading 175 | MD036: 176 | # Punctuation characters 177 | punctuation: ".,;:!?。,;:!?" 178 | 179 | # MD037/no-space-in-emphasis - Spaces inside emphasis markers 180 | MD037: true 181 | 182 | # MD038/no-space-in-code - Spaces inside code span elements 183 | MD038: true 184 | 185 | # MD039/no-space-in-links - Spaces inside link text 186 | MD039: true 187 | 188 | # MD040/fenced-code-language - Fenced code blocks should have a language specified 189 | MD040: 190 | # List of languages 191 | allowed_languages: [] 192 | # Require language only 193 | language_only: false 194 | 195 | # MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading 196 | MD041: 197 | # Heading level 198 | level: 1 199 | # RegExp for matching title in front matter 200 | front_matter_title: "^\\s*title\\s*[:=]" 201 | 202 | # MD042/no-empty-links - No empty links 203 | MD042: true 204 | 205 | # Modified for LSR 206 | # Disabling, we do not need this 207 | # MD043/required-headings/required-headers - Required heading structure 208 | MD043: false 209 | # # List of headings 210 | # headings: [] 211 | # # List of headings 212 | # headers: [] 213 | # # Match case of headings 214 | # match_case: false 215 | 216 | # MD044/proper-names - Proper names should have the correct capitalization 217 | MD044: 218 | # List of proper names 219 | names: [] 220 | # Include code blocks 221 | code_blocks: true 222 | # Include HTML elements 223 | html_elements: true 224 | 225 | # MD045/no-alt-text - Images should have alternate text (alt text) 226 | MD045: true 227 | 228 | # MD046/code-block-style - Code block style 229 | MD046: 230 | # Block style 231 | style: "consistent" 232 | 233 | # MD047/single-trailing-newline - Files should end with a single newline character 234 | MD047: true 235 | 236 | # MD048/code-fence-style - Code fence style 237 | MD048: 238 | # Code fence style 239 | style: "consistent" 240 | 241 | # MD049/emphasis-style - Emphasis style should be consistent 242 | MD049: 243 | # Emphasis style should be consistent 244 | style: "consistent" 245 | 246 | # MD050/strong-style - Strong style should be consistent 247 | MD050: 248 | # Strong style should be consistent 249 | style: "consistent" 250 | 251 | # MD051/link-fragments - Link fragments should be valid 252 | MD051: true 253 | 254 | # MD052/reference-links-images - Reference links and images should use a label that is defined 255 | MD052: true 256 | 257 | # MD053/link-image-reference-definitions - Link and image reference definitions should be needed 258 | MD053: 259 | # Ignored definitions 260 | ignored_definitions: 261 | - "//" 262 | -------------------------------------------------------------------------------- /.ostree/README.md: -------------------------------------------------------------------------------- 1 | *NOTE*: The `*.txt` files are used by `get_ostree_data.sh` to create the lists 2 | of packages, and to find other system roles used by this role. DO NOT use them 3 | directly. 4 | -------------------------------------------------------------------------------- /.ostree/get_ostree_data.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | ostree_dir="${OSTREE_DIR:-"$(dirname "$(realpath "$0")")"}" 6 | 7 | if [ -z "${4:-}" ] || [ "${1:-}" = help ] || [ "${1:-}" = -h ]; then 8 | cat <&2 echo ERROR - could not find role "$role" - please use ANSIBLE_COLLECTIONS_PATH 64 | exit 2 65 | } 66 | 67 | get_packages() { 68 | local ostree_dir pkgtype pkgfile rolefile 69 | ostree_dir="$1" 70 | for pkgtype in "${pkgtypes[@]}"; do 71 | for suff in "" "-$distro" "-${distro}-${major_ver}" "-${distro}-${ver}"; do 72 | pkgfile="$ostree_dir/packages-${pkgtype}${suff}.txt" 73 | if [ -f "$pkgfile" ]; then 74 | cat "$pkgfile" 75 | fi 76 | done 77 | rolefile="$ostree_dir/roles-${pkgtype}.txt" 78 | if [ -f "$rolefile" ]; then 79 | local roles role rolepath 80 | roles="$(cat "$rolefile")" 81 | for role in $roles; do 82 | rolepath="$(get_rolepath "$ostree_dir" "$role")" 83 | if [ -z "$rolepath" ]; then 84 | 1>&2 echo ERROR - could not find role "$role" - please use ANSIBLE_COLLECTIONS_PATH 85 | exit 2 86 | fi 87 | get_packages "$rolepath" 88 | done 89 | fi 90 | done | sort -u 91 | } 92 | 93 | format_packages_json() { 94 | local comma pkgs pkg 95 | comma="" 96 | pkgs="[" 97 | while read -r pkg; do 98 | pkgs="${pkgs}${comma}\"${pkg}\"" 99 | comma=, 100 | done 101 | pkgs="${pkgs}]" 102 | echo "$pkgs" 103 | } 104 | 105 | format_packages_raw() { 106 | cat 107 | } 108 | 109 | format_packages_yaml() { 110 | while read -r pkg; do 111 | echo "- $pkg" 112 | done 113 | } 114 | 115 | format_packages_toml() { 116 | while read -r pkg; do 117 | echo "[[packages]]" 118 | echo "name = \"$pkg\"" 119 | echo "version = \"*\"" 120 | done 121 | } 122 | 123 | distro="${distro_ver%%-*}" 124 | ver="${distro_ver##*-}" 125 | if [[ "$ver" =~ ^([0-9]*) ]]; then 126 | major_ver="${BASH_REMATCH[1]}" 127 | else 128 | echo ERROR: cannot parse major version number from version "$ver" 129 | exit 1 130 | fi 131 | 132 | "get_$category" "$ostree_dir" | "format_${category}_$format" 133 | -------------------------------------------------------------------------------- /.packit.yaml: -------------------------------------------------------------------------------- 1 | downstream_package_name: ansible-collection-microsoft-sql 2 | 3 | specfile_path: ansible-collection-microsoft-sql.spec 4 | 5 | files_to_sync: 6 | - ansible-collection-microsoft-sql.spec 7 | - src: CHANGELOG.md 8 | dest: CHANGELOG.md 9 | 10 | upstream_project_url: https://github.com/linux-system-roles/mssql 11 | 12 | actions: 13 | post-upstream-clone: 14 | - "wget https://src.fedoraproject.org/rpms/ansible-collection-microsoft-sql/raw/rawhide/f/ansible-collection-microsoft-sql.spec -O ansible-collection-microsoft-sql.spec" 15 | - "wget https://src.fedoraproject.org/rpms/ansible-collection-microsoft-sql/raw/rawhide/f/ansible-packaging.inc -O ansible-packaging.inc" 16 | jobs: 17 | - job: propose_downstream 18 | trigger: release 19 | dist_git_branches: 20 | - fedora-all 21 | -------------------------------------------------------------------------------- /.yamllint.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | ignore: | 4 | /.tox/ 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Red Hat, Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README-ansible.md: -------------------------------------------------------------------------------- 1 | # Introduction to Ansible for Linux System Roles 2 | 3 | If you are not familiar with Ansible, please see 4 | [Introduction to Ansible for Linux System Roles](https://linux-system-roles.github.io/documentation/intro-to-ansible-for-system-roles.html), 5 | where many useful links are presented. 6 | -------------------------------------------------------------------------------- /README-ostree.md: -------------------------------------------------------------------------------- 1 | # rpm-ostree 2 | 3 | The role supports running on [rpm-ostree](https://coreos.github.io/rpm-ostree/) 4 | systems. The primary issue is that the `/usr` filesystem is read-only, and the 5 | role cannot install packages. Instead, it will just verify that the necessary 6 | packages and any other `/usr` files are pre-installed. The role will change the 7 | package manager to one that is compatible with `rpm-ostree` systems. 8 | 9 | ## Building 10 | 11 | To build an ostree image for a particular operating system distribution and 12 | version, use the script `.ostree/get_ostree_data.sh` to get the list of 13 | packages. If the role uses other system roles, then the script will include the 14 | packages for the other roles in the list it outputs. The list of packages will 15 | be sorted in alphanumeric order. 16 | 17 | Usage: 18 | 19 | ```bash 20 | .ostree/get_ostree_data.sh packages runtime DISTRO-VERSION FORMAT 21 | ``` 22 | 23 | `DISTRO-VERSION` is in the format that Ansible uses for `ansible_distribution` 24 | and `ansible_distribution_version` - for example, `Fedora-38`, `CentOS-8`, 25 | `RedHat-9.4` 26 | 27 | `FORMAT` is one of `toml`, `json`, `yaml`, `raw` 28 | 29 | * `toml` - each package in a TOML `[[packages]]` element 30 | 31 | ```toml 32 | [[packages]] 33 | name = "package-a" 34 | version = "*" 35 | [[packages]] 36 | name = "package-b" 37 | version = "*" 38 | ... 39 | ``` 40 | 41 | * `yaml` - a YAML list of packages 42 | 43 | ```yaml 44 | - package-a 45 | - package-b 46 | ... 47 | ``` 48 | 49 | * `json` - a JSON list of packages 50 | 51 | ```json 52 | ["package-a","package-b",...] 53 | ``` 54 | 55 | * `raw` - a plain text list of packages, one per line 56 | 57 | ```bash 58 | package-a 59 | package-b 60 | ... 61 | ``` 62 | 63 | What format you choose depends on which image builder you are using. For 64 | example, if you are using something based on 65 | [osbuild-composer](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/9/html-single/composing_installing_and_managing_rhel_for_edge_images/index#creating-an-image-builder-blueprint-for-a-rhel-for-edge-image-using-the-command-line-interface_composing-a-rhel-for-edge-image-using-image-builder-command-line), 66 | you will probably want to use the `toml` output format. 67 | -------------------------------------------------------------------------------- /ansible_pytest_extra_requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | # ansible and dependencies for all supported platforms 4 | ansible ; python_version > "2.6" 5 | idna<2.8 ; python_version < "2.7" 6 | PyYAML<5.1 ; python_version < "2.7" 7 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing to the mssql Linux System Role 2 | 3 | ## Where to start 4 | 5 | The first place to go is [Contribute](https://linux-system-roles.github.io/contribute.html). 6 | This has all of the common information that all role developers need: 7 | 8 | * Role structure and layout 9 | * Development tools - How to run tests and checks 10 | * Ansible recommended practices 11 | * Basic git and github information 12 | * How to create git commits and submit pull requests 13 | 14 | **Bugs and needed implementations** are listed on 15 | [Github Issues](https://github.com/linux-system-roles/mssql/issues). 16 | Issues labeled with 17 | [**help wanted**](https://github.com/linux-system-roles/mssql/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) 18 | are likely to be suitable for new contributors! 19 | 20 | **Code** is managed on [Github](https://github.com/linux-system-roles/mssql), using 21 | [Pull Requests](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests). 22 | -------------------------------------------------------------------------------- /custom_requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | # Write requirements for running your custom commands in tox here: 4 | -------------------------------------------------------------------------------- /defaults/main.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: false 4 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: false 5 | mssql_accept_microsoft_sql_server_standard_eula: false 6 | mssql_version: null 7 | mssql_upgrade: false 8 | mssql_password: null 9 | mssql_edition: null 10 | mssql_tools_versions: [18] 11 | mssql_tcp_port: 1433 12 | mssql_manage_firewall: false 13 | mssql_ip_address: null 14 | mssql_enable_sql_agent: null 15 | mssql_install_fts: null 16 | mssql_install_powershell: null 17 | mssql_tune_for_fua_storage: null 18 | mssql_datadir: null 19 | mssql_datadir_mode: '755' 20 | mssql_logdir: null 21 | mssql_logdir_mode: '755' 22 | 23 | mssql_pre_input_sql_file: [] 24 | mssql_post_input_sql_file: [] 25 | mssql_pre_input_sql_content: [] 26 | mssql_post_input_sql_content: [] 27 | mssql_debug: false 28 | 29 | mssql_tls_enable: null 30 | mssql_tls_cert: null 31 | mssql_tls_private_key: null 32 | mssql_tls_force: false 33 | mssql_tls_version: 1.2 34 | mssql_tls_remote_src: false 35 | mssql_tls_certificates: [] 36 | mssql_tls_self_sign: false 37 | 38 | mssql_rpm_key: https://packages.microsoft.com/keys/microsoft.asc 39 | mssql_server_repository: "{{ __mssql_server_repository }}" 40 | mssql_client_repository: "{{ __mssql_client_repository }}" 41 | 42 | mssql_ha_configure: false 43 | mssql_ha_ag_cluster_type: external 44 | # mssql_ha_replica_type must be set per host in inventory. Setting it hear to 45 | # avoid "variable not defined" error in Ansible 46 | mssql_ha_replica_type: null 47 | mssql_ha_endpoint_port: 5022 48 | mssql_ha_cert_name: null 49 | mssql_ha_private_key_password: null 50 | mssql_ha_master_key_password: null 51 | mssql_ha_reset_cert: false 52 | mssql_ha_endpoint_name: null 53 | mssql_ha_ag_name: null 54 | mssql_ha_db_names: [] 55 | 56 | mssql_ha_prep_for_pacemaker: "{{ mssql_ha_ag_cluster_type | lower != 'none' }}" 57 | mssql_ha_virtual_ip: null 58 | mssql_ha_login: null 59 | mssql_ha_login_password: null 60 | mssql_manage_ha_cluster: false 61 | 62 | mssql_ad_configure: false 63 | mssql_ad_join: true 64 | mssql_ad_sql_user: null 65 | mssql_ad_sql_password: null 66 | # Default to CN={{ mssql_ad_sql_user }},CN=Users,DC=DOMAIN,DC=SUBDOMAIN... 67 | mssql_ad_sql_user_dn: >- 68 | CN={{ mssql_ad_sql_user }}, 69 | CN=Users, 70 | {{ ad_integration_realm.split(".") 71 | | map("regex_replace", "^", "DC=") 72 | | join(",") }} 73 | mssql_ad_keytab_file: null 74 | mssql_ad_keytab_remote_src: false 75 | mssql_ad_kerberos_user: >- 76 | {{ ad_integration_user if ad_integration_user is defined 77 | else mssql_ad_sql_user }} 78 | mssql_ad_kerberos_password: >- 79 | {{ ad_integration_password if ad_integration_user is defined 80 | else mssql_ad_sql_password }} 81 | 82 | mssql_run_selinux_confined: "{{ __mssql_confined_supported | d(false) }}" 83 | mssql_manage_selinux: false 84 | -------------------------------------------------------------------------------- /files/ha_is_primary.sql: -------------------------------------------------------------------------------- 1 | IF EXISTS( 2 | SELECT role 3 | FROM sys.dm_hadr_availability_replica_states states 4 | JOIN sys.availability_replicas replicas 5 | ON states.replica_id = replicas.replica_id 6 | WHERE states.is_local = 1 7 | AND 8 | role = 1 9 | ) 10 | BEGIN 11 | PRINT(1) 12 | END 13 | ELSE 14 | BEGIN 15 | PRINT(0) 16 | END 17 | -------------------------------------------------------------------------------- /handlers/main.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Restart the mssql-server service 4 | service: 5 | name: mssql-server 6 | state: restarted 7 | when: __mssql_is_booted | bool 8 | -------------------------------------------------------------------------------- /meta/collection-requirements.yml: -------------------------------------------------------------------------------- 1 | --- 2 | collections: 3 | - name: fedora.linux_system_roles 4 | -------------------------------------------------------------------------------- /meta/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | galaxy_info: 3 | author: Sergei Petrosian 4 | description: This role installs and configures Microsoft SQL Server 5 | company: Red Hat, Inc. 6 | license: MIT 7 | min_ansible_version: "2.9" 8 | platforms: 9 | - name: Fedora 10 | versions: 11 | - all 12 | - name: EL 13 | versions: 14 | - "9" 15 | - "8" 16 | - "7" 17 | - name: SLES 18 | versions: 19 | - "15SP5" 20 | - name: opensuse 21 | versions: 22 | - "15.5" 23 | galaxy_tags: 24 | - centos 25 | - containerbuild 26 | - database 27 | - el7 28 | - el8 29 | - el9 30 | - fedora 31 | - microsoft 32 | - mssql 33 | - opensuse15dot5 34 | - rhel 35 | - sles15sp5 36 | - sql 37 | dependencies: [] 38 | -------------------------------------------------------------------------------- /molecule/default/Dockerfile.j2: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # Molecule managed 3 | 4 | {% if item.registry is defined %} 5 | FROM {{ item.registry.url }}/{{ item.image }} 6 | {% else %} 7 | FROM {{ item.image }} 8 | {% endif %} 9 | 10 | RUN set -euo pipefail; \ 11 | pkgs="python sudo yum-plugin-ovl bash"; \ 12 | if grep 'CentOS release 6' /etc/centos-release > /dev/null 2>&1; then \ 13 | for file in /etc/yum.repos.d/CentOS-*.repo; do \ 14 | if ! grep '^baseurl=.*vault[.]centos[.]org' "$file"; then \ 15 | sed -i -e 's,^mirrorlist,#mirrorlist,' \ 16 | -e 's,^#baseurl=,baseurl=,' \ 17 | -e 's,mirror.centos.org/centos/$releasever,vault.centos.org/6.10,' \ 18 | "$file"; \ 19 | fi; \ 20 | done; \ 21 | pkgs="$pkgs upstart chkconfig initscripts"; \ 22 | fi; \ 23 | if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ 24 | elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install python3 sudo python3-devel python3-dnf bash && dnf clean all; \ 25 | elif [ $(command -v yum) ]; then yum makecache fast && yum install -y $pkgs && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ 26 | elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml && zypper clean -a; \ 27 | elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates; \ 28 | elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates && xbps-remove -O; fi 29 | -------------------------------------------------------------------------------- /molecule/default/molecule.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | dependency: 4 | name: galaxy 5 | driver: 6 | name: ${LSR_MOLECULE_DRIVER:-docker} 7 | platforms: 8 | - name: centos-6 9 | image: registry.centos.org/centos:6 10 | volumes: 11 | - /sys/fs/cgroup:/sys/fs/cgroup:ro 12 | privileged: true 13 | command: /sbin/init 14 | - name: centos-7 15 | image: registry.centos.org/centos/systemd:latest 16 | volumes: 17 | - /sys/fs/cgroup:/sys/fs/cgroup:ro 18 | privileged: true 19 | command: /usr/lib/systemd/systemd --system 20 | - name: centos-8 21 | image: registry.centos.org/centos:8 22 | volumes: 23 | - /sys/fs/cgroup:/sys/fs/cgroup:ro 24 | privileged: true 25 | command: /usr/lib/systemd/systemd --system 26 | provisioner: 27 | name: ansible 28 | log: true 29 | playbooks: 30 | converge: ../../tests/tests_default.yml 31 | scenario: 32 | name: default 33 | test_sequence: 34 | - destroy 35 | - create 36 | - converge 37 | - idempotence 38 | - check 39 | - destroy 40 | -------------------------------------------------------------------------------- /molecule_extra_requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | # Write extra requirements for running molecule here: 4 | -------------------------------------------------------------------------------- /plans/README-plans.md: -------------------------------------------------------------------------------- 1 | # Introduction CI Testing Plans 2 | 3 | Linux System Roles CI runs [tmt](https://tmt.readthedocs.io/en/stable/index.html) test plans in [Testing farm](https://docs.testing-farm.io/Testing%20Farm/0.1/index.html) with the [tft.yml](https://github.com/linux-system-roles/mssql/blob/main/.github/workflows/tft.yml) GitHub workflow. 4 | 5 | The `plans/test_playbooks_parallel.fmf` plan is a test plan that runs test playbooks in parallel on multiple managed nodes. 6 | `plans/test_playbooks_parallel.fmf` is generated centrally from `https://github.com/linux-system-roles/.github/`. 7 | The automation calculates the number of managed nodes to provision with this formula: 8 | 9 | ```plain 10 | number-of-test-playbooks / 10 + 1 11 | ``` 12 | 13 | The `plans/test_playbooks_parallel.fmf` plan does the following steps: 14 | 15 | 1. Provisions systems to be used as a control node and as managed nodes. 16 | 2. Does the required preparation on systems. 17 | 3. For the given role and the given PR, runs the general test from [test.sh](https://github.com/linux-system-roles/tft-tests/blob/main/tests/general/test.sh). 18 | 19 | The [tft.yml](https://github.com/linux-system-roles/mssql/blob/main/.github/workflows/tft.yml) workflow runs the above plan and uploads the results to our Fedora storage for public access. 20 | This workflow uses Testing Farm's Github Action [Schedule tests on Testing Farm](https://github.com/marketplace/actions/schedule-tests-on-testing-farm). 21 | 22 | ## Running Tests 23 | 24 | You can run tests locally with the `tmt try` cli or remotely in Testing Farm. 25 | 26 | ### Running Tests Locally 27 | 28 | 1. Install `tmt` as described in [Installation](https://tmt.readthedocs.io/en/stable/stories/install.html). 29 | 2. Change to the role repository directory. 30 | 3. Modify `plans/test_playbooks_parallel.fmf` to suit your requirements: 31 | 1. Due to [issue #3138](https://github.com/teemtee/tmt/issues/3138), comment out all managed nodes except for one. 32 | 2. Optionally modify environment variables to, e.g. run only specified test playbooks by modifying `SYSTEM_ROLES_ONLY_TESTS`. 33 | 4. Enter `tmt try -p plans/test_playbooks_parallel `. 34 | This command identifies the `plans/test_playbooks_parallel.fmf` plan and provisions local VMs, a control node and a managed node. 35 | 5. `tmt try` is in development and does not identify tests from URL automatically, so after provisioning the machines, you must type `t`, `p`, `t` from the interactive prompt to identify tests, run preparation steps, and run the tests. 36 | 37 | ### Running in Testing Farm 38 | 39 | 1. Install `testing-farm` as described in [Installation](https://gitlab.com/testing-farm/cli/-/blob/main/README.adoc#user-content-installation). 40 | 2. Change to the role repository directory. 41 | 3. If you want to run tests with edits in your branch, you need to commit and push changes first to some branch. 42 | 4. You can uncomment "Inject your ssh public key to test systems" discover step in the plan if you want to troubleshoot tests by SSHing into test systems in Testing Farm. 43 | 5. Enter `testing-farm request`. 44 | Edit to your needs. 45 | 46 | ```bash 47 | $ TESTING_FARM_API_TOKEN= \ 48 | testing-farm request --pipeline-type="tmt-multihost" \ 49 | --plan-filter="tag:playbooks_parallel" \ 50 | --git-url "https://github.com//mssql" \ 51 | --git-ref "" \ 52 | --compose CentOS-Stream-9 \ 53 | -e "SYSTEM_ROLES_ONLY_TESTS=tests_default.yml" \ 54 | --no-wait 55 | ``` 56 | -------------------------------------------------------------------------------- /plans/mssql_ha.fmf: -------------------------------------------------------------------------------- 1 | summary: A general test for a system role 2 | tag: mssql 3 | provision: 4 | - name: control-node1 5 | role: control_node 6 | - name: managed-node1 7 | role: managed_node 8 | hardware: 9 | memory: ">= 4096 MB" 10 | # Provision all machines in the same AWS pool to ensure that they are in the same subnet 11 | pool: aws-testing-farm-09 12 | - name: managed-node2 13 | role: managed_node 14 | hardware: 15 | memory: ">= 4096 MB" 16 | pool: aws-testing-farm-09 17 | - name: managed-node3 18 | role: managed_node 19 | hardware: 20 | memory: ">= 4096 MB" 21 | pool: aws-testing-farm-09 22 | - name: virtualip-node1 23 | role: virtualip 24 | pool: aws-testing-farm-09 25 | environment: 26 | SR_ANSIBLE_VER: 2.17 27 | SR_PYTHON_VERSION: 3.12 28 | SR_REPO_NAME: mssql 29 | SR_ONLY_TESTS: "tests_configure_ha_cluster_external.yml tests_configure_ha_cluster_read_scale.yml" 30 | SR_TEST_LOCAL_CHANGES: false 31 | SR_PR_NUM: "" 32 | SR_LSR_USER: "" 33 | SR_LSR_DOMAIN: "" 34 | SR_LSR_SSH_KEY: "" 35 | SR_ARTIFACTS_DIR: "" 36 | SR_ARTIFACTS_URL: "" 37 | SR_TFT_DEBUG: false 38 | prepare: 39 | - name: Use vault.centos.org repos (CS 7, 8 EOL workaround) 40 | script: | 41 | if grep -q 'CentOS Stream release 8' /etc/redhat-release; then 42 | sed -i '/^mirror/d;s/#\(baseurl=http:\/\/\)mirror/\1vault/' /etc/yum.repos.d/*.repo 43 | fi 44 | if grep -q 'CentOS Linux release 7.9' /etc/redhat-release; then 45 | sed -i '/^mirror/d;s/#\?\(baseurl=http:\/\/\)mirror/\1vault/' /etc/yum.repos.d/*.repo 46 | fi 47 | # Replace with feature: epel: enabled once https://github.com/teemtee/tmt/pull/3128 is merged 48 | - name: Enable epel to install beakerlib 49 | script: | 50 | # CS 10 and Fedora doesn't require epel 51 | if grep -q -e 'CentOS Stream release 10' -e 'Fedora release' /etc/redhat-release; then 52 | exit 0 53 | fi 54 | yum install epel-release yum-utils -y 55 | yum-config-manager --enable epel epel-debuginfo epel-source 56 | discover: 57 | - name: Prepare managed nodes 58 | how: fmf 59 | where: managed_node 60 | filter: tag:prep_managed_node 61 | url: https://github.com/linux-system-roles/tft-tests 62 | ref: main 63 | - name: Run test playbooks from control_node 64 | how: fmf 65 | where: control_node 66 | filter: tag:mssql_ha 67 | url: https://github.com/linux-system-roles/tft-tests 68 | ref: main 69 | # Uncomment this step for troubleshooting 70 | # This is required because currently testing-farm cli doesn't support running multi-node plans 71 | # You can set ID_RSA_PUB in the environment section above to your public key to distribute it to nodes 72 | # - name: Inject your ssh public key to test systems 73 | # how: fmf 74 | # where: control_node 75 | # filter: tag:reserve_system 76 | execute: 77 | how: tmt 78 | -------------------------------------------------------------------------------- /plans/test_playbooks_parallel.fmf: -------------------------------------------------------------------------------- 1 | summary: A general test for a system role 2 | tag: playbooks_parallel 3 | provision: 4 | # TF uses `how: artemis`, and `tmt try`` uses `how: virtual`. 5 | # Hence there is no need to define `how` explicitly. 6 | - name: control-node1 7 | role: control_node 8 | - name: managed-node1 9 | role: managed_node 10 | hardware: 11 | memory: '>= 4096 MB' 12 | - name: managed-node2 13 | role: managed_node 14 | hardware: 15 | memory: '>= 4096 MB' 16 | - name: managed-node3 17 | role: managed_node 18 | hardware: 19 | memory: '>= 4096 MB' 20 | environment: 21 | SR_ANSIBLE_VER: 2.17 22 | SR_REPO_NAME: mssql 23 | SR_PYTHON_VERSION: 3.12 24 | SR_ONLY_TESTS: "" # tests_default.yml 25 | SR_TEST_LOCAL_CHANGES: true 26 | SR_PR_NUM: "" 27 | SR_LSR_USER: "" 28 | SR_LSR_DOMAIN: "" 29 | SR_LSR_SSH_KEY: "" 30 | SR_ARTIFACTS_DIR: "" 31 | SR_ARTIFACTS_URL: "" 32 | SR_TFT_DEBUG: false 33 | prepare: 34 | - name: Use vault.centos.org repos (CS 7, 8 EOL workaround) 35 | script: | 36 | if grep -q 'CentOS Stream release 8' /etc/redhat-release; then 37 | sed -i '/^mirror/d;s/#\(baseurl=http:\/\/\)mirror/\1vault/' /etc/yum.repos.d/*.repo 38 | fi 39 | if grep -q 'CentOS Linux release 7.9' /etc/redhat-release; then 40 | sed -i '/^mirror/d;s/#\?\(baseurl=http:\/\/\)mirror/\1vault/' /etc/yum.repos.d/*.repo 41 | fi 42 | # Replace with feature: epel: enabled once https://github.com/teemtee/tmt/pull/3128 is merged 43 | - name: Enable epel to install beakerlib 44 | script: | 45 | # CS 10 and Fedora doesn't require epel 46 | if grep -q -e 'CentOS Stream release 10' -e 'Fedora release' /etc/redhat-release; then 47 | exit 0 48 | fi 49 | yum install epel-release yum-utils -y 50 | yum-config-manager --enable epel epel-debuginfo epel-source 51 | discover: 52 | - name: Prepare managed node 53 | how: fmf 54 | where: managed_node 55 | filter: tag:prep_managed_node 56 | url: https://github.com/linux-system-roles/tft-tests 57 | ref: main 58 | - name: Run test playbooks from control_node 59 | how: fmf 60 | where: control_node 61 | filter: tag:test_playbooks 62 | url: https://github.com/linux-system-roles/tft-tests 63 | ref: main 64 | # Uncomment this step for troubleshooting 65 | # This is required because currently testing-farm cli doesn't support running multi-node plans 66 | # You can set ID_RSA_PUB in the environment section above to your public key to distribute it to nodes 67 | # - name: Inject your ssh public key to test systems 68 | # how: fmf 69 | # where: control_node 70 | # filter: tag:reserve_system 71 | # url: https://github.com/linux-system-roles/tft-tests 72 | # ref: main 73 | execute: 74 | how: tmt 75 | -------------------------------------------------------------------------------- /pylint_extra_requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | # Write extra requirements for running pylint here: 4 | -------------------------------------------------------------------------------- /pytest_extra_requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | 3 | # Write extra requirements for running pytest here: 4 | # If you need ansible then uncomment the following line: 5 | #-ransible_pytest_extra_requirements.txt 6 | # If you need mock then uncomment the following line: 7 | #mock ; python_version < "3.0" 8 | -------------------------------------------------------------------------------- /tasks/configure_storage_paths.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: >- 4 | Ensure the directory and permissions {{ __mssql_storage_path }} 5 | file: 6 | path: "{{ __mssql_storage_path }}" 7 | state: directory 8 | owner: mssql 9 | group: mssql 10 | mode: "{{ __mssql_storage_mode }}" 11 | 12 | - name: Append facts for the selinux role 13 | vars: 14 | selinux_fcontext: 15 | - target: "{{ __mssql_storage_path }}" 16 | setype: "{{ 'mssql_db_t' if __mssql_storage_setting == 'defaultlogdir' 17 | else 'mssql_var_t' }}" 18 | ftype: d 19 | state: present 20 | selinux_restore_dir: 21 | - "{{ __mssql_storage_path }}" 22 | set_fact: 23 | selinux_fcontexts: "{{ 24 | (selinux_fcontexts | default([])) + selinux_fcontext }}" 25 | selinux_restore_dirs: "{{ 26 | (selinux_restore_dirs | default([])) + selinux_restore_dir }}" 27 | when: 28 | - mssql_manage_selinux | bool 29 | - mssql_run_selinux_confined | bool 30 | 31 | - name: Configure the setting {{ __mssql_storage_setting }} 32 | include_tasks: mssql_conf_setting.yml 33 | vars: 34 | __mssql_conf_setting: filelocation {{ __mssql_storage_setting }} 35 | __mssql_conf_setting_value: "{{ __mssql_storage_path }}" 36 | -------------------------------------------------------------------------------- /tasks/input_sql_files.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # This task files inputs sql files into MSSQL. 3 | # This task takes a list of files. 4 | # If you feed a file with .j2 extension, it generates a template from it. 5 | # If you feed a file with other extension, it just copies the file and runs it. 6 | --- 7 | # This is required because in the case when a task that precedes the input 8 | # task fails, the print task prints a previous result 9 | - name: Unset the __mssql_sqlcmd_input variable 10 | set_fact: 11 | __mssql_sqlcmd_input: [] 12 | 13 | - name: Verify that the mssql_password variable is defined 14 | assert: 15 | that: 16 | - mssql_password is not none 17 | fail_msg: >- 18 | You must define the mssql_password variable because MSSQL requires 19 | the sa user to authenticate to input SQL files. 20 | 21 | - name: Ensure that the mssql-server service is started 22 | service: 23 | name: mssql-server 24 | state: started 25 | 26 | - name: Wait for mssql-server to prepare for client connections 27 | wait_for: 28 | path: /var/opt/mssql/log/errorlog 29 | search_regex: SQL Server is now ready for client connections 30 | delay: 1 31 | 32 | - name: Prepare MSSQL and facts for logging in 33 | include_tasks: verify_password.yml 34 | when: >- 35 | (__mssql_sqlcmd_login_cmd is none) or 36 | (__mssql_sqlcmd_login_cmd is not defined) 37 | 38 | - name: Input SQL script files to MSSQL 39 | include_tasks: sqlcmd_input_file.yml 40 | loop: "{{ __mssql_sql_files_to_input }}" 41 | when: __mssql_sql_files_to_input is defined 42 | 43 | - name: Input SQL script contents to MSSQL 44 | include_tasks: sqlcmd_input_content.yml 45 | loop: "{{ __mssql_sql_content_to_input }}" 46 | when: __mssql_sql_content_to_input is defined 47 | -------------------------------------------------------------------------------- /tasks/mssql_conf_setting.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Get the setting {{ __mssql_conf_setting }} 4 | # noqa jinja[spacing] 5 | command: >- 6 | grep '^{{ __mssql_conf_setting | regex_replace('^.*\s' ,'') }} = ' 7 | {{ __mssql_conf_path }} 8 | changed_when: false 9 | failed_when: false 10 | register: __mssql_conf_get_setting 11 | 12 | - name: Configure the setting {{ __mssql_conf_setting }} 13 | # noqa jinja[spacing] 14 | command: >- 15 | {{ __mssql_conf_cli }} set {{ __mssql_conf_setting | 16 | regex_replace(' ','.') }} {{ __mssql_conf_setting_value | quote }} 17 | vars: 18 | __mssql_conf_get_value: >- 19 | {{ __mssql_conf_get_setting.stdout | 20 | regex_replace('^.*\s=\s', '') }} 21 | when: >- 22 | (__mssql_conf_setting_value != 'unset') and 23 | (("No setting for the given" in __mssql_conf_get_setting.stdout) or 24 | ((__mssql_conf_setting_value | type_debug != "bool") and 25 | (__mssql_conf_setting_value | string | lower not in 26 | __mssql_conf_get_value | lower)) or 27 | ((__mssql_conf_setting_value | type_debug == "bool") and 28 | (__mssql_conf_setting_value != __mssql_conf_get_value | bool))) 29 | register: __mssql_conf_set 30 | failed_when: 31 | - >- 32 | ("error" in __mssql_conf_set.stdout | lower) or 33 | (__mssql_conf_set is failed) 34 | - >- 35 | "is already in use. Please use another port" not in 36 | __mssql_conf_set.stdout 37 | changed_when: >- 38 | "SQL Server needs to be restarted in order to apply this setting." in 39 | __mssql_conf_set.stdout 40 | notify: Restart the mssql-server service 41 | 42 | - name: Unset the setting {{ __mssql_conf_setting }} 43 | command: >- 44 | {{ __mssql_conf_cli }} unset 45 | {{ __mssql_conf_setting | regex_replace('\s', '.') }} 46 | when: 47 | - __mssql_conf_setting_value == "unset" 48 | - '"No setting for the given" not in __mssql_conf_get_setting.stdout' 49 | register: __mssql_conf_unset 50 | failed_when: >- 51 | ("error" in __mssql_conf_unset.stdout | lower) or 52 | (__mssql_conf_unset is failed) 53 | notify: Restart the mssql-server service 54 | changed_when: true 55 | -------------------------------------------------------------------------------- /tasks/set_vars.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure ansible_facts used by role 4 | setup: 5 | gather_subset: "{{ __mssql_required_facts_subsets }}" 6 | when: __mssql_required_facts | 7 | difference(ansible_facts.keys() | list) | length > 0 8 | 9 | - name: Set platform/version specific variables 10 | include_vars: "{{ __mssql_vars_file }}" 11 | loop: 12 | - "{{ ansible_os_family }}.yml" 13 | - "{{ ansible_distribution }}.yml" 14 | - "{{ ansible_distribution }}_{{ ansible_distribution_major_version }}.yml" 15 | - "{{ ansible_distribution }}_{{ ansible_distribution_version }}.yml" 16 | vars: 17 | __mssql_vars_file: "{{ role_path }}/vars/{{ item }}" 18 | when: __mssql_vars_file is file 19 | 20 | - name: Determine if system is booted with systemd 21 | when: __mssql_is_booted is not defined 22 | block: 23 | - name: Run systemctl 24 | # noqa command-instead-of-module 25 | command: systemctl is-system-running 26 | register: __is_system_running 27 | changed_when: false 28 | failed_when: false 29 | 30 | - name: Require installed systemd 31 | fail: 32 | msg: "Error: This role requires systemd to be installed." 33 | when: '"No such file or directory" in __is_system_running.msg | d("")' 34 | 35 | - name: Set flag to indicate that systemd runtime operations are available 36 | set_fact: 37 | # see https://www.man7.org/linux/man-pages/man1/systemctl.1.html#:~:text=is-system-running%20output 38 | __mssql_is_booted: "{{ __is_system_running.stdout != 'offline' }}" 39 | -------------------------------------------------------------------------------- /tasks/sqlcmd_input_content.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Input script content with sqlcmd 3 | block: 4 | - name: Input script content 5 | command: >- 6 | {{ __mssql_sqlcmd_login_cmd }} -Q {{ item | quote }} -b 7 | register: __mssql_sqlcmd_input 8 | changed_when: '"successfully" in __mssql_sqlcmd_input.stdout' 9 | no_log: true 10 | until: >- 11 | "TCP Provider: Error code 0x102" not in __mssql_sqlcmd_input.stdout and 12 | "TCP Provider: Error code 0x102" not in __mssql_sqlcmd_input.stderr and 13 | "TCP Provider: Error code 0x2749" not in __mssql_sqlcmd_input.stdout and 14 | "TCP Provider: Error code 0x2749" not in __mssql_sqlcmd_input.stderr 15 | 16 | always: 17 | # Role prints the output if the input succeeds, otherwise Ansible prints the 18 | # output from the failed input tasks 19 | - name: Print results and clean up after inputting script content 20 | when: 21 | - >- 22 | (mssql_debug | bool) or 23 | (__mssql_sqlcmd_input is failed) 24 | - __mssql_sqlcmd_loop | length > 0 25 | debug: 26 | var: __mssql_sqlcmd_loop 27 | loop: 28 | - "{{ __mssql_sqlcmd_input.stdout_lines }}" 29 | - "{{ __mssql_sqlcmd_input.stderr_lines }}" 30 | loop_control: 31 | loop_var: __mssql_sqlcmd_loop 32 | -------------------------------------------------------------------------------- /tasks/sqlcmd_input_file.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Input with sqlcmd {{ item }} 3 | block: 4 | - name: Create a tempfile on the host for {{ item }} 5 | tempfile: 6 | state: file 7 | prefix: "{{ item | basename }}_" 8 | suffix: .sql 9 | register: __mssql_sql_tempfile 10 | changed_when: false 11 | 12 | - name: Copy the file to the host {{ item }} 13 | copy: 14 | src: "{{ item }}" 15 | dest: "{{ __mssql_sql_tempfile.path }}" 16 | mode: preserve 17 | when: item is not search(".*\.j2$") 18 | changed_when: false 19 | 20 | - name: Generate the template on the host for {{ item }} 21 | template: 22 | src: "{{ item }}" 23 | dest: "{{ __mssql_sql_tempfile.path }}" 24 | mode: preserve 25 | when: item is search(".*\.j2$") 26 | changed_when: false 27 | 28 | - name: Input with the sqlcmd command {{ item }} 29 | command: >- 30 | {{ __mssql_sqlcmd_login_cmd }} -i {{ __mssql_sql_tempfile.path }} -b 31 | register: __mssql_sqlcmd_input 32 | changed_when: '"successfully" in __mssql_sqlcmd_input.stdout' 33 | no_log: true 34 | until: >- 35 | "TCP Provider: Error code 0x68" not in __mssql_sqlcmd_input.stdout and 36 | "TCP Provider: Error code 0x68" not in __mssql_sqlcmd_input.stderr and 37 | "TCP Provider: Error code 0x102" not in __mssql_sqlcmd_input.stdout and 38 | "TCP Provider: Error code 0x102" not in __mssql_sqlcmd_input.stderr and 39 | "TCP Provider: Error code 0x2749" not in __mssql_sqlcmd_input.stdout and 40 | "TCP Provider: Error code 0x2749" not in __mssql_sqlcmd_input.stderr 41 | always: 42 | # Role prints the output if the input succeeds, otherwise Ansible prints the 43 | # output from the failed input tasks 44 | - name: Print file location and output from inputting {{ item }} 45 | when: 46 | - >- 47 | (mssql_debug | bool) or 48 | (__mssql_sqlcmd_input is failed) 49 | - __mssql_sqlcmd_loop | length > 0 50 | debug: 51 | var: __mssql_sqlcmd_loop 52 | loop: 53 | - "{{ __mssql_sql_tempfile.path }}" 54 | - "{{ __mssql_sqlcmd_input.stdout_lines }}" 55 | - "{{ __mssql_sqlcmd_input.stderr_lines }}" 56 | loop_control: 57 | loop_var: __mssql_sqlcmd_loop 58 | 59 | # Role keeps the file if the input failed 60 | - name: Remove the tempfile {{ item }} 61 | file: 62 | path: "{{ __mssql_sql_tempfile.path }}" 63 | state: absent 64 | when: 65 | - __mssql_sqlcmd_input is succeeded 66 | - not mssql_debug 67 | changed_when: false 68 | -------------------------------------------------------------------------------- /tasks/verify_password.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that the mssql-server service is started 4 | service: 5 | name: mssql-server 6 | state: started 7 | when: __mssql_is_booted | bool 8 | 9 | - name: Check if a custom tcpport setting exist 10 | command: grep '^tcpport = ' {{ __mssql_conf_path }} 11 | failed_when: false 12 | changed_when: false 13 | register: __mssql_custom_tcp_port 14 | 15 | - name: Check if a custom ipaddress setting exist 16 | command: grep '^ipaddress = ' {{ __mssql_conf_path }} 17 | failed_when: false 18 | changed_when: false 19 | register: __mssql_custom_ip_address 20 | 21 | - name: Set a fact with a login command 22 | vars: 23 | __ipaddress: >- 24 | {{ __mssql_custom_ip_address.stdout | 25 | regex_search('[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}') }} 26 | __tcpport: >- 27 | {{ __mssql_custom_tcp_port.stdout | regex_search('[1-9][0-9]{0,4}') }} 28 | __s_arg: >- 29 | {{ (__ipaddress or __tcpport) | ternary('-S', '') }} 30 | __ipaddress_arg: >- 31 | {{ __ipaddress if __ipaddress else '127.0.0.1' if __tcpport else '' }} 32 | set_fact: 33 | __mssql_sqlcmd_login_cmd: >- 34 | {{ __sqlcmd_cli }} 35 | {{ __s_arg }} 36 | {{ __ipaddress_arg }}{{ ',' if __tcpport 37 | else '' }}{{ __tcpport if __tcpport else '' }} 38 | -U sa -P {{ mssql_password | quote }} 39 | no_log: true 40 | -------------------------------------------------------------------------------- /templates/configure_endpoint.j2: -------------------------------------------------------------------------------- 1 | IF NOT EXISTS ( 2 | SELECT name 3 | FROM sys.database_mirroring_endpoints 4 | WHERE name = '{{ mssql_ha_endpoint_name }}' 5 | ) 6 | BEGIN 7 | PRINT 'Endpoint {{ mssql_ha_endpoint_name }} does not exist, creating'; 8 | CREATE ENDPOINT {{ mssql_ha_endpoint_name }} 9 | STATE = STARTED 10 | AS TCP (LISTENER_PORT = {{ mssql_ha_endpoint_port }}) 11 | FOR DATABASE_MIRRORING ( 12 | ROLE = {{ __mssql_ha_endpoint_role }}, 13 | AUTHENTICATION = CERTIFICATE {{ mssql_ha_cert_name }}, 14 | ENCRYPTION = REQUIRED ALGORITHM AES 15 | ); 16 | PRINT 'Endpoint {{ mssql_ha_endpoint_name }} created successfully' 17 | END 18 | ELSE 19 | BEGIN 20 | PRINT 'Verifying the existing endpoint {{ mssql_ha_endpoint_name }}'; 21 | IF NOT EXISTS ( 22 | SELECT name, port FROM sys.tcp_endpoints 23 | WHERE name = '{{ mssql_ha_endpoint_name }}' AND 24 | port = {{ mssql_ha_endpoint_port }} 25 | ) 26 | BEGIN 27 | ALTER ENDPOINT {{ mssql_ha_endpoint_name }} 28 | AS TCP (LISTENER_PORT = {{ mssql_ha_endpoint_port }}); 29 | PRINT 'The LISTENER_PORT setting for the {{ mssql_ha_endpoint_name }} \ 30 | endpoint updated to {{ mssql_ha_endpoint_port }} successfully'; 31 | END 32 | ELSE 33 | BEGIN 34 | PRINT 'The LISTENER_PORT setting for the {{ mssql_ha_endpoint_name }} \ 35 | endpoint is already set to {{ mssql_ha_endpoint_port }}, skipping'; 36 | END 37 | IF NOT EXISTS ( 38 | SELECT name, role_desc FROM sys.database_mirroring_endpoints 39 | WHERE name = '{{ mssql_ha_endpoint_name }}' AND 40 | role_desc = '{{ __mssql_ha_endpoint_role }}' 41 | ) 42 | BEGIN 43 | ALTER ENDPOINT {{ mssql_ha_endpoint_name }} 44 | FOR DATABASE_MIRRORING (ROLE = {{ __mssql_ha_endpoint_role }}); 45 | PRINT 'The ROLE setting for the {{ mssql_ha_endpoint_name }} \ 46 | endpoint updated to {{ __mssql_ha_endpoint_role }} successfully'; 47 | END 48 | ELSE 49 | BEGIN 50 | PRINT 'The ROLE setting for the {{ mssql_ha_endpoint_name }} \ 51 | endpoint is already set to {{ __mssql_ha_endpoint_role }}, skipping'; 52 | END 53 | IF NOT EXISTS ( 54 | SELECT endp.name as endpoint_name, 55 | cert.name as cert_name, 56 | cert.certificate_id cert_id 57 | FROM sys.certificates cert 58 | JOIN sys.database_mirroring_endpoints endp 59 | ON cert.certificate_id = endp.certificate_id 60 | WHERE endp.name = '{{ mssql_ha_endpoint_name }}' AND 61 | cert.name = '{{ mssql_ha_cert_name }}' 62 | ) 63 | BEGIN 64 | ALTER ENDPOINT {{ mssql_ha_endpoint_name }} 65 | FOR DATABASE_MIRRORING ( 66 | AUTHENTICATION = CERTIFICATE {{ mssql_ha_cert_name }} 67 | ); 68 | PRINT 'The certificate for the {{ mssql_ha_endpoint_name }} \ 69 | endpoint updated to {{ mssql_ha_cert_name }} successfully'; 70 | END 71 | ELSE 72 | BEGIN 73 | PRINT 'The certificate for the {{ mssql_ha_endpoint_name }} \ 74 | endpoint is already set to {{ mssql_ha_cert_name }}, skipping'; 75 | END 76 | IF NOT EXISTS ( 77 | SELECT name, encryption_algorithm_desc 78 | FROM sys.database_mirroring_endpoints 79 | WHERE name = '{{ mssql_ha_endpoint_name }}' AND 80 | encryption_algorithm_desc = 'AES' 81 | ) 82 | BEGIN 83 | ALTER ENDPOINT {{ mssql_ha_endpoint_name }} 84 | FOR DATABASE_MIRRORING (ENCRYPTION = REQUIRED ALGORITHM AES); 85 | PRINT 'The ENCRYPTION setting for the {{ mssql_ha_endpoint_name }} \ 86 | endpoint updated to AES successfully'; 87 | END 88 | ELSE 89 | BEGIN 90 | PRINT 'The ENCRYPTION setting for the {{ mssql_ha_endpoint_name }} \ 91 | endpoint is already set to AES, skipping'; 92 | END 93 | IF NOT EXISTS ( 94 | SELECT name, state 95 | FROM sys.tcp_endpoints 96 | WHERE name = '{{ mssql_ha_endpoint_name }}' AND 97 | state = 0 98 | ) 99 | BEGIN 100 | PRINT 'Endpoint {{ mssql_ha_endpoint_name }} is not started, starting'; 101 | ALTER ENDPOINT {{ mssql_ha_endpoint_name }} STATE = STARTED; 102 | PRINT 'Endpoint {{ mssql_ha_endpoint_name }} started successfully'; 103 | END 104 | ELSE 105 | BEGIN 106 | PRINT 'Endpoint {{ mssql_ha_endpoint_name }} is already started, skipping'; 107 | END 108 | END 109 | -------------------------------------------------------------------------------- /templates/configure_listener.j2: -------------------------------------------------------------------------------- 1 | IF NOT EXISTS ( 2 | SELECT dns_name 3 | FROM sys.availability_group_listeners 4 | ) 5 | BEGIN 6 | PRINT 'Adding the {{ mssql_ha_ag_name }}-listener listener to the \ 7 | {{ mssql_ha_ag_name }} availability group'; 8 | ALTER AVAILABILITY GROUP {{ mssql_ha_ag_name }} 9 | ADD LISTENER '{{ mssql_ha_ag_name }}-listener' ( 10 | WITH IP ( ('{{ mssql_ha_virtual_ip }}','255.255.255.0') ), 11 | PORT = {{ mssql_tcp_port }} 12 | ); 13 | PRINT 'Added the {{ mssql_ha_ag_name }}-listener listener successfully'; 14 | END 15 | ELSE 16 | BEGIN 17 | DECLARE @ListenerName VARCHAR(30) = ( 18 | SELECT dns_name FROM sys.availability_group_listeners 19 | ); 20 | PRINT 'Verifying the existing listener ' + CAST(@ListenerName AS VARCHAR); 21 | IF NOT EXISTS ( 22 | SELECT port 23 | FROM sys.availability_group_listeners 24 | WHERE port = '{{ mssql_tcp_port }}' 25 | ) 26 | BEGIN 27 | PRINT 'Modifying the listener port setting' 28 | EXEC ( 29 | 'ALTER AVAILABILITY GROUP {{ mssql_ha_ag_name }} 30 | MODIFY LISTENER "'+@ListenerName+'" ( 31 | PORT = {{ mssql_tcp_port }} 32 | )' 33 | ); 34 | PRINT 'Set listener port to {{ mssql_tcp_port }} successfully' 35 | END 36 | ELSE 37 | BEGIN 38 | PRINT 'The port setting is already set correctly, skipping' 39 | END 40 | IF NOT EXISTS ( 41 | SELECT listener_id 42 | FROM sys.availability_group_listener_ip_addresses 43 | WHERE ip_address = '{{ mssql_ha_virtual_ip }}' AND 44 | ip_subnet_mask = '255.255.255.0' 45 | ) 46 | BEGIN 47 | PRINT 'Modifying the listener ip address setting' 48 | EXEC ( 49 | 'ALTER AVAILABILITY GROUP {{ mssql_ha_ag_name }} 50 | MODIFY LISTENER "'+@ListenerName+'" ( 51 | ADD IP ("{{ mssql_ha_virtual_ip }}","255.255.255.0") 52 | )' 53 | ); 54 | PRINT 'Added listener ip address \ 55 | {{ mssql_ha_virtual_ip}},255.255.255.0 successfully' 56 | END 57 | ELSE 58 | BEGIN 59 | PRINT 'The listener ip address setting is already set correctly, skipping' 60 | END 61 | END 62 | -------------------------------------------------------------------------------- /templates/create_and_back_up_cert.j2: -------------------------------------------------------------------------------- 1 | -- Enabling NOCOUNT to suppress (1 rows affected) messages from DECLARE 2 | -- keywords on the output 3 | SET NOCOUNT ON; 4 | 5 | DECLARE @cerExists INT; 6 | EXEC master.dbo.xp_fileexist '{{ __mssql_ha_cert_dest }}', @cerExists OUTPUT; 7 | DECLARE @pvkExists INT; 8 | EXEC master.dbo.xp_fileexist '{{ __mssql_ha_private_key_dest }}', 9 | @pvkExists OUTPUT; 10 | 11 | IF NOT EXISTS( 12 | SELECT name 13 | FROM sys.certificates 14 | WHERE name = '{{ mssql_ha_cert_name }}' 15 | ) 16 | BEGIN 17 | PRINT 'Certificate {{ mssql_ha_cert_name }} does not exist, creating'; 18 | IF (@cerExists = 1 AND @pvkExists = 1) OR (@cerExists != @pvkExists) 19 | BEGIN 20 | THROW 51000, 'Certificate {{ mssql_ha_cert_name }} does not exist in \ 21 | SQL Server, however, {{ __mssql_ha_cert_dest }} \ 22 | and/or {{ __mssql_ha_private_key_dest }} files do exist. \ 23 | You must either remove the files, or run the role with \ 24 | `mssql_ha_reset_cert: true` to regenerate certificates. Ensure to read \ 25 | carefully what `mssql_ha_reset_cert: true` does in the README.md file of the \ 26 | role beforehand', 1; 27 | END 28 | ELSE 29 | BEGIN 30 | CREATE CERTIFICATE {{ mssql_ha_cert_name }} 31 | WITH SUBJECT = 'Managed by microsoft.sql.server'; 32 | PRINT 'Certificate {{ mssql_ha_cert_name }} created successfully'; 33 | END 34 | END 35 | ELSE 36 | BEGIN 37 | PRINT 'Certificate {{ mssql_ha_cert_name }} already exists, skipping'; 38 | END 39 | 40 | IF @cerExists = 1 AND @pvkExists = 1 41 | BEGIN 42 | PRINT '{{ __mssql_ha_cert_dest }} and \ 43 | {{ __mssql_ha_private_key_dest }} already exist, skipping'; 44 | END 45 | ELSE IF @cerExists = 0 AND @pvkExists = 0 46 | BEGIN 47 | PRINT 'Exporting a certificate and private key to \ 48 | {{ __mssql_ha_cert_dest }} and \ 49 | {{ __mssql_ha_private_key_dest }}'; 50 | BACKUP CERTIFICATE {{ mssql_ha_cert_name }} 51 | TO FILE = '{{ __mssql_ha_cert_dest }}' 52 | WITH PRIVATE KEY ( 53 | FILE = '{{ __mssql_ha_private_key_dest }}', 54 | ENCRYPTION BY PASSWORD = '{{ mssql_ha_private_key_password }}' 55 | ); 56 | PRINT 'Certificate and private key files \ 57 | {{ __mssql_ha_cert_dest }} and \ 58 | {{ __mssql_ha_private_key_dest }} exported successfully'; 59 | END 60 | ELSE IF @cerExists = 1 AND @pvkExists = 0 61 | BEGIN 62 | PRINT '{{ __mssql_ha_private_key_dest }} does not exist \ 63 | while {{ __mssql_ha_cert_dest }} exists. You must \ 64 | either remove the files, or run the role with `mssql_ha_reset_cert: true` to \ 65 | regenerate certificates.'; 66 | END 67 | ELSE IF @cerExists = 0 AND @pvkExists = 1 68 | BEGIN 69 | PRINT '{{ __mssql_ha_cert_dest }} does not exist \ 70 | while {{ __mssql_ha_private_key_dest }} exists. You must \ 71 | either remove the files, or run the role with `mssql_ha_reset_cert: true` to \ 72 | regenerate certificates.'; 73 | END 74 | -------------------------------------------------------------------------------- /templates/create_ha_login.j2: -------------------------------------------------------------------------------- 1 | USE master; 2 | IF NOT EXISTS ( 3 | SELECT name FROM sys.server_principals 4 | WHERE name = '{{ mssql_ha_login }}' 5 | ) 6 | BEGIN 7 | PRINT 'A {{ mssql_ha_login }} login does not exist, creating'; 8 | CREATE LOGIN {{ mssql_ha_login }} 9 | WITH PASSWORD = N'{{ mssql_ha_login_password }}'; 10 | PRINT 'The {{ mssql_ha_login }} login created successfully'; 11 | END 12 | ELSE 13 | BEGIN 14 | PRINT 'A {{ mssql_ha_login }} login already exists, skipping' 15 | END 16 | 17 | IF IS_SRVROLEMEMBER ('sysadmin','{{ mssql_ha_login }}') = 1 18 | BEGIN 19 | PRINT '{{ mssql_ha_login }} is a member of sysadmin role, skipping'; 20 | END 21 | ELSE 22 | BEGIN 23 | PRINT 'Adding {{ mssql_ha_login }} to the sysadmin server role'; 24 | ALTER SERVER ROLE sysadmin ADD MEMBER {{ mssql_ha_login }}; 25 | PRINT '{{ mssql_ha_login }} added to the sysadmin server role successfully'; 26 | END 27 | -------------------------------------------------------------------------------- /templates/create_master_key_encryption.j2: -------------------------------------------------------------------------------- 1 | IF NOT EXISTS ( 2 | SELECT name 3 | FROM sys.symmetric_keys 4 | WHERE name LIKE '%databaseMasterkey%' 5 | ) 6 | BEGIN 7 | PRINT 'Master key does not exist, creating'; 8 | CREATE MASTER KEY ENCRYPTION BY PASSWORD = 9 | '{{ mssql_ha_master_key_password }}'; 10 | PRINT 'Master key created successfully'; 11 | END 12 | ELSE 13 | BEGIN 14 | PRINT 'Master key already exists, verifying the provided password against \ 15 | the existing master key'; 16 | BEGIN TRY 17 | OPEN MASTER KEY DECRYPTION BY PASSWORD = 18 | '{{ mssql_ha_master_key_password }}'; 19 | PRINT 'The provided master key password is correct'; 20 | END TRY 21 | BEGIN CATCH 22 | {% if not mssql_ha_reset_cert %} 23 | PRINT 'You provided an incorrect master key password with the \ 24 | mssql_ha_master_key_password variable'; 25 | THROW; 26 | {% elif mssql_ha_reset_cert %} 27 | PRINT 'Master key password provided with the \ 28 | mssql_ha_master_key_password variable does not match the existing password, \ 29 | dropping master key to re-create it'; 30 | DROP MASTER KEY; 31 | PRINT 'Master key dropped successfully'; 32 | CREATE MASTER KEY ENCRYPTION BY PASSWORD = 33 | '{{ mssql_ha_master_key_password }}'; 34 | PRINT 'Master key created successfully'; 35 | {% endif %} 36 | END CATCH 37 | END 38 | -------------------------------------------------------------------------------- /templates/drop_cert.j2: -------------------------------------------------------------------------------- 1 | IF EXISTS( 2 | SELECT name 3 | FROM sys.certificates 4 | WHERE name = '{{ mssql_ha_cert_name }}' 5 | ) 6 | BEGIN 7 | PRINT 'Certificate {{ mssql_ha_cert_name }} already exists, checking if \ 8 | there is an endopint associated with this certificate'; 9 | DECLARE @EndpointName VARCHAR(30) = ( 10 | SELECT endp.name as endpoint_name 11 | FROM sys.certificates cert 12 | JOIN sys.database_mirroring_endpoints endp 13 | ON cert.certificate_id = endp.certificate_id 14 | WHERE cert.name = '{{ mssql_ha_cert_name }}' 15 | ); 16 | IF @EndpointName IS NOT NULL 17 | BEGIN 18 | PRINT 'Removing the endpoint ' + CAST(@EndpointName AS VARCHAR) + 19 | ' to re-create it'; 20 | EXEC ('DROP ENDPOINT ' +@EndpointName); 21 | PRINT 'The {{ mssql_ha_endpoint_name }} endpoint removed successfully'; 22 | END 23 | ELSE 24 | BEGIN 25 | PRINT 'There is no endpoint associated with this certificate, skipping'; 26 | END 27 | PRINT 'Removing the {{ mssql_ha_cert_name }} certificate to re-create it'; 28 | DROP CERTIFICATE {{ mssql_ha_cert_name }}; 29 | PRINT 'The {{ mssql_ha_cert_name }} certificate removed successfully' 30 | END 31 | -------------------------------------------------------------------------------- /templates/enable_alwayson.j2: -------------------------------------------------------------------------------- 1 | IF NOT EXISTS ( 2 | SELECT name, startup_state 3 | FROM sys.server_event_sessions WHERE 4 | name = 'AlwaysOn_health' and 5 | startup_state = 1 6 | ) 7 | BEGIN 8 | PRINT 'AlwaysOn Health events are not enabled, enabling'; 9 | ALTER EVENT SESSION AlwaysOn_health ON SERVER WITH (STARTUP_STATE=ON); 10 | PRINT 'AlwaysOn Health events enabled successfully'; 11 | END 12 | ELSE 13 | BEGIN 14 | PRINT 'AlwaysOn Health events already enabled, skipping'; 15 | END 16 | -------------------------------------------------------------------------------- /templates/get_ansible_managed.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | {{ "system_role:mssql" | comment(prefix="", postfix="") }} 3 | -------------------------------------------------------------------------------- /templates/grant_permissions_to_ha_login.j2: -------------------------------------------------------------------------------- 1 | -- Need to find how to add permissions idempotently 2 | PRINT 'Granting the required permissions to {{ mssql_ha_login }}'; 3 | GRANT ALTER, CONTROL, VIEW DEFINITION ON 4 | AVAILABILITY GROUP::{{ mssql_ha_ag_name }} 5 | TO {{ mssql_ha_login }}; 6 | GRANT VIEW SERVER STATE TO {{ mssql_ha_login }}; 7 | PRINT 'Required permissions granted to {{ mssql_ha_login }}'; 8 | -------------------------------------------------------------------------------- /templates/join_to_ag.j2: -------------------------------------------------------------------------------- 1 | IF EXISTS ( 2 | SELECT name 3 | FROM sys.availability_groups 4 | WHERE name = '{{ mssql_ha_ag_name }}' 5 | ) 6 | AND NOT EXISTS ( 7 | SELECT ag.name, replica.replica_server_name, replica.availability_mode_desc 8 | FROM sys.availability_groups ag 9 | JOIN sys.availability_replicas replica 10 | ON ag.group_id = replica.group_id 11 | WHERE ag.name = '{{ mssql_ha_ag_name }}' AND 12 | ag.cluster_type_desc = '{{ mssql_ha_ag_cluster_type }}' AND 13 | replica.replica_server_name = '{{ ansible_hostname }}' AND 14 | replica.availability_mode_desc = '{{ __mssql_ha_availability_mode }}' 15 | ) 16 | BEGIN 17 | PRINT 'The existing availability group {{ mssql_ha_ag_name }} has \ 18 | incorrect availability mode set for the {{ ansible_hostname }} replica, \ 19 | removing this availability group to re-create it' 20 | DROP AVAILABILITY GROUP {{ mssql_ha_ag_name }}; 21 | PRINT 'The availability group {{ mssql_ha_ag_name }} removed successfully' 22 | END 23 | ELSE IF EXISTS ( 24 | SELECT name 25 | FROM sys.availability_groups 26 | WHERE name = '{{ mssql_ha_ag_name }}' 27 | ) 28 | AND EXISTS ( 29 | SELECT ag.name, replica.replica_server_name, replica.availability_mode_desc 30 | FROM sys.availability_groups ag 31 | JOIN sys.availability_replicas replica 32 | ON ag.group_id = replica.group_id 33 | WHERE ag.name = '{{ mssql_ha_ag_name }}' AND 34 | replica.replica_server_name = '{{ ansible_hostname }}' AND 35 | replica.availability_mode_desc = '{{ __mssql_ha_availability_mode }}' 36 | ) 37 | BEGIN 38 | PRINT 'Already joined to the {{ mssql_ha_ag_name }} availability group, \ 39 | skipping' 40 | END 41 | 42 | IF NOT EXISTS( 43 | SELECT name 44 | FROM sys.availability_groups 45 | WHERE name = '{{ mssql_ha_ag_name }}' 46 | ) 47 | BEGIN 48 | PRINT 'Joining to the {{ mssql_ha_ag_name }} availability group'; 49 | ALTER AVAILABILITY GROUP {{ mssql_ha_ag_name }} 50 | JOIN WITH (CLUSTER_TYPE = {{ mssql_ha_ag_cluster_type }}); 51 | PRINT 'Joined to the {{ mssql_ha_ag_name }} availability group successfully'; 52 | END 53 | 54 | {% if mssql_ha_replica_type in ['synchronous', 'asynchronous'] %} 55 | -- It is not possible to grant permissions fully idempotently 56 | ALTER AVAILABILITY GROUP {{ mssql_ha_ag_name }} GRANT CREATE ANY DATABASE; 57 | PRINT 'Granted the CREATE ANY DATABASE permission to the \ 58 | {{ mssql_ha_ag_name }} availability group'; 59 | {% endif %} 60 | -------------------------------------------------------------------------------- /templates/replicate_db.j2: -------------------------------------------------------------------------------- 1 | {% if mssql_ha_db_names | length > 0 %} 2 | {% for item in mssql_ha_db_names %} 3 | {% set db_backup_path = "/var/opt/mssql/data/" + item %} 4 | IF NOT EXISTS ( 5 | SELECT name, recovery_model_desc 6 | FROM sys.databases 7 | WHERE name = '{{ item }}' AND 8 | recovery_model_desc = 'FULL' 9 | ) 10 | BEGIN 11 | PRINT 'Setting RECOVERY FULL on the {{ item }} database'; 12 | ALTER DATABASE {{ item }} SET RECOVERY FULL; 13 | PRINT 'RECOVERY FULL on the {{ item }} database set successfully'; 14 | END 15 | ELSE 16 | BEGIN 17 | PRINT 'RECOVERY FULL on the {{ item }} database is set, skipping'; 18 | END 19 | 20 | IF NOT EXISTS ( 21 | SELECT [database_name], backup_start_date, backup_finish_date, [type] 22 | FROM msdb.dbo.backupset 23 | WHERE [type]='D' AND 24 | [database_name]='{{ item }}' AND 25 | backup_finish_date >= DATEADD(hh, -3, GETDATE()) 26 | ) 27 | BEGIN 28 | PRINT 'Backing up the {{ item }} database to \ 29 | {{ db_backup_path }}'; 30 | BACKUP DATABASE {{ item }} 31 | TO DISK = N'{{ db_backup_path }}'; 32 | PRINT 'The {{ item }} database backed up successfully'; 33 | END 34 | ELSE 35 | BEGIN 36 | PRINT 'The {{ item }} database is already backed up, skipping'; 37 | END 38 | 39 | IF NOT EXISTS ( 40 | SELECT 41 | AG.name AS [AvailabilityGroupName], 42 | ISNULL(agstates.primary_replica, '') AS [PrimaryReplicaServerName], 43 | dbcs.database_name AS [DatabaseName], 44 | ISNULL(dbrs.synchronization_state, 0) AS [SynchronizationState], 45 | ISNULL(dbrs.is_suspended, 0) AS [IsSuspended], 46 | ISNULL(dbcs.is_database_joined, 0) AS [IsJoined] 47 | FROM master.sys.availability_groups AS AG 48 | LEFT OUTER JOIN master.sys.dm_hadr_availability_group_states as agstates 49 | ON AG.group_id = agstates.group_id 50 | INNER JOIN master.sys.availability_replicas AS AR 51 | ON AG.group_id = AR.group_id 52 | INNER JOIN master.sys.dm_hadr_availability_replica_states AS arstates 53 | ON AR.replica_id = arstates.replica_id AND arstates.is_local = 1 54 | INNER JOIN master.sys.dm_hadr_database_replica_cluster_states AS dbcs 55 | ON arstates.replica_id = dbcs.replica_id 56 | LEFT OUTER JOIN master.sys.dm_hadr_database_replica_states AS dbrs 57 | ON dbcs.replica_id = dbrs.replica_id 58 | AND dbcs.group_database_id = dbrs.group_database_id 59 | WHERE dbcs.database_name = '{{ item }}' 60 | ) 61 | BEGIN 62 | PRINT 'Adding the {{ item }} database to the \ 63 | {{ mssql_ha_ag_name }} availability group'; 64 | ALTER AVAILABILITY GROUP {{ mssql_ha_ag_name }} 65 | ADD DATABASE {{ item }}; 66 | PRINT 'The {{ item }} database added to the \ 67 | {{ mssql_ha_ag_name }} availability group successfully'; 68 | END 69 | ELSE 70 | BEGIN 71 | PRINT 'The {{ item }} database is already added to the \ 72 | {{ mssql_ha_ag_name }} availability group, skipping'; 73 | END 74 | {% endfor %} 75 | {% endif %} 76 | -------------------------------------------------------------------------------- /templates/restore_cert.j2: -------------------------------------------------------------------------------- 1 | IF NOT EXISTS( 2 | SELECT name 3 | FROM sys.certificates 4 | WHERE name = '{{ mssql_ha_cert_name }}' 5 | ) 6 | BEGIN 7 | PRINT 'Certificate {{ mssql_ha_cert_name }} does not exist, creating'; 8 | CREATE CERTIFICATE {{ mssql_ha_cert_name }} 9 | FROM FILE = '{{ __mssql_ha_cert_dest }}' 10 | WITH PRIVATE KEY ( 11 | FILE = '{{ __mssql_ha_private_key_dest }}', 12 | DECRYPTION BY PASSWORD = '{{ mssql_ha_private_key_password }}' 13 | ); 14 | PRINT 'Certificate {{ mssql_ha_cert_name }} created successfully'; 15 | END 16 | ELSE 17 | BEGIN 18 | PRINT 'Certificate {{ mssql_ha_cert_name }} already exists, skipping'; 19 | END 20 | -------------------------------------------------------------------------------- /tests/.fmf/version: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /tests/collection-requirements.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | collections: 4 | - name: ansible.windows 5 | - name: fedora.linux_system_roles 6 | -------------------------------------------------------------------------------- /tests/files/create_ExampleDB1.sql: -------------------------------------------------------------------------------- 1 | IF NOT EXISTS( 2 | SELECT name 3 | FROM sys.databases 4 | WHERE name = 'ExampleDB1' 5 | ) 6 | BEGIN 7 | PRINT 'Creating the ExampleDB1 database'; 8 | CREATE DATABASE ExampleDB1; 9 | PRINT 'The ExampleDB1 database created successfully'; 10 | END 11 | ELSE 12 | BEGIN 13 | PRINT 'The ExampleDB1 database already exists, skipping'; 14 | END 15 | GO 16 | 17 | USE ExampleDB1; 18 | GO 19 | 20 | IF NOT EXISTS ( 21 | SELECT name, xtype 22 | FROM sysobjects 23 | WHERE name='Inventory' and xtype='U' 24 | ) 25 | BEGIN 26 | PRINT 'Adding the Inventory table to the ExampleDB1 database'; 27 | CREATE TABLE Inventory (id INT, name NVARCHAR(50), quantity INT); 28 | INSERT INTO Inventory VALUES (1, 'apple', 100); 29 | INSERT INTO Inventory VALUES (2, 'orange', 150); 30 | INSERT INTO Inventory VALUES (3, 'banana', 154); 31 | INSERT INTO Inventory VALUES (4, N'バナナ', 170); 32 | PRINT 'The Inventory table created successfully'; 33 | END 34 | ELSE 35 | BEGIN 36 | PRINT 'The Inventory table already exists, skipping'; 37 | END 38 | GO 39 | -------------------------------------------------------------------------------- /tests/files/create_ExampleDB2.sql: -------------------------------------------------------------------------------- 1 | IF NOT EXISTS( 2 | SELECT name 3 | FROM sys.databases 4 | WHERE name = 'ExampleDB2' 5 | ) 6 | BEGIN 7 | PRINT 'Creating the ExampleDB2 database'; 8 | CREATE DATABASE ExampleDB2; 9 | PRINT 'The ExampleDB2 database created successfully'; 10 | END 11 | ELSE 12 | BEGIN 13 | PRINT 'The ExampleDB2 database already exists, skipping'; 14 | END 15 | GO 16 | 17 | USE ExampleDB2; 18 | GO 19 | 20 | IF NOT EXISTS ( 21 | SELECT name, xtype 22 | FROM sysobjects 23 | WHERE name='Inventory' and xtype='U' 24 | ) 25 | BEGIN 26 | PRINT 'Adding the Inventory table to the ExampleDB2 database'; 27 | CREATE TABLE Inventory (id INT, name NVARCHAR(50), quantity INT); 28 | INSERT INTO Inventory VALUES (1, 'pineapple', 100); 29 | INSERT INTO Inventory VALUES (2, 'grapefruit', 150); 30 | INSERT INTO Inventory VALUES (3, 'cucumber', 154); 31 | INSERT INTO Inventory VALUES (4, N'バナナ', 170); 32 | PRINT 'The Inventory table created successfully'; 33 | END 34 | ELSE 35 | BEGIN 36 | PRINT 'The Inventory table already exists, skipping'; 37 | END 38 | GO 39 | -------------------------------------------------------------------------------- /tests/files/sql_script.sql: -------------------------------------------------------------------------------- 1 | IF NOT EXISTS ( 2 | SELECT name 3 | FROM master.sys.server_principals 4 | WHERE name = 'MyLogin' 5 | ) 6 | BEGIN 7 | PRINT 'Creating the MyLogin login'; 8 | CREATE LOGIN MyLogin WITH PASSWORD = 'p@55w0rD' 9 | PRINT 'The MyLogin login created successfully'; 10 | END 11 | ELSE 12 | BEGIN 13 | PRINT 'The MyLogin login already exists, skipping'; 14 | END 15 | 16 | IF NOT EXISTS ( 17 | SELECT name 18 | FROM sys.database_principals 19 | WHERE name = 'MyUser') 20 | BEGIN 21 | PRINT 'Creating the MyUser user'; 22 | CREATE USER MyUser FOR LOGIN MyLogin 23 | PRINT 'The MyUser user created successfully'; 24 | END 25 | ELSE 26 | BEGIN 27 | PRINT 'The MyUser user already exists, skipping'; 28 | END 29 | 30 | IF NOT EXISTS( 31 | SELECT name 32 | FROM sys.databases 33 | WHERE name = 'ExampleDB' 34 | ) 35 | BEGIN 36 | PRINT 'Creating the ExampleDB database'; 37 | CREATE DATABASE ExampleDB; 38 | PRINT 'The ExampleDB database created successfully'; 39 | END 40 | ELSE 41 | BEGIN 42 | PRINT 'The ExampleDB database already exists, skipping'; 43 | END 44 | GO 45 | 46 | USE ExampleDB; 47 | GO 48 | 49 | IF NOT EXISTS ( 50 | SELECT name, xtype 51 | FROM sysobjects 52 | WHERE name='Inventory' and xtype='U' 53 | ) 54 | BEGIN 55 | PRINT 'Adding the Inventory table to the ExampleDB database'; 56 | CREATE TABLE Inventory (id INT, name NVARCHAR(50), quantity INT); 57 | INSERT INTO Inventory VALUES (1, 'apple', 100); 58 | INSERT INTO Inventory VALUES (2, 'orange', 150); 59 | INSERT INTO Inventory VALUES (3, 'banana', 154); 60 | INSERT INTO Inventory VALUES (4, N'バナナ', 170); 61 | PRINT 'The Inventory table created successfully'; 62 | END 63 | ELSE 64 | BEGIN 65 | PRINT 'The Inventory table already exists, skipping'; 66 | END 67 | GO -------------------------------------------------------------------------------- /tests/files/verify_fts.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | CASE FULLTEXTSERVICEPROPERTY('IsFullTextInstalled') 3 | WHEN 1 THEN 'Full-Text Search is enabled' 4 | ELSE 'Full-Text Search is not enabled' 5 | END 6 | ; -------------------------------------------------------------------------------- /tests/no-vault-variables.txt: -------------------------------------------------------------------------------- 1 | tests_configure_ha_cluster.yml 2 | tests_idempotency_2017.yml 3 | tests_idempotency_2019.yml 4 | tests_idempotency_2022.yml 5 | tests_input_sql_file_2017.yml 6 | tests_input_sql_file_2019.yml 7 | tests_input_sql_file_2022.yml 8 | tests_password_2017.yml 9 | tests_password_2019.yml 10 | tests_password_2022.yml 11 | -------------------------------------------------------------------------------- /tests/playbooks/tests_ad_integration.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | # To run this test, AD server must be configured and it's properties 4 | # provided with the following variables: 5 | # ad_integration_realm 6 | # ad_integration_user 7 | # ad_integration_password 8 | # Inventory must contain the client Linux node and the ad AD Server node like: 9 | # all: 10 | # hosts: 11 | # client: 12 | # ansible_connection: local 13 | # ansible_host: 127.0.0.1 14 | # ad: 15 | # ansible_host: 10.192.1.1 16 | # ad_fqdn: ad1.domain.com 17 | # ansible_connection: winrm 18 | # ansible_password: Secret123 19 | # ansible_port: 5986 20 | # ansible_user: Administrator 21 | # ansible_winrm_server_cert_validation: ignore 22 | - name: Test integration with AD server 23 | hosts: client 24 | vars: 25 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 26 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 27 | mssql_accept_microsoft_sql_server_standard_eula: true 28 | mssql_version: 2022 29 | mssql_password: "p@55w0rD" 30 | mssql_edition: Evaluation 31 | mssql_manage_firewall: true 32 | mssql_debug: true 33 | mssql_ad_configure: true 34 | mssql_ad_sql_user: sqluser 35 | mssql_ad_sql_password: "p@55w0rD1" 36 | ad_integration_realm: domain.com 37 | ad_integration_user: Administrator 38 | ad_integration_password: Secret123 39 | ad_integration_manage_dns: true 40 | ad_integration_dns_server: 1.1.1.1 41 | ad_integration_dns_connection_name: eth0 42 | ad_integration_dns_connection_type: ethernet 43 | # ad_integration_realm is randomized in IDM CI hence need to use variables 44 | # Cannot use variable for Administrator because test with 45 | # mssql_ad_join: false does ad_integration_user: null 46 | __mssql_ad_login: >- 47 | {{ ad_integration_realm.split('.') | first + '\Administrator' }} 48 | mssql_post_input_sql_content: |- 49 | USE master; 50 | IF NOT EXISTS ( 51 | SELECT name FROM sys.server_principals 52 | WHERE name = '{{ __mssql_ad_login }}' 53 | ) 54 | BEGIN 55 | PRINT 'A {{ __mssql_ad_login }} login does not exist, creating'; 56 | CREATE LOGIN [{{ __mssql_ad_login }}] FROM WINDOWS; 57 | PRINT 'The {{ __mssql_ad_login }} login created successfully'; 58 | END 59 | ELSE 60 | BEGIN 61 | PRINT 'A {{ __mssql_ad_login }} login already exists, skipping' 62 | END 63 | 64 | tasks: 65 | # Test general scenario 66 | - name: Set up MSSQL and configure AD authentication 67 | include_role: 68 | name: linux-system-roles.mssql 69 | 70 | - name: Test AD integration 71 | include_tasks: ../tasks/verify_ad_auth.yml 72 | 73 | # Test with mssql_ad_keytab_file 74 | - name: Fetch keytab file that was prepared above 75 | fetch: 76 | src: /var/opt/mssql/secrets/mssql.keytab 77 | dest: /tmp/mssql.keytab 78 | flat: true 79 | mode: preserve 80 | run_once: true 81 | 82 | - name: Clean up after the role invocation 83 | include_tasks: ../tasks/cleanup.yml 84 | -------------------------------------------------------------------------------- /tests/playbooks/tests_ad_integration_join_false.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | # To run this test, AD server must be configured and it's properties 4 | # provided with the following variables: 5 | # ad_integration_realm 6 | # ad_integration_user 7 | # ad_integration_password 8 | # Inventory must contain the client Linux node and the ad AD Server node like: 9 | # all: 10 | # hosts: 11 | # client: 12 | # ansible_connection: local 13 | # ansible_host: 127.0.0.1 14 | # ad: 15 | # ansible_host: 10.192.1.1 16 | # ad_fqdn: ad1.domain.com 17 | # ansible_connection: winrm 18 | # ansible_password: Secret123 19 | # ansible_port: 5986 20 | # ansible_user: Administrator 21 | # ansible_winrm_server_cert_validation: ignore 22 | - name: Test integration with AD server 23 | hosts: client 24 | vars: 25 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 26 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 27 | mssql_accept_microsoft_sql_server_standard_eula: true 28 | mssql_version: 2022 29 | mssql_password: "p@55w0rD" 30 | mssql_edition: Evaluation 31 | mssql_manage_firewall: true 32 | mssql_debug: true 33 | mssql_ad_configure: true 34 | mssql_ad_sql_user: sqluser 35 | mssql_ad_sql_password: "p@55w0rD1" 36 | ad_integration_realm: domain.com 37 | ad_integration_user: Administrator 38 | ad_integration_password: Secret123 39 | ad_integration_manage_dns: true 40 | ad_integration_dns_server: 1.1.1.1 41 | ad_integration_dns_connection_name: eth0 42 | ad_integration_dns_connection_type: ethernet 43 | # ad_integration_realm is randomized in IDM CI hence need to use variables 44 | # Cannot use variable for Administrator because test with 45 | # mssql_ad_join: false does ad_integration_user: null 46 | __mssql_ad_login: >- 47 | {{ ad_integration_realm.split('.') | first + '\Administrator' }} 48 | mssql_post_input_sql_content: |- 49 | USE master; 50 | IF NOT EXISTS ( 51 | SELECT name FROM sys.server_principals 52 | WHERE name = '{{ __mssql_ad_login }}' 53 | ) 54 | BEGIN 55 | PRINT 'A {{ __mssql_ad_login }} login does not exist, creating'; 56 | CREATE LOGIN [{{ __mssql_ad_login }}] FROM WINDOWS; 57 | PRINT 'The {{ __mssql_ad_login }} login created successfully'; 58 | END 59 | ELSE 60 | BEGIN 61 | PRINT 'A {{ __mssql_ad_login }} login already exists, skipping' 62 | END 63 | 64 | tasks: 65 | # Test with mssql_ad_join: false 66 | - name: Authenticate to AD 67 | include_role: 68 | name: linux-system-roles.ad_integration 69 | 70 | - name: Set up MSSQL and configure AD authentication without joining to AD 71 | include_role: 72 | name: linux-system-roles.mssql 73 | vars: 74 | mssql_ad_join: false 75 | # Explicitly set kerberos vars, otherwise the value becomes null 76 | mssql_ad_kerberos_user: Administrator 77 | mssql_ad_kerberos_password: Secret123 78 | ad_integration_user: null 79 | ad_integration_password: null 80 | ad_integration_manage_dns: null 81 | ad_integration_dns_server: null 82 | ad_integration_dns_connection_name: null 83 | ad_integration_dns_connection_type: null 84 | 85 | - name: Test AD integration 86 | include_tasks: ../tasks/verify_ad_auth.yml 87 | -------------------------------------------------------------------------------- /tests/playbooks/tests_ad_integration_w_keytab.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | # To run this test, AD server must be configured and it's properties 4 | # provided with the following variables: 5 | # ad_integration_realm 6 | # ad_integration_user 7 | # ad_integration_password 8 | # Inventory must contain the client Linux node and the ad AD Server node like: 9 | # all: 10 | # hosts: 11 | # client: 12 | # ansible_connection: local 13 | # ansible_host: 127.0.0.1 14 | # ad: 15 | # ansible_host: 10.192.1.1 16 | # ad_fqdn: ad1.domain.com 17 | # ansible_connection: winrm 18 | # ansible_password: Secret123 19 | # ansible_port: 5986 20 | # ansible_user: Administrator 21 | # ansible_winrm_server_cert_validation: ignore 22 | - name: Test integration with AD server 23 | hosts: client 24 | vars: 25 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 26 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 27 | mssql_accept_microsoft_sql_server_standard_eula: true 28 | mssql_version: 2022 29 | mssql_password: "p@55w0rD" 30 | mssql_edition: Evaluation 31 | mssql_manage_firewall: true 32 | mssql_debug: true 33 | mssql_ad_configure: true 34 | mssql_ad_sql_user: sqluser 35 | mssql_ad_sql_password: "p@55w0rD1" 36 | ad_integration_realm: domain.com 37 | ad_integration_user: Administrator 38 | ad_integration_password: Secret123 39 | ad_integration_manage_dns: true 40 | ad_integration_dns_server: 1.1.1.1 41 | ad_integration_dns_connection_name: eth0 42 | ad_integration_dns_connection_type: ethernet 43 | # ad_integration_realm is randomized in IDM CI hence need to use variables 44 | # Cannot use variable for Administrator because test with 45 | # mssql_ad_join: false does ad_integration_user: null 46 | __mssql_ad_login: >- 47 | {{ ad_integration_realm.split('.') | first + '\Administrator' }} 48 | mssql_post_input_sql_content: |- 49 | USE master; 50 | IF NOT EXISTS ( 51 | SELECT name FROM sys.server_principals 52 | WHERE name = '{{ __mssql_ad_login }}' 53 | ) 54 | BEGIN 55 | PRINT 'A {{ __mssql_ad_login }} login does not exist, creating'; 56 | CREATE LOGIN [{{ __mssql_ad_login }}] FROM WINDOWS; 57 | PRINT 'The {{ __mssql_ad_login }} login created successfully'; 58 | END 59 | ELSE 60 | BEGIN 61 | PRINT 'A {{ __mssql_ad_login }} login already exists, skipping' 62 | END 63 | 64 | tasks: 65 | - name: Set up MSSQL and configure AD authentication with keytab 66 | vars: 67 | # /tmp/mssql.keytab is created in tests_ad_integration.yml 68 | mssql_ad_keytab_file: /tmp/mssql.keytab 69 | mssql_ad_keytab_remote_src: false 70 | mssql_ad_sql_password: null 71 | include_role: 72 | name: linux-system-roles.mssql 73 | 74 | - name: Test AD integration 75 | include_tasks: ../tasks/verify_ad_auth.yml 76 | 77 | - name: Clean up after the role invocation 78 | include_tasks: ../tasks/cleanup.yml 79 | -------------------------------------------------------------------------------- /tests/provision.fmf: -------------------------------------------------------------------------------- 1 | standard-inventory-qcow2: 2 | qemu: 3 | m: 4096 4 | -------------------------------------------------------------------------------- /tests/requirements.txt: -------------------------------------------------------------------------------- 1 | # Python requirements for tests 2 | # Requirement for playbooks/tests_ad_integration.yml 3 | pywinrm==0.4.3 4 | -------------------------------------------------------------------------------- /tests/roles/caller/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for caller 3 | 4 | - name: Include role 5 | include_role: 6 | name: "{{ roletoinclude }}" 7 | 8 | - name: Test variable override 9 | assert: 10 | that: not __caller_override 11 | -------------------------------------------------------------------------------- /tests/roles/caller/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for caller 3 | __caller_override: false 4 | -------------------------------------------------------------------------------- /tests/roles/linux-system-roles.mssql/defaults: -------------------------------------------------------------------------------- 1 | ../../../defaults -------------------------------------------------------------------------------- /tests/roles/linux-system-roles.mssql/files: -------------------------------------------------------------------------------- 1 | ../../../files -------------------------------------------------------------------------------- /tests/roles/linux-system-roles.mssql/handlers: -------------------------------------------------------------------------------- 1 | ../../../handlers -------------------------------------------------------------------------------- /tests/roles/linux-system-roles.mssql/meta: -------------------------------------------------------------------------------- 1 | ../../../meta -------------------------------------------------------------------------------- /tests/roles/linux-system-roles.mssql/tasks: -------------------------------------------------------------------------------- 1 | ../../../tasks -------------------------------------------------------------------------------- /tests/roles/linux-system-roles.mssql/templates: -------------------------------------------------------------------------------- 1 | ../../../templates -------------------------------------------------------------------------------- /tests/roles/linux-system-roles.mssql/vars: -------------------------------------------------------------------------------- 1 | ../../../vars -------------------------------------------------------------------------------- /tests/setup-snapshot.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Setup snapshot 3 | hosts: all 4 | tasks: 5 | - name: Set facts used by role 6 | include_role: 7 | name: linux-system-roles.mssql 8 | tasks_from: set_vars.yml 9 | public: true 10 | 11 | - name: Deploy the GPG key for Microsoft repositories 12 | rpm_key: 13 | key: "{{ mssql_rpm_key }}" 14 | state: present 15 | 16 | # The tools repo is required for powershell 17 | - name: Configure the Microsoft SQL Server Tools repository 18 | yum_repository: 19 | name: packages-microsoft-com-prod 20 | description: Microsoft SQL Server Tools 21 | baseurl: "{{ mssql_client_repository }}" 22 | gpgcheck: true 23 | 24 | - name: Cache packages required for tests 25 | vars: 26 | __mssql_packages: 27 | - "{{ __mssql_server_packages }}" 28 | - "{{ __mssql_server_fts_packages }}" 29 | - "{{ __mssql_server_ha_packages }}" 30 | - "{{ __mssql_powershell_packages }}" 31 | mssql_version: "{{ item }}" 32 | include_tasks: tasks/snapshot_install_packages.yml 33 | loop: "{{ __mssql_supported_versions }}" 34 | 35 | - name: Remove the Microsoft SQL Server Tools repository 36 | yum_repository: 37 | name: packages-microsoft-com-prod 38 | state: absent 39 | -------------------------------------------------------------------------------- /tests/tasks/assert_fail_on_unsupported_ver.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ensure ansible_facts to get ansible_distribution 3 | setup: 4 | gather_subset: min 5 | 6 | - name: Assert fail on EL 7 with version = 2022 and EL 9 with version != 2022 7 | when: >- 8 | (ansible_distribution in ['CentOS', 'RedHat'] and 9 | ansible_distribution_major_version is version('7', '=') and 10 | mssql_version | int == 2022) or 11 | (((ansible_distribution in ['CentOS', 'RedHat'] and 12 | ansible_distribution_major_version is version('9', '=')) or 13 | (ansible_distribution in ['Fedora'])) 14 | and 15 | mssql_version | int != 2022) 16 | block: 17 | - name: Run the role 18 | vars: 19 | mssql_password: "p@55w0rD" 20 | mssql_edition: Evaluation 21 | include_role: 22 | name: linux-system-roles.mssql 23 | 24 | - name: Unreachable task 25 | fail: 26 | msg: The above task must fail 27 | rescue: 28 | - name: Assert that the role failed with EL < 8 not supported 29 | assert: 30 | that: >- 31 | 'You must set the mssql_version variable to one of' 32 | in ansible_failed_result.msg 33 | 34 | - name: Clean up after the role invocation 35 | include_tasks: tasks/cleanup.yml 36 | 37 | # Putting end_host into a rescue block results in a failed task 38 | - name: End unsupported host 39 | meta: end_host 40 | -------------------------------------------------------------------------------- /tests/tasks/check_header.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | # Find two words in ansible_managed and grep for them in the config file 4 | - name: Grep the ansible_managed header in {{ __mssql_conf_path }} 5 | vars: 6 | __mssql_conf_path: /var/opt/mssql/mssql.conf 7 | command: >- 8 | grep 9 | {{ lookup('template', 'get_ansible_managed.j2') | 10 | regex_search('(\b\w+\s){2}') | quote }} 11 | {{ __mssql_conf_path }} 12 | changed_when: false 13 | -------------------------------------------------------------------------------- /tests/tasks/cleanup.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Gather package facts 3 | package_facts: 4 | no_log: true 5 | 6 | - name: Set role variables needed for cleanup 7 | include_role: 8 | name: linux-system-roles.mssql 9 | tasks_from: set_vars.yml 10 | public: true 11 | 12 | - name: Purge cluster configuration 13 | vars: 14 | ha_cluster_cluster_present: false 15 | ha_cluster_enable_repos: false 16 | include_role: 17 | name: fedora.linux_system_roles.ha_cluster 18 | when: ansible_facts.packages.pcs is defined 19 | 20 | - name: Debug ansible_python_version 21 | debug: 22 | var: ansible_python_version 23 | 24 | - name: Purge firewall configuration 25 | vars: 26 | firewall: 27 | - previous: replaced 28 | include_role: 29 | name: fedora.linux_system_roles.firewall 30 | when: ansible_facts.packages.firewalld is defined 31 | 32 | - name: Purge selinux configuration 33 | vars: 34 | selinux_all_purge: true 35 | include_role: 36 | name: fedora.linux_system_roles.selinux 37 | when: mssql_manage_selinux | d(false) | bool 38 | 39 | - name: Leave realm 40 | command: realm leave 41 | register: realm_leave 42 | failed_when: false 43 | changed_when: >- 44 | not "Couldn't find a configured realm" in realm_leave.stderr 45 | when: ansible_facts.packages.realmd is defined 46 | 47 | - name: Destroy Kerberos tickets # noqa no-changed-when 48 | command: kdestroy -A 49 | when: ansible_facts.packages["krb5-workstation"] is defined 50 | 51 | - name: Remove related packages 52 | package: 53 | name: 54 | - adutil 55 | - mssql-server* 56 | - mssql-tools* 57 | - unixODBC-devel 58 | - mssql-server-fts 59 | - mssql-server-ha 60 | - powershell 61 | state: absent 62 | autoremove: true 63 | 64 | - name: Remove related files 65 | shell: >- 66 | rm -rfv /var/opt/mssql* 67 | /opt/mssql* 68 | /var/log/pacemaker/pacemaker.log 69 | /etc/yum.repos.d/packages-microsoft-com-* 70 | /tmp/*.j2 71 | /tmp/mssql_data 72 | /tmp/mssql_log 73 | /etc/systemd/system/mssql-server.service.d 74 | /etc/systemd/system/multi-user.target.wants/mssql-server.service 75 | register: __mssql_cleanup_remove 76 | changed_when: "'removed' in __mssql_cleanup_remove.stdout" 77 | 78 | # On SQL Server 2017, 2019 the service remains after removing RPMs 79 | - name: Stop the mssql-server service # noqa command-instead-of-module 80 | shell: systemctl stop mssql-server || true 81 | changed_when: false 82 | when: __mssql_is_booted | bool 83 | 84 | - name: Get SELinux policy modules 85 | command: semodule -l 86 | changed_when: false 87 | register: __mssql_policy_loaded 88 | 89 | # not installed in Fedora by default 90 | - name: Ensure that semanage command is available 91 | package: 92 | name: policycoreutils-python-utils 93 | state: present 94 | when: "'mssql' in __mssql_policy_loaded.stdout_lines" 95 | 96 | # removing the mssql-server-selinux package fails to actually remove the 97 | # policy (and ignores the failure) 98 | - name: Hack around broken mssql SELinux profile and actually remove it 99 | shell: |- 100 | set -eux 101 | semanage fcontext -D 102 | semanage port -D 103 | semodule --priority=200 -r mssql 104 | changed_when: true 105 | when: "'mssql' in __mssql_policy_loaded.stdout_lines" 106 | 107 | - name: Check for leftover mssql processes 108 | command: pgrep -au mssql 109 | register: __mssql_processes 110 | changed_when: false 111 | # 0: found, 1: no processes, 2: user mssql does not exist 112 | failed_when: not __mssql_processes.rc in [0, 1, 2] 113 | 114 | - name: Kill leftover mssql processes 115 | command: pkill --signal KILL -e -u mssql 116 | changed_when: true 117 | when: __mssql_processes.rc == 0 118 | 119 | - name: Remove system user 120 | user: 121 | name: mssql 122 | state: absent 123 | remove: true 124 | -------------------------------------------------------------------------------- /tests/tasks/mssql-sever-increase-start-limit.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Create service drop-in directory 4 | file: 5 | path: /etc/systemd/system/mssql-server.service.d 6 | state: directory 7 | owner: root 8 | group: root 9 | mode: "0755" 10 | 11 | - name: Modify the mssql-server service start limit interval and burst 12 | copy: 13 | dest: /etc/systemd/system/mssql-server.service.d/startlimit.conf 14 | content: | 15 | [Service] 16 | StartLimitInterval=0 17 | StartLimitBurst=0 18 | owner: root 19 | group: root 20 | mode: "0644" 21 | register: __mssql_modify_limit 22 | 23 | - name: Reload service daemon 24 | systemd: # noqa no-handler 25 | daemon_reload: true 26 | when: 27 | - __mssql_modify_limit is changed 28 | - __mssql_is_booted | d(true) 29 | -------------------------------------------------------------------------------- /tests/tasks/mssql_conf_verify.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | # This tasks file verifies the value of a mssql-conf setting. 4 | # It takes two variables: 5 | # __mssql_conf_setting - the setting name 6 | # __mssql_conf_value - the setting value 7 | - name: Get the value of the setting {{ __mssql_conf_setting }} 8 | vars: 9 | __mssql_conf_path: /var/opt/mssql/mssql.conf 10 | shell: grep '^{{ __mssql_conf_setting }}' {{ __mssql_conf_path }} || true 11 | changed_when: false 12 | register: __mssql_conf_get_setting 13 | 14 | - name: Verify the setting when it is type str {{ __mssql_conf_setting }} 15 | vars: 16 | __mssql_conf_get_value: >- 17 | {{ __mssql_conf_get_setting.stdout | 18 | regex_replace('^.*\s=\s', '') | string }} 19 | assert: 20 | that: __mssql_conf_get_value == __mssql_conf_value | string 21 | when: 22 | - __mssql_conf_value | type_debug != 'bool' 23 | - __mssql_conf_setting != "tcpport" 24 | 25 | - name: Verify the setting when it is type bool {{ __mssql_conf_setting }} 26 | vars: 27 | __mssql_conf_get_value: >- 28 | {{ __mssql_conf_get_setting.stdout | 29 | regex_replace('^.*\s=\s', '') | bool }} 30 | assert: 31 | that: __mssql_conf_get_value == __mssql_conf_value 32 | when: 33 | - __mssql_conf_value | type_debug == 'bool' 34 | - __mssql_conf_setting != "tcpport" 35 | 36 | # Special case for tcpport because sqlcmd does not set tcpport to 1433 37 | # When you try to set tcpport to 1433, sqlcmd just keeps tcpport entry in 38 | # mssql.conf empty 39 | # Empty previous tcp port setting means that the port is default 1433 40 | - name: Verify that tcpport = {{ __mssql_conf_value }} 41 | vars: 42 | __mssql_conf_get_value: >- 43 | {{ __mssql_conf_get_setting.stdout | regex_search('[1-9][0-9]{0,4}') 44 | if __mssql_conf_get_setting.stdout 45 | else '1433' }} 46 | assert: 47 | that: __mssql_conf_get_value | int == __mssql_conf_value | int 48 | when: 49 | - __mssql_conf_setting == "tcpport" 50 | -------------------------------------------------------------------------------- /tests/tasks/snapshot_install_packages.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Install packages but avoid installing sql-server 2022 on RHEL < 8 4 | block: 5 | - name: Configure the Microsoft SQL Server repository {{ mssql_version }} 6 | yum_repository: 7 | name: packages-microsoft-com-mssql-server-{{ mssql_version | int }} 8 | description: Microsoft SQL Server {{ mssql_version }} 9 | baseurl: "{{ mssql_server_repository }}" 10 | gpgcheck: true 11 | 12 | - name: Install required packages 13 | package: 14 | name: "{{ __mssql_packages }}" 15 | state: present 16 | 17 | # NOTE: Removed packages will still be in the local package cache 18 | # and when installed will be installed from the local disk 19 | - name: Remove mssql packages to keep them in cache only 20 | package: 21 | name: "{{ __mssql_packages }}" 22 | state: absent 23 | autoremove: true 24 | 25 | - name: Remove the Microsoft SQL Server repo version {{ mssql_version }} 26 | yum_repository: 27 | name: packages-microsoft-com-mssql-server-{{ mssql_version | int }} 28 | state: absent 29 | -------------------------------------------------------------------------------- /tests/tasks/tests_idempotency.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Assert fail on EL 7 with version = 2022 and EL 9 with version != 2022 3 | include_tasks: assert_fail_on_unsupported_ver.yml 4 | 5 | - name: Run on a fresh host and set all parameters 6 | include_role: 7 | name: linux-system-roles.mssql 8 | vars: 9 | mssql_password: "p@55w0rD" 10 | mssql_edition: Evaluation 11 | mssql_tcp_port: 1433 12 | mssql_ip_address: 0.0.0.0 13 | mssql_enable_sql_agent: true 14 | mssql_install_fts: true 15 | mssql_tune_for_fua_storage: true 16 | mssql_install_powershell: true 17 | mssql_datadir: /tmp/mssql_data 18 | mssql_logdir: /tmp/mssql_log 19 | 20 | - name: Configure the mssql-server service start limit interval and burst 21 | include_tasks: mssql-sever-increase-start-limit.yml 22 | 23 | - name: Run again with the same settings - should report not changed 24 | include_role: 25 | name: linux-system-roles.mssql 26 | vars: 27 | mssql_password: "p@55w0rD" 28 | mssql_edition: Evaluation 29 | mssql_tcp_port: 1433 30 | mssql_ip_address: 0.0.0.0 31 | mssql_enable_sql_agent: true 32 | mssql_install_fts: true 33 | mssql_tune_for_fua_storage: true 34 | mssql_install_powershell: true 35 | mssql_datadir: /tmp/mssql_data 36 | mssql_logdir: /tmp/mssql_log 37 | 38 | - name: Verify settings 39 | include_tasks: verify_settings.yml 40 | vars: 41 | __verify_mssql_password: "p@55w0rD" 42 | __verify_mssql_edition: Evaluation 43 | __verify_mssql_tcp_port: 1433 44 | __verify_mssql_ip_address: 0.0.0.0 45 | __verify_mssql_agent_is_enabled: true 46 | __verify_mssql_fts_is_installed: true 47 | __verify_mssql_is_tuned_for_fua: true 48 | __verify_mssql_powershell_is_installed: true 49 | __verify_mssql_datadir: /tmp/mssql_data 50 | __verify_mssql_logdir: /tmp/mssql_log 51 | 52 | - name: Run to edit settings 53 | include_role: 54 | name: linux-system-roles.mssql 55 | vars: 56 | mssql_password: "p@55w0rD1" 57 | mssql_edition: Standard 58 | mssql_tcp_port: 1435 59 | mssql_ip_address: 127.0.0.1 60 | mssql_enable_sql_agent: false 61 | mssql_install_fts: false 62 | mssql_tune_for_fua_storage: false 63 | mssql_install_powershell: false 64 | mssql_datadir: /var/opt/mssql/data 65 | mssql_logdir: /var/opt/mssql/log 66 | mssql_datadir_mode: '755' 67 | mssql_logdir_mode: '755' 68 | 69 | - name: Run with the edited settings again - should report not changed 70 | include_role: 71 | name: linux-system-roles.mssql 72 | vars: 73 | mssql_password: "p@55w0rD1" 74 | mssql_edition: Standard 75 | mssql_tcp_port: 1435 76 | mssql_ip_address: 127.0.0.1 77 | mssql_enable_sql_agent: false 78 | mssql_install_fts: false 79 | mssql_tune_for_fua_storage: false 80 | mssql_install_powershell: false 81 | mssql_datadir: /var/opt/mssql/data 82 | mssql_logdir: /var/opt/mssql/log 83 | 84 | - name: Verify disabled settings 85 | include_tasks: verify_settings.yml 86 | vars: 87 | __verify_mssql_password: "p@55w0rD1" 88 | # package defaults to Evaluation, but the tests above change it to 89 | # 'Standard' in the case where the server actually runs 90 | __verify_mssql_edition: "{{ 'Standard' if __mssql_is_booted else 'Evaluation' }}" 91 | __verify_mssql_tcp_port: 1435 92 | __verify_mssql_ip_address: 127.0.0.1 93 | __verify_mssql_agent_is_enabled: false 94 | __verify_mssql_fts_is_installed: false 95 | __verify_mssql_ha_is_installed: false 96 | __verify_mssql_is_tuned_for_fua: false 97 | __verify_mssql_powershell_is_installed: false 98 | __verify_mssql_datadir: /var/opt/mssql/data 99 | __verify_mssql_logdir: /var/opt/mssql/log 100 | __verify_mssql_datadir_mode: '0755' 101 | __verify_mssql_logdir_mode: '0755' 102 | 103 | - name: Check the ansible_managed header in the configuration file 104 | include_tasks: check_header.yml 105 | -------------------------------------------------------------------------------- /tests/tasks/tests_input_sql_file.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Assert fail on EL 7 with version = 2022 and EL 9 with version != 2022 3 | include_tasks: assert_fail_on_unsupported_ver.yml 4 | 5 | - name: Set up MSSQL and input sql files with pre_input 6 | include_role: 7 | name: linux-system-roles.mssql 8 | vars: 9 | mssql_pre_input_sql_file: 10 | - create_example_db.j2 11 | - create_example_db.j2 12 | mssql_password: "p@55w0rD" 13 | mssql_edition: Evaluation 14 | # Enable FTS in the beginning so that it works at the end 15 | # It takes some time for mssql-server to configure it after RPM is installed 16 | mssql_install_fts: true 17 | # noqa var-naming[no-role-prefix] 18 | __mssql_test_db_name: ExampleDB1 19 | 20 | - name: Assert the latest script invocation resulted in no changes 21 | assert: 22 | that: 23 | - >- 24 | 'The ExampleDB1 database already exists, skipping' in 25 | __mssql_sqlcmd_input.stdout 26 | - >- 27 | 'The Inventory table already exists, skipping' in 28 | __mssql_sqlcmd_input.stdout 29 | 30 | - name: Input sql files with post_input into custom storage directory 31 | include_role: 32 | name: linux-system-roles.mssql 33 | vars: 34 | mssql_datadir: /tmp/mssql_data 35 | mssql_logdir: /tmp/mssql_log 36 | mssql_datadir_mode: '0700' 37 | mssql_logdir_mode: '0700' 38 | mssql_post_input_sql_file: 39 | - sql_script.sql 40 | - sql_script.sql 41 | mssql_password: "p@55w0rD" 42 | 43 | - name: Assert the latest script invocation resulted in no changes 44 | assert: 45 | that: 46 | - >- 47 | 'The MyLogin login already exists, skipping' 48 | in __mssql_sqlcmd_input.stdout 49 | - >- 50 | 'The MyUser user already exists, skipping' 51 | in __mssql_sqlcmd_input.stdout 52 | 53 | - name: Verify custom storage 54 | include_tasks: verify_settings.yml 55 | vars: 56 | __verify_mssql_datadir: /tmp/mssql_data 57 | __verify_mssql_logdir: /tmp/mssql_log 58 | __verify_mssql_datadir_mode: '0700' 59 | __verify_mssql_logdir_mode: '0700' 60 | 61 | - name: Set up MSSQL and input sql content with pre_input 62 | include_role: 63 | name: linux-system-roles.mssql 64 | vars: 65 | mssql_pre_input_sql_content: 66 | - "{{ lookup('template', 'create_example_db.j2') }}" 67 | - "{{ lookup('template', 'create_example_db.j2') }}" 68 | __mssql_test_db_name: ExampleDB2 69 | mssql_password: "p@55w0rD" 70 | mssql_edition: Evaluation 71 | 72 | - name: Assert the latest script invocation resulted in no changes 73 | assert: 74 | that: >- 75 | 'The ExampleDB2 database already exists, skipping' in 76 | __mssql_sqlcmd_input.stdout 77 | 78 | - name: Set up MSSQL and input sql content with post_input 79 | include_role: 80 | name: linux-system-roles.mssql 81 | vars: 82 | mssql_post_input_sql_content: 83 | - "{{ lookup('file', 'sql_script.sql') }}" 84 | mssql_password: "p@55w0rD" 85 | 86 | - name: Assert the latest script invocation resulted in no changes 87 | assert: 88 | that: >- 89 | 'The MyLogin login already exists, skipping' 90 | in __mssql_sqlcmd_input.stdout 91 | 92 | - name: Verify the failure when the mssql_password var is not specified 93 | block: 94 | - name: Input the sql file without the mssql_password variable 95 | include_role: 96 | name: linux-system-roles.mssql 97 | vars: 98 | mssql_pre_input_sql_file: sql_script.sql 99 | 100 | - name: Unreachable task 101 | fail: 102 | msg: The above task must fail 103 | 104 | rescue: 105 | - name: Assert that the role failed with mssql_password not defined 106 | assert: 107 | that: >- 108 | 'You must define the mssql_password variable' in 109 | ansible_failed_result.msg 110 | 111 | - name: Verify the failure when the mssql_password var is not specified 112 | block: 113 | - name: Input the sql file without the mssql_password variable 114 | include_role: 115 | name: linux-system-roles.mssql 116 | vars: 117 | mssql_post_input_sql_file: sql_script.sql 118 | 119 | - name: Unreachable task 120 | fail: 121 | msg: The above task must fail 122 | 123 | rescue: 124 | - name: Assert that the role failed with mssql_password not defined 125 | assert: 126 | that: >- 127 | 'You must define the mssql_password variable' in 128 | ansible_failed_result.msg 129 | 130 | - name: Verify that FTS was enabled in the beginning of this test playbook 131 | include_role: 132 | name: linux-system-roles.mssql 133 | vars: 134 | mssql_post_input_sql_file: verify_fts.sql 135 | mssql_password: "p@55w0rD" 136 | 137 | - name: Assert the latest script invocation resulted in no changes 138 | assert: 139 | that: 140 | - >- 141 | 'Full-Text Search is enabled' 142 | in __mssql_sqlcmd_input.stdout 143 | -------------------------------------------------------------------------------- /tests/tasks/tests_password.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Assert fail on EL 7 with version = 2022 and EL 9 with version != 2022 3 | include_tasks: assert_fail_on_unsupported_ver.yml 4 | 5 | # role does not run during bootc QEMU validation, thus _is_booted is undefined 6 | - name: Set __mssql_is_booted for bootc validation tests 7 | set_fact: 8 | __mssql_is_booted: true 9 | when: __bootc_validation | d(false) 10 | 11 | - name: Set up MSSQL 12 | include_role: 13 | name: linux-system-roles.mssql 14 | vars: 15 | mssql_password: "p@55w0rD" 16 | mssql_edition: Evaluation 17 | mssql_tools_versions: [17] 18 | when: not __bootc_validation | d(false) 19 | 20 | - name: Configure the mssql-server service start limit interval and burst 21 | include_tasks: tasks/mssql-sever-increase-start-limit.yml 22 | when: not __bootc_validation | d(false) 23 | 24 | - name: >- 25 | Change the password with default settings. 26 | Should report Changed. 27 | include_role: 28 | name: linux-system-roles.mssql 29 | vars: 30 | mssql_password: "p@55w0rD11" 31 | when: not __bootc_validation | d(false) 32 | 33 | - name: Verify settings 34 | include_tasks: tasks/verify_settings.yml 35 | vars: 36 | __verify_mssql_password: "p@55w0rD11" 37 | when: not __bootc_validation | d(false) 38 | 39 | - name: Verify the package {{ __mssql_verify_package_name }} 40 | include_tasks: verify_package.yml 41 | vars: 42 | __mssql_verify_package_name: mssql-tools 43 | __mssql_verify_package_installed: true 44 | 45 | - name: Change the IP address setting. 46 | include_role: 47 | name: linux-system-roles.mssql 48 | vars: 49 | mssql_ip_address: 127.0.0.1 50 | when: not __bootc_validation | d(false) 51 | 52 | - name: Create QEMU deployment during bootc end-to-end test 53 | delegate_to: localhost 54 | become: false 55 | command: "{{ lsr_scriptdir }}/bootc-buildah-qcow.sh {{ ansible_host }}" 56 | changed_when: true 57 | when: ansible_connection == "buildah" 58 | 59 | - name: >- 60 | Change the password with a custom IP address. 61 | Should report Changed. 62 | include_role: 63 | name: linux-system-roles.mssql 64 | vars: 65 | mssql_password: "p@55w0rD" 66 | mssql_tools_versions: [17, 18] 67 | when: not __bootc_validation | d(false) 68 | 69 | - name: Verify settings 70 | include_tasks: tasks/verify_settings.yml 71 | vars: 72 | __verify_mssql_password: "p@55w0rD" 73 | when: not __bootc_validation | d(false) 74 | 75 | - name: Verify the package {{ __mssql_verify_package_name }} 76 | include_tasks: verify_package.yml 77 | vars: 78 | __mssql_verify_package_name: mssql-tools18 79 | __mssql_verify_package_installed: true 80 | 81 | - name: Change the TCP port setting. 82 | include_role: 83 | name: linux-system-roles.mssql 84 | vars: 85 | mssql_tcp_port: 1432 86 | when: not __bootc_validation | d(false) 87 | 88 | - name: >- 89 | Change the password with a custom TCP port and IP address. 90 | Should report Changed. 91 | include_role: 92 | name: linux-system-roles.mssql 93 | vars: 94 | mssql_password: "p@55w0rD11" 95 | mssql_tools_versions: [18] 96 | when: not __bootc_validation | d(false) 97 | 98 | - name: Verify settings 99 | include_tasks: tasks/verify_settings.yml 100 | vars: 101 | __verify_mssql_password: "p@55w0rD11" 102 | when: not __bootc_validation | d(false) 103 | 104 | - name: Verify the package {{ __mssql_verify_package_name }} 105 | include_tasks: verify_package.yml 106 | vars: 107 | __mssql_verify_package_name: mssql-tools18 108 | __mssql_verify_package_installed: true 109 | 110 | - name: Check the ansible_managed header in the configuration file 111 | include_tasks: tasks/check_header.yml 112 | -------------------------------------------------------------------------------- /tests/tasks/tests_tcp_firewall.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Assert fail on EL 7 with version = 2022 and EL 9 with version != 2022 3 | include_tasks: assert_fail_on_unsupported_ver.yml 4 | 5 | - name: Test while not settings mssql_tcp_port to default it to 1433 6 | vars: 7 | __mssql_test_port: 1433 8 | block: 9 | - name: Set up SQL Server 10 | include_role: 11 | name: linux-system-roles.mssql 12 | vars: 13 | mssql_password: "p@55w0rD" 14 | mssql_edition: Evaluation 15 | 16 | - name: >- 17 | Verify that the port is configured properly {{ __mssql_test_port }} 18 | include_tasks: tasks/verify_tcp_port.yml 19 | vars: 20 | __mssql_tcp_port_new: "{{ __mssql_test_port }}" 21 | 22 | - name: Configure the mssql-server service start limit interval and burst 23 | include_tasks: tasks/mssql-sever-increase-start-limit.yml 24 | 25 | - name: Test while settings mssql_tcp_port to to 1433 26 | vars: 27 | __mssql_test_port: 1433 28 | block: 29 | - name: Set up SQL Server 30 | include_role: 31 | name: linux-system-roles.mssql 32 | vars: 33 | mssql_password: "p@55w0rD" 34 | mssql_edition: Evaluation 35 | mssql_tcp_port: "{{ __mssql_test_port }}" 36 | 37 | - name: >- 38 | Verify that the port is configured properly {{ __mssql_test_port }} 39 | include_tasks: tasks/verify_tcp_port.yml 40 | vars: 41 | __mssql_tcp_port_new: "{{ __mssql_test_port }}" 42 | 43 | - name: Test with mssql_tcp_port changed to 1435 44 | vars: 45 | __mssql_test_port: 1435 46 | block: 47 | - name: Set up SQL Server 48 | include_role: 49 | name: linux-system-roles.mssql 50 | vars: 51 | mssql_password: "p@55w0rD" 52 | mssql_edition: Evaluation 53 | mssql_tcp_port: "{{ __mssql_test_port }}" 54 | 55 | - name: >- 56 | Verify that the port is configured properly {{ __mssql_test_port }} 57 | include_tasks: tasks/verify_tcp_port.yml 58 | vars: 59 | __mssql_tcp_port_new: "{{ __mssql_test_port }}" 60 | __mssql_tcp_port_previous: 1433 61 | -------------------------------------------------------------------------------- /tests/tasks/tests_tls.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Assert fail on EL 7 with version = 2022 and EL 9 with version != 2022 3 | include_tasks: assert_fail_on_unsupported_ver.yml 4 | 5 | - name: Ensure the openssl package 6 | package: 7 | name: openssl 8 | state: present 9 | run_once: true 10 | 11 | - name: Create a tempfile for a certificate on hosts 12 | tempfile: 13 | state: file 14 | register: __mssql_cert_tempfile 15 | run_once: true 16 | 17 | - name: Create a tempfile for a private key on hosts 18 | tempfile: 19 | state: file 20 | register: __mssql_pvk_tempfile 21 | run_once: true 22 | 23 | - name: Generate a self-signed certificate and public key 24 | command: >- 25 | openssl req -x509 -nodes -newkey rsa:2048 26 | -subj "/CN={{ ansible_default_ipv4.address }}" 27 | -out {{ __mssql_cert_tempfile.path }} 28 | -keyout {{ __mssql_pvk_tempfile.path }} -days 365 29 | changed_when: true 30 | run_once: true 31 | 32 | - name: Copy certificate files to local tmp 33 | fetch: 34 | src: "{{ item }}" 35 | dest: "{{ item }}" 36 | flat: true 37 | mode: preserve 38 | run_once: true 39 | loop: 40 | - "{{ __mssql_cert_tempfile.path }}" 41 | - "{{ __mssql_pvk_tempfile.path }}" 42 | 43 | # Test relative and full path with mssql_tls_remote_src: false 44 | - name: Copy a private key to the playbook directory to test a relative path 45 | copy: 46 | src: "{{ __mssql_pvk_tempfile.path }}" 47 | dest: "{{ __mssql_pvk_tempfile.path | basename }}" 48 | mode: preserve 49 | delegate_to: localhost 50 | run_once: true 51 | 52 | - name: Set mssql_tls_cert and mssql_tls_private_key for the following test 53 | set_fact: 54 | mssql_tls_cert: "{{ __mssql_cert_tempfile.path }}" 55 | mssql_tls_private_key: "{{ __mssql_pvk_tempfile.path | basename }}" 56 | 57 | - name: Test relative and full path with certs on control node 58 | block: 59 | - name: Run role 60 | include_role: 61 | name: linux-system-roles.mssql 62 | public: true 63 | vars: 64 | mssql_tls_enable: true 65 | mssql_tls_cert: "{{ __mssql_cert_tempfile.path }}" 66 | mssql_tls_private_key: "{{ __mssql_pvk_tempfile.path | basename }}" 67 | always: 68 | - name: Remove a private key from the playbook directory 69 | file: 70 | path: "{{ __mssql_pvk_tempfile.path | basename }}" 71 | state: absent 72 | delegate_to: localhost 73 | run_once: true 74 | 75 | - name: Configure the mssql-server service start limit interval and burst 76 | include_tasks: tasks/mssql-sever-increase-start-limit.yml 77 | 78 | - name: Verify connectivity and settings 79 | include_tasks: tasks/verify_settings.yml 80 | vars: 81 | __verify_mssql_password: "p@55w0rD" 82 | __verify_mssql_is_tls_encrypted: true 83 | 84 | # Test disabling TLS encryption 85 | - name: Disable TLS encryption 86 | include_role: 87 | name: linux-system-roles.mssql 88 | public: true 89 | vars: 90 | mssql_tls_enable: false 91 | 92 | - name: Verify connectivity and settings 93 | include_tasks: tasks/verify_settings.yml 94 | vars: 95 | __verify_mssql_password: "p@55w0rD" 96 | __verify_mssql_is_tls_encrypted: false 97 | 98 | # Test mssql_tls_remote_src: true 99 | - name: Remove certificates from hosts 100 | file: 101 | path: "{{ item }}" 102 | state: absent 103 | loop: 104 | - /etc/pki/tls/certs/{{ __mssql_cert_tempfile.path | basename }} 105 | - /etc/pki/tls/private/{{ __mssql_pvk_tempfile.path | basename }} 106 | 107 | - name: Copy certificates to hosts 108 | copy: 109 | src: "{{ item }}" 110 | dest: "{{ item }}" 111 | mode: preserve 112 | loop: 113 | - "{{ __mssql_cert_tempfile.path }}" 114 | - "{{ __mssql_pvk_tempfile.path }}" 115 | 116 | - name: Set mssql_tls_cert and mssql_tls_private_key for the following test 117 | set_fact: 118 | mssql_tls_cert: "{{ __mssql_cert_tempfile.path }}" 119 | mssql_tls_private_key: "{{ __mssql_pvk_tempfile.path | basename }}" 120 | 121 | - name: Test with certs on managed nodes 122 | include_role: 123 | name: linux-system-roles.mssql 124 | public: true 125 | vars: 126 | mssql_tls_enable: true 127 | mssql_tls_cert: "{{ __mssql_cert_tempfile.path }}" 128 | mssql_tls_private_key: "{{ __mssql_pvk_tempfile.path }}" 129 | mssql_tls_remote_src: true 130 | 131 | - name: Verify connectivity and settings 132 | include_tasks: tasks/verify_settings.yml 133 | vars: 134 | __verify_mssql_password: "p@55w0rD" 135 | __verify_mssql_is_tls_encrypted: true 136 | 137 | - name: Check the ansible_managed header in the configuration file 138 | include_tasks: tasks/check_header.yml 139 | 140 | # Test disabling TLS encryption 141 | - name: Disable TLS encryption 142 | include_role: 143 | name: linux-system-roles.mssql 144 | vars: 145 | mssql_tls_enable: false 146 | 147 | - name: Verify connectivity and settings 148 | include_tasks: tasks/verify_settings.yml 149 | vars: 150 | __verify_mssql_password: "p@55w0rD" 151 | __verify_mssql_is_tls_encrypted: false 152 | 153 | # Test mssql_tls_certificates 154 | - name: Test with certs created by the certificate role 155 | include_role: 156 | name: linux-system-roles.mssql 157 | public: true 158 | vars: 159 | mssql_tls_enable: true 160 | mssql_tls_certificates: 161 | - name: mssql_2019_cert 162 | common_name: "{{ ansible_default_ipv4.address }}" 163 | ca: self-sign 164 | 165 | - name: Flush handlers 166 | meta: flush_handlers 167 | 168 | - name: Verify connectivity and settings 169 | include_tasks: tasks/verify_settings.yml 170 | vars: 171 | __verify_mssql_password: "p@55w0rD" 172 | __verify_mssql_is_tls_encrypted: true 173 | 174 | # Disable TLS encryption for future tests 175 | - name: Disable TLS encryption 176 | include_role: 177 | name: linux-system-roles.mssql 178 | vars: 179 | mssql_tls_enable: false 180 | -------------------------------------------------------------------------------- /tests/tasks/upgrade_and_assert.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # With __mssql_version provide the version to upgrade to 3 | # With __mssql_prev_version provide the current version of SQL Server 4 | - name: Upgrade to a new version 5 | vars: 6 | mssql_upgrade: true 7 | mssql_version: "{{ __mssql_version }}" 8 | block: 9 | - name: Upgrade to {{ __mssql_version }} 10 | include_role: 11 | name: linux-system-roles.mssql 12 | 13 | - name: Upgrade again to test idempotency - should report not changed 14 | include_role: 15 | name: linux-system-roles.mssql 16 | rescue: 17 | - name: Assert that upgrading EL 7 to 2022 fails 18 | when: 19 | - __mssql_version == 2022 20 | - ansible_distribution in ['CentOS', 'RedHat'] 21 | - ansible_distribution_version is version('8', '<') 22 | assert: 23 | that: >- 24 | 'SQL Server 2022 does not support EL 7 hosts' 25 | in ansible_failed_result.msg 26 | always: 27 | - name: Clean up EL 7 after the role invocation 28 | when: 29 | - __mssql_version == 2022 30 | - ansible_distribution in ['CentOS', 'RedHat'] 31 | - ansible_distribution_version is version('8', '<') 32 | include_tasks: tasks/cleanup.yml 33 | 34 | # Putting end_host into a rescue block results in a failed task 35 | - name: End EL 7 host 36 | when: 37 | - __mssql_version == 2022 38 | - ansible_distribution in ['CentOS', 'RedHat'] 39 | - ansible_distribution_version is version('8', '<') 40 | meta: end_host 41 | 42 | - name: >- 43 | Verify the failure when mssql_version < current version 44 | block: 45 | - name: >- 46 | Upgrade to new version on mssql 47 | new version {{ __mssql_prev_version }} 48 | include_role: 49 | name: linux-system-roles.mssql 50 | vars: 51 | mssql_version: "{{ __mssql_prev_version }}" 52 | 53 | - name: Unreachable task 54 | fail: 55 | msg: The above task must fail 56 | rescue: 57 | # Verify the error message 58 | # Verify that the error is returned only once in ansible_failed_result 59 | - name: Assert that the role failed with the correct message 60 | assert: 61 | that: 62 | - >- 63 | 'You set mssql_version to {{ __mssql_prev_version }}, but your SQL' 64 | in ansible_failed_result.results 65 | | selectattr('msg', 'defined') 66 | | map(attribute='msg') 67 | | join(' ') 68 | - ansible_failed_result.results | selectattr('msg', 'defined') 69 | | list 70 | | length == 1 71 | -------------------------------------------------------------------------------- /tests/tasks/verify_ad_auth.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Test AD integration with keytab 4 | vars: 5 | __mssql_kinit_user: >- 6 | {{ ad_integration_user }}@{{ ad_integration_realm 7 | | upper }} 8 | block: 9 | - name: Install sshpass for use in the following task 10 | package: 11 | name: sshpass 12 | state: present 13 | 14 | - name: Print credential caches to check if AD principal exists 15 | command: klist -l 16 | register: __mssql_klist 17 | changed_when: false 18 | failed_when: false 19 | 20 | # Do not fail when there are no tickets 21 | - name: Print status of credential cache for {{ __mssql_kinit_user }} 22 | shell: >- 23 | set -euo pipefail; 24 | klist -l $(klist -l | grep {{ __mssql_kinit_user }} 25 | | awk '{print $2}') 26 | register: __mssql_klist_kinit_user 27 | when: __mssql_kinit_user in __mssql_klist.stdout 28 | changed_when: false 29 | 30 | - name: Obtain Kerberos ticket of the AD user {{ ad_integration_user }} 31 | shell: >- 32 | set -euo pipefail; 33 | echo {{ ad_integration_password | quote }} 34 | | kinit {{ __mssql_kinit_user }} 35 | when: >- 36 | (__mssql_kinit_user not in __mssql_klist.stdout) 37 | or 38 | ("(Expired)" in __mssql_klist_kinit_user.stdout | d()) 39 | changed_when: true 40 | 41 | - name: SSH into AD, kinit as Administrator, verify authentication 42 | shell: >- 43 | set -euo pipefail; 44 | sshpass -p {{ mssql_ad_sql_password | quote }} 45 | ssh -o StrictHostKeyChecking=no 46 | -l {{ mssql_ad_sql_user }}@{{ ad_integration_realm }} 47 | {{ ansible_fqdn }} 48 | "echo {{ ad_integration_password | quote }} 49 | | kinit {{ __mssql_kinit_user }} && 50 | /opt/mssql-tools18/bin/sqlcmd -S. -Q 'SELECT SYSTEM_USER'" 51 | register: __mssql_ad_test 52 | changed_when: false 53 | always: 54 | - name: Print test results 55 | debug: 56 | var: __mssql_ad_test.stdout_lines 57 | 58 | - name: Print test results 59 | debug: 60 | var: __mssql_ad_test.stderr_lines 61 | -------------------------------------------------------------------------------- /tests/tasks/verify_package.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | # This tasks file verifies if the provided package is installed or not. 4 | # It takes to variables: 5 | # __mssql_verify_package - the package to verify 6 | # __mssql_verify_installed - boolean 7 | - name: Gather package facts 8 | package_facts: 9 | manager: auto 10 | no_log: true 11 | 12 | - name: Verify if the package is installed {{ __mssql_verify_package_name }} 13 | assert: 14 | that: __mssql_verify_package_name in ansible_facts.packages 15 | when: __mssql_verify_package_installed | bool 16 | 17 | - name: Verify if the package is not installed {{ __mssql_verify_package_name }} 18 | assert: 19 | that: __mssql_verify_package_name not in ansible_facts.packages 20 | when: not __mssql_verify_package_installed 21 | -------------------------------------------------------------------------------- /tests/tasks/verify_tcp_port.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # This task files verifies that the provided port is configured in SQL Server 3 | # and opened in firewall. It can verify if a port is closed too. 4 | # It takes the following variables: 5 | # __mssql_tcp_port_new - the configured port to verify 6 | # __mssql_tcp_port_previous - optional: the previous port to verify it is closed 7 | --- 8 | - name: List opened firewall ports 9 | command: firewall-cmd --list-ports 10 | register: __mssql_firewall_ports 11 | changed_when: false 12 | 13 | - name: Verify that the port is open in firewall {{ __mssql_tcp_port_new }} 14 | assert: 15 | that: >- 16 | '{{ __mssql_tcp_port_new }}/tcp' in __mssql_firewall_ports.stdout 17 | when: __mssql_tcp_port_new is defined 18 | 19 | - name: Verify that the firewall port is closed {{ __mssql_tcp_port_previous }} 20 | assert: 21 | that: >- 22 | '{{ __mssql_tcp_port_previous }}/tcp' not in 23 | __mssql_firewall_ports.stdout 24 | when: __mssql_tcp_port_previous is defined 25 | 26 | - name: >- 27 | Verify that the port is configured in SQL Server {{ __mssql_tcp_port_new }} 28 | include_tasks: tasks/verify_settings.yml 29 | vars: 30 | __mssql_tcp_port_matches: "{{ __mssql_tcp_port_new }}" 31 | when: __mssql_tcp_port_new is defined 32 | -------------------------------------------------------------------------------- /tests/templates/add_test_listener.j2: -------------------------------------------------------------------------------- 1 | PRINT 'Adding the TEST-listener listener to the \ 2 | {{ mssql_ha_ag_name }} availability group'; 3 | ALTER AVAILABILITY GROUP {{ mssql_ha_ag_name }} 4 | ADD LISTENER 'TEST-listener' ( 5 | WITH IP ( ('{{ mssql_ha_virtual_ip }}','255.255.255.0') ), 6 | PORT = {{ mssql_tcp_port }} 7 | ); 8 | -------------------------------------------------------------------------------- /tests/templates/alter_ag.j2: -------------------------------------------------------------------------------- 1 | IF EXISTS ( 2 | SELECT name, cluster_type_desc 3 | FROM sys.availability_groups 4 | WHERE name = '{{ mssql_ha_ag_name }}' 5 | ) 6 | BEGIN 7 | PRINT 'Altering the existing availability group {{ mssql_ha_ag_name }}' 8 | IF NOT EXISTS ( 9 | SELECT name, db_failover 10 | FROM sys.availability_groups 11 | WHERE name = '{{ mssql_ha_ag_name }}' AND 12 | db_failover = 0 13 | ) 14 | BEGIN 15 | ALTER AVAILABILITY GROUP {{ mssql_ha_ag_name }} SET (DB_FAILOVER = ON) 16 | PRINT 'Set DB_FAILOVER to ON succesfully' 17 | END 18 | ELSE 19 | BEGIN 20 | PRINT 'DB_FAILOVER = OFF is already set, skipping' 21 | END 22 | PRINT 'Altering replicas' 23 | {% for item in ansible_play_hosts %} 24 | {% if hostvars[item]['mssql_ha_replica_type'] != 'absent' %} 25 | IF EXISTS ( 26 | SELECT replica_server_name 27 | FROM sys.availability_replicas 28 | WHERE replica_server_name = '{{ hostvars[item]['ansible_hostname'] }}' 29 | ) 30 | BEGIN 31 | PRINT 'Verifying the existing replica {{ item }}'; 32 | {% if (hostvars[item]['mssql_ha_replica_type'] == 'primary') or 33 | (hostvars[item]['mssql_ha_replica_type'] == 'synchronous') %} 34 | {% set ag_replica_settings = ({ 35 | "endpoint_url":{ 36 | "sql_setting_name":"ENDPOINT_URL", 37 | "sys_setting_name":"endpoint_url", 38 | "setting_value":"N'tcp://" + hostvars[item]['ansible_fqdn'] + ":" + 39 | "1234" + "'" 40 | }, 41 | "failover_mode":{ 42 | "sql_setting_name":"FAILOVER_MODE", 43 | "sys_setting_name":"failover_mode_desc", 44 | "setting_value":"EXTERNAL" 45 | }, 46 | "seeding_mode":{ 47 | "sql_setting_name":"SEEDING_MODE", 48 | "sys_setting_name":"seeding_mode_desc", 49 | "setting_value":"MANUAL" 50 | }, 51 | "allow_connections":{ 52 | "sql_setting_name": "SECONDARY_ROLE (ALLOW_CONNECTIONS = NO)", 53 | "sys_setting_name": "secondary_role_allow_connections_desc", 54 | "setting_value": "NO" 55 | } 56 | }) %} 57 | {% elif hostvars[item]['mssql_ha_replica_type'] == 'witness' %} 58 | {% set ag_replica_settings = ({ 59 | "endpoint_url":{ 60 | "sql_setting_name":"ENDPOINT_URL", 61 | "sys_setting_name":"endpoint_url", 62 | "setting_value":"N'tcp://" + hostvars[item]['ansible_fqdn'] + ":" + 63 | "1234" + "'" 64 | } 65 | }) %} 66 | {% endif %} 67 | {% for key, value in ag_replica_settings.items() %} 68 | IF NOT EXISTS ( 69 | SELECT replica_server_name, {{ value.sys_setting_name }} 70 | FROM sys.availability_replicas 71 | WHERE replica_server_name = N'{{ hostvars[item]['ansible_hostname'] }}' 72 | AND 73 | {% if key == 'endpoint_url' %} 74 | {{ value.sys_setting_name }} = {{ value.setting_value }} 75 | {% else %} 76 | {{ value.sys_setting_name }} = '{{ value.setting_value }}' 77 | {% endif %} 78 | ) 79 | BEGIN 80 | ALTER AVAILABILITY GROUP {{ mssql_ha_ag_name }} MODIFY REPLICA ON 81 | N'{{ hostvars[item]['ansible_hostname'] }}' WITH ( 82 | {% if key == 'allow_connections' %} 83 | {{ value.sql_setting_name }} 84 | {% else %} 85 | {{ value.sql_setting_name }} = {{ value.setting_value }} 86 | {% endif %} 87 | ); 88 | PRINT '{{ hostvars[item]['ansible_hostname'] }}: \ 89 | The {{ value.sql_setting_name }} setting on this \ 90 | {{ hostvars[item]['mssql_ha_replica_type'] }} replica configured successfully'; 91 | END 92 | ELSE 93 | BEGIN 94 | PRINT '{{ hostvars[item]['ansible_hostname'] }}: \ 95 | The {{ value.sql_setting_name }} setting on this \ 96 | {{ hostvars[item]['mssql_ha_replica_type'] }} replica is already set \ 97 | correctly, skipping'; 98 | END 99 | {% endfor %} 100 | END 101 | {% endif %} 102 | {% endfor %} 103 | END 104 | -------------------------------------------------------------------------------- /tests/templates/alter_endpoint.j2: -------------------------------------------------------------------------------- 1 | IF NOT EXISTS( 2 | SELECT name 3 | FROM sys.certificates 4 | WHERE name = 'test_cert' 5 | ) 6 | BEGIN 7 | PRINT 'Certificate test_cert does not exist, creating'; 8 | CREATE CERTIFICATE test_cert 9 | WITH SUBJECT = 'Test certificate'; 10 | PRINT 'Certificate test_cert created successfully'; 11 | END 12 | ELSE 13 | BEGIN 14 | PRINT 'Certificate test_cert already exists, skipping'; 15 | END 16 | 17 | ALTER ENDPOINT {{ mssql_ha_endpoint_name }} 18 | STATE = STOPPED 19 | AS TCP (LISTENER_PORT = 1234) 20 | FOR DATABASE_MIRRORING ( 21 | ROLE = PARTNER, 22 | AUTHENTICATION = CERTIFICATE test_cert, 23 | ENCRYPTION = REQUIRED ALGORITHM RC4 24 | ); 25 | -------------------------------------------------------------------------------- /tests/templates/configure_ag_cluster_type_none.j2: -------------------------------------------------------------------------------- 1 | CREATE AVAILABILITY GROUP {{ mssql_ha_ag_name }} 2 | WITH (CLUSTER_TYPE = NONE) 3 | FOR REPLICA ON 4 | N'{{ ansible_hostname }}' WITH ( 5 | ENDPOINT_URL = N'tcp://{{ ansible_fqdn }}:{{ mssql_ha_endpoint_port }}', 6 | AVAILABILITY_MODE = SYNCHRONOUS_COMMIT, 7 | FAILOVER_MODE = MANUAL, 8 | SEEDING_MODE = AUTOMATIC, 9 | SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL) 10 | ); 11 | -------------------------------------------------------------------------------- /tests/templates/create_example_db.j2: -------------------------------------------------------------------------------- 1 | IF NOT EXISTS( 2 | SELECT name 3 | FROM sys.databases 4 | WHERE name = '{{ __mssql_test_db_name }}' 5 | ) 6 | BEGIN 7 | PRINT 'Creating the {{ __mssql_test_db_name }} database'; 8 | CREATE DATABASE {{ __mssql_test_db_name }}; 9 | PRINT 'The {{ __mssql_test_db_name }} database created successfully'; 10 | END 11 | ELSE 12 | BEGIN 13 | PRINT 'The {{ __mssql_test_db_name }} database already exists, skipping'; 14 | END 15 | GO 16 | 17 | USE {{ __mssql_test_db_name }}; 18 | GO 19 | 20 | IF NOT EXISTS ( 21 | SELECT name, xtype 22 | FROM sysobjects 23 | WHERE name='Inventory' and xtype='U' 24 | ) 25 | BEGIN 26 | PRINT 'Adding the Inventory table to the {{ __mssql_test_db_name }} database'; 27 | CREATE TABLE Inventory (id INT, name NVARCHAR(50), quantity INT); 28 | INSERT INTO Inventory VALUES (1, 'apple', 100); 29 | INSERT INTO Inventory VALUES (2, 'orange', 150); 30 | INSERT INTO Inventory VALUES (3, 'banana', 154); 31 | INSERT INTO Inventory VALUES (4, N'バナナ', 170); 32 | PRINT 'The Inventory table created successfully'; 33 | END 34 | ELSE 35 | BEGIN 36 | PRINT 'The Inventory table already exists, skipping'; 37 | END 38 | GO 39 | -------------------------------------------------------------------------------- /tests/templates/disable_alwayson.j2: -------------------------------------------------------------------------------- 1 | ALTER EVENT SESSION AlwaysOn_health ON SERVER WITH (STARTUP_STATE=OFF); 2 | -------------------------------------------------------------------------------- /tests/templates/drop_ag.j2: -------------------------------------------------------------------------------- 1 | IF EXISTS ( 2 | SELECT name 3 | FROM sys.availability_groups 4 | WHERE name = '{{ mssql_ha_ag_name }}' 5 | ) 6 | BEGIN 7 | DROP AVAILABILITY GROUP ag1; 8 | PRINT 'The {{ mssql_ha_ag_name }} availability group dropped successfully'; 9 | END 10 | ELSE 11 | BEGIN 12 | PRINT 'The {{ mssql_ha_ag_name }} ag does not exist, skipping'; 13 | END 14 | -------------------------------------------------------------------------------- /tests/templates/drop_endpoint.j2: -------------------------------------------------------------------------------- 1 | PRINT 'Dropping the {{ mssql_ha_endpoint_name }} endpoint' 2 | IF NOT EXISTS ( 3 | SELECT name 4 | FROM sys.database_mirroring_endpoints 5 | WHERE name = '{{ mssql_ha_endpoint_name }}' 6 | ) 7 | BEGIN 8 | PRINT 'Endpoint {{ mssql_ha_endpoint_name }} does not exist, skipping' 9 | END 10 | ELSE 11 | BEGIN 12 | DROP ENDPOINT {{ mssql_ha_endpoint_name }}; 13 | PRINT 'Endpoint {{ mssql_ha_endpoint_name }} dropped successfully' 14 | END 15 | -------------------------------------------------------------------------------- /tests/templates/get_ansible_managed.j2: -------------------------------------------------------------------------------- 1 | {{ ansible_managed | comment }} 2 | {{ "system_role:mssql" | comment(prefix="", postfix="") }} 3 | -------------------------------------------------------------------------------- /tests/templates/remove_listener.j2: -------------------------------------------------------------------------------- 1 | IF NOT EXISTS ( 2 | SELECT dns_name 3 | FROM sys.availability_group_listeners 4 | ) 5 | BEGIN 6 | PRINT 'Listeners do not exist, skipping' 7 | END 8 | ELSE 9 | BEGIN 10 | DECLARE @ListenerName VARCHAR(30) = ( 11 | SELECT dns_name FROM sys.availability_group_listeners 12 | ); 13 | PRINT 'Removing the listener ' + CAST(@ListenerName AS VARCHAR); 14 | EXEC ( 15 | 'ALTER AVAILABILITY GROUP {{ mssql_ha_ag_name }} 16 | REMOVE LISTENER "'+@ListenerName+'"' 17 | ); 18 | PRINT 'Removed the listener ' + CAST(@ListenerName AS VARCHAR) + ' successfully'; 19 | END 20 | -------------------------------------------------------------------------------- /tests/templates/remove_sql_cert.j2: -------------------------------------------------------------------------------- 1 | DROP CERTIFICATE {{ mssql_ha_cert_name }}; 2 | -------------------------------------------------------------------------------- /tests/templates/separate_from_ag.j2: -------------------------------------------------------------------------------- 1 | DROP DATABASE {{ __mssql_test_database }} 2 | DROP AVAILABILITY GROUP {{ mssql_ha_ag_name }} 3 | -------------------------------------------------------------------------------- /tests/tests_2019_upgrade.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that the role installs version 2017 and upgrades to 2019 4 | hosts: all 5 | vars: 6 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 7 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 8 | mssql_accept_microsoft_sql_server_standard_eula: true 9 | __mssql_gather_facts_no_log: true 10 | tasks: 11 | - name: Assert fail on EL 7 with version=2022 and EL 9 with version!=2022 12 | include_tasks: tasks/assert_fail_on_unsupported_ver.yml 13 | vars: 14 | mssql_version: 2017 15 | 16 | - name: Run test in a block to clean up in always 17 | block: 18 | - name: Verify the failure when mssql_version is provided incorrectly 19 | block: 20 | - name: Run the role with incorrect mssql_version 21 | include_role: 22 | name: linux-system-roles.mssql 23 | public: true 24 | vars: 25 | mssql_version: 2018 26 | 27 | - name: Unreachable task 28 | fail: 29 | msg: The above task must fail 30 | 31 | rescue: 32 | - name: Assert that the role failed with the correct message 33 | assert: 34 | that: >- 35 | 'You must set the mssql_version variable to one of 36 | {{ __mssql_supported_versions | join(", ") }}' 37 | in ansible_failed_result.msg 38 | 39 | - name: Verify the failure when mssql_version is not provided 40 | block: 41 | - name: Run the role with incorrect mssql_version 42 | include_role: 43 | name: linux-system-roles.mssql 44 | public: true 45 | 46 | - name: Unreachable task 47 | fail: 48 | msg: The above task must fail 49 | 50 | rescue: 51 | - name: Assert that the role failed with the correct message 52 | assert: 53 | that: >- 54 | 'You must set the mssql_version variable to one of 55 | {{ __mssql_supported_versions | join(", ") }}' 56 | in ansible_failed_result.msg 57 | 58 | - name: Set up MSSQL 2017 59 | include_role: 60 | name: linux-system-roles.mssql 61 | vars: 62 | mssql_version: 2017 63 | mssql_password: "p@55w0rD" 64 | mssql_edition: Evaluation 65 | 66 | - name: Upgrade to 2019 and assert expected results 67 | include_tasks: tasks/upgrade_and_assert.yml 68 | vars: 69 | __mssql_version: 2019 70 | __mssql_prev_version: 2017 71 | 72 | - name: Check the ansible_managed header in the configuration file 73 | include_tasks: tasks/check_header.yml 74 | always: 75 | - name: Clean up after the role invocation 76 | include_tasks: tasks/cleanup.yml 77 | -------------------------------------------------------------------------------- /tests/tests_2022_upgrade.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that the role installs version 2017 and upgrades to 2019 4 | hosts: all 5 | vars: 6 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 7 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 8 | mssql_accept_microsoft_sql_server_standard_eula: true 9 | __mssql_gather_facts_no_log: true 10 | tasks: 11 | - name: Assert fail on EL 7 with version=2022 and EL 9 with version!=2022 12 | include_tasks: tasks/assert_fail_on_unsupported_ver.yml 13 | vars: 14 | mssql_version: 2019 15 | 16 | - name: Run test in a block to clean up in always 17 | block: 18 | - name: Verify the failure when mssql_version is provided incorrectly 19 | block: 20 | - name: Run the role with incorrect mssql_version 21 | include_role: 22 | name: linux-system-roles.mssql 23 | public: true 24 | vars: 25 | mssql_version: 2018 26 | 27 | - name: Unreachable task 28 | fail: 29 | msg: The above task must fail 30 | 31 | rescue: 32 | - name: Assert that the role failed with the correct message 33 | assert: 34 | that: >- 35 | 'You must set the mssql_version variable to one of 36 | {{ __mssql_supported_versions | join(", ") }}' 37 | in ansible_failed_result.msg 38 | 39 | - name: Verify the failure when mssql_version is not provided 40 | block: 41 | - name: Run the role with incorrect mssql_version 42 | include_role: 43 | name: linux-system-roles.mssql 44 | public: true 45 | 46 | - name: Unreachable task 47 | fail: 48 | msg: The above task must fail 49 | 50 | rescue: 51 | - name: Assert that the role failed with the correct message 52 | assert: 53 | that: >- 54 | 'You must set the mssql_version variable to one of 55 | {{ __mssql_supported_versions | join(", ") }}' 56 | in ansible_failed_result.msg 57 | 58 | - name: Set up MSSQL 2019 59 | include_role: 60 | name: linux-system-roles.mssql 61 | vars: 62 | mssql_version: 2019 63 | mssql_password: "p@55w0rD" 64 | mssql_edition: Evaluation 65 | 66 | - name: Upgrade to 2022 and assert expected results 67 | include_tasks: tasks/upgrade_and_assert.yml 68 | vars: 69 | __mssql_version: 2022 70 | __mssql_prev_version: 2019 71 | 72 | - name: Check the ansible_managed header in the configuration file 73 | include_tasks: tasks/check_header.yml 74 | always: 75 | - name: Clean up after the role invocation 76 | include_tasks: tasks/cleanup.yml 77 | -------------------------------------------------------------------------------- /tests/tests_accept_eula.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that the role runs when EULA are accepted 4 | hosts: all 5 | vars: 6 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 7 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 8 | mssql_accept_microsoft_sql_server_standard_eula: true 9 | mssql_version: "{{ 10 | '2022' if (ansible_distribution_major_version is version('9', '>=') or 11 | ansible_distribution in ['Fedora']) 12 | else '2017' }}" 13 | __mssql_test_confined_supported: "{{ 14 | ((ansible_distribution in ['CentOS', 'RedHat']) and 15 | (ansible_distribution_major_version is version('9', '>='))) or 16 | (ansible_distribution in ['Fedora']) }}" 17 | mssql_manage_selinux: "{{ __mssql_test_confined_supported }}" 18 | mssql_run_selinux_confined: "{{ __mssql_test_confined_supported }}" 19 | __mssql_gather_facts_no_log: true 20 | tasks: 21 | - name: Run test in a block to clean up in always 22 | block: 23 | - name: Verify the failure on a fresh host when required vars undefined 24 | block: 25 | - name: Run the role with default parameters 26 | include_role: 27 | name: linux-system-roles.mssql 28 | 29 | - name: Unreachable task 30 | fail: 31 | msg: The above task must fail 32 | 33 | rescue: 34 | - name: Assert that the role failed with variables undefined 35 | assert: 36 | that: >- 37 | 'You must define the following variables to set up MSSQL' in 38 | ansible_failed_result.msg.0 39 | always: 40 | - name: Clean up after the role invocation 41 | include_tasks: tasks/cleanup.yml 42 | -------------------------------------------------------------------------------- /tests/tests_configure_ha_cluster_read_scale.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # Example test inventory: 3 | # all: 4 | # hosts: 5 | # managed_host1: 6 | # ansible_host: 7 | # mssql_ha_replica_type: primary 8 | # managed_host2: 9 | # ansible_host: 10 | # mssql_ha_replica_type: synchronous 11 | # managed_host3: 12 | # ansible_host: 13 | # mssql_ha_replica_type: asynchronous 14 | --- 15 | - name: Verify HA functionality with read-scale clusters 16 | hosts: all 17 | vars: 18 | mssql_debug: true 19 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 20 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 21 | mssql_accept_microsoft_sql_server_standard_eula: true 22 | mssql_password: "p@55w0rD" 23 | mssql_edition: Evaluation 24 | mssql_version: 2022 25 | mssql_manage_firewall: true 26 | __mssql_gather_facts_no_log: true 27 | mssql_manage_selinux: false 28 | mssql_run_selinux_confined: false 29 | mssql_ha_configure: true 30 | mssql_ha_endpoint_port: 5022 31 | mssql_ha_cert_name: ExampleCert 32 | mssql_ha_master_key_password: "p@55w0rD1" 33 | mssql_ha_private_key_password: "p@55w0rD2" 34 | mssql_ha_reset_cert: false 35 | mssql_ha_endpoint_name: Example_Endpoint 36 | mssql_ha_ag_name: ExampleAG 37 | mssql_ha_db_names: 38 | - ExampleDB1 39 | - ExampleDB2 40 | mssql_ha_ag_cluster_type: none 41 | tasks: 42 | - name: Run test in a block to clean up in always 43 | when: ansible_play_hosts_all | length >= 3 44 | block: 45 | - name: Clusters of a read_scale type must not have a witness node 46 | meta: end_play 47 | when: ansible_play_hosts_all | 48 | map('extract', hostvars, 'mssql_ha_replica_type') | 49 | select('match', '^witness$') | 50 | list | 51 | length > 0 52 | 53 | - name: Verify that by default the role fails on EL < 8 54 | when: 55 | - ansible_distribution in ['CentOS', 'RedHat'] 56 | - ansible_distribution_version is version('8', '<') 57 | block: 58 | - name: Run the role 59 | vars: 60 | mssql_ha_configure: true 61 | include_role: 62 | name: linux-system-roles.mssql 63 | 64 | - name: Unreachable task 65 | fail: 66 | msg: The above task must fail 67 | rescue: 68 | - name: Assert that the role failed with EL 7 not supported 69 | assert: 70 | that: >- 71 | 'mssql_ha_configure=true does not support running against EL 7 72 | hosts' in ansible_failed_result.msg 73 | always: 74 | - name: Clean up after the role invocation 75 | include_tasks: tasks/cleanup.yml 76 | when: ansible_play_hosts_all | length == 1 77 | 78 | # Putting end_host into a rescue block results in a failed task 79 | - name: End EL 7 host 80 | meta: end_host 81 | 82 | - name: Set facts to create test DBs on primary as a pre task 83 | set_fact: 84 | mssql_pre_input_sql_file: 85 | - create_ExampleDB1.sql 86 | - create_ExampleDB2.sql 87 | when: mssql_ha_replica_type == 'primary' 88 | 89 | - name: Run on all hosts to configure read-scale cluster 90 | include_role: 91 | name: linux-system-roles.mssql 92 | 93 | - name: Verify that the database exists on the secondary servers 94 | vars: 95 | __mssql_sql_content_to_input: 96 | - |- 97 | IF EXISTS( 98 | SELECT name 99 | FROM sys.databases 100 | WHERE name = 'ExampleDB1' 101 | ) 102 | BEGIN 103 | PRINT 'SUCCESS, The database ExampleDB1 exists' 104 | END 105 | ELSE 106 | BEGIN 107 | PRINT 'FAIL, The database ExampleDB1 exists' 108 | END 109 | include_role: 110 | name: linux-system-roles.mssql 111 | tasks_from: input_sql_files.yml 112 | 113 | - name: Assert that the template reported changed state 114 | assert: 115 | that: >- 116 | "SUCCESS, The database ExampleDB1 exists" in 117 | __mssql_sqlcmd_input.stdout 118 | always: 119 | - name: Clean up after the role invocation 120 | include_tasks: tasks/cleanup.yml 121 | -------------------------------------------------------------------------------- /tests/tests_default_2019.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that the role runs with default parameters 4 | hosts: all 5 | tasks: 6 | - name: Run test in a block to clean up in always 7 | block: 8 | - name: Verify that by default the role fails with EULA not accepted 9 | block: 10 | - name: Run the role with default parameters 11 | include_role: 12 | name: linux-system-roles.mssql 13 | 14 | - name: Unreachable task 15 | fail: 16 | msg: The above task must fail 17 | 18 | rescue: 19 | - name: Assert that the role failed with EULA not accepted 20 | assert: 21 | that: >- 22 | 'You must accept EULA' in ansible_failed_result.msg.0 23 | always: 24 | - name: Clean up after the role invocation 25 | include_tasks: tasks/cleanup.yml 26 | -------------------------------------------------------------------------------- /tests/tests_idempotency_2017.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that the role is idempotent 4 | hosts: all 5 | gather_facts: false 6 | vars: 7 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 8 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 9 | mssql_accept_microsoft_sql_server_standard_eula: true 10 | mssql_version: 2017 11 | __mssql_gather_facts_no_log: true 12 | tasks: 13 | - name: Run test in a block to clean up in always 14 | block: 15 | - name: Run tests_idempotency with SQL Server {{ mssql_version }} 16 | include_tasks: tasks/tests_idempotency.yml 17 | always: 18 | - name: Clean up after the role invocation 19 | include_tasks: tasks/cleanup.yml 20 | -------------------------------------------------------------------------------- /tests/tests_idempotency_2019.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that the role is idempotent 4 | hosts: all 5 | gather_facts: false 6 | vars: 7 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 8 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 9 | mssql_accept_microsoft_sql_server_standard_eula: true 10 | mssql_version: 2019 11 | __mssql_gather_facts_no_log: true 12 | tasks: 13 | - name: Run test in a block to clean up in always 14 | block: 15 | - name: Run tests_idempotency with SQL Server {{ mssql_version }} 16 | include_tasks: tasks/tests_idempotency.yml 17 | always: 18 | - name: Clean up after the role invocation 19 | include_tasks: tasks/cleanup.yml 20 | -------------------------------------------------------------------------------- /tests/tests_idempotency_2022.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that the role is idempotent 4 | hosts: all 5 | gather_facts: false 6 | vars: 7 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 8 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 9 | mssql_accept_microsoft_sql_server_standard_eula: true 10 | mssql_version: 2022 11 | mssql_manage_selinux: "{{ mssql_run_selinux_confined }}" 12 | __mssql_gather_facts_no_log: true 13 | tasks: 14 | - name: Run test in a block to clean up in always 15 | block: 16 | - name: Run tests_idempotency with SQL Server {{ mssql_version }} 17 | include_tasks: tasks/tests_idempotency.yml 18 | always: 19 | - name: Clean up after the role invocation 20 | include_tasks: tasks/cleanup.yml 21 | -------------------------------------------------------------------------------- /tests/tests_include_vars_from_parent.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Test role include variable override 3 | hosts: all 4 | gather_facts: true 5 | tasks: 6 | - name: Run test in a block to clean up in always 7 | block: 8 | - name: >- 9 | Create var file in caller that can override the one in called role 10 | delegate_to: localhost 11 | copy: 12 | # usually the fake file will cause the called role to crash of 13 | # overriding happens, but if not, set a variable that will 14 | # allow to detect the bug 15 | content: "__caller_override: true" 16 | # XXX ugly, self-modifying code - changes the "caller" role on 17 | # the controller 18 | dest: "{{ playbook_dir }}/roles/caller/vars/{{ item }}.yml" 19 | mode: preserve 20 | loop: "{{ varfiles | unique }}" 21 | # In case the playbook is executed against multiple hosts, use 22 | # only the first one. Otherwise the hosts would stomp on each 23 | # other since they are changing files on the controller. 24 | when: inventory_hostname == ansible_play_hosts_all[0] 25 | vars: 26 | # change to hostvars['localhost']['ansible_facts'] to use the 27 | # information for localhost 28 | facts: "{{ ansible_facts }}" 29 | versions: 30 | - "{{ facts['distribution_version'] }}" 31 | - "{{ facts['distribution_major_version'] }}" 32 | separators: ["-", "_"] 33 | # create all variants like CentOS, CentOS_8.1, CentOS-8.1, 34 | # CentOS-8, CentOS-8.1 35 | # more formally: 36 | # {{ ansible_distribution }}-{{ ansible_distribution_version }} 37 | # {{ ansible_distribution }}-\ 38 | # {{ ansible_distribution_major_version }} 39 | # {{ ansible_distribution }} 40 | # {{ ansible_os_family }} 41 | # and the same for _ as separator. 42 | varfiles: "{{ [facts['distribution']] | product(separators) | 43 | map('join') | product(versions) | map('join') | list + 44 | [facts['distribution'], facts['os_family']] }}" 45 | register: __varfiles_created 46 | 47 | - name: Import role 48 | import_role: 49 | name: caller 50 | vars: 51 | roletoinclude: linux-system-roles.mssql 52 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 53 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 54 | mssql_accept_microsoft_sql_server_standard_eula: true 55 | mssql_version: "{{ 56 | '2022' if (ansible_distribution_major_version 57 | is version('9', '>=') or 58 | ansible_distribution in ['Fedora']) 59 | else '2017' }}" 60 | mssql_password: P@55w0rD 61 | mssql_edition: Evaluation 62 | __mssql_test_confined_supported: "{{ 63 | ((ansible_distribution in ['CentOS', 'RedHat']) and 64 | (ansible_distribution_major_version is version('9', '>='))) or 65 | (ansible_distribution in ['Fedora']) }}" 66 | mssql_manage_selinux: "{{ __mssql_test_confined_supported }}" 67 | mssql_run_selinux_confined: "{{ __mssql_test_confined_supported }}" 68 | always: 69 | - name: Cleanup 70 | file: 71 | path: "{{ item.dest }}" 72 | state: absent 73 | loop: "{{ __varfiles_created.results }}" 74 | delegate_to: localhost 75 | when: inventory_hostname == ansible_play_hosts_all[0] 76 | 77 | - name: Clean up after the role invocation 78 | include_tasks: tasks/cleanup.yml 79 | -------------------------------------------------------------------------------- /tests/tests_input_sql_file_2017.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that the role can input sql files to MSSQL 4 | hosts: all 5 | vars: 6 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 7 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 8 | mssql_accept_microsoft_sql_server_standard_eula: true 9 | mssql_debug: true 10 | mssql_version: 2017 11 | __mssql_gather_facts_no_log: true 12 | tasks: 13 | - name: Run test in a block to clean up in always 14 | block: 15 | - name: Run tests_input_sql_file with SQL Server {{ mssql_version }} 16 | include_tasks: tasks/tests_input_sql_file.yml 17 | always: 18 | - name: Clean up after the role invocation 19 | include_tasks: tasks/cleanup.yml 20 | -------------------------------------------------------------------------------- /tests/tests_input_sql_file_2019.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that the role can input sql files to MSSQL 4 | hosts: all 5 | vars: 6 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 7 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 8 | mssql_accept_microsoft_sql_server_standard_eula: true 9 | mssql_debug: true 10 | mssql_version: 2019 11 | __mssql_gather_facts_no_log: true 12 | tasks: 13 | - name: Run test in a block to clean up in always 14 | block: 15 | - name: Run tests_input_sql_file with SQL Server {{ mssql_version }} 16 | include_tasks: tasks/tests_input_sql_file.yml 17 | always: 18 | - name: Clean up after the role invocation 19 | include_tasks: tasks/cleanup.yml 20 | -------------------------------------------------------------------------------- /tests/tests_input_sql_file_2022.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that the role can input sql files to MSSQL 4 | hosts: all 5 | tags: 6 | # this test doesn't work in container builds 7 | - tests::booted 8 | vars: 9 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 10 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 11 | mssql_accept_microsoft_sql_server_standard_eula: true 12 | mssql_debug: true 13 | mssql_version: 2022 14 | mssql_manage_selinux: "{{ mssql_run_selinux_confined }}" 15 | __mssql_gather_facts_no_log: true 16 | tasks: 17 | - name: Run test in a block to clean up in always 18 | block: 19 | - name: Run tests_input_sql_file with SQL Server {{ mssql_version }} 20 | include_tasks: tasks/tests_input_sql_file.yml 21 | always: 22 | - name: Clean up after the role invocation 23 | include_tasks: tasks/cleanup.yml 24 | -------------------------------------------------------------------------------- /tests/tests_password_2017.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that the role is idempotent when changing the sa user password 4 | hosts: all 5 | vars: 6 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 7 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 8 | mssql_accept_microsoft_sql_server_standard_eula: true 9 | mssql_version: 2017 10 | __mssql_gather_facts_no_log: true 11 | tasks: 12 | - name: Run test in a block to clean up in always 13 | block: 14 | - name: Run tests_password with SQL Server {{ mssql_version }} 15 | include_tasks: tasks/tests_password.yml 16 | always: 17 | - name: Clean up after the role invocation 18 | include_tasks: tasks/cleanup.yml 19 | -------------------------------------------------------------------------------- /tests/tests_password_2019.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that the role is idempotent when changing the sa user password 4 | hosts: all 5 | vars: 6 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 7 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 8 | mssql_accept_microsoft_sql_server_standard_eula: true 9 | mssql_version: 2019 10 | __mssql_gather_facts_no_log: true 11 | tasks: 12 | - name: Run test in a block to clean up in always 13 | block: 14 | - name: Run tests_password with SQL Server {{ mssql_version }} 15 | include_tasks: tasks/tests_password.yml 16 | always: 17 | - name: Clean up after the role invocation 18 | include_tasks: tasks/cleanup.yml 19 | -------------------------------------------------------------------------------- /tests/tests_password_2022.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that the role is idempotent when changing the sa user password 4 | hosts: all 5 | vars: 6 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 7 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 8 | mssql_accept_microsoft_sql_server_standard_eula: true 9 | mssql_version: 2022 10 | mssql_manage_selinux: "{{ mssql_run_selinux_confined }}" 11 | __mssql_gather_facts_no_log: true 12 | tasks: 13 | - name: Run test in a block to clean up in always 14 | block: 15 | - name: Run tests_password with SQL Server {{ mssql_version }} 16 | include_tasks: tasks/tests_password.yml 17 | always: 18 | - name: Clean up after the role invocation 19 | include_tasks: tasks/cleanup.yml 20 | when: not __bootc_validation | d(false) 21 | -------------------------------------------------------------------------------- /tests/tests_selinux_enforcing_2022.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Test mssql_run_selinux_confined 4 | hosts: all 5 | gather_facts: false 6 | vars: 7 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 8 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 9 | mssql_accept_microsoft_sql_server_standard_eula: true 10 | mssql_version: 2022 11 | mssql_password: "p@55w0rD" 12 | mssql_edition: Evaluation 13 | __mssql_gather_facts_no_log: true 14 | tasks: 15 | - name: Assert expected failures on unsupported platforms 16 | include_tasks: tasks/assert_fail_on_unsupported_ver.yml 17 | 18 | - name: Check error message on not supported hosts and clean up 19 | when: 20 | - ansible_distribution in ['CentOS', 'RedHat'] 21 | - ansible_distribution_major_version is version('9', '<') 22 | block: 23 | - name: Check error message on not supported hosts 24 | include_role: 25 | name: linux-system-roles.mssql 26 | vars: 27 | mssql_manage_selinux: true 28 | mssql_run_selinux_confined: true 29 | rescue: 30 | - name: Assert that the role failed with selinux_confined not supported 31 | assert: 32 | that: >- 33 | 'Variables mssql_run_selinux_confined and mssql_manage_selinux are 34 | supported only on RHEL 9' in ansible_failed_result.msg 35 | always: 36 | - name: Clean up after the role invocation 37 | include_tasks: tasks/cleanup.yml 38 | 39 | - name: End unsupported host 40 | meta: end_host 41 | 42 | - name: Run test in a block to clean up in always 43 | block: 44 | - name: Run with selinux_confined 45 | include_role: 46 | name: linux-system-roles.mssql 47 | vars: 48 | mssql_manage_selinux: true 49 | mssql_run_selinux_confined: true 50 | 51 | - name: Verify settings 52 | include_tasks: tasks/verify_settings.yml 53 | vars: 54 | __verify_mssql_password: "p@55w0rD" 55 | __verify_mssql_edition: Evaluation 56 | __verify_mssql_is_confined: true 57 | 58 | - name: Configure the mssql-server service start limit interval and burst 59 | include_tasks: tasks/mssql-sever-increase-start-limit.yml 60 | 61 | - name: Run without selinux_confined 62 | include_role: 63 | name: linux-system-roles.mssql 64 | vars: 65 | mssql_manage_selinux: false 66 | mssql_run_selinux_confined: false 67 | 68 | - name: Verify settings 69 | include_tasks: tasks/verify_settings.yml 70 | vars: 71 | __verify_mssql_password: "p@55w0rD" 72 | __verify_mssql_edition: Evaluation 73 | __verify_mssql_is_confined: false 74 | always: 75 | - name: Clean up after the role invocation 76 | include_tasks: tasks/cleanup.yml 77 | -------------------------------------------------------------------------------- /tests/tests_tcp_firewall_2017.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Verify the use of the firewall role to configure SQL Server TCP ports 4 | hosts: all 5 | vars: 6 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 7 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 8 | mssql_accept_microsoft_sql_server_standard_eula: true 9 | mssql_manage_firewall: true 10 | mssql_version: 2017 11 | __mssql_gather_facts_no_log: true 12 | tasks: 13 | - name: Run test in a block to clean up in always 14 | block: 15 | - name: Run tests_tcp_firewall with SQL Server {{ mssql_version }} 16 | include_tasks: tasks/tests_tcp_firewall.yml 17 | always: 18 | - name: Clean up after the role invocation 19 | include_tasks: tasks/cleanup.yml 20 | -------------------------------------------------------------------------------- /tests/tests_tcp_firewall_2019.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Verify the use of the firewall role to configure SQL Server TCP ports 4 | hosts: all 5 | vars: 6 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 7 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 8 | mssql_accept_microsoft_sql_server_standard_eula: true 9 | mssql_manage_firewall: true 10 | mssql_version: 2019 11 | __mssql_gather_facts_no_log: true 12 | tasks: 13 | - name: Run test in a block to clean up in always 14 | block: 15 | - name: Run tests_tcp_firewall with SQL Server {{ mssql_version }} 16 | include_tasks: tasks/tests_tcp_firewall.yml 17 | always: 18 | - name: Clean up after the role invocation 19 | include_tasks: tasks/cleanup.yml 20 | -------------------------------------------------------------------------------- /tests/tests_tcp_firewall_2022.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Verify the use of the firewall role to configure SQL Server TCP ports 4 | hosts: all 5 | tags: 6 | # FIXME: firewall role does not currently work in container builds 7 | # https://issues.redhat.com/browse/RHEL-88425 8 | - tests::booted 9 | vars: 10 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 11 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 12 | mssql_accept_microsoft_sql_server_standard_eula: true 13 | mssql_manage_firewall: true 14 | mssql_version: 2022 15 | mssql_manage_selinux: "{{ mssql_run_selinux_confined }}" 16 | __mssql_gather_facts_no_log: true 17 | tasks: 18 | - name: Run test in a block to clean up in always 19 | block: 20 | - name: Run tests_tcp_firewall with SQL Server {{ mssql_version }} 21 | include_tasks: tasks/tests_tcp_firewall.yml 22 | always: 23 | - name: Clean up after the role invocation 24 | include_tasks: tasks/cleanup.yml 25 | -------------------------------------------------------------------------------- /tests/tests_tls_2017.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that tls encryption configuration works 4 | hosts: all 5 | vars: 6 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 7 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 8 | mssql_accept_microsoft_sql_server_standard_eula: true 9 | mssql_password: "p@55w0rD" 10 | mssql_edition: Evaluation 11 | mssql_tcp_port: 1433 12 | mssql_version: 2017 13 | mssql_tls_self_sign: true 14 | __mssql_gather_facts_no_log: true 15 | tasks: 16 | - name: Run test in a block to clean up in always 17 | block: 18 | - name: Run tests_tls with SQL Server {{ mssql_version }} 19 | include_tasks: tasks/tests_tls.yml 20 | always: 21 | - name: Clean up after the role invocation 22 | include_tasks: tasks/cleanup.yml 23 | -------------------------------------------------------------------------------- /tests/tests_tls_2019.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that tls encryption configuration works 4 | hosts: all 5 | vars: 6 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 7 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 8 | mssql_accept_microsoft_sql_server_standard_eula: true 9 | mssql_password: "p@55w0rD" 10 | mssql_edition: Evaluation 11 | mssql_tcp_port: 1433 12 | mssql_version: 2019 13 | mssql_tls_self_sign: true 14 | __mssql_gather_facts_no_log: true 15 | tasks: 16 | - name: Run test in a block to clean up in always 17 | block: 18 | - name: Run tests_tls with SQL Server {{ mssql_version }} 19 | include_tasks: tasks/tests_tls.yml 20 | always: 21 | - name: Clean up after the role invocation 22 | include_tasks: tasks/cleanup.yml 23 | -------------------------------------------------------------------------------- /tests/tests_tls_2022.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | - name: Ensure that tls encryption configuration works 4 | hosts: all 5 | tags: 6 | # certificate role can not work in container builds 7 | - tests::booted 8 | vars: 9 | mssql_accept_microsoft_odbc_driver_for_sql_server_eula: true 10 | mssql_accept_microsoft_cli_utilities_for_sql_server_eula: true 11 | mssql_accept_microsoft_sql_server_standard_eula: true 12 | mssql_password: "p@55w0rD" 13 | mssql_edition: Evaluation 14 | mssql_tcp_port: 1433 15 | mssql_version: 2022 16 | mssql_tls_self_sign: true 17 | mssql_manage_selinux: "{{ mssql_run_selinux_confined }}" 18 | __mssql_gather_facts_no_log: true 19 | tasks: 20 | - name: Run test in a block to clean up in always 21 | block: 22 | - name: Run tests_tls with SQL Server {{ mssql_version }} 23 | include_tasks: tasks/tests_tls.yml 24 | always: 25 | - name: Clean up after the role invocation 26 | include_tasks: tasks/cleanup.yml 27 | -------------------------------------------------------------------------------- /tests/vars/rh_distros_vars.yml: -------------------------------------------------------------------------------- 1 | # vars for handling conditionals for RedHat and clones 2 | # DO NOT EDIT - file is auto-generated 3 | # repo is https://github.com/linux-system-roles/.github 4 | # file is playbooks/templates/tests/vars/rh_distros_vars.yml 5 | --- 6 | # Ansible distribution identifiers that the role treats like RHEL 7 | __mssql_rh_distros: 8 | - AlmaLinux 9 | - CentOS 10 | - RedHat 11 | - Rocky 12 | 13 | # Same as above but includes Fedora 14 | __mssql_rh_distros_fedora: "{{ __mssql_rh_distros + ['Fedora'] }}" 15 | 16 | # Use this in conditionals to check if distro is Red Hat or clone 17 | __mssql_is_rh_distro: "{{ ansible_distribution in __mssql_rh_distros }}" 18 | 19 | # Use this in conditionals to check if distro is Red Hat or clone, or Fedora 20 | __mssql_is_rh_distro_fedora: "{{ ansible_distribution in __mssql_rh_distros_fedora }}" 21 | -------------------------------------------------------------------------------- /tests/vars/vault-variables.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | mssql_password: !vault | 4 | $ANSIBLE_VAULT;1.1;AES256 5 | 66336361663164656232326461653662643537386337346563613939356466313835383235313234 6 | 3634333565616161316639666662613564353237653663610a616639306439653033386664303839 7 | 30663030636161326137656235636230356162373234386461363632353863393161353035316162 8 | 3664353366373231360a343334326337353861366233656330363634353164316434616561333161 9 | 3234 10 | -------------------------------------------------------------------------------- /tests/vault_pwd: -------------------------------------------------------------------------------- 1 | password 2 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | [lsr_config] 3 | lsr_enable = true 4 | 5 | [lsr_ansible-lint] 6 | configfile = {toxinidir}/.ansible-lint 7 | -------------------------------------------------------------------------------- /vars/AlmaLinux_8.yml: -------------------------------------------------------------------------------- 1 | RedHat_8.yml -------------------------------------------------------------------------------- /vars/CentOS_7.yml: -------------------------------------------------------------------------------- 1 | RedHat_7.yml -------------------------------------------------------------------------------- /vars/CentOS_8.yml: -------------------------------------------------------------------------------- 1 | RedHat_8.yml -------------------------------------------------------------------------------- /vars/RedHat.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | __mssql_server_repository: >- 4 | https://packages.microsoft.com/rhel/9/mssql-server-{{ mssql_version | int }}/ 5 | __mssql_client_repository: >- 6 | https://packages.microsoft.com/rhel/9/prod/ 7 | __mssql_supported_versions: 8 | - 2022 9 | __mssql_confined_supported: true 10 | __mssql_tuned_supported: true 11 | -------------------------------------------------------------------------------- /vars/RedHat_7.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | __mssql_server_repository: >- 4 | https://packages.microsoft.com/rhel/7/mssql-server-{{ mssql_version | int }}/ 5 | __mssql_client_repository: >- 6 | https://packages.microsoft.com/rhel/7/prod/ 7 | __mssql_supported_versions: 8 | - 2017 9 | - 2019 10 | __mssql_confined_supported: false 11 | -------------------------------------------------------------------------------- /vars/RedHat_8.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | __mssql_server_repository: >- 4 | https://packages.microsoft.com/rhel/8/mssql-server-{{ mssql_version | int }}/ 5 | __mssql_client_repository: >- 6 | https://packages.microsoft.com/rhel/8/prod/ 7 | __mssql_supported_versions: 8 | - 2017 9 | - 2019 10 | - 2022 11 | __mssql_confined_supported: false 12 | -------------------------------------------------------------------------------- /vars/Rocky_8.yml: -------------------------------------------------------------------------------- 1 | RedHat_8.yml -------------------------------------------------------------------------------- /vars/Suse.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | __mssql_base_repository: "https://packages.microsoft.com/sles/{{ ansible_facts['distribution_major_version'] | int }}/" 4 | __mssql_server_repository: "{{ __mssql_base_repository }}mssql-server-{{ mssql_version | int }}/" 5 | __mssql_client_repository: "{{ __mssql_base_repository }}prod/" 6 | __mssql_client_packages: "mssql-tools{{ '' if __sqlcmd_ver == '17' else __sqlcmd_ver }}" 7 | __mssql_supported_versions: 8 | - 2019 9 | - 2022 10 | __mssql_confined_supported: false 11 | __mssql_tuned_supported: false 12 | -------------------------------------------------------------------------------- /vars/main.yml: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | --- 3 | __mssql_required_facts: 4 | - distribution 5 | - distribution_major_version 6 | - distribution_version 7 | - os_family 8 | # the subsets of ansible_facts that need to be gathered in case any of the 9 | # facts in required_facts is missing; see the documentation of 10 | # the 'gather_subset' parameter of the 'setup' module 11 | __mssql_required_facts_subsets: "{{ ['!all', '!min'] + 12 | __mssql_required_facts }}" 13 | __mssql_server_packages: mssql-server 14 | __mssql_server_selinux_packages: mssql-server-selinux 15 | __sqlcmd_ver: "{{ mssql_tools_versions | sort | last }}" 16 | __sqlcmd_cli: "{{ 17 | '/opt/mssql-tools/bin/sqlcmd' if __sqlcmd_ver == '17' 18 | else '/opt/mssql-tools' + __sqlcmd_ver + '/bin/sqlcmd' }}" 19 | __mssql_client_packages: 20 | - mssql-tools{{ '' if __sqlcmd_ver == '17' else __sqlcmd_ver }} 21 | - unixODBC-devel 22 | __mssql_server_fts_packages: mssql-server-fts 23 | __mssql_server_ha_packages: mssql-server-ha 24 | __mssql_powershell_packages: powershell 25 | # Dicts define SQL Server version and the corresponding mssql-server version 26 | __mssql_version_package_mapping: 27 | - 2017: 14 28 | - 2019: 15 29 | - 2022: 16 30 | __mssql_keytab_path: /var/opt/mssql/secrets/mssql.keytab 31 | __mssql_conf_path: /var/opt/mssql/mssql.conf 32 | __mssql_conf_cli: /opt/mssql/bin/mssql-conf 33 | __mssql_ha_replica_types_all: 34 | - primary 35 | - synchronous 36 | - asynchronous 37 | - witness 38 | - absent 39 | __mssql_ha_replica_types_secondary: 40 | - synchronous 41 | - asynchronous 42 | - witness 43 | __mssql_ha_replica_types_external: 44 | - primary 45 | - synchronous 46 | - asynchronous 47 | __mssql_ha_endpoint_role: >- 48 | {%- if mssql_ha_replica_type in __mssql_ha_replica_types_external -%} 49 | ALL 50 | {%- elif mssql_ha_replica_type == 'witness' -%} 51 | WITNESS 52 | {%- else -%} 53 | null 54 | {%- endif -%} 55 | __mssql_ha_db_failover: >- 56 | {%- if mssql_ha_ag_cluster_type | lower == 'external' -%} 57 | ON 58 | {%- elif mssql_ha_ag_cluster_type | lower == 'none' -%} 59 | OFF 60 | {%- else -%} 61 | null 62 | {%- endif -%} 63 | __mssql_ha_cert_dest: /var/opt/mssql/data/{{ mssql_ha_cert_name }}.cer 64 | __mssql_ha_private_key_dest: /var/opt/mssql/data/{{ mssql_ha_cert_name }}.pvk 65 | 66 | __mssql_ad_kinit_user: >- 67 | {{ mssql_ad_kerberos_user }}@{{ ad_integration_realm | upper }} 68 | 69 | # BEGIN - DO NOT EDIT THIS BLOCK - rh distros variables 70 | # Ansible distribution identifiers that the role treats like RHEL 71 | __mssql_rh_distros: 72 | - AlmaLinux 73 | - CentOS 74 | - RedHat 75 | - Rocky 76 | 77 | # Same as above but includes Fedora 78 | __mssql_rh_distros_fedora: "{{ __mssql_rh_distros + ['Fedora'] }}" 79 | 80 | # Use this in conditionals to check if distro is Red Hat or clone 81 | __mssql_is_rh_distro: "{{ ansible_distribution in __mssql_rh_distros }}" 82 | 83 | # Use this in conditionals to check if distro is Red Hat or clone, or Fedora 84 | __mssql_is_rh_distro_fedora: "{{ ansible_distribution in __mssql_rh_distros_fedora }}" 85 | # END - DO NOT EDIT THIS BLOCK - rh distros variables 86 | --------------------------------------------------------------------------------