├── .buildkite ├── pipeline.yml └── pull-request.json ├── .ci ├── docker │ └── azure-vm-tools │ │ ├── requirements.txt │ │ ├── Dockerfile │ │ └── entrypoint.sh ├── jobs │ ├── defaults.yml │ ├── azure-vm-extension-mbp.yml │ └── azure-vm-extension-daily-mbp.yml ├── packer_cache.sh ├── virtual-machines.yml ├── bump-stack-release-version.sh ├── Makefile ├── Jenkinsfile └── its.groovy ├── src ├── packages │ ├── Elastic.ElasticAgent.linux-arm │ │ ├── Icons │ │ │ ├── wide.png │ │ │ ├── large.png │ │ │ ├── medium.png │ │ │ └── small.png │ │ ├── Screenshots │ │ │ └── Screenshot1.png │ │ ├── UiDefinition.json │ │ ├── Strings │ │ │ └── Resources.resjson │ │ ├── Artifacts │ │ │ ├── MainTemplate.json │ │ │ └── CreateUiDefinition.json │ │ └── Manifest.json │ └── Elastic.ElasticAgent-windows-arm │ │ ├── Icons │ │ ├── wide.png │ │ ├── large.png │ │ ├── medium.png │ │ └── small.png │ │ ├── Screenshots │ │ └── Screenshot1.png │ │ ├── UiDefinition.json │ │ ├── Strings │ │ └── Resources.resjson │ │ ├── Artifacts │ │ ├── MainTemplate.json │ │ └── CreateUiDefinition.json │ │ └── Manifest.json ├── ubuntu.dockerfile ├── handler │ ├── windows │ │ ├── scripts │ │ │ ├── enable.cmd │ │ │ ├── update.cmd │ │ │ ├── install.cmd │ │ │ ├── uninstall.cmd │ │ │ ├── disable.cmd │ │ │ ├── install.ps1 │ │ │ ├── update.ps1 │ │ │ ├── disable.ps1 │ │ │ ├── newconfig.ps1 │ │ │ ├── uninstall.ps1 │ │ │ ├── log.ps1 │ │ │ ├── enable.ps1 │ │ │ └── helper.ps1 │ │ └── HandlerManifest.json │ └── linux │ │ ├── update.sh │ │ ├── HandlerManifest.json │ │ ├── disable.sh │ │ ├── newconfig.sh │ │ ├── uninstall.sh │ │ ├── install.sh │ │ ├── enable.sh │ │ └── helper.sh ├── ubuntu-24-04.dockerfile ├── test_centos.ps1 ├── test_ubuntu.ps1 ├── centos.dockerfile ├── test_ubuntu.ps2 ├── windows.dockerfile └── test_windows.ps1 ├── test ├── README.md ├── deployment │ ├── deployment.sh │ ├── README.md │ └── elastic_cloud.yml ├── ats │ ├── README.md │ └── validate.py └── terraform │ ├── main.tf │ ├── windows.tf │ ├── linux.tf │ ├── README.md │ └── variables.tf ├── .gitignore ├── CHANGELOG-release.asciidoc ├── catalog-info.yaml ├── README.md └── LICENSE.txt /.buildkite/pipeline.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - label: "Example test" 3 | command: echo "Hello!" -------------------------------------------------------------------------------- /.ci/docker/azure-vm-tools/requirements.txt: -------------------------------------------------------------------------------- 1 | elasticsearch>=7.0.0,<8.0.0 2 | unittest-xml-reporting==3.0.4 3 | -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent.linux-arm/Icons/wide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elastic/azure-vm-extension/main/src/packages/Elastic.ElasticAgent.linux-arm/Icons/wide.png -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent-windows-arm/Icons/wide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elastic/azure-vm-extension/main/src/packages/Elastic.ElasticAgent-windows-arm/Icons/wide.png -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent.linux-arm/Icons/large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elastic/azure-vm-extension/main/src/packages/Elastic.ElasticAgent.linux-arm/Icons/large.png -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent.linux-arm/Icons/medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elastic/azure-vm-extension/main/src/packages/Elastic.ElasticAgent.linux-arm/Icons/medium.png -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent.linux-arm/Icons/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elastic/azure-vm-extension/main/src/packages/Elastic.ElasticAgent.linux-arm/Icons/small.png -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent-windows-arm/Icons/large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elastic/azure-vm-extension/main/src/packages/Elastic.ElasticAgent-windows-arm/Icons/large.png -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent-windows-arm/Icons/medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elastic/azure-vm-extension/main/src/packages/Elastic.ElasticAgent-windows-arm/Icons/medium.png -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent-windows-arm/Icons/small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elastic/azure-vm-extension/main/src/packages/Elastic.ElasticAgent-windows-arm/Icons/small.png -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent-windows-arm/Screenshots/Screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elastic/azure-vm-extension/main/src/packages/Elastic.ElasticAgent-windows-arm/Screenshots/Screenshot1.png -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent.linux-arm/Screenshots/Screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elastic/azure-vm-extension/main/src/packages/Elastic.ElasticAgent.linux-arm/Screenshots/Screenshot1.png -------------------------------------------------------------------------------- /test/README.md: -------------------------------------------------------------------------------- 1 | # Test 2 | 3 | ## terraform folder 4 | 5 | How to test this particular extension using terraform. 6 | 7 | ## ats folder 8 | 9 | Run the Acceptance Tests for this particular extension. 10 | -------------------------------------------------------------------------------- /src/ubuntu.dockerfile: -------------------------------------------------------------------------------- 1 | FROM jrei/systemd-ubuntu:20.04 AS vm_extension_ubuntu 2 | 3 | RUN apt-get update && apt-get -y install sudo wget 4 | WORKDIR /sln 5 | 6 | COPY ./handler ./handler 7 | COPY settings ./tests 8 | 9 | -------------------------------------------------------------------------------- /src/handler/windows/scripts/enable.cmd: -------------------------------------------------------------------------------- 1 | set ES_EXT_DIR=%~dp0 2 | 3 | echo %ES_EXT_DIR% 4 | 5 | powershell -nologo -noprofile -executionpolicy unrestricted Import-Module %ES_EXT_DIR%\enable.ps1;Run-Powershell2-With-Dot-Net4 6 | -------------------------------------------------------------------------------- /src/handler/windows/scripts/update.cmd: -------------------------------------------------------------------------------- 1 | set ES_EXT_DIR=%~dp0 2 | 3 | echo %ES_EXT_DIR% 4 | 5 | powershell -nologo -noprofile -executionpolicy unrestricted Import-Module %ES_EXT_DIR%\update.ps1;Run-Powershell2-With-Dot-Net4 6 | -------------------------------------------------------------------------------- /src/ubuntu-24-04.dockerfile: -------------------------------------------------------------------------------- 1 | FROM jrei/systemd-ubuntu:24.04 AS vm_extension_ubuntu 2 | 3 | RUN apt-get update && apt-get -y install sudo wget 4 | WORKDIR /sln 5 | 6 | COPY ./handler ./handler 7 | COPY settings ./tests 8 | 9 | -------------------------------------------------------------------------------- /src/handler/windows/scripts/install.cmd: -------------------------------------------------------------------------------- 1 | set ES_EXT_DIR=%~dp0 2 | 3 | echo %ES_EXT_DIR% 4 | 5 | powershell -nologo -noprofile -executionpolicy unrestricted Import-Module %ES_EXT_DIR%\install.ps1;Run-Powershell2-With-Dot-Net4 6 | -------------------------------------------------------------------------------- /src/test_centos.ps1: -------------------------------------------------------------------------------- 1 | docker build -f centos.dockerfile --tag vm_extension_centos . 2 | docker run --name vm_extension_centos_run --rm -i -t vm_extension_centos bash -c "./handler/scripts/linux/install.sh;bash" --privileged=true 3 | -------------------------------------------------------------------------------- /src/test_ubuntu.ps1: -------------------------------------------------------------------------------- 1 | docker build -f ubuntu.dockerfile --tag vm_extension_ubuntu . 2 | docker run --name vm_extension_ubuntu_run --rm -i -t vm_extension_ubuntu bash -c "./handler/scripts/linux/install.sh;bash" --privileged=true 3 | -------------------------------------------------------------------------------- /src/handler/windows/scripts/uninstall.cmd: -------------------------------------------------------------------------------- 1 | set ES_EXT_DIR=%~dp0 2 | 3 | echo %ES_EXT_DIR% 4 | 5 | powershell -nologo -noprofile -executionpolicy unrestricted Import-Module %ES_EXT_DIR%\uninstall.ps1;Run-Powershell2-With-Dot-Net4 6 | -------------------------------------------------------------------------------- /src/centos.dockerfile: -------------------------------------------------------------------------------- 1 | FROM centos/systemd:latest AS vm_extension_centos 2 | RUN yum -y install initscripts && yum clean all 3 | RUN yum install sudo wget -y 4 | WORKDIR /sln 5 | 6 | COPY ./handler ./handler 7 | COPY settings ./tests 8 | 9 | -------------------------------------------------------------------------------- /src/handler/windows/scripts/disable.cmd: -------------------------------------------------------------------------------- 1 | set ES_EXT_DIR=%~dp0 2 | 3 | echo %ES_EXT_DIR% 4 | 5 | powershell -nologo -noprofile -executionpolicy unrestricted Import-Module %ES_EXT_DIR%\disable.ps1;Run-Powershell2-With-Dot-Net4 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/test_ubuntu.ps2: -------------------------------------------------------------------------------- 1 | docker build -f ubuntu-24-04.dockerfile --tag vm_extension_ubuntu . 2 | docker run --name vm_extension_ubuntu_run --rm -i -t vm_extension_ubuntu bash -c "./handler/scripts/linux/install.sh;bash" --privileged=true 3 | -------------------------------------------------------------------------------- /src/windows.dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/windows:10.0.19041.746-amd64 AS vm_extension_windows 2 | 3 | WORKDIR /sln 4 | USER ContainerAdministrator 5 | RUN NET USER my_admin /add 6 | RUN NET LOCALGROUP Administrators /add my_admin 7 | USER my_admin 8 | COPY ./handler ./handler 9 | -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent.linux-arm/UiDefinition.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://gallery.azure.com/schemas/2015-02-12/uiDefinition.json#", 3 | "createDefinition": { 4 | "createBlade": { 5 | "name": "AddVmExtension", 6 | "extension": "Microsoft_Azure_Compute" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent-windows-arm/UiDefinition.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://gallery.azure.com/schemas/2015-02-12/uiDefinition.json#", 3 | "createDefinition": { 4 | "createBlade": { 5 | "name": "AddVmExtension", 6 | "extension": "Microsoft_Azure_Compute" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/test_windows.ps1: -------------------------------------------------------------------------------- 1 | docker build -f windows.dockerfile --tag vm_extension_windows . 2 | docker run --env STACK_VERSION=7.10.2 ` 3 | --name vm_extension_windows_run --rm -i -t vm_extension_windows ` 4 | powershell -noexit -nologo -noprofile -executionpolicy bypass 'C://sln/handler/scripts/windows/install.ps1' 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | src/settings 2 | src/handler/linux/HandlerEnvironment.json 3 | src/handler/windows/HandlerEnvironment.json 4 | .idea 5 | build/deploy 6 | build/*.json 7 | src/handler/linux/test.sh 8 | 9 | # Deployment automation 10 | .obs 11 | 12 | .terraform* 13 | terraform.tfstate* 14 | .azure 15 | 16 | *.bck 17 | test/ats/TEST-*.xml 18 | .local 19 | .cache 20 | -------------------------------------------------------------------------------- /src/handler/linux/update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | script_path=$(dirname $(readlink -f "$0")) 4 | source $script_path/helper.sh 5 | 6 | #update script is ran at update time during a vm extension update, will set flag that this is an update operation 7 | 8 | log "INFO" "[Update_ElasticAgent] set update environment variable" 9 | set_update_var 10 | -------------------------------------------------------------------------------- /.ci/jobs/defaults.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | ##### GLOBAL METADATA 4 | 5 | - meta: 6 | cluster: beats-ci 7 | 8 | ##### JOB DEFAULTS 9 | 10 | - job: 11 | view: BEATS-CI 12 | logrotate: 13 | daysToKeep: 30 14 | numToKeep: 100 15 | node: linux 16 | concurrent: true 17 | publishers: 18 | - email: 19 | recipients: infra-root+build@elastic.co 20 | -------------------------------------------------------------------------------- /.ci/packer_cache.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # shellcheck disable=SC1091 4 | source /usr/local/bin/bash_standard_lib.sh 5 | 6 | DOCKER_IMAGES="docker.elastic.co/observability-ci/azure-vm-tools" 7 | if [ -x "$(command -v docker)" ]; then 8 | for di in ${DOCKER_IMAGES} 9 | do 10 | (retry 2 docker pull "${di}") || echo "Error pulling ${di} Docker image, we continue" 11 | done 12 | fi 13 | -------------------------------------------------------------------------------- /src/handler/linux/HandlerManifest.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "version": 1.0, 3 | "handlerManifest": { 4 | "installCommand": "install.sh", 5 | "uninstallCommand": "uninstall.sh", 6 | "updateCommand": "update.sh", 7 | "enableCommand": "enable.sh", 8 | "disableCommand": "disable.sh", 9 | "rebootAfterInstall": false, 10 | "reportHeartbeat": false, 11 | "updateMode": "UpdateWithInstall" 12 | } 13 | }] 14 | -------------------------------------------------------------------------------- /src/handler/windows/scripts/install.ps1: -------------------------------------------------------------------------------- 1 | $ScriptDirectory = Split-Path $MyInvocation.MyCommand.Path 2 | . (Join-Path $ScriptDirectory log.ps1) 3 | $ScriptDirectory = Split-Path $MyInvocation.MyCommand.Path 4 | . (Join-Path $ScriptDirectory helper.ps1) 5 | 6 | # install script ran at the installation time, nothing to be configured at this point 7 | Write-Log "Install command has been executed. Elastic Agent will be installed on enable" "INFO" 8 | 9 | -------------------------------------------------------------------------------- /src/handler/windows/HandlerManifest.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "version": 1.0, 3 | "handlerManifest": { 4 | "installCommand": "scripts\\install.cmd", 5 | "uninstallCommand": "scripts\\uninstall.cmd", 6 | "updateCommand": "scripts\\update.cmd", 7 | "enableCommand": "scripts\\enable.cmd", 8 | "disableCommand": "scripts\\disable.cmd", 9 | "rebootAfterInstall": false, 10 | "reportHeartbeat": false, 11 | "updateMode": "UpdateWithInstall" 12 | } 13 | }] 14 | -------------------------------------------------------------------------------- /src/handler/windows/scripts/update.ps1: -------------------------------------------------------------------------------- 1 | $ScriptDirectory = Split-Path $MyInvocation.MyCommand.Path 2 | . (Join-Path $ScriptDirectory log.ps1) 3 | $ScriptDirectory = Split-Path $MyInvocation.MyCommand.Path 4 | . (Join-Path $ScriptDirectory helper.ps1) 5 | 6 | #update script is ran at update time during a vm extension update, will set flag that this is an update operation 7 | 8 | Write-Log "Update command has been triggered. Elastic Agent reinstall" "INFO" 9 | 10 | Write-Log "Update env variable is set" "INFO" 11 | Set-UpdateEnvVariables 12 | -------------------------------------------------------------------------------- /test/deployment/deployment.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 4 | 5 | if [ -d .obs ] ; then 6 | rm -rf .obs 7 | fi 8 | git clone git@github.com:elastic/observability-test-environments.git .obs 9 | 10 | cd .obs 11 | 12 | if [ $1 == "destroy" ] ; then 13 | CLUSTER_CONFIG_FILE=$SCRIPT_DIR/elastic_cloud.yml \ 14 | make -C ansible destroy-cluster 15 | else 16 | CLUSTER_CONFIG_FILE=$SCRIPT_DIR/elastic_cloud.yml \ 17 | make -C ansible create-cluster 18 | fi 19 | -------------------------------------------------------------------------------- /.buildkite/pull-request.json: -------------------------------------------------------------------------------- 1 | { 2 | "jobs": [ 3 | { 4 | "enabled": true, 5 | "pipelineSlug": "azure-vm-extension", 6 | "allow_org_users": true, 7 | "allowed_repo_permissions": ["admin", "write"], 8 | "set_commit_status": true, 9 | "build_on_commit": true, 10 | "build_on_comment": true, 11 | "trigger_comment_regex": "^(?:(?:buildkite\\W+)?(?:build|test)\\W+(?:this|it))|^/test$", 12 | "always_trigger_comment_regex": "^(?:(?:buildkite\\W+)?(?:build|test)\\W+(?:this|it))|^/test$", 13 | "skip_ci_labels": ["skip-ci"], 14 | "skip_target_branches": [ ], 15 | "skip_ci_on_only_changed": ["\\.md$"] 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /test/ats/README.md: -------------------------------------------------------------------------------- 1 | # Ats 2 | 3 | This is the folder that contains the Acceptance Tests to validate the Elastic Agent VM extension. 4 | 5 | ## Requirements 6 | 7 | 1. Create a Elastic Cloud cluster 8 | 2. Run the terraform plan. 9 | 3. Install the requirements.txt dependencies defined in .ci/docker/azure-vm-tools/requirements.txt 10 | 11 | ## How does it work? 12 | 13 | 1. Run the below script in your terminal to test the given VM in the given cloud environment. 14 | 15 | ```bash 16 | $ ES_USERNAME= \ 17 | ES_PASSWORD= \ 18 | ES_URL= \ 19 | VM_NAME= \ 20 | TF_VAR_isWindows= \ 21 | python -m xmlrunner validate.py 22 | ``` 23 | -------------------------------------------------------------------------------- /test/deployment/README.md: -------------------------------------------------------------------------------- 1 | # Deployment 2 | 3 | This will help to spin up a elastic cloud deployment 4 | 5 | ```bash 6 | $ VAULT_TOKEN=$(cat "${HOME}/.vault-token") \ 7 | ELASTIC_STACK_VERSION=7.12.0 \ 8 | CLUSTER_NAME=ec-test-azure \ 9 | ./deployment.sh "create" 10 | ``` 11 | 12 | ```bash 13 | $ VAULT_TOKEN=$(cat "${HOME}/.vault-token") \ 14 | ELASTIC_STACK_VERSION=7.12.0 \ 15 | CLUSTER_NAME=ec-test-azure \ 16 | ./deployment.sh "destroy" 17 | ``` 18 | 19 | 20 | ## Credentials 21 | 22 | Those credentials details can be found in vault: 23 | 24 | ```bash 25 | $ id=ec-test-azure 26 | $ vault read secret/observability-team/ci/test-clusters/$id/ec-deployment 27 | ``` 28 | -------------------------------------------------------------------------------- /.ci/docker/azure-vm-tools/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.9.5-slim 2 | 3 | RUN pip3 install azure-cli 4 | 5 | RUN apt-get update && \ 6 | apt-get install -y gnupg software-properties-common curl && \ 7 | curl -fsSL https://apt.releases.hashicorp.com/gpg | apt-key add - && \ 8 | apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" 9 | 10 | RUN apt-get update && \ 11 | apt-get install -y terraform jq 12 | 13 | WORKDIR /app 14 | 15 | ## this image is build from the .ci folder location 16 | COPY docker/azure-vm-tools/requirements.txt /app/ 17 | RUN pip install --no-cache-dir -r requirements.txt 18 | 19 | ENTRYPOINT [ "/app/.ci/docker/azure-vm-tools/entrypoint.sh" ] 20 | -------------------------------------------------------------------------------- /.ci/virtual-machines.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ## This is the file that contains what azure images are supported 3 | ## 4 | ## The data structure: 5 | ## name: the image id name used in the pipelines 6 | ## publisher: the VM publisher 7 | ## publisher: the VM offer 8 | ## publisher: the VM sku 9 | ## 10 | 11 | # az vm image list --publisher RedHat --all --offer RHEL 12 | - name: redhat-8 13 | publisher: RedHat 14 | offer: RHEL 15 | sku: "8.2" 16 | 17 | # az vm image list --publisher Canonical --all --offer UbuntuServer 18 | - name: ubuntu-18 19 | publisher: Canonical 20 | offer: UbuntuServer 21 | sku: 18.04-LTS 22 | 23 | # az vm image list --publisher MicrosoftWindowsServer --all --offer WindowsServer 24 | - name: windows-2016 25 | publisher: MicrosoftWindowsServer 26 | offer: WindowsServer 27 | sku: 2016-Datacenter 28 | 29 | - name: windows-2012 30 | publisher: MicrosoftWindowsServer 31 | offer: WindowsServer 32 | sku: 2012-Datacenter 33 | -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent.linux-arm/Strings/Resources.resjson: -------------------------------------------------------------------------------- 1 | { 2 | "displayName": "Elastic Agent for Linux", 3 | "publisherDisplayName": "Elastic N.V.", 4 | "summary": "Elastic Agent for Linux is a lightweight data shipper that collects system logs and metrics from your virtual machines and stores them in Elasticsearch.", 5 | "description": "To configure the agent you’ll need to set your Elastic Cloud ID along with your Elasticsearch Service user credentials. Don’t have an account? Sign up to Elastic Cloud if you are a new user. Elastic Agent lets you get started with monitoring your infrastructure health in seconds: collect logs, metrics and APM data in once place. Out of the box integrations for more than 50 applications, such as MSSQL, Zookeeper, Memchached, and more. Powerful Kibana UI apps for infrastructure and application observability. Flexible alerting and integration with 3pp ticketing systems, e.g. PagerDuty, Slack, Jira.", 6 | "link1": "https://www.elastic.co/guide/en/fleet/current/index.html" 7 | } -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent-windows-arm/Strings/Resources.resjson: -------------------------------------------------------------------------------- 1 | { 2 | "displayName": "Elastic Agent for Windows", 3 | "publisherDisplayName": "Elastic N.V.", 4 | "summary": "Elastic Agent for Windows is a lightweight data shipper that collects system logs and metrics from your virtual machines and stores them in Elasticsearch.", 5 | "description": "To configure the agent you’ll need to set your Elastic Cloud ID along with your Elasticsearch Service user credentials. Don’t have an account? Sign up to Elastic Cloud if you are a new user. Elastic Agent lets you get started with monitoring your infrastructure health in seconds: collect logs, metrics and APM data in once place. Out of the box integrations for more than 50 applications, such as MSSQL, Zookeeper, Memchached, and more. Powerful Kibana UI apps for infrastructure and application observability. Flexible alerting and integration with 3pp ticketing systems, e.g. PagerDuty, Slack, Jira.", 6 | "link1": "https://www.elastic.co/guide/en/fleet/current/index.html" 7 | } -------------------------------------------------------------------------------- /test/deployment/elastic_cloud.yml: -------------------------------------------------------------------------------- 1 | --- 2 | cluster_name: "{{ lookup('env','CLUSTER_NAME') }}" 3 | stack_version: "{{ lookup('env','ELASTIC_STACK_VERSION') }}" 4 | create_ilm: false 5 | create_users: false 6 | grab_cluster_info: false 7 | certificate_issuer: letsencrypt-staging 8 | 9 | elastic_cloud: 10 | provider: azure 11 | region: azure-eastus2 12 | endpoint: https://staging.found.no 13 | zones: 1 14 | template: azure-observability-v2 15 | 16 | k8s: 17 | enabled: false 18 | provider: none 19 | project: "elastic-observability" 20 | region: us-central1-c 21 | max_node_count: "3" 22 | machine_type: "n1-standard-4" 23 | default_namespace: "default" 24 | domain: "ip.es.io" 25 | 26 | elasticsearch: 27 | enabled: true 28 | version: "{{ stack_version }}" 29 | type: tf 30 | mem: 2 31 | 32 | kibana: 33 | enabled: true 34 | version: "{{ stack_version }}" 35 | type: tf 36 | mem: 2 37 | apm_enabled: false 38 | 39 | apm: 40 | enabled: true 41 | version: "{{ stack_version }}" 42 | type: tf 43 | mem: 0.5 44 | -------------------------------------------------------------------------------- /CHANGELOG-release.asciidoc: -------------------------------------------------------------------------------- 1 | [[release-notes-1.3.1.0]] 2 | === Elastic Agent VM extension version 1.3.1.0 3 | 4 | - Add suppor for Ubuntu 24.04 5 | 6 | [[release-notes-1.3.0.0]] 7 | === Elastic Agent VM extension version 1.3.0.0 8 | 9 | - Remove settings file after extension install 10 | 11 | [[release-notes-1.2.0.0]] 12 | === Elastic Agent VM extension version 1.2.0.0 13 | 14 | - Add dedicated Azure policy for agents installed through the VM extension 15 | 16 | [[release-notes-1.1.1.0]] 17 | === Elastic Agent VM extension version 1.1.1.0 18 | 19 | - Default agent on cloud policy id/name has changed in Fleet, this needs to be reflected in the code since the elastic agent needs to avoid this policy 20 | 21 | [[release-notes-1.1.0.0]] 22 | === Elastic Agent VM extension version 1.1.0.0 23 | 24 | - Releasing Linux VM extension 25 | - Adding support for 7.13 Fleet Server for both Windows and Linux VM extensions 26 | - Small fixes as selecting Default policy fixed 27 | 28 | [[release-notes-1.0.0.0]] 29 | === Elastic Agent VM extension version 1.0.0.0 30 | 31 | - Releasing only the Windows vm extension 32 | -------------------------------------------------------------------------------- /src/handler/linux/disable.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | script_path=$(dirname $(readlink -f "$0")) 4 | source $script_path/helper.sh 5 | 6 | # disable script is ran at disable time, during an uninstall or update 7 | 8 | # var for vm extension status 9 | name="Disable elastic agent" 10 | operation="stopping elastic agent" 11 | message="Disable elastic agent" 12 | sub_name="Elastic Agent" 13 | 14 | # Stop_ElasticAgent stops the Elastic Agent 15 | Stop_ElasticAgent() 16 | { 17 | if [[ $(systemctl) =~ -\.mount ]]; then 18 | log "INFO" "[Stop_ElasticAgent] stopping Elastic Agent" 19 | sudo systemctl stop elastic-agent 20 | log "INFO" "[Stop_ElasticAgent] Elastic Agent stopped" 21 | write_status "$name" "$operation" "success" "$message" "$sub_name" "success" "Elastic Agent service has stopped" 22 | else 23 | log "INFO" "[Stop_ElasticAgent] stopping Elastic Agent" 24 | sudo service elastic-agent stop 25 | log "INFO" "[Stop_ElasticAgent] Elastic Agent stopped" 26 | write_status "$name" "$operation" "success" "$message" "$sub_name" "success" "Elastic Agent service has stopped" 27 | fi 28 | } 29 | 30 | retry_backoff Stop_ElasticAgent 31 | -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent.linux-arm/Artifacts/MainTemplate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "vmName": { 6 | "type": "string" 7 | }, 8 | "location": { 9 | "type": "string" 10 | }, 11 | "cloudId": { 12 | "type": "string" 13 | }, 14 | "username": { 15 | "type": "string" 16 | }, 17 | "password": { 18 | "type": "string" 19 | } 20 | }, 21 | "resources": [ 22 | { 23 | "name": "[concat(parameters('vmName'),'/ElasticAgent')]", 24 | "type": "Microsoft.Compute/virtualMachines/extensions", 25 | "location": "[parameters('location')]", 26 | "apiVersion": "2015-06-15", 27 | "properties": { 28 | "publisher": "Elastic", 29 | "type": "ElasticAgent.linux", 30 | "typeHandlerVersion": "1.1", 31 | "autoUpgradeMinorVersion": true, 32 | "settings": { 33 | "cloudId": "[parameters('cloudId')]", 34 | "username": "[parameters('username')]", 35 | "password": "[parameters('password')]" 36 | } 37 | } 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /src/packages/Elastic.ElasticAgent-windows-arm/Artifacts/MainTemplate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "parameters": { 5 | "vmName": { 6 | "type": "string" 7 | }, 8 | "location": { 9 | "type": "string" 10 | }, 11 | "cloudId": { 12 | "type": "string" 13 | }, 14 | "username": { 15 | "type": "string" 16 | }, 17 | "password": { 18 | "type": "string" 19 | } 20 | }, 21 | "resources": [ 22 | { 23 | "name": "[concat(parameters('vmName'),'/ElasticAgent')]", 24 | "type": "Microsoft.Compute/virtualMachines/extensions", 25 | "location": "[parameters('location')]", 26 | "apiVersion": "2015-06-15", 27 | "properties": { 28 | "publisher": "Elastic", 29 | "type": "ElasticAgent.windows", 30 | "typeHandlerVersion": "1.1", 31 | "autoUpgradeMinorVersion": true, 32 | "settings": { 33 | "cloudId": "[parameters('cloudId')]", 34 | "username": "[parameters('username')]", 35 | "password": "[parameters('password')]" 36 | } 37 | } 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /.ci/bump-stack-release-version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Given the stack version this script will bump the release version. 4 | # 5 | # This script is executed by the automation we are putting in place 6 | # and it requires the git add/commit commands. 7 | # 8 | # Parameters: 9 | # $1 -> the minor version to be bumped. Mandatory. 10 | # $1 -> the major version to be bumped. Mandatory. 11 | # 12 | set -euo pipefail 13 | MSG="parameter missing." 14 | VERSION_RELEASE=${1:?$MSG} 15 | VERSION_DEV=${2:?$MSG} 16 | 17 | OS=$(uname -s| tr '[:upper:]' '[:lower:]') 18 | 19 | if [ "${OS}" == "darwin" ] ; then 20 | SED="sed -i .bck" 21 | else 22 | SED="sed -i" 23 | fi 24 | 25 | echo "Update stack with versions ${VERSION_RELEASE} and ${VERSION_DEV}" 26 | ${SED} -E -e "s#(ELASTIC_STACK_VERSION =) '[0-9]+\.[0-9]+\.[0-9]+'#\1 '${VERSION_RELEASE}'#g" .ci/Jenkinsfile 27 | ${SED} -E -e "s#(values '8.0.0-SNAPSHOT',) '[0-9]+\.[0-9]+\.[0-9]+', '[0-9]+\.[0-9]+\.[0-9]+'#\1 '${VERSION_RELEASE}', '${VERSION_DEV}'#g" .ci/its.groovy 28 | 29 | git add .ci/Jenkinsfile .ci/its.groovy 30 | git diff --staged --quiet || git commit -m "[Automation] Update elastic stack release version to ${VERSION_RELEASE} and ${VERSION_DEV}" 31 | git --no-pager log -1 32 | 33 | echo "You can now push and create a Pull Request" 34 | -------------------------------------------------------------------------------- /test/terraform/main.tf: -------------------------------------------------------------------------------- 1 | provider "azurerm" { 2 | features {} 3 | } 4 | 5 | resource "azurerm_resource_group" "main" { 6 | name = format("%s-%s", var.prefix, var.name) 7 | location = "Central US" 8 | } 9 | 10 | resource "azurerm_virtual_network" "main" { 11 | name = format("%s-%s", var.prefix, var.name) 12 | address_space = ["10.0.0.0/16"] 13 | location = azurerm_resource_group.main.location 14 | resource_group_name = azurerm_resource_group.main.name 15 | } 16 | 17 | resource "azurerm_subnet" "internal" { 18 | name = format("%s-%s", var.prefix, var.name) 19 | resource_group_name = azurerm_resource_group.main.name 20 | virtual_network_name = azurerm_virtual_network.main.name 21 | address_prefixes = ["10.0.2.0/24"] 22 | } 23 | 24 | resource "azurerm_network_interface" "main" { 25 | name = format("%s-%s", var.prefix, var.name) 26 | resource_group_name = azurerm_resource_group.main.name 27 | location = azurerm_resource_group.main.location 28 | 29 | ip_configuration { 30 | name = format("%s-%s", var.prefix, var.name) 31 | subnet_id = azurerm_subnet.internal.id 32 | private_ip_address_allocation = "Dynamic" 33 | } 34 | } 35 | 36 | output "resource_group_name" { 37 | value = azurerm_resource_group.main.name 38 | description = "The resource group name." 39 | sensitive = true 40 | } 41 | -------------------------------------------------------------------------------- /test/terraform/windows.tf: -------------------------------------------------------------------------------- 1 | resource "azurerm_windows_virtual_machine" "main" { 2 | count = var.isWindows ? 1 : 0 3 | name = var.vmName 4 | resource_group_name = azurerm_resource_group.main.name 5 | location = azurerm_resource_group.main.location 6 | size = "Standard_F2" 7 | admin_username = "adminuser" 8 | admin_password = "P@ssw0rd1234!" 9 | network_interface_ids = [ 10 | azurerm_network_interface.main.id, 11 | ] 12 | 13 | source_image_reference { 14 | publisher = var.publisher 15 | offer = var.offer 16 | sku = var.sku 17 | version = "latest" 18 | } 19 | 20 | os_disk { 21 | storage_account_type = "Standard_LRS" 22 | caching = "ReadWrite" 23 | } 24 | } 25 | 26 | resource "azurerm_virtual_machine_extension" "windows" { 27 | count = var.isWindows ? 1 : 0 28 | name = "ElasticAgent.windows" 29 | virtual_machine_id = azurerm_windows_virtual_machine.main[count.index].id 30 | publisher = "Elastic" 31 | type = "ElasticAgent.windows" 32 | type_handler_version = "1.1" 33 | 34 | protected_settings = <` in your terminal to create a service principal. 38 | 1. Run `azure role assignment create --assignee "****" --role "Contributor" --scope "/subscriptions/****"` 39 | 1. Create secret in vault `vault write secret/observability-team/ci/service-account/azure-vm-extension username="****" password="***" tenant="****" ticket=https://github.com/elastic/observability-robots/issues/471 subscription="****"` 40 | -------------------------------------------------------------------------------- /test/terraform/variables.tf: -------------------------------------------------------------------------------- 1 | variable "username" { 2 | description = "The username for the elastic cloud cluster" 3 | type = string 4 | sensitive = true 5 | } 6 | 7 | variable "password" { 8 | description = "The password for the elastic cloud cluster user" 9 | type = string 10 | sensitive = true 11 | } 12 | 13 | variable "cloudId" { 14 | description = "The elastic cloud ID (deployment ID)" 15 | type = string 16 | sensitive = true 17 | } 18 | 19 | variable "prefix" { 20 | description = "The prefix for the name of the terraform resources" 21 | type = string 22 | sensitive = true 23 | default = "def" 24 | validation { 25 | condition = length(var.prefix) < 11 26 | error_message = "Maximum length of prefix is 10 characters." 27 | } 28 | } 29 | 30 | variable "name" { 31 | description = "The name of the terraform resources" 32 | type = string 33 | sensitive = true 34 | default = "az-vm-ext" 35 | validation { 36 | condition = length(var.name) < 16 37 | error_message = "Maximum length of name is 15 characters." 38 | } 39 | } 40 | 41 | variable "vmName" { 42 | description = "The virtual machine name" 43 | type = string 44 | sensitive = true 45 | default = "az-vm-ext" 46 | validation { 47 | condition = length(var.vmName) < 16 48 | error_message = "Maximum length of vmName is 15 characters." 49 | } 50 | } 51 | 52 | variable "isWindows" { 53 | description = "If true, resources will be a Windows" 54 | type = bool 55 | default = true 56 | } 57 | 58 | variable "publisher" { 59 | description = "The publisher for source_image_reference" 60 | type = string 61 | default = "MicrosoftWindowsServer" 62 | } 63 | 64 | variable "offer" { 65 | description = "The offer for source_image_reference" 66 | type = string 67 | default = "WindowsServer" 68 | } 69 | 70 | variable "sku" { 71 | description = "The sku for source_image_reference" 72 | type = string 73 | default = "2016-Datacenter" 74 | } 75 | -------------------------------------------------------------------------------- /src/handler/linux/newconfig.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | script_path=$(dirname $(readlink -f "$0")) 4 | source $script_path/helper.sh 5 | 6 | # neconfig script used during enable time, will help will uninstalling the elastic agent previously configured, it will try to retrieve the previous configuration and uninstall/remove folders for the elastic agent 7 | 8 | # var for uninstall status 9 | name="Uninstall elastic agent" 10 | first_operation="unenrolling elastic agent" 11 | second_operation="uninstalling elastic agent and removing any elastic agent related folders" 12 | message="Uninstall elastic agent" 13 | sub_name="Elastic Agent" 14 | 15 | checkOS 16 | 17 | # Uninstall_Old_ElasticAgent_DEB_RPM uninstalls the elastic agent and removes directories for Debian and RPM os's 18 | Uninstall_Old_ElasticAgent_DEB_RPM() { 19 | if [ "$DISTRO_OS" = "RPM" ]; then 20 | sudo rpm -e elastic-agent 21 | fi 22 | log "INFO" "[Uninstall_Old_ElasticAgent_DEB_RPM] removing Elastic Agent directories" 23 | if [[ $(systemctl) =~ -\.mount ]]; then 24 | sudo systemctl stop elastic-agent 25 | sudo systemctl disable elastic-agent 26 | fi 27 | sudo rm -rf /usr/share/elastic-agent 28 | sudo rm -rf /etc/elastic-agent 29 | sudo rm -rf /var/lib/elastic-agent 30 | sudo rm -rf /usr/bin/elastic-agent 31 | if [[ $(systemctl) =~ -\.mount ]]; then 32 | sudo systemctl daemon-reload 33 | sudo systemctl reset-failed 34 | fi 35 | if [ "$DISTRO_OS" = "DEB" ]; then 36 | sudo dpkg -r elastic-agent 37 | sudo dpkg -P elastic-agent 38 | fi 39 | log "INFO" "[Uninstall_Old_ElasticAgent_DEB_RPM] Elastic Agent removed" 40 | } 41 | 42 | # Uninstall_Old_ElasticAgent checks distro and removes previous installation of the elastic agent 43 | Uninstall_Old_ElasticAgent() 44 | { 45 | log "INFO" "[Uninstall_Old_ElasticAgent] Uninstalling Elastic Agent" 46 | if [ "$DISTRO_OS" = "DEB" ] || [ "$DISTRO_OS" = "RPM" ]; then 47 | retry_backoff Uninstall_Old_ElasticAgent_DEB_RPM 48 | else 49 | sudo elastic-agent uninstall 50 | log "INFO" "[Uninstall_Old_ElasticAgent] Elastic Agent removed" 51 | fi 52 | log "INFO" "Elastic Agent is uninstalled" 53 | write_status "$name" "$second_operation" "success" "$message" "$sub_name" "error" "Elastic Agent service has been uninstalled" 54 | } 55 | -------------------------------------------------------------------------------- /src/handler/linux/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | script_path=$(dirname $(readlink -f "$0")) 4 | source $script_path/helper.sh 5 | 6 | # uninstall script is ran at uninstall time, either triggered by user or during vm extension update 7 | 8 | # var for reporting uninstall status 9 | name="Uninstall elastic agent" 10 | first_operation="unenrolling elastic agent" 11 | second_operation="uninstalling elastic agent and removing any elastic agent related folders" 12 | message="Uninstall elastic agent" 13 | sub_name="Elastic Agent" 14 | 15 | checkOS 16 | 17 | # Uninstall_ElasticAgent_DEB_RPM uninstalls the elastic agent and removes directories for Debian and RPM os's 18 | Uninstall_ElasticAgent_DEB_RPM() { 19 | if [ "$DISTRO_OS" = "RPM" ]; then 20 | sudo rpm -e elastic-agent 21 | fi 22 | log "INFO" "[Uninstall_ElasticAgent_DEB_RPM] removing Elastic Agent directories" 23 | if [[ $(systemctl) =~ -\.mount ]]; then 24 | sudo systemctl stop elastic-agent 25 | sudo systemctl disable elastic-agent 26 | fi 27 | sudo rm -rf /usr/share/elastic-agent 28 | sudo rm -rf /etc/elastic-agent 29 | sudo rm -rf /var/lib/elastic-agent 30 | sudo rm -rf /usr/bin/elastic-agent 31 | if [[ $(systemctl) =~ -\.mount ]]; then 32 | sudo systemctl daemon-reload 33 | sudo systemctl reset-failed 34 | fi 35 | if [ "$DISTRO_OS" = "DEB" ]; then 36 | sudo dpkg -r elastic-agent 37 | sudo dpkg -P elastic-agent 38 | fi 39 | log "INFO" "[Uninstall_ElasticAgent_DEB_RPM] Elastic Agent removed" 40 | } 41 | 42 | # Uninstall_ElasticAgent checks distro and removes installation of the elastic agent 43 | Uninstall_ElasticAgent() 44 | { 45 | # Agent unenrollment is temporary removed from the uninstall script. It will be 46 | # added back in a future release. 47 | # 48 | # To learn more, see https://github.com/elastic/azure-vm-extension/pull/88 49 | # 50 | log "INFO" "[Uninstall_ElasticAgent] Uninstalling Elastic Agent" 51 | if [ "$DISTRO_OS" = "DEB" ] || [ "$DISTRO_OS" = "RPM" ]; then 52 | retry_backoff Uninstall_ElasticAgent_DEB_RPM 53 | else 54 | sudo elastic-agent uninstall 55 | log "INFO" "[Uninstall_ElasticAgent] Elastic Agent removed" 56 | fi 57 | log "INFO" "Elastic Agent is uninstalled" 58 | write_status "$name" "$second_operation" "success" "$message" "$sub_name" "error" "Elastic Agent service has been uninstalled" 59 | } 60 | 61 | Uninstall_ElasticAgent 62 | -------------------------------------------------------------------------------- /src/handler/windows/scripts/newconfig.ps1: -------------------------------------------------------------------------------- 1 | $ScriptDirectory = Split-Path $MyInvocation.MyCommand.Path 2 | . (Join-Path $ScriptDirectory log.ps1) 3 | $ScriptDirectory = Split-Path $MyInvocation.MyCommand.Path 4 | . (Join-Path $ScriptDirectory helper.ps1) 5 | 6 | # neconfig script used during enable time, will help will uninstalling the elastic agent previously configured, it will try to retrieve the previous configuration and uninstall/remove folders for the elastic agent 7 | 8 | # for status 9 | $name = "Uninstall elastic agent" 10 | $firstOperation = "unenrolling elastic agent" 11 | $secondOperation = "uninstalling elastic agent and removing any elastic agent related folders" 12 | $operation = "uninstalling elastic agent and removing any elastic agent related folders" 13 | $message = "Uninstall elastic agent" 14 | $subName = "Elastic Agent" 15 | 16 | function Uninstall-Old-ElasticAgent { 17 | $INSTALL_LOCATION="C:\Program Files" 18 | $retries = 3 19 | $retryCount = 0 20 | $completed = $false 21 | while (-not $completed) { 22 | Try 23 | { 24 | Write-Log "Uninstalling Elastic Agent" "INFO" 25 | & "$INSTALL_LOCATION\Elastic\Agent\elastic-agent.exe" uninstall --force 26 | Write-Log "Elastic Agent has been uninstalled" "INFO" 27 | Write-Log "removing directories" "INFO" 28 | Remove-Item "$INSTALL_LOCATION\Elastic\Agent" -Recurse -Force 29 | Remove-Item "$INSTALL_LOCATION\Elastic-Agent" -Recurse -Force 30 | Write-Log "elastic agent directories removed" "INFO" 31 | Write-Status "$name" "$operation" "success" "$message" "$subName" "success" "Elastic Agent service has been uninstalled" 32 | $completed = $true 33 | } 34 | Catch { 35 | if ($retryCount -ge $retries) { 36 | Write-Log "Elastic Agent installation failed after 3 retries" "ERROR" 37 | Write-Log $_ "ERROR" 38 | Write-Log $_.ScriptStackTrace "ERROR" 39 | Write-Status "$name" "$operation" "error" "$message" "$subName" "error" "Elastic Agent service has been uninstalled" 40 | Clean-And-Exit 1 41 | } else { 42 | Write-Log "Elastic Agent installation failed. retrying in 20s" "ERROR" 43 | Write-Log $_ "ERROR" 44 | Write-Log $_.ScriptStackTrace "ERROR" 45 | sleep 20 46 | $retryCount++ 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elastic Agent VM extensions 2 | 3 | ElasticAgent.windows for Windows systems 4 | ElasticAgent.linux for Linux systems 5 | 6 | The ElasticAgent VM extensions are small applications that provide post-deployment configuration and automation on Azure VMs. 7 | Once installed, the ElasticAgent VM extension will download the Elastic Agent artifacts, install the Elastic Agent on the virtual machine, enroll it to Fleet and then start the agent service. 8 | 9 | 10 | ## Platforms supported 11 | 12 | | Platform | Version | 13 | |----------|--------------| 14 | | Windows | 2008r2 + | 15 | | Centos | 6.10+ | 16 | | Debian | 9,10 | 17 | | Oracle | 6.8+ | 18 | | RHEL | 7+ | 19 | | Ubuntu | 16+ | 20 | 21 | ## Elastic Cloud dependency 22 | 23 | To automate the installation and configuration of the Elastic Agent, the Azure VM extension code makes several API calls which requires specific Elastic stack version. 24 | 25 | | VM extension version | Elastic Cloud dependency | 26 | |----------|--------------| 27 | | 1.3.1.0 | 7.13.0 or later | 28 | | 1.3.0.0 | 7.13.0 or later | 29 | | 1.2.0.0 | 7.13.0 or later | 30 | | 1.1.1.0 | 7.13.0 or later | 31 | | 1.1.0.0 | 7.13.0 or later | 32 | | 1.0.0.0 | 7.13.0 or later | 33 | 34 | ## Configuration 35 | 36 | For a successful installation the following configuration settings are required: 37 | 38 | Public settings: 39 | - username - a valid username that can have access to the elastic cloud cluster 40 | - cloudId - the elastic cloud ID (deployment ID) 41 | 42 | Protected settings: 43 | - password - a valid password that can be used in combination with the username public setting to access the elastic cloud cluster 44 | 45 | 46 | ## Managing the Elastic Agent VM extensions 47 | 48 | The Elastic Agent VM extensions can be managed using the Azure CLI, PowerShell, Resource Manager templates, and in the future the Azure portal. 49 | 50 | For Windows Azure VM's users will need to install the ElasticAgent.windows VM extension. 51 | 52 | Example installation from CLI: 53 | ``` 54 | az vm extension set -n ElasticAgent.windows --publisher Elastic --version {version number} --vm-name "{resource name}" --resource-group "{resource group name}" --protected-settings '{\"password\":\"{elastic password}\"}' --settings '{\"username\":\"{elastic username}\",\"cloudId\":\"{elastic cloud ID}\"}' 55 | ``` 56 | 57 | For Linux based VM's users will need to install the ElasticAgent.linux VM extension. 58 | 59 | Example installation from CLI: 60 | ``` 61 | az vm extension set -n ElasticAgent.linux --publisher Elastic --version {version number} --vm-name "{resource name}" --resource-group "{resource group name}" --protected-settings '{\"password\":\"{elastic password}\"}' --settings '{\"username\":\"{elastic username}\",\"cloudId\":\"{elastic cloud ID}\"}' 62 | ``` 63 | -------------------------------------------------------------------------------- /src/handler/windows/scripts/uninstall.ps1: -------------------------------------------------------------------------------- 1 | $ScriptDirectory = Split-Path $MyInvocation.MyCommand.Path 2 | . (Join-Path $ScriptDirectory log.ps1) 3 | $ScriptDirectory = Split-Path $MyInvocation.MyCommand.Path 4 | . (Join-Path $ScriptDirectory helper.ps1) 5 | 6 | # uninstall script is ran at uninstall time, either triggered by user or during vm extension update 7 | 8 | # var for vm extension status 9 | $name = "Uninstall elastic agent" 10 | $firstOperation = "unenrolling elastic agent" 11 | $secondOperation = "uninstalling elastic agent and removing any elastic agent related folders" 12 | $operation = "uninstalling elastic agent and removing any elastic agent related folders" 13 | 14 | $message = "Uninstall elastic agent" 15 | $subName = "Elastic Agent" 16 | 17 | $serviceName = 'elastic agent' 18 | 19 | # Uninstall-ElasticAgent function retrieves the agent id, unenrolls the elastic agent, uninstalls it and removes any additional files 20 | function Uninstall-ElasticAgent { 21 | $INSTALL_LOCATION="C:\Program Files" 22 | $retries = 3 23 | $retryCount = 0 24 | $completed = $false 25 | while (-not $completed) { 26 | Try { 27 | # Agent unenrollment is temporary removed from the uninstall script. It will be 28 | # added back in a future release. 29 | # 30 | # To learn more, see https://github.com/elastic/azure-vm-extension/pull/88 31 | # 32 | Write-Log "Uninstalling Elastic Agent" "INFO" 33 | & "$INSTALL_LOCATION\Elastic\Agent\elastic-agent.exe" uninstall --force 34 | Write-Log "Elastic Agent has been uninstalled" "INFO" 35 | Write-Log "removing directories" "INFO" 36 | Remove-Item "$INSTALL_LOCATION\Elastic\Agent" -Recurse -Force 37 | Remove-Item "$INSTALL_LOCATION\Elastic-Agent" -Recurse -Force 38 | Write-Log "elastic agent directories removed" "INFO" 39 | Write-Status "$name" "$operation" "success" "$message" "$subName" "success" "Elastic Agent service has been uninstalled" 40 | $completed = $true 41 | } 42 | Catch { 43 | if ($retryCount -ge $retries) { 44 | Write-Log "Elastic Agent installation failed after 3 retries" "ERROR" 45 | Write-Log $_ "ERROR" 46 | Write-Log $_.ScriptStackTrace "ERROR" 47 | Write-Status "$name" "$operation" "error" "$message" "$subName" "error" "Elastic Agent service has been uninstalled" 48 | Clean-And-Exit 1 49 | } else { 50 | Write-Log "Elastic Agent installation failed. retrying in 20s" "ERROR" 51 | Write-Log $_ "ERROR" 52 | Write-Log $_.ScriptStackTrace "ERROR" 53 | sleep 20 54 | $retryCount++ 55 | } 56 | } 57 | } 58 | } 59 | 60 | 61 | If (Get-Service $serviceName -ErrorAction SilentlyContinue) { 62 | Uninstall-ElasticAgent 63 | } Else { 64 | Write-Log "Elastic Agent has been previously uninstalled. Cannot be found as a service." "INFO" 65 | Write-Status "$name" "$operation" "success" "$message" "$subName" "success" "Elastic Agent service has been uninstalled" 66 | } 67 | 68 | -------------------------------------------------------------------------------- /.ci/docker/azure-vm-tools/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -eo pipefail 3 | 4 | ## Run ITs in python 5 | if [ "${TYPE}" == "test" ] ; then 6 | cd test/ats 7 | python -m xmlrunner validate.py || exit 1 8 | exit 0 9 | fi 10 | 11 | echo "What azure version?" 12 | az version 13 | 14 | az login \ 15 | --service-principal \ 16 | --username "${AZ_USERNAME}" \ 17 | --password "${AZ_PASSWORD}" \ 18 | --tenant "${AZ_TENANT}" > /dev/null 19 | 20 | echo "Prepare the terraform env variables" 21 | # See https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/service_principal_client_secret 22 | export ARM_CLIENT_ID="${AZ_USERNAME}" 23 | export ARM_CLIENT_SECRET="${AZ_PASSWORD}" 24 | export ARM_SUBSCRIPTION_ID="${AZ_SUBSCRIPTION}" 25 | export ARM_TENANT_ID="${AZ_TENANT}" 26 | 27 | echo "Go to the terraform folder" 28 | cd test/terraform 29 | 30 | if [ "${TYPE}" == "run" ] ; then 31 | echo "Configure the terraform environment" 32 | terraform init 33 | 34 | echo "Validate the terraform plan" 35 | terraform plan 36 | 37 | echo "Prepare the VM and enable the VM extension" 38 | TF_VAR_username="${TF_VAR_username}" \ 39 | TF_VAR_password="${TF_VAR_password}" \ 40 | TF_VAR_cloudId="${TF_VAR_cloudId}" \ 41 | TF_VAR_prefix="${TF_VAR_prefix}" \ 42 | TF_VAR_vmName="${TF_VAR_vmName}" \ 43 | TF_VAR_isWindows="${TF_VAR_isWindows}" \ 44 | TF_VAR_sku="${TF_VAR_sku}" \ 45 | TF_VAR_publisher="${TF_VAR_publisher}" \ 46 | TF_VAR_offer="${TF_VAR_offer}" \ 47 | terraform apply -auto-approve 48 | fi 49 | 50 | if [ "${TYPE}" == "debug" ] ; then 51 | echo "debug" 52 | TF_VAR_prefix="${TF_VAR_prefix}" \ 53 | TF_VAR_vmName="${TF_VAR_vmName}" \ 54 | TF_VAR_isWindows="${TF_VAR_isWindows}" \ 55 | TF_VAR_sku="${TF_VAR_sku}" \ 56 | TF_VAR_publisher="${TF_VAR_publisher}" \ 57 | TF_VAR_offer="${TF_VAR_offer}" \ 58 | terraform output 59 | RESOURCE_GROUP=$(jq -r '.outputs.resource_group_name.value' terraform.tfstate) 60 | 61 | mkdir -p debug 62 | if [ "${TF_VAR_isWindows}" == "false" ] ; then 63 | for file in "/var/log/waagent.log" "/var/log/azure/Elastic.ElasticAgent.linux/es-agent.log" 64 | do 65 | filename=$(basename $file) 66 | az vm run-command invoke \ 67 | -g "$RESOURCE_GROUP" \ 68 | -n "${TF_VAR_vmName}" \ 69 | --command-id RunShellScript \ 70 | --scripts "cat ${file}" > debug/"$TF_VAR_vmName"-"$filename".log || true 71 | done 72 | else 73 | az vm run-command invoke \ 74 | -g "$RESOURCE_GROUP" \ 75 | -n "${TF_VAR_vmName}" \ 76 | --command-id RunPowerShellScript \ 77 | --scripts "get-content C:\WindowsAzure\Logs\WaAppAgent.log" > debug/"$TF_VAR_vmName"-WaAppAgent.log || true 78 | az vm run-command invoke \ 79 | -g "$RESOURCE_GROUP" \ 80 | -n "${TF_VAR_vmName}" \ 81 | --command-id RunPowerShellScript \ 82 | --scripts "Get-ChildItem -Path C:\WindowsAzure\Logs\Plugins\Elastic.ElasticAgent.windows\*\CommandExecution.log | get-content" > debug/"$TF_VAR_vmName"-CommandExecution.log || true 83 | az vm run-command invoke \ 84 | -g "$RESOURCE_GROUP" \ 85 | -n "${TF_VAR_vmName}" \ 86 | --command-id RunPowerShellScript \ 87 | --scripts "Get-ChildItem -Path C:\WindowsAzure\Logs\Plugins\Elastic.ElasticAgent.windows\*\es-agent.log | get-content" > debug/"$TF_VAR_vmName"-es-agent.log || true 88 | fi 89 | fi 90 | 91 | if [ "${TYPE}" == "destroy" ] ; then 92 | echo "Destroy" 93 | TF_VAR_username="${TF_VAR_username}" \ 94 | TF_VAR_password="${TF_VAR_password}" \ 95 | TF_VAR_cloudId="${TF_VAR_cloudId}" \ 96 | TF_VAR_prefix="${TF_VAR_prefix}" \ 97 | TF_VAR_vmName="${TF_VAR_vmName}" \ 98 | TF_VAR_isWindows="${TF_VAR_isWindows}" \ 99 | TF_VAR_sku="${TF_VAR_sku}" \ 100 | TF_VAR_publisher="${TF_VAR_publisher}" \ 101 | TF_VAR_offer="${TF_VAR_offer}" \ 102 | terraform destroy -auto-approve 103 | fi 104 | -------------------------------------------------------------------------------- /src/handler/linux/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # install script ran at the installation time, check on distros the extension supports and installs required packages for the elastic agent to run 5 | 6 | # log will log any exceptions in the install process 7 | log() 8 | { 9 | echo \[$(date +%H:%M:%ST%d-%m-%Y)\] "$1" "$2" 10 | if [ -d "/var/log/azure/" ]; then 11 | echo \[$(date +%H:%M:%ST%d-%m-%Y)\] "$1" "$2" >> /var/log/azure/install-es-agent.log 12 | fi 13 | } 14 | 15 | 16 | DISTRO_NAME="" 17 | DISTRO_VERSION="" 18 | 19 | # get_distro will return distro name and version 20 | get_distro() { 21 | if [ -f /etc/os-release ]; then 22 | # freedesktop.org and systemd 23 | . /etc/os-release 24 | DISTRO_NAME=$NAME 25 | DISTRO_VERSION=$VERSION_ID 26 | elif type lsb_release >/dev/null 2>&1; then 27 | # linuxbase.org 28 | DISTRO_NAME=$(lsb_release -si) 29 | DISTRO_VERSION=$(lsb_release -sr) 30 | elif [ -f /etc/lsb-release ]; then 31 | # For some versions of Debian/Ubuntu without lsb_release command 32 | . /etc/lsb-release 33 | DISTRO_NAME=$DISTRIB_ID 34 | DISTRO_VERSION=$DISTRIB_RELEASE 35 | elif [ -f /etc/debian_version ]; then 36 | # Older Debian/Ubuntu/etc. 37 | DISTRO_NAME=Debian 38 | DISTRO_VERSION=$(cat /etc/debian_version) 39 | elif [ -f /etc/SuSe-release ]; then 40 | echo -e "Unsupported OS" 41 | clean_and_exit 51 42 | #elif [ -f /etc/redhat-release ]; then 43 | # Older Red Hat, CentOS, etc. 44 | else 45 | # Fall back to uname, e.g. "Linux ", also works for BSD, etc. 46 | DISTRO_NAME=$(uname -s) 47 | DISTRO_VERSION=$(uname -r) 48 | fi 49 | } 50 | 51 | # install_dependencies will install jq, wget packages if missing 52 | install_dependencies() { 53 | get_distro 54 | distro=${DISTRO_NAME,,} 55 | if [[ "$distro" = "sles" ]] || [[ "$distro" = *"suse"* ]] || [[ "$distro" = *"flatcar"* ]] ; then 56 | echo -e "Unsupported OS" 57 | clean_and_exit 51 58 | fi 59 | if [[ $distro == "redhat"* && $DISTRO_VERSION == "6"* ]] || [[ $distro == "red hat"* && $DISTRO_VERSION == "6"* ]] ; then 60 | echo -e "Unsupported OS" 61 | clean_and_exit 51 62 | fi 63 | log "distro: $DISTRO_NAME version: $DISTRO_VERSION" "INFO" 64 | if dpkg -S /bin/ls >/dev/null 2>&1 || dpkg -S /usr/bin/ls >/dev/null 2>&1; then 65 | log "[install_dependencies] distro is Debian" "INFO" 66 | sudo apt-get update 67 | if [ $(dpkg-query -W -f='${Status}' curl 2>/dev/null | grep -c "ok installed") -eq 0 ]; then 68 | #sudo apt-get --yes install curl; 69 | (sudo apt-get --yes install curl || (sleep 15; sudo apt-get --yes install curl)) 70 | fi 71 | if [ $(dpkg-query -W -f='${Status}' jq 2>/dev/null | grep -c "ok installed") -eq 0 ]; then 72 | #sudo apt-get --yes install jq; 73 | (sudo apt-get --yes install jq || (sleep 15; apt-get --yes install jq)) 74 | fi 75 | elif rpm -q -f /bin/ls >/dev/null 2>&1; then 76 | log "[install_dependencies] distro is RPM" "INFO" 77 | if [[ $distro == "red hat"* && $DISTRO_VERSION == "6"* ]] || [[ $distro == "red hat"* && $DISTRO_VERSION == "7.2" ]] ;then 78 | sed -i -e "s/Defaults requiretty.*/ #Defaults requiretty/g" /etc/sudoers 79 | fi 80 | #sudo yum update -y --disablerepo='*' --enablerepo='*microsoft*' 81 | if ! command -v wget &> /dev/null; then 82 | sudo yum install wget -y 83 | else 84 | log "[install_dependencies] wget is already installed" "INFO" 85 | fi 86 | if ! command -v jq &> /dev/null; then 87 | if [[ $distro == *"centos"* ]] && [[ $DISTRO_VERSION == "6"* ]] ; then 88 | log "CentOS install jq" "INFO" 89 | sudo wget -O jq https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 90 | sudo chmod +x ./jq 91 | sudo cp jq /usr/bin 92 | log "CentOS install jq finished" "INFO" 93 | elif [[ $distro == "oracle"* ]] && [[ $DISTRO_VERSION == "6"* ]] ; then 94 | log "Redhat install jq" "INFO" 95 | sudo wget -O jq https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64 96 | sudo chmod +x ./jq 97 | sudo cp jq /usr/bin 98 | log "Redhat install jq finished" "INFO" 99 | else 100 | sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm -y -q 101 | sudo yum install jq -y 102 | fi 103 | else 104 | log "[install_dependencies] jq is already installed" "INFO" 105 | fi 106 | else 107 | log "[install_dependencies] distro is OTHER" "INFO" 108 | pacman -Qq | grep -qw jq || pacman -S jq 109 | fi 110 | } 111 | 112 | install_dependencies 113 | 114 | 115 | -------------------------------------------------------------------------------- /.ci/Makefile: -------------------------------------------------------------------------------- 1 | 2 | SHELL=/bin/bash -o pipefail 3 | CURRENT_UID := $(shell id -u) 4 | CURRENT_GID := $(shell id -g) 5 | DOCKER_IMAGE=docker.elastic.co/observability-ci/azure-vm-tools 6 | 7 | .PHONY: help 8 | .DEFAULT_GOAL := help 9 | help: ## Display this help text 10 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' 11 | 12 | .PHONY: prepare 13 | prepare: ## Prepare the dependencies to run the tools 14 | @docker build --tag $(DOCKER_IMAGE) -f docker/azure-vm-tools/Dockerfile . 15 | 16 | terraform-%: ## Run the terraform action 17 | ifndef TF_VAR_username 18 | @echo "Please set TF_VAR_username in the environment to generate the extension" 19 | exit 1 20 | endif 21 | ifndef TF_VAR_password 22 | @echo "Please set TF_VAR_password in the environment to generate the extension" 23 | exit 1 24 | endif 25 | ifndef TF_VAR_cloudId 26 | @echo "Please set TF_VAR_cloudId in the environment to generate the extension" 27 | exit 1 28 | endif 29 | ifndef TF_VAR_prefix 30 | @echo "Please set TF_VAR_prefix in the environment to generate the extension" 31 | exit 1 32 | endif 33 | ifndef TF_VAR_sku 34 | @echo "Please set TF_VAR_sku in the environment to generate the VM" 35 | exit 1 36 | endif 37 | ifndef TF_VAR_publisher 38 | @echo "Please set TF_VAR_publisher in the environment to generate the VM" 39 | exit 1 40 | endif 41 | ifndef TF_VAR_offer 42 | @echo "Please set TF_VAR_offer in the environment to generate the VM" 43 | exit 1 44 | endif 45 | ifndef TF_VAR_vmName 46 | @echo "Please set TF_VAR_vmName in the environment to generate the extension" 47 | exit 1 48 | endif 49 | ifndef TF_VAR_isWindows 50 | @echo "Please set TF_VAR_isWindows in the environment to generate the extension" 51 | exit 1 52 | endif 53 | ifndef AZ_USERNAME 54 | @echo "Please set AZ_USERNAME in the environment to generate the extension" 55 | exit 1 56 | endif 57 | ifndef AZ_PASSWORD 58 | @echo "Please set AZ_PASSWORD in the environment to generate the extension" 59 | exit 1 60 | endif 61 | ifndef AZ_TENANT 62 | @echo "Please set AZ_TENANT in the environment to generate the extension" 63 | exit 1 64 | endif 65 | ifndef AZ_SUBSCRIPTION 66 | @echo "Please set AZ_SUBSCRIPTION in the environment to generate the extension" 67 | exit 1 68 | endif 69 | @docker run --rm -t \ 70 | --volume "$(PWD)":/app \ 71 | --workdir /app \ 72 | --env TF_VAR_username="$(TF_VAR_username)" \ 73 | --env TF_VAR_password="$(TF_VAR_password)" \ 74 | --env TF_VAR_cloudId="$(TF_VAR_cloudId)" \ 75 | --env TF_VAR_prefix="$(TF_VAR_prefix)" \ 76 | --env TF_VAR_vmName="$(TF_VAR_vmName)" \ 77 | --env TF_VAR_isWindows="$(TF_VAR_isWindows)" \ 78 | --env TF_VAR_sku="$(TF_VAR_sku)" \ 79 | --env TF_VAR_publisher="$(TF_VAR_publisher)" \ 80 | --env TF_VAR_offer="$(TF_VAR_offer)" \ 81 | --env AZ_USERNAME="$(AZ_USERNAME)" \ 82 | --env AZ_PASSWORD="$(AZ_PASSWORD)" \ 83 | --env AZ_TENANT="$(AZ_TENANT)" \ 84 | --env AZ_SUBSCRIPTION="$(AZ_SUBSCRIPTION)" \ 85 | --env TYPE=$* \ 86 | --env HOME=/app \ 87 | -u $(CURRENT_UID):$(CURRENT_GID) \ 88 | $(DOCKER_IMAGE) 89 | 90 | 91 | terraform-debug: ## Debug the terraform plan 92 | 93 | terraform-destroy: ## Destroy the terraform plan 94 | 95 | terraform-run: ## Run the terraform plan 96 | 97 | validate: ## Validate the VM extension 98 | ifndef ES_USERNAME 99 | @echo "Please set ES_USERNAME in the environment to test the extension" 100 | exit 1 101 | endif 102 | ifndef ES_PASSWORD 103 | @echo "Please set ES_PASSWORD in the environment to test the extension" 104 | exit 1 105 | endif 106 | ifndef ES_URL 107 | @echo "Please set ES_URL in the environment to test the extension" 108 | exit 1 109 | endif 110 | ifndef VM_NAME 111 | @echo "Please set VM_NAME in the environment to test the extension" 112 | exit 1 113 | endif 114 | ifndef TF_VAR_isWindows 115 | @echo "Please set TF_VAR_isWindows in the environment to test the extension" 116 | exit 1 117 | endif 118 | @docker run --rm -t \ 119 | --volume "$(PWD)":/app \ 120 | --workdir /app \ 121 | --env ES_USERNAME="$(ES_USERNAME)" \ 122 | --env ES_PASSWORD="$(ES_PASSWORD)" \ 123 | --env ES_URL="$(ES_URL)" \ 124 | --env VM_NAME="$(VM_NAME)" \ 125 | --env TF_VAR_isWindows="$(TF_VAR_isWindows)" \ 126 | --env TYPE=test \ 127 | --env HOME=/app \ 128 | -u $(CURRENT_UID):$(CURRENT_GID) \ 129 | $(DOCKER_IMAGE) 130 | 131 | create-cluster: ## Create a cluster in the cloud 132 | ifndef CLUSTER_NAME 133 | @echo "Please set CLUSTER_NAME in the environment to create the cluster" 134 | exit 1 135 | endif 136 | CLUSTER_NAME=$(CLUSTER_NAME) ../test/deployment/deployment.sh 'create' 137 | 138 | destroy-cluster: ## Destroy the cluster in the cloud 139 | ifndef CLUSTER_NAME 140 | @echo "Please set CLUSTER_NAME in the environment to destroy the cluster" 141 | exit 1 142 | endif 143 | CLUSTER_NAME=$(CLUSTER_NAME) ../test/deployment/deployment.sh 'destroy' 144 | -------------------------------------------------------------------------------- /src/handler/windows/scripts/log.ps1: -------------------------------------------------------------------------------- 1 | $ScriptDirectory = Split-Path $MyInvocation.MyCommand.Path 2 | . (Join-Path $ScriptDirectory helper.ps1) 3 | # all logging settins are here on top 4 | 5 | $logLevel = "DEBUG" # ("DEBUG","INFO","WARN","ERROR","FATAL") 6 | $logSize = 1mb # 30kb 7 | $logCount = 10 8 | # end of settings 9 | 10 | function Get-LogFile { 11 | $logDir = Get-Azure-Logs-Path 12 | return "$logDir\es-agent.log" 13 | } 14 | $logFile = Get-LogFile 15 | 16 | function Write-Log-Line ($line) { 17 | Add-Content $logFile -Value $Line 18 | Write-Host $Line 19 | } 20 | 21 | # http://stackoverflow.com/a/38738942 22 | Function Write-Log { 23 | [CmdletBinding()] 24 | Param( 25 | [Parameter(Mandatory=$True)] 26 | [string] 27 | $Message, 28 | 29 | [Parameter(Mandatory=$False)] 30 | [String] 31 | $Level = "DEBUG" 32 | ) 33 | 34 | $levels = ("DEBUG","INFO","WARN","ERROR","FATAL") 35 | $logLevelPos = [array]::IndexOf($levels, $logLevel) 36 | $levelPos = [array]::IndexOf($levels, $Level) 37 | $Stamp = (Get-Date).toString("yyyy/MM/dd HH:mm:ss:fff") 38 | 39 | if ($logLevelPos -lt 0){ 40 | Write-Log-Line "$Stamp ERROR Wrong logLevel configuration [$logLevel]" 41 | } 42 | 43 | if ($levelPos -lt 0){ 44 | Write-Log-Line "$Stamp ERROR Wrong log level parameter [$Level]" 45 | } 46 | 47 | # if level parameter is wrong or configuration is wrong I still want to see the 48 | # message in log 49 | if ($levelPos -lt $logLevelPos -and $levelPos -ge 0 -and $logLevelPos -ge 0){ 50 | return 51 | } 52 | 53 | $Line = "$Stamp $Level $Message" 54 | Write-Log-Line $Line 55 | } 56 | 57 | # https://gallery.technet.microsoft.com/scriptcenter/PowerShell-Script-to-Roll-a96ec7d4 58 | function Reset-Log 59 | { 60 | # function checks to see if file in question is larger than the paramater specified 61 | # if it is it will roll a log and delete the oldes log if there are more than x logs. 62 | param([string]$fileName, [int64]$filesize = 1mb , [int] $logcount = 5) 63 | $logRollStatus = $true 64 | if(test-path $filename) 65 | { 66 | $file = Get-ChildItem $filename 67 | if((($file).length) -ige $filesize) #this starts the log roll 68 | { 69 | $fileDir = $file.Directory 70 | #this gets the name of the file we started with 71 | $fn = $file.name 72 | $files = Get-ChildItem $filedir | ?{$_.name -like "$fn*"} | Sort-Object lastwritetime 73 | #this gets the fullname of the file we started with 74 | $filefullname = $file.fullname 75 | #$logcount +=1 #add one to the count as the base file is one more than the count 76 | for ($i = ($files.count); $i -gt 0; $i--) 77 | { 78 | #[int]$fileNumber = ($f).name.Trim($file.name) #gets the current number of 79 | # the file we are on 80 | $files = Get-ChildItem $filedir | ?{$_.name -like "$fn*"} | Sort-Object lastwritetime 81 | $operatingFile = $files | ?{($_.name).trim($fn) -eq $i} 82 | if ($operatingfile) 83 | {$operatingFilenumber = ($files | ?{($_.name).trim($fn) -eq $i}).name.trim($fn)} 84 | else 85 | {$operatingFilenumber = $null} 86 | 87 | if(($operatingFilenumber -eq $null) -and ($i -ne 1) -and ($i -lt $logcount)) 88 | { 89 | $operatingFilenumber = $i 90 | $newfilename = "$filefullname.$operatingFilenumber" 91 | $operatingFile = $files | ?{($_.name).trim($fn) -eq ($i-1)} 92 | write-host "moving to $newfilename" 93 | move-item ($operatingFile.FullName) -Destination $newfilename -Force 94 | } 95 | elseif($i -ge $logcount) 96 | { 97 | if($operatingFilenumber -eq $null) 98 | { 99 | $operatingFilenumber = $i - 1 100 | $operatingFile = $files | ?{($_.name).trim($fn) -eq $operatingFilenumber} 101 | 102 | } 103 | write-host "deleting " ($operatingFile.FullName) 104 | remove-item ($operatingFile.FullName) -Force 105 | } 106 | elseif($i -eq 1) 107 | { 108 | $operatingFilenumber = 1 109 | $newfilename = "$filefullname.$operatingFilenumber" 110 | write-host "moving to $newfilename" 111 | move-item $filefullname -Destination $newfilename -Force 112 | } 113 | else 114 | { 115 | $operatingFilenumber = $i +1 116 | $newfilename = "$filefullname.$operatingFilenumber" 117 | $operatingFile = $files | ?{($_.name).trim($fn) -eq ($i-1)} 118 | write-host "moving to $newfilename" 119 | move-item ($operatingFile.FullName) -Destination $newfilename -Force 120 | } 121 | } 122 | } 123 | else 124 | { $logRollStatus = $false} 125 | } 126 | else 127 | { 128 | $logrollStatus = $false 129 | } 130 | $LogRollStatus 131 | } 132 | 133 | # to null to avoid output 134 | $Null = @( 135 | Reset-Log -fileName $logFile -filesize $logSize -logcount $logCount 136 | ) 137 | -------------------------------------------------------------------------------- /test/ats/validate.py: -------------------------------------------------------------------------------- 1 | from typing import Counter 2 | import unittest 3 | import xmlrunner 4 | from elasticsearch import Elasticsearch 5 | import os 6 | import time 7 | 8 | class TestIndices(unittest.TestCase): 9 | 10 | es = Elasticsearch( 11 | [os.environ.get('ES_URL')], 12 | http_auth=(os.environ.get('ES_USERNAME'), os.environ.get('ES_PASSWORD')), 13 | ) 14 | 15 | hostname = os.environ.get('VM_NAME') 16 | is_windows = "true" in os.getenv('TF_VAR_isWindows', 'true').lower() 17 | 18 | def count_enrollment(self, index_name, hostname, compare_with): 19 | tries = 1 20 | total = 20 21 | count = 0 22 | print("count_enrollment(index_name: {}, hostname: {})".format(index_name, hostname)) 23 | while count < compare_with: 24 | if tries > total: 25 | break 26 | print("count_enrollment: {} out of {}".format(tries, total)) 27 | records_count = self.es.count(index=index_name,body={ 28 | "query": { 29 | "bool": { 30 | "filter": [ 31 | { 32 | "match_all": {} 33 | }, 34 | { 35 | "match_phrase": { 36 | "local_metadata.host.hostname": hostname 37 | } 38 | }, 39 | { 40 | "match_phrase": { 41 | "active": True 42 | } 43 | } 44 | ], 45 | "must_not": [ 46 | { 47 | "match_phrase": { 48 | "policy_id": "policy-elastic-agent-on-cloud" 49 | } 50 | } 51 | ] 52 | } 53 | } 54 | }, 55 | ignore=503 56 | ) 57 | if "count" in records_count: 58 | count = records_count['count'] 59 | if count >= compare_with: 60 | break 61 | tries += 1 62 | time.sleep(5) 63 | continue 64 | ## Print will be shown in the junit xml as system-out. This should help to debug if needed. 65 | print(records_count) 66 | return count 67 | 68 | def count(self, index_name, hostname, compare_with): 69 | tries = 1 70 | total = 20 71 | count = 0 72 | print("count(index_name: {}, hostname: {})".format(index_name, hostname)) 73 | while count < compare_with: 74 | if tries > total: 75 | break 76 | print("count: {} out of {}".format(tries, total)) 77 | records_count = self.es.count(index=index_name, body={"query": {"match": {"agent.hostname": hostname}}}, ignore=503) 78 | if "count" in records_count: 79 | count = records_count['count'] 80 | if count >= compare_with: 81 | break 82 | tries += 1 83 | time.sleep(5) 84 | continue 85 | 86 | ## Print will be shown in the junit xml as system-out. This should help to debug if needed. 87 | print(records_count) 88 | return count 89 | 90 | def exists(self, index_name): 91 | tries = 1 92 | total = 10 93 | exist = False 94 | print("exists(index_name: {})".format(index_name)) 95 | while not exist: 96 | if tries > total: 97 | break 98 | print("exists: {} out of {}".format(tries, total)) 99 | ## Deprecated to access system indices 100 | ## https://github.com/elastic/elasticsearch/issues/50251 101 | exist = self.es.indices.exists(index_name, ignore=503) 102 | if exist: 103 | break 104 | tries += 1 105 | time.sleep(5) 106 | continue 107 | return exist 108 | 109 | def count_and_test(self, index_name, hostname, compare_with): 110 | records_count = self.count(index_name, hostname, compare_with) 111 | self.assertTrue(records_count >= compare_with, "Expected at least one entry in index {}, got {}".format(index_name, records_count)) 112 | 113 | def test_green_indices(self): 114 | records_indices = self.es.cat.indices() 115 | ## Print will be shown in the junit xml as system-out. This should help to debug if needed. 116 | print(records_indices) 117 | self.assertTrue("green" in records_indices, "Expected green indices") 118 | 119 | def test_indice_fleet_agents_7_exists(self): 120 | self.assertTrue(self.exists('.fleet-agents-7'), "Expected .fleet-agents-7 index") 121 | 122 | def test_enrollment(self): 123 | index_name = '.fleet-agents-7' 124 | compare_with = 1 125 | records_count = self.count_enrollment(index_name, self.hostname, compare_with) 126 | self.assertTrue(records_count >= compare_with, "Expected at least one entry in index {}, got {}".format(index_name, records_count)) 127 | 128 | def test_indice_ds_metrics_memory(self): 129 | self.count_and_test('.ds-metrics-system.memory-default-*', self.hostname, 1) 130 | 131 | def test_indice_ds_metrics_cpu(self): 132 | self.count_and_test('.ds-metrics-system.cpu-default-*', self.hostname, 1) 133 | 134 | def test_indice_ds_metrics_diskio(self): 135 | self.count_and_test('.ds-metrics-system.diskio-default-*', self.hostname, 1) 136 | 137 | def test_indice_ds_logs_windows_diskio(self): 138 | if self.is_windows: 139 | self.count_and_test('.ds-logs-system.application-default-*', self.hostname, 1) 140 | 141 | def test_indice_ds_logs_linux_diskio(self): 142 | if not self.is_windows: 143 | self.count_and_test('.ds-logs-system.syslog-default-*', self.hostname, 1) 144 | 145 | if __name__ == '__main__': 146 | unittest.main(testRunner=xmlrunner.XMLTestRunner(output='test-reports')) 147 | -------------------------------------------------------------------------------- /.ci/Jenkinsfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env groovy 2 | // Licensed to Elasticsearch B.V. under one or more contributor 3 | // license agreements. See the NOTICE file distributed with 4 | // this work for additional information regarding copyright 5 | // ownership. Elasticsearch B.V. licenses this file to you under 6 | // the Apache License, Version 2.0 (the "License"); you may 7 | // not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, 13 | // software distributed under the License is distributed on an 14 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | // KIND, either express or implied. See the License for the 16 | // specific language governing permissions and limitations 17 | // under the License. 18 | @Library('apm@current') _ 19 | 20 | import groovy.transform.Field 21 | 22 | // VM names created to run later on the terraform plan 23 | // key is the cluster name and value is the VM name 24 | @Field def vms = [:] 25 | 26 | // Passed stages to workaround the matrix/post-build 27 | // See https://issues.jenkins.io/browse/JENKINS-65997 28 | @Field def successStages = [:] 29 | 30 | pipeline { 31 | agent none 32 | environment { 33 | REPO = "azure-vm-extension" 34 | NOTIFY_TO = credentials('notify-to') 35 | PIPELINE_LOG_LEVEL = 'INFO' 36 | LANG = "C.UTF-8" 37 | LC_ALL = "C.UTF-8" 38 | ELASTIC_STACK_VERSION = '7.17.0' 39 | } 40 | options { 41 | buildDiscarder(logRotator(numToKeepStr: '5', artifactNumToKeepStr: '5', daysToKeepStr: '7')) 42 | timestamps() 43 | ansiColor('xterm') 44 | disableResume() 45 | durabilityHint('PERFORMANCE_OPTIMIZED') 46 | timeout(time: 2, unit: 'HOURS') 47 | disableConcurrentBuilds() 48 | } 49 | triggers { 50 | issueCommentTrigger("${obltGitHubComments()}") 51 | } 52 | parameters { 53 | booleanParam(name: 'skipDestroy', defaultValue: "false", description: "Whether to skip the destroy of the cluster and terraform.") 54 | } 55 | stages { 56 | stage('ITs') { 57 | options { skipDefaultCheckout() } 58 | failFast false 59 | matrix { 60 | agent { label 'ubuntu-20' } 61 | axes { 62 | axis { 63 | name 'OS_VERSION' 64 | values 'ubuntu-18', 'windows-2016', 'redhat-8' 65 | } 66 | } 67 | environment { 68 | HOME = "${env.WORKSPACE}" 69 | PATH = "${env.HOME}/bin:${env.PATH}" 70 | } 71 | stages { 72 | stage('Checkout'){ 73 | options { skipDefaultCheckout() } 74 | steps { 75 | deleteDir() 76 | checkout scm 77 | } 78 | } 79 | stage('Create cluster'){ 80 | options { skipDefaultCheckout() } 81 | environment { 82 | STAGE_NAME = "Create cluster|${STACK_VERSION}|${OS_VERSION}" 83 | } 84 | steps { 85 | withGithubNotify(context: "Create Cluster ${ELASTIC_STACK_VERSION} ${OS_VERSION}") { 86 | withVaultEnv(){ 87 | sh(label: 'Deploy Cluster', script: 'make -C .ci create-cluster') 88 | } 89 | } 90 | successStage(env.STAGE_NAME) 91 | } 92 | post { 93 | failure { 94 | whenFalse(isSuccessStage(env.STAGE_NAME)) { 95 | destroyCluster() 96 | } 97 | } 98 | } 99 | } 100 | stage('Prepare tools') { 101 | options { skipDefaultCheckout() } 102 | environment { 103 | STAGE_NAME = "Tools|${STACK_VERSION}|${OS_VERSION}" 104 | } 105 | steps { 106 | withCloudEnv() { 107 | sh(label: 'Prepare tools', script: 'make -C .ci prepare') 108 | } 109 | successStage(env.STAGE_NAME) 110 | } 111 | post { 112 | failure { 113 | whenFalse(isSuccessStage(env.STAGE_NAME)) { 114 | destroyCluster() 115 | } 116 | } 117 | } 118 | } 119 | stage('Terraform') { 120 | options { skipDefaultCheckout() } 121 | environment { 122 | STAGE_NAME = "Terraform|${STACK_VERSION}|${OS_VERSION}" 123 | } 124 | steps { 125 | withGithubNotify(context: "Terraform ${ELASTIC_STACK_VERSION} ${OS_VERSION}") { 126 | terraform(goal: 'terraform-run') 127 | } 128 | successStage(env.STAGE_NAME) 129 | } 130 | post { 131 | failure { 132 | whenFalse(isSuccessStage(env.STAGE_NAME)) { 133 | destroyTerraform() 134 | destroyCluster() 135 | } 136 | } 137 | } 138 | } 139 | stage('Validate') { 140 | options { skipDefaultCheckout() } 141 | steps { 142 | withGithubNotify(context: "Validate ${ELASTIC_STACK_VERSION} ${OS_VERSION}") { 143 | withValidationEnv() { 144 | sh(label: 'Validate', script: 'make -C .ci validate') 145 | } 146 | } 147 | } 148 | post { 149 | always { 150 | junit(allowEmptyResults: true, keepLongStdio: true, testResults: 'test/ats/TEST-*.xml') 151 | destroyTerraform() 152 | destroyCluster() 153 | } 154 | } 155 | } 156 | } 157 | } 158 | } 159 | } 160 | post { 161 | cleanup { 162 | notifyBuildResult(prComment: true) 163 | } 164 | } 165 | } 166 | 167 | def successStage(String name) { 168 | successStages[name] = true 169 | } 170 | 171 | def isSuccessStage(String name) { 172 | return successStages.containsKey(name) 173 | } 174 | 175 | def destroyCluster( ) { 176 | if (params.skipDestroy) { 177 | echo 'Skipped the destroy cluster step' 178 | return 179 | } 180 | withVaultEnv(){ 181 | sh(label: 'Destroy Cluster', script: 'make -C .ci destroy-cluster') 182 | } 183 | } 184 | 185 | def destroyTerraform( ) { 186 | terraform(goal: 'terraform-debug', returnStatus: true) 187 | if (params.skipDestroy) { 188 | echo 'Skipped the destroy terraform step' 189 | return 190 | } 191 | terraform(goal: 'terraform-destroy', returnStatus: true) 192 | } 193 | 194 | def terraform(Map args = [:]) { 195 | withCloudEnv() { 196 | withAzEnv() { 197 | sh(label: "Run ${args.goal}", script: "make -C .ci ${args.goal}", returnStatus: args.get('returnStatus', false)) 198 | archiveArtifacts allowEmptyArchive: true, artifacts: 'test/terraform/debug/*.log' 199 | } 200 | } 201 | } 202 | 203 | def withVaultEnv(Closure body){ 204 | getVaultSecret.readSecretWrapper { 205 | withMatrixEnv() { 206 | withEnvMask(vars: [ 207 | [var: 'VAULT_ADDR', password: env.VAULT_ADDR], 208 | [var: 'VAULT_ROLE_ID', password: env.VAULT_ROLE_ID], 209 | [var: 'VAULT_SECRET_ID', password: env.VAULT_SECRET_ID], 210 | [var: 'VAULT_AUTH_METHOD', password: 'approle'], 211 | [var: 'VAULT_AUTHTYPE', password: 'approle'] 212 | ]){ 213 | body() 214 | } 215 | } 216 | } 217 | } 218 | 219 | def withValidationEnv(Closure body) { 220 | withMatrixEnv() { 221 | withClusterEnv(cluster: env.CLUSTER_NAME) { 222 | body() 223 | } 224 | } 225 | } 226 | 227 | def withCloudEnv(Closure body) { 228 | withMatrixEnv() { 229 | withCloudEnv(cluster: env.CLUSTER_NAME) { 230 | // withCloudEnv creates different env variables, let's create the 231 | // ones needed for the terraform runs 232 | withEnvMask(vars: [ 233 | [var: 'TF_VAR_username', password: env.CLOUD_USERNAME], 234 | [var: 'TF_VAR_password', password: env.CLOUD_PASSWORD], 235 | [var: 'TF_VAR_cloudId', password: env.CLOUD_ID] 236 | ]){ 237 | body() 238 | } 239 | } 240 | } 241 | } 242 | 243 | def withAzEnv(Closure body) { 244 | withMatrixEnv() { 245 | withAzureEnv(secret: 'secret/observability-team/ci/service-account/azure-vm-extension') { 246 | body() 247 | } 248 | } 249 | } 250 | 251 | def withMatrixEnv(Closure body) { 252 | def clusterName = "tst-az-${BUILD_ID}-${BRANCH_NAME}-${ELASTIC_STACK_VERSION}-${env.OS_VERSION}" 253 | def vmName = getCachedVmNameOrAssignVmName(clusterName) 254 | def vms = readYaml(file: ".ci/virtual-machines.yml") 255 | def os = vms.find { it.name == env.OS_VERSION } 256 | withEnv([ 257 | "CLUSTER_NAME=${clusterName}", 258 | 'TF_VAR_prefix=tst-' + vmName.take(6), 259 | "TF_VAR_vmName=${vmName}", 260 | "TF_VAR_isWindows=${isWindows()}", 261 | "TF_VAR_sku=${os.sku}", 262 | "TF_VAR_publisher=${os.publisher}", 263 | "TF_VAR_offer=${os.offer}", 264 | "VM_NAME=${vmName}" 265 | ]) { 266 | echo "CLUSTER_NAME=${CLUSTER_NAME} - VM_NAME=${VM_NAME} - TF_VAR_prefix=${TF_VAR_prefix} - TF_VAR_isWindows=${TF_VAR_isWindows}" 267 | echo "TF_VAR_sku=${TF_VAR_sku} - TF_VAR_publisher=${TF_VAR_publisher} - TF_VAR_offer=${TF_VAR_offer}" 268 | body() 269 | } 270 | } 271 | 272 | def getCachedVmNameOrAssignVmName(String key) { 273 | if (vms.containsKey(key)) { 274 | return vms.get(key) 275 | } else { 276 | def vmName = randomString(size: 15) 277 | vms[key] = vmName 278 | return vmName 279 | } 280 | } 281 | 282 | def isWindows() { 283 | return env.OS_VERSION?.toLowerCase()?.contains('windows') 284 | } 285 | -------------------------------------------------------------------------------- /.ci/its.groovy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env groovy 2 | // Licensed to Elasticsearch B.V. under one or more contributor 3 | // license agreements. See the NOTICE file distributed with 4 | // this work for additional information regarding copyright 5 | // ownership. Elasticsearch B.V. licenses this file to you under 6 | // the Apache License, Version 2.0 (the "License"); you may 7 | // not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, 13 | // software distributed under the License is distributed on an 14 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | // KIND, either express or implied. See the License for the 16 | // specific language governing permissions and limitations 17 | // under the License. 18 | @Library('apm@current') _ 19 | 20 | import groovy.transform.Field 21 | 22 | // VM names created to run later on the terraform plan 23 | // key is the cluster name and value is the VM name 24 | @Field def vms = [:] 25 | 26 | // Failed stages to notify in the slack message 27 | @Field def failedStages = [:] 28 | 29 | // Passed stages to workaround the matrix/post-build 30 | // See https://issues.jenkins.io/browse/JENKINS-65997 31 | @Field def successStages = [:] 32 | 33 | pipeline { 34 | agent none 35 | environment { 36 | REPO = "azure-vm-extension" 37 | NOTIFY_TO = credentials('notify-to') 38 | PIPELINE_LOG_LEVEL = 'INFO' 39 | LANG = "C.UTF-8" 40 | LC_ALL = "C.UTF-8" 41 | SLACK_CHANNEL = '#beats-build' 42 | } 43 | options { 44 | buildDiscarder(logRotator(numToKeepStr: '5', artifactNumToKeepStr: '5', daysToKeepStr: '7')) 45 | timestamps() 46 | ansiColor('xterm') 47 | disableResume() 48 | durabilityHint('PERFORMANCE_OPTIMIZED') 49 | timeout(time: 2, unit: 'HOURS') 50 | disableConcurrentBuilds() 51 | } 52 | triggers { 53 | cron("${(env.BRANCH_NAME.trim() == 'main') ? 'H H(5-6) * * 1-5' : ''}") 54 | } 55 | parameters { 56 | booleanParam(name: 'skipDestroy', defaultValue: "false", description: "Whether to skip the destroy of the cluster and terraform.") 57 | } 58 | stages { 59 | stage('ITs') { 60 | options { skipDefaultCheckout() } 61 | failFast false 62 | matrix { 63 | agent { label 'ubuntu-20' } 64 | axes { 65 | axis { 66 | name 'STACK_VERSION' 67 | // The below line is part of the bump release automation 68 | // if you change anything please modifies the file 69 | // .ci/bump-stack-release-version.sh 70 | values '8.0.0-SNAPSHOT', '7.16.0', '7.15.0' 71 | } 72 | axis { 73 | name 'OS_VERSION' 74 | values 'ubuntu-18', 'windows-2016', 'windows-2012', 'redhat-8' 75 | } 76 | } 77 | environment { 78 | HOME = "${env.WORKSPACE}" 79 | PATH = "${env.HOME}/bin:${env.PATH}" 80 | } 81 | stages { 82 | stage('Checkout'){ 83 | steps { 84 | deleteDir() 85 | checkout scm 86 | } 87 | } 88 | stage('Create cluster'){ 89 | options { skipDefaultCheckout() } 90 | environment { 91 | STAGE_NAME = "Create cluster|${STACK_VERSION}|${OS_VERSION}" 92 | } 93 | steps { 94 | withGithubNotify(context: "Create Cluster ${STACK_VERSION} ${OS_VERSION}") { 95 | withVaultEnv(){ 96 | sh(label: 'Deploy Cluster', script: 'make -C .ci create-cluster') 97 | } 98 | } 99 | successStage(env.STAGE_NAME) 100 | } 101 | post { 102 | failure { 103 | failedStage(env.STAGE_NAME) 104 | whenFalse(isSuccessStage(env.STAGE_NAME)) { 105 | destroyCluster() 106 | } 107 | } 108 | } 109 | } 110 | stage('Prepare tools') { 111 | options { skipDefaultCheckout() } 112 | environment { 113 | STAGE_NAME = "Prepare tools|${STACK_VERSION}|${OS_VERSION}" 114 | } 115 | steps { 116 | withCloudEnv() { 117 | sh(label: 'Prepare tools', script: 'make -C .ci prepare') 118 | } 119 | successStage(env.STAGE_NAME) 120 | } 121 | post { 122 | failure { 123 | failedStage(env.STAGE_NAME) 124 | whenFalse(isSuccessStage(env.STAGE_NAME)) { 125 | destroyCluster() 126 | } 127 | } 128 | } 129 | } 130 | stage('Terraform') { 131 | options { skipDefaultCheckout() } 132 | environment { 133 | STAGE_NAME = "Terraform|${STACK_VERSION}|${OS_VERSION}" 134 | } 135 | steps { 136 | withGithubNotify(context: "Terraform ${STACK_VERSION} ${OS_VERSION}") { 137 | terraform(goal: 'terraform-run') 138 | } 139 | successStage(env.STAGE_NAME) 140 | } 141 | post { 142 | failure { 143 | failedStage(env.STAGE_NAME) 144 | whenFalse(isSuccessStage(env.STAGE_NAME)) { 145 | destroyTerraform() 146 | destroyCluster() 147 | } 148 | } 149 | } 150 | } 151 | stage('Validate') { 152 | options { skipDefaultCheckout() } 153 | steps { 154 | withGithubNotify(context: "Validate ${STACK_VERSION} ${OS_VERSION}") { 155 | withValidationEnv() { 156 | sh(label: 'Validate', script: 'make -C .ci validate') 157 | } 158 | } 159 | } 160 | post { 161 | failure { 162 | failedStage("ITs|${STACK_VERSION}|${OS_VERSION}") 163 | } 164 | always { 165 | junit(allowEmptyResults: true, keepLongStdio: true, testResults: 'test/ats/TEST-*.xml') 166 | destroyTerraform() 167 | destroyCluster() 168 | } 169 | } 170 | } 171 | } 172 | } 173 | } 174 | } 175 | post { 176 | cleanup { 177 | notifyBuildResult(prComment: true, slackHeader: "${slackStageStatus()}", slackChannel: "${env.SLACK_CHANNEL}", slackComment: true, slackNotify: true) 178 | } 179 | } 180 | } 181 | 182 | def failedStage(String name) { 183 | failedStages[name] = true 184 | } 185 | 186 | def slackStageStatus() { 187 | echo "slackStageStatus: started" 188 | if (failedStages.isEmpty()) { 189 | return "*ITs*: ${env.REPO}" 190 | } else { 191 | def message = "*ITs failed*: " 192 | failedStages.each { k, v -> 193 | def data = k?.split('\\|') 194 | message += "\n- Stage: ${data[0]} (${data[1]} - ${data[2]})" 195 | } 196 | echo "slackStageStatus: ${message}" 197 | return message 198 | } 199 | } 200 | 201 | def successStage(String name) { 202 | successStages[name] = true 203 | } 204 | 205 | def isSuccessStage(String name) { 206 | return successStages.containsKey(name) 207 | } 208 | 209 | def destroyCluster( ) { 210 | terraform(goal: 'terraform-debug', returnStatus: true) 211 | if (params.skipDestroy) { 212 | echo 'Skipped the destroy cluster step' 213 | return 214 | } 215 | withVaultEnv(){ 216 | sh(label: 'Destroy Cluster', script: 'make -C .ci destroy-cluster') 217 | } 218 | } 219 | 220 | def destroyTerraform( ) { 221 | if (params.skipDestroy) { 222 | echo 'Skipped the destroy terraform step' 223 | return 224 | } 225 | terraform(goal: 'terraform-destroy', returnStatus: true) 226 | } 227 | 228 | def terraform(Map args = [:]) { 229 | withCloudEnv() { 230 | withAzEnv() { 231 | sh(label: "Run ${args.goal}", script: "make -C .ci ${args.goal}", returnStatus: args.get('returnStatus', false)) 232 | archiveArtifacts allowEmptyArchive: true, artifacts: 'test/terraform/debug/*.log' 233 | } 234 | } 235 | } 236 | 237 | def withVaultEnv(Closure body){ 238 | getVaultSecret.readSecretWrapper { 239 | withMatrixEnv() { 240 | withEnvMask(vars: [ 241 | [var: 'VAULT_ADDR', password: env.VAULT_ADDR], 242 | [var: 'VAULT_ROLE_ID', password: env.VAULT_ROLE_ID], 243 | [var: 'VAULT_SECRET_ID', password: env.VAULT_SECRET_ID], 244 | [var: 'VAULT_AUTH_METHOD', password: 'approle'], 245 | [var: 'VAULT_AUTHTYPE', password: 'approle'] 246 | ]){ 247 | body() 248 | } 249 | } 250 | } 251 | } 252 | 253 | def withValidationEnv(Closure body) { 254 | withMatrixEnv() { 255 | withClusterEnv(cluster: env.CLUSTER_NAME) { 256 | body() 257 | } 258 | } 259 | } 260 | 261 | def withCloudEnv(Closure body) { 262 | withMatrixEnv() { 263 | withCloudEnv(cluster: env.CLUSTER_NAME) { 264 | // withCloudEnv creates different env variables, let's create the 265 | // ones needed for the terraform runs 266 | withEnvMask(vars: [ 267 | [var: 'TF_VAR_username', password: env.CLOUD_USERNAME], 268 | [var: 'TF_VAR_password', password: env.CLOUD_PASSWORD], 269 | [var: 'TF_VAR_cloudId', password: env.CLOUD_ID] 270 | ]){ 271 | body() 272 | } 273 | } 274 | } 275 | } 276 | 277 | def withAzEnv(Closure body) { 278 | withMatrixEnv() { 279 | withAzureEnv(secret: 'secret/observability-team/ci/service-account/azure-vm-extension') { 280 | body() 281 | } 282 | } 283 | } 284 | 285 | def withMatrixEnv(Closure body) { 286 | def stackVersion = (env.STACK_VERSION == '7.x') ? artifactsApi(action: '7.x-version') : env.STACK_VERSION 287 | def clusterName = "tst-az-${BUILD_ID}-${BRANCH_NAME}-${stackVersion}-${env.OS_VERSION}" 288 | def vmName = getCachedVmNameOrAssignVmName(clusterName) 289 | def vms = readYaml(file: ".ci/virtual-machines.yml") 290 | def os = vms.find { it.name == env.OS_VERSION } 291 | withEnv([ 292 | "CLUSTER_NAME=${clusterName}", 293 | 'TF_VAR_prefix=tst-' + vmName.take(6), 294 | "TF_VAR_vmName=${vmName}", 295 | "TF_VAR_isWindows=${isWindows()}", 296 | "TF_VAR_sku=${os.sku}", 297 | "TF_VAR_publisher=${os.publisher}", 298 | "TF_VAR_offer=${os.offer}", 299 | "VM_NAME=${vmName}", 300 | "ELASTIC_STACK_VERSION=${stackVersion}" 301 | ]) { 302 | echo "CLUSTER_NAME=${CLUSTER_NAME} - VM_NAME=${VM_NAME} - TF_VAR_prefix=${TF_VAR_prefix} - TF_VAR_isWindows=${TF_VAR_isWindows}" 303 | echo "TF_VAR_sku=${TF_VAR_sku} - TF_VAR_publisher=${TF_VAR_publisher} - TF_VAR_offer=${TF_VAR_offer}" 304 | body() 305 | } 306 | } 307 | 308 | def getCachedVmNameOrAssignVmName(String key) { 309 | if (vms.containsKey(key)) { 310 | return vms.get(key) 311 | } else { 312 | def vmName = randomString(size: 15) 313 | vms[key] = vmName 314 | return vmName 315 | } 316 | } 317 | 318 | def isWindows() { 319 | return env.OS_VERSION?.toLowerCase()?.contains('windows') 320 | } 321 | -------------------------------------------------------------------------------- /src/handler/windows/scripts/enable.ps1: -------------------------------------------------------------------------------- 1 | $ScriptDirectory = Split-Path $MyInvocation.MyCommand.Path 2 | . (Join-Path $ScriptDirectory log.ps1) 3 | $ScriptDirectory = Split-Path $MyInvocation.MyCommand.Path 4 | . (Join-Path $ScriptDirectory helper.ps1) 5 | $ScriptDirectory = Split-Path $MyInvocation.MyCommand.Path 6 | . (Join-Path $ScriptDirectory newconfig.ps1) 7 | 8 | # enable script will be run at enable time, will download artifacts, install elastic agent, enroll it to Fleet. Also, handles update configuration only. 9 | 10 | # var enabling status 11 | $nameE = "Enable elastic agent" 12 | $operationE = "starting elastic agent" 13 | $messageE = "Enable elastic agent" 14 | 15 | # var for install status 16 | $name = "Install elastic agent" 17 | $firstOperation = "installing elastic agent" 18 | $secondOperation = "enrolling elastic agent" 19 | $message = "Install elastic agent" 20 | $subName = "Elastic Agent" 21 | 22 | $serviceName = 'elastic agent' 23 | $policyName = 'Azure VM extension policy' 24 | 25 | # Install-ElasticAgent function gets es version, downloads correspondent es agent, enrolls it and starts the agent 26 | function Install-ElasticAgent { 27 | $installLocation ="C:\Program Files" 28 | $retries = 3 29 | $retryCount = 0 30 | $completed = $false 31 | $enrollmenToken= "" 32 | while (-not $completed) { 33 | Try { 34 | $stackVersion = Get-Stack-Version 35 | if ( $stackVersion -eq "" ) { 36 | throw "Elastic stack version could not be found" 37 | } 38 | $installationName = "elastic-agent-${stackVersion}-windows-x86_64" 39 | $package="${installationName}.zip" 40 | $savedFile="$env:temp\" + $package 41 | Write-Log "Starting download of elastic agent package with version $stackVersion" "INFO" 42 | DownloadFile -Params @{'Package'="$package";'OutFile'="$savedFile"} 43 | # write wm extension status 44 | Write-Status "$name" "$firstOperation" "transitioning" "$message" "$subName" "success" "Elastic Agent package has been downloaded" 45 | Write-Log "Unzip elastic agent archive" "INFO" 46 | if ( $powershellVersion -le 4 ) { 47 | if ("$installLocation\$installationName") { 48 | Remove-Item "$installLocation\$installationName" -Recurse -Force 49 | } 50 | Add-Type -Assembly "System.IO.Compression.Filesystem" 51 | [System.IO.Compression.ZipFile]::ExtractToDirectory($savedFile,$installLocation) 52 | }else { 53 | Expand-Archive -LiteralPath $savedFile -DestinationPath $installLocation -Force 54 | } 55 | Write-Log "Elastic agent unzipped location $installLocation" "INFO" 56 | Write-Log "Rename folder ..." 57 | Rename-Item -Path "$installLocation\$installationName" -NewName "Elastic-Agent" -Force 58 | Write-Log "Folder $installationName renamed to 'Agent'" 59 | Write-Log "Start retrieving KIBANA_URL" "INFO" 60 | $powershellVersion = Get-PowershellVersion 61 | $kibanaUrl = Get-Kibana-URL $powershellVersion 62 | if (-Not $kibanaUrl) { 63 | throw "Kibana url could not be found" 64 | } 65 | $password = Get-Password $powershellVersion 66 | $base64Auth = Get-Base64Auth $powershellVersion 67 | if (-Not $password -And -Not $base64Auth) { 68 | throw "Password or base64auto key could not be found" 69 | } 70 | Write-Log "Found Kibana url $kibanaUrl" "INFO" 71 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 72 | $headers.Add("kbn-xsrf", "true") 73 | $encodedCredentials = "" 74 | if ($password) { 75 | $username = Get-Username $powershellVersion 76 | if (-Not $username) { 77 | throw "Username could not be found" 78 | } 79 | $pair = "$($username):$($password)" 80 | $encodedCredentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair)) 81 | } else { 82 | $encodedCredentials = $base64Auth 83 | } 84 | $headers.Add('Authorization', "Basic $encodedCredentials") 85 | if ( $powershellVersion -gt 3 ) { 86 | $headers.Add("Accept","application/json") 87 | } 88 | #enable Fleet 89 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 90 | $jsonResult = Invoke-WebRequest -Uri "$($kibanaUrl)/api/fleet/setup" -Method 'POST' -Headers $headers -UseBasicParsing 91 | if ($jsonResult.statuscode -eq '200') { 92 | Write-Log "Enable Fleet is now available $jsonResult" "INFO" 93 | if (!(HasFleetServer("$stackVersion"))) { 94 | $jsonResult = Invoke-WebRequest -Uri "$($kibanaUrl)/api/fleet/agents/setup" -Method 'POST' -Headers $headers -UseBasicParsing 95 | if ($jsonResult.statuscode -eq '200') { 96 | Write-Log "Enable Fleet agents if now available $jsonResult" "INFO" 97 | }else { 98 | throw "Enabling Fleet Agents failed with $jsonResult.statuscode" 99 | } 100 | } 101 | } 102 | else { 103 | throw "Enabling Fleet failed with $jsonResult.statuscode" 104 | } 105 | # end enable Fleet 106 | $jsonResult = Invoke-WebRequest -Uri "$($kibanaUrl)/api/fleet/agent_policies" -Method 'GET' -Headers $headers -UseBasicParsing 107 | if ($jsonResult.statuscode -eq '200') { 108 | $keyValue= ConvertFrom-Json $jsonResult.Content | Select-Object -expand "items" 109 | $azurePolicy = Get-Azure-Policy $keyValue 110 | if (-Not $azurePolicy) { 111 | Write-Log "No active Azure VM extension policy has been found, create a VM extension policy instead" "WARN" 112 | $azurePolicy = Create-Azure-Policy $keyValue 113 | } 114 | if (-Not $azurePolicy) { 115 | #query for all agent policies again 116 | $jsonResult = Invoke-WebRequest -Uri "$($kibanaUrl)/api/fleet/agent_policies" -Method 'GET' -Headers $headers -UseBasicParsing 117 | if ($jsonResult.statuscode -eq '200') { 118 | $keyValue= ConvertFrom-Json $jsonResult.Content | Select-Object -expand "items" 119 | $azurePolicy = Get-AnyActive-Policy $keyValue 120 | } else { 121 | throw "Retrieving the agent policies has failed, api request returned status $jsonResult.statuscode" 122 | } 123 | } 124 | if (-Not $azurePolicy) { 125 | throw "No active policies were found. Please create a policy in Kibana Fleet" 126 | } 127 | Write-Log "Found policy id $azurePolicy" "INFO" 128 | $jsonResult = Invoke-WebRequest -Uri "$($kibanaUrl)/api/fleet/enrollment-api-keys" -Method 'GET' -Headers $headers -UseBasicParsing 129 | if ($jsonResult.statuscode -eq '200') { 130 | $enrollment= ConvertFrom-Json $jsonResult.Content | Select-Object -expand "list" 131 | foreach ($policy in $enrollment) { 132 | if ($policy.active -eq "true" -And $policy.policy_id -eq $azurePolicy) { 133 | $policyID = $policy.id 134 | break 135 | } 136 | } 137 | } else { 138 | throw "Retrieving enrollment api keys has failed, api request returned status $jsonResult.statuscode" 139 | } 140 | $jsonResult = Invoke-WebRequest -Uri "$($kibanaUrl)/api/fleet/enrollment-api-keys/$($policyID)" -Method 'GET' -Headers $headers -UseBasicParsing 141 | if ($jsonResult.statuscode -eq '200') { 142 | $keyValue= ConvertFrom-Json $jsonResult.Content | Select-Object -expand "item" 143 | $enrollmenToken=$keyValue.api_key 144 | Write-Log "Found enrollment_token $enrollmenToken" "INFO" 145 | if (HasFleetServer("$stackVersion")) { 146 | Write-Log "Getting FLeet Serverl URL" "INFO" 147 | $jsonResultSettings = Invoke-WebRequest -Uri "$($kibanaUrl)/api/fleet/settings" -Method 'GET' -Headers $headers -UseBasicParsing 148 | if ($jsonResultSettings.statuscode -eq '200') 149 | { 150 | $keyValue = ConvertFrom-Json $jsonResultSettings.Content | Select-Object -expand "item" 151 | $fleetServer = $keyValue.fleet_server_hosts[0] 152 | } else { 153 | throw "Retrieving Fleet Server URL has failed, please check if it has been enabled." 154 | } 155 | Write-Log "Installing Elastic Agent and enrolling to Fleet Server $fleetServer" "INFO" 156 | & "$installLocation\Elastic-Agent\elastic-agent.exe" install -f --url=$fleetServer --enrollment-token=$enrollmenToken 157 | } 158 | else { 159 | Write-Log "Installing Elastic Agent and enrolling to Fleet $kibanaUrl" "INFO" 160 | & "$installLocation\Elastic-Agent\elastic-agent.exe" install -f --kibana-url=$kibanaUrl --enrollment-token=$enrollmenToken 161 | } 162 | Write-Log "Elastic Agent has been enrolled" "INFO" 163 | }else { 164 | throw "Retrieving the enrollment tokens has failed, api request returned status $jsonResult.statuscode" 165 | } 166 | } else { 167 | throw "Retrieving the agent policies has failed, api request returned status $jsonResult.statuscode" 168 | } 169 | Write-Log "Setting Env Variable for sequence" "INFO" 170 | Set-SequenceEnvVariables 171 | $completed = $true 172 | # write status for both install and enroll 173 | Write-Status "$name" "$firstOperation" "success" "$message" "$subName" "success" "Elastic Agent has been installed" 174 | Write-Status "$name" "$secondOperation" "success" "$message" "$subName" "success" "Elastic Agent has been enrolled" 175 | } 176 | Catch { 177 | if ($retryCount -ge $retries) { 178 | Write-Log "Elastic Agent installation failed after 3 retries" "ERROR" 179 | Write-Log $_ "ERROR" 180 | Write-Log $_.ScriptStackTrace "ERROR" 181 | # write status for fail 182 | Write-Status "$name" "$firstOperation" "error" "$message" "$subName" "error" "Elastic Agent has not been installed" 183 | Clean-And-Exit 1 184 | } else { 185 | Write-Log "Elastic Agent installation failed. retrying in 20s" "ERROR" 186 | Write-Log $_ "ERROR" 187 | Write-Log $_.ScriptStackTrace "ERROR" 188 | sleep 20 189 | $retryCount++ 190 | } 191 | } 192 | } 193 | } 194 | 195 | # Enable-ElasticAgent function starts the es agent and logs status 196 | function Enable-ElasticAgent { 197 | $retries = 3 198 | $retryCount = 0 199 | $completed = $false 200 | while (-not $completed) { 201 | Try { 202 | Write-Log "Starting the elastic agent" "INFO" 203 | Start-Service "$serviceName" 204 | Write-Log "The elastic agent is started" "INFO" 205 | $completed = $true 206 | Write-Status "$nameE" "$operationE" "success" "$messageE" "$subName" "success" "Elastic Agent service has started" 207 | } 208 | Catch { 209 | if ($retryCount -ge $retries) { 210 | Write-Log "Starting the Elastic Agent failed after 3 retries" "ERROR" 211 | Write-Log $_ "ERROR" 212 | Write-Log $_.ScriptStackTrace "ERROR" 213 | Write-Status "$nameE" "$operationE" "error" "$messageE" "$subName" "error" "Elastic Agent service has not started" 214 | Clean-And-Exit 1 215 | } else { 216 | Write-Log "Starting the Elastic Agent has failed. retrying in 20s" "ERROR" 217 | Write-Log $_ "ERROR" 218 | Write-Log $_.ScriptStackTrace "ERROR" 219 | sleep 20 220 | $retryCount++ 221 | } 222 | } 223 | } 224 | } 225 | 226 | # Reconfigure-ElasticAgent function uninstalls the current es agent and reinstall a new elastic agent based on the new configuration settings 227 | function Reconfigure-ElasticAgent { 228 | $retries = 3 229 | $retryCount = 0 230 | $completed = $false 231 | while (-not $completed) { 232 | Try { 233 | Write-Log "Stopping Elastic Agent" "INFO" 234 | Stop-Service "elastic agent" 235 | Write-Log "Elastic Agent has been stopped" "INFO" 236 | Uninstall-Old-ElasticAgent 237 | Install-ElasticAgent 238 | $completed = $true 239 | Write-Status "$name" "$operationE" "success" "$message" "$subName" "success" "Elastic Agent has been reconfigured and reinstalled" 240 | } 241 | Catch { 242 | if ($retryCount -ge $retries) { 243 | Write-Log "Starting the Elastic Agent failed after 3 retries" "ERROR" 244 | Write-Log $_ "ERROR" 245 | Write-Log $_.ScriptStackTrace "ERROR" 246 | Write-Status "$nameE" "$operationE" "error" "$messageE" "$subName" "error" "Elastic Agent service has not been reconfigured" 247 | Clean-And-Exit 1 248 | } else { 249 | Write-Log "Starting the Elastic Agent has failed. retrying in 20s" "ERROR" 250 | Write-Log $_ "ERROR" 251 | Write-Log $_.ScriptStackTrace "ERROR" 252 | sleep 20 253 | $retryCount++ 254 | } 255 | } 256 | } 257 | } 258 | 259 | # check if es agent service exists, check if new configuration was entered else install es agent 260 | If (Get-Service $serviceName -ErrorAction SilentlyContinue) { 261 | If (Is-New-Config) { 262 | Write-Log "New configuration file has been added. The elastic agent will reinstall" "INFO" 263 | Reconfigure-ElasticAgent 264 | } 265 | If ((Get-Service $serviceName).Status -eq 'Running') { 266 | Write-Log "Elastic Agent service is running" "INFO" 267 | Write-Status "$nameE" "$operationE" "success" "$messageE" "$subName" "success" "Elastic Agent service is running" 268 | } Else { 269 | Enable-ElasticAgent 270 | } 271 | } Else { 272 | Install-ElasticAgent 273 | Enable-ElasticAgent 274 | } 275 | 276 | Clean-And-Exit 0 277 | -------------------------------------------------------------------------------- /src/handler/linux/enable.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | script_path=$(dirname $(readlink -f "$0")) 4 | source $script_path/helper.sh 5 | source $script_path/newconfig.sh 6 | 7 | # enable script will be run at enable time, will download artifacts, install elastic agent, enroll it to Fleet. Also, handles update configuration only. 8 | 9 | # var for install status 10 | name="Install elastic agent" 11 | first_operation="installing elastic agent" 12 | second_operation="enrolling elastic agent" 13 | message="Install elastic agent" 14 | sub_name="Elastic Agent" 15 | service_name="elastic-agent" 16 | 17 | # var enabling status 18 | name_en="Enable elastic agent" 19 | operation_en="starting elastic agent" 20 | message_en="Enable elastic agent" 21 | 22 | # Install_ElasticAgent_DEB_RPM function will download and install the elastic agent on Debian and RPM os's 23 | Install_ElasticAgent_DEB_RPM() 24 | { 25 | local algorithm="sha512" 26 | get_cloud_stack_version 27 | if [ $STACK_VERSION = "" ]; then 28 | log "ERROR" "[Install_ElasticAgent_DEB_RPM] Stack version could not be found" 29 | return 1 30 | else 31 | log "INFO" "[Install_ElasticAgent_DEB_RPM] installing Elastic Agent $STACK_VERSION" 32 | if [ "$DISTRO_OS" = "DEB" ]; then 33 | package="elastic-agent-${STACK_VERSION}-amd64.deb" 34 | elif [ "$DISTRO_OS" = "RPM" ]; then 35 | package="elastic-agent-${STACK_VERSION}-x86_64.rpm" 36 | fi 37 | local shasum="$package.$algorithm" 38 | local release_url="https://artifacts.elastic.co/downloads/beats/elastic-agent/" 39 | local staging_url="https://artifacts-api.elastic.co/v1/downloads/beats/" 40 | if [[ $(wget -S --spider "${release_url}${package}" 2>&1 | grep 'HTTP/1.1 200 OK') ]] ; then 41 | log "[Install_ElasticAgent_DEB_RPM] download location - ${release_url}${package}" "INFO" 42 | wget --retry-connrefused --waitretry=1 "${release_url}${package}" -O $package 43 | EXIT_CODE=$? 44 | if [[ $EXIT_CODE -ne 0 ]]; then 45 | log "ERROR" "[Install_ElasticAgent_DEB_RPM] error downloading Elastic Agent $STACK_VERSION" 46 | return $EXIT_CODE 47 | fi 48 | log "INFO" "[Install_ElasticAgent_DEB_RPM] downloaded Elastic Agent $STACK_VERSION" 49 | wget --retry-connrefused --waitretry=1 "${release_url}${package}.${algorithm}" -O "$shasum" 50 | local EXIT_CODE=$? 51 | if [[ $EXIT_CODE -ne 0 ]]; then 52 | log "ERROR" "[Install_ElasticAgent_DEB_RPM] error downloading Elastic Agent $STACK_VERSION $algorithm checksum" 53 | return $EXIT_CODE 54 | fi 55 | #checkShasum $package $shasum 56 | #EXIT_CODE=$? 57 | #if [[ $EXIT_CODE -ne 0 ]]; then 58 | # log "ERROR" "[Install_ElasticAgent_DEB_RPM] error validating checksum for Elastic Agent $STACK_VERSION" 59 | # return $EXIT_CODE 60 | #fi 61 | else 62 | log "[Install_ElasticAgent_DEB_RPM] download location - $staging_url" "INFO" 63 | wget --retry-connrefused --waitretry=1 "${staging_url}${package}" -O $package 64 | EXIT_CODE=$? 65 | if [[ $EXIT_CODE -ne 0 ]]; then 66 | log "ERROR" "[Install_ElasticAgent_DEB_RPM] error downloading Elastic Agent $STACK_VERSION" 67 | return $EXIT_CODE 68 | fi 69 | #no shasum version of the package 70 | fi 71 | write_status "$name" "$first_operation" "transitioning" "$message" "$sub_name" "success" "Elastic Agent package has been downloaded" 72 | if [ "$DISTRO_OS" = "DEB" ]; then 73 | sudo dpkg -i $package 74 | sudo apt-get install -f 75 | elif [ "$DISTRO_OS" = "RPM" ]; then 76 | sudo rpm -vi $package 77 | fi 78 | log "INFO" "[Install_ElasticAgent_DEB_RPM] installed Elastic Agent $STACK_VERSION" 79 | write_status "$name" "$first_operation" "success" "$message" "$sub_name" "success" "Elastic Agent has been installed" 80 | fi 81 | } 82 | 83 | # Install_ElasticAgent_OTHER will download and install the elastic agent in other os's 84 | Install_ElasticAgent_OTHER() 85 | { 86 | local os_suffix="-linux-x86_64" 87 | local package="elastic-agent-${STACK_VERSION}${os_suffix}.tar.gz" 88 | local algorithm="512" 89 | local shasum="$package.sha$algorithm" 90 | local download_url="https://artifacts.elastic.co/downloads/beats/elastic-agent/${package}" 91 | local shasum_url="https://artifacts.elastic.co/downloads/beats/elastic-agent/${package}.sha512" 92 | log "INFO" "[Install_ElasticAgent_OTHER] installing Elastic Agent $STACK_VERSION" 93 | wget --retry-connrefused --waitretry=1 "$shasum_url" -O "$shasum" 94 | local EXIT_CODE=$? 95 | if [[ $EXIT_CODE -ne 0 ]]; then 96 | log "ERROR" "[Install_ElasticAgent_OTHER] error downloading Elastic Agent $STACK_VERSION sha$algorithm checksum" 97 | return $EXIT_CODE 98 | fi 99 | log "INFO" "[Install_ElasticAgent_OTHER] download location - $download_url" 100 | wget --retry-connrefused --waitretry=1 "$download_url" -O $package 101 | EXIT_CODE=$? 102 | if [[ $EXIT_CODE -ne 0 ]]; then 103 | log "ERROR" "[Install_ElasticAgent_OTHER] error downloading Elastic Agent $STACK_VERSION" 104 | return $EXIT_CODE 105 | fi 106 | log "INFO" "[Install_ElasticAgent_OTHER] downloaded Elastic Agent $STACK_VERSION" 107 | #checkShasum $package $shasum 108 | EXIT_CODE=$? 109 | if [[ $EXIT_CODE -ne 0 ]]; then 110 | log "ERROR" "[Install_ElasticAgent_OTHER] error validating checksum for Elastic Agent $STACK_VERSION" 111 | return $EXIT_CODE 112 | fi 113 | tar xzvf $package 114 | log "INFO" "[Install_ElasticAgent_OTHER] installed Elastic Agent $STACK_VERSION" 115 | } 116 | 117 | # Enroll_ElasticAgent enrolls the elastic agent to Fleet 118 | Enroll_ElasticAgent() { 119 | get_kibana_host 120 | if [[ "$KIBANA_URL" = "" ]]; then 121 | log "ERROR" "[Enroll_ElasticAgent] Kibana URL could not be found/parsed" 122 | return 1 123 | fi 124 | get_password 125 | get_base64Auth 126 | if [ "$PASSWORD" = "" ] && [ "$BASE64_AUTH" = "" ]; then 127 | log "ERROR" "[Enroll_ElasticAgent] Password could not be found/parsed" 128 | return 1 129 | fi 130 | local cred="" 131 | if [[ "$PASSWORD" != "" ]] && [[ "$PASSWORD" != "null" ]]; then 132 | get_username 133 | if [[ "$USERNAME" = "" ]]; then 134 | log "ERROR" "[Enroll_ElasticAgent] Username could not be found/parsed" 135 | return 1 136 | fi 137 | cred=${USERNAME}:${PASSWORD} 138 | else 139 | cred=$(echo "$BASE64_AUTH" | base64 --decode) 140 | fi 141 | if [[ $STACK_VERSION = "" ]]; then 142 | get_cloud_stack_version 143 | fi 144 | #enable Fleet 145 | has_fleet_server $STACK_VERSION 146 | result=$(curl -X POST "${KIBANA_URL}"/api/fleet/setup -H 'Content-Type: application/json' -H 'kbn-xsrf: true' -u "$cred" ) 147 | local EXITCODE=$? 148 | if [ $EXITCODE -ne 0 ]; then 149 | log "ERROR" "[Enroll_ElasticAgent] error calling $KIBANA_URL/api/fleet/setup in order to enable Kibana Fleet $result" 150 | return $EXITCODE 151 | fi 152 | if [[ $IS_FLEET_SERVER = false ]]; then 153 | result=$(curl -X POST "${KIBANA_URL}"/api/fleet/agents/setup -H 'Content-Type: application/json' -H 'kbn-xsrf: true' -u "$cred" ) 154 | local EXITCODE=$? 155 | if [ $EXITCODE -ne 0 ]; then 156 | log "ERROR" "[Enroll_ElasticAgent] error calling $KIBANA_URL/api/fleet/setup in order to enable Kibana Fleet Agents $result" 157 | return $EXITCODE 158 | fi 159 | fi 160 | #end enable Fleet 161 | #query for all agent policies 162 | jsonResult=$(curl -X GET "${KIBANA_URL}"/api/fleet/agent_policies -H 'Content-Type: application/json' -H 'kbn-xsrf: true' -u "$cred" ) 163 | local EXITCODE=$? 164 | if [ $EXITCODE -ne 0 ]; then 165 | log "ERROR" "[Enroll_ElasticAgent] error calling $KIBANA_URL/api/fleet/agent_policies in order to retrieve the agent_policies" 166 | return $EXITCODE 167 | fi 168 | 169 | get_azure_policy "\${jsonResult}" 170 | if [[ "$POLICY_ID" = "" ]]; then 171 | log "INFO" "[Enroll_ElasticAgent] Azure VM extension policy could not be found or is not active. Will create a VM extension policy instead" 172 | create_azure_policy 173 | fi 174 | 175 | if [[ "$POLICY_ID" = "" ]]; then 176 | log "ERROR" "[Enroll_ElasticAgent] Failed creating Azure VM extension policy. Please manually create one in Kibana Fleet instead" 177 | return 1 178 | fi 179 | 180 | log "INFO" "[Enroll_ElasticAgent] policy selected is $POLICY_ID" 181 | # get POLICY_ID for /api/fleet/enrollment-api-keys 182 | jsonResult=$(curl ${KIBANA_URL}/api/fleet/enrollment-api-keys -H 'Content-Type: application/json' -H 'kbn-xsrf: true' -u "$cred" ) 183 | EXITCODE=$? 184 | if [ $EXITCODE -ne 0 ]; then 185 | log "ERROR" "[Enroll_ElasticAgent] error calling $KIBANA_URL/api/fleet/enrollment-api-keys in order to list all enrollment_token" 186 | return $EXITCODE 187 | fi 188 | list=$(echo "$jsonResult" | jq -r '.list') 189 | for row in $(echo "${list}" | jq -r '.[] | @base64'); do 190 | _jq() { 191 | echo ${row} | base64 --decode | jq -r ${1} 192 | } 193 | is_active=$(_jq '.active') 194 | policy_id=$(_jq '.policy_id') 195 | if [[ "$is_active" = "true" ]] && [[ "$policy_id" = "$POLICY_ID" ]]; then 196 | POLICY_ID=$(_jq '.id') 197 | break 198 | fi 199 | done 200 | 201 | jsonResult=$(curl ${KIBANA_URL}/api/fleet/enrollment-api-keys/$POLICY_ID \ 202 | -H 'Content-Type: application/json' \ 203 | -H 'kbn-xsrf: true' \ 204 | -u "$cred" ) 205 | EXITCODE=$? 206 | if [ $EXITCODE -ne 0 ]; then 207 | log "ERROR" "[Enroll_ElasticAgent] error calling $KIBANA_URL/api/fleet/enrollment-api-keys in order to retrieve the enrollment_token" 208 | return $EXITCODE 209 | fi 210 | enrollment_token=$(echo $jsonResult | jq -r '.item.api_key') 211 | if [[ "$enrollment_token" = "" ]]; then 212 | log "ERROR" "[Enroll_ElasticAgent] enrollment_token could not be found/parsed" 213 | return 1 214 | fi 215 | log "INFO" "[Enroll_ElasticAgent] enrollment_token is $enrollment_token" 216 | log "INFO" "[Enroll_ElasticAgent] Enrolling the Elastic Agent to Fleet ${KIBANA_URL}" 217 | has_flag_version $STACK_VERSION 218 | if [[ $IS_FLEET_SERVER = true ]]; then 219 | log "INFO" "[Enroll_ElasticAgent] Getting Fleet Server info" 220 | jsonResult=$(curl ${KIBANA_URL}/api/fleet/settings \ 221 | -H 'Content-Type: application/json' \ 222 | -H 'kbn-xsrf: true' \ 223 | -u "$cred" ) 224 | EXITCODE=$? 225 | if [ $EXITCODE -ne 0 ]; then 226 | log "ERROR" "[Enroll_ElasticAgent] error calling $KIBANA_URL/api/fleet/settings in order to retrieve the Fleet Server URL" 227 | return $EXITCODE 228 | fi 229 | fleet_server=$(echo $jsonResult | jq -r '.item.fleet_server_hosts[0]') 230 | log "INFO" "[Enroll_ElasticAgent] Found fleet server $fleet_server" 231 | sudo elastic-agent enroll --url="${fleet_server}" --enrollment-token="$enrollment_token" -f 232 | elif [[ $HAS_FLAG_VERSION = true ]]; then 233 | sudo elastic-agent enroll --kibana-url="${KIBANA_URL}" --enrollment-token="$enrollment_token" -f 234 | else 235 | sudo elastic-agent enroll "${KIBANA_URL}" "$enrollment_token" -f 236 | fi 237 | write_status "$name" "$second_operation" "success" "$message" "$sub_name" "success" "Elastic Agent has been enrolled" 238 | set_sequence_to_file 239 | } 240 | 241 | #Install_ElasticAgent checks os distro and cllas the install, enroll functions 242 | Install_ElasticAgent() { 243 | if [ "$DISTRO_OS" = "DEB" ] || [ "$DISTRO_OS" = "RPM" ]; then 244 | retry_backoff Install_ElasticAgent_DEB_RPM 245 | else 246 | retry_backoff Install_ElasticAgent_OTHER 247 | fi 248 | log "INFO" "[Install_ElasticAgent] enrolling Elastic Agent $STACK_VERSION" 249 | retry_backoff Enroll_ElasticAgent 250 | log "INFO" "[Install_ElasticAgent] Elastic Agent $STACK_VERSION enrolled" 251 | retry_backoff Start_ElasticAgent 252 | } 253 | 254 | # Start_ElasticAgent starts the elastic agent based on systemd availability 255 | Start_ElasticAgent() 256 | { 257 | if [[ $(systemctl) =~ -\.mount ]]; then 258 | log "INFO" "[Start_ElasticAgent] enabling and starting Elastic Agent" 259 | sudo systemctl enable elastic-agent 260 | sudo systemctl start elastic-agent 261 | log "INFO" "[Start_ElasticAgent] Elastic Agent started" 262 | write_status "$name_en" "$operation_en" "success" "$message_en" "$sub_name" "success" "Elastic Agent service has started" 263 | else 264 | log "INFO" "[Start_ElasticAgent] starting Elastic Agent" 265 | sudo service elastic-agent start 266 | log "INFO" "[Start_ElasticAgent] Elastic Agent started" 267 | write_status "$name_en" "$operation_en" "success" "$message_en" "$sub_name" "success" "Elastic Agent service has started" 268 | fi 269 | } 270 | 271 | # Run_Agent_Other checks os is other and then installs elastic agent 272 | Run_Agent_Other() { 273 | log "INFO" "[Run_Agent_Other] prepare elastic agent for install/enable for other Linux os" 274 | if sudo service --status-all | grep -Fq "$service_name"; then 275 | log "INFO" "[Run_Agent_Other] start Elastic Agent" 276 | retry_backoff Start_ElasticAgent 277 | else 278 | log "INFO" "[Run_Agent_Other] install Elastic Agent" 279 | Install_ElasticAgent 280 | fi 281 | } 282 | 283 | # Reconfigure_Elastic_agent_DEB_RPM will reinstall elastic agent based on new configuration options entered 284 | Reconfigure_Elastic_agent_DEB_RPM() { 285 | log "INFO" "[Reconfigure_Elastic_agent_DEB_RPM] Stopping Elastic Agent" 286 | if [[ $(systemctl) =~ -\.mount ]]; then 287 | sudo systemctl stop elastic-agent 288 | else 289 | sudo service elastic-agent stop 290 | fi 291 | log "INFO" "[Reconfigure_Elastic_agent_DEB_RPM] Elastic Agent stopped" 292 | Uninstall_Old_ElasticAgent 293 | Install_ElasticAgent 294 | write_status "$name_en" "$operation_en" "success" "$message_en" "$sub_name" "success" "Elastic Agent service has started" 295 | } 296 | 297 | #Run_Agent_DEB_RPM will check if es agent service is installed or if is vm extension update, else will start a clean installation 298 | Run_Agent_DEB_RPM() { 299 | log "INFO" "[Start_ElasticAgent] starting Elastic Agent" 300 | log "INFO" "[Run_Agent_DEB_RPM] Prepare elastic agent for DEB/RPM systems" 301 | if [[ $(systemctl) =~ -\.mount ]]; then 302 | log "INFO" "[Run_Agent_DEB_RPM] Systemd detected" 303 | if [[ $(systemctl list-units --all -t service --full --no-legend "$service_name.service" | cut -f1 -d' ') == $service_name.service ]] && [[ $(systemctl list-units --all -t service --full --no-legend "$service_name.service" | cut -f2 -d' ') != "not-found" ]]; then 304 | service_status="$(sudo systemctl is-active --quiet elastic-agent && echo Running || echo Stopped)" 305 | is_new_config 306 | if [[ $IS_NEW_CONFIG = true ]]; then 307 | log "INFO" "[Run_Agent_DEB_RPM] New configuration has been added, the elastic agent will be reinstalled" 308 | retry_backoff Reconfigure_Elastic_agent_DEB_RPM 309 | fi 310 | if [[ "$service_status" = "Running" ]]; then 311 | log "INFO" "[Run_Agent_DEB_RPM] Elastic Agent is running" 312 | else 313 | log "INFO" "[Run_Agent_DEB_RPM] Elastic Agent is not running" 314 | retry_backoff Start_ElasticAgent 315 | fi 316 | else 317 | log "INFO" "[Run_Agent_DEB_RPM] Elastic Agent is not installed" 318 | Install_ElasticAgent 319 | fi 320 | else 321 | log "INFO" "[Run_Agent_DEB_RPM] No Systemd detected" 322 | if sudo service --status-all | grep -q "elastic-agent" ;then 323 | is_new_config 324 | if [[ $IS_NEW_CONFIG = true ]]; then 325 | log "INFO" "[Run_Agent_DEB_RPM] New configuration has been added, the elastic agent will be reinstalled" 326 | retry_backoff Reconfigure_Elastic_agent_DEB_RPM 327 | fi 328 | status=$(sudo service "elastic-agent" status || true) 329 | if [[ $status == *"running"* ]]; then 330 | log "INFO" "[Run_Agent_DEB_RPM] Elastic Agent is running" 331 | else 332 | log "INFO" "[Run_Agent_DEB_RPM] Elastic Agent is not running" 333 | retry_backoff Start_ElasticAgent 334 | fi 335 | else 336 | log "INFO" "[Run_Agent_DEB_RPM] Elastic Agent is not installed" 337 | Install_ElasticAgent 338 | fi 339 | fi 340 | } 341 | 342 | # Run_Agent checks distro and calls corespondent functions 343 | Run_Agent() 344 | { 345 | if [ "$DISTRO_OS" = "DEB" ] || [ "$DISTRO_OS" = "RPM" ]; then 346 | Run_Agent_DEB_RPM 347 | else 348 | Run_Agent_Other 349 | fi 350 | 351 | clean_and_exit 0 352 | } 353 | 354 | Run_Agent 355 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | ELASTIC LICENSE AGREEMENT 2 | 3 | PLEASE READ CAREFULLY THIS ELASTIC LICENSE AGREEMENT (THIS "AGREEMENT"), WHICH 4 | CONSTITUTES A LEGALLY BINDING AGREEMENT AND GOVERNS ALL OF YOUR USE OF ALL OF 5 | THE ELASTIC SOFTWARE WITH WHICH THIS AGREEMENT IS INCLUDED ("ELASTIC SOFTWARE") 6 | THAT IS PROVIDED IN OBJECT CODE FORMAT, AND, IN ACCORDANCE WITH SECTION 2 BELOW, 7 | CERTAIN OF THE ELASTIC SOFTWARE THAT IS PROVIDED IN SOURCE CODE FORMAT. BY 8 | INSTALLING OR USING ANY OF THE ELASTIC SOFTWARE GOVERNED BY THIS AGREEMENT, YOU 9 | ARE ASSENTING TO THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE 10 | WITH SUCH TERMS AND CONDITIONS, YOU MAY NOT INSTALL OR USE THE ELASTIC SOFTWARE 11 | GOVERNED BY THIS AGREEMENT. IF YOU ARE INSTALLING OR USING THE SOFTWARE ON 12 | BEHALF OF A LEGAL ENTITY, YOU REPRESENT AND WARRANT THAT YOU HAVE THE ACTUAL 13 | AUTHORITY TO AGREE TO THE TERMS AND CONDITIONS OF THIS AGREEMENT ON BEHALF OF 14 | SUCH ENTITY. 15 | 16 | Posted Date: April 20, 2018 17 | 18 | This Agreement is entered into by and between Elasticsearch BV ("Elastic") and 19 | You, or the legal entity on behalf of whom You are acting (as applicable, 20 | "You"). 21 | 22 | 1. OBJECT CODE END USER LICENSES, RESTRICTIONS AND THIRD PARTY OPEN SOURCE 23 | SOFTWARE 24 | 25 | 1.1 Object Code End User License. Subject to the terms and conditions of 26 | Section 1.2 of this Agreement, Elastic hereby grants to You, AT NO CHARGE and 27 | for so long as you are not in breach of any provision of this Agreement, a 28 | License to the Basic Features and Functions of the Elastic Software. 29 | 30 | 1.2 Reservation of Rights; Restrictions. As between Elastic and You, Elastic 31 | and its licensors own all right, title and interest in and to the Elastic 32 | Software, and except as expressly set forth in Sections 1.1, and 2.1 of this 33 | Agreement, no other license to the Elastic Software is granted to You under 34 | this Agreement, by implication, estoppel or otherwise. You agree not to: (i) 35 | reverse engineer or decompile, decrypt, disassemble or otherwise reduce any 36 | Elastic Software provided to You in Object Code, or any portion thereof, to 37 | Source Code, except and only to the extent any such restriction is prohibited 38 | by applicable law, (ii) except as expressly permitted in this Agreement, 39 | prepare derivative works from, modify, copy or use the Elastic Software Object 40 | Code or the Commercial Software Source Code in any manner; (iii) except as 41 | expressly permitted in Section 1.1 above, transfer, sell, rent, lease, 42 | distribute, sublicense, loan or otherwise transfer, Elastic Software Object 43 | Code, in whole or in part, to any third party; (iv) use Elastic Software 44 | Object Code for providing time-sharing services, any software-as-a-service, 45 | service bureau services or as part of an application services provider or 46 | other service offering (collectively, "SaaS Offering") where obtaining access 47 | to the Elastic Software or the features and functions of the Elastic Software 48 | is a primary reason or substantial motivation for users of the SaaS Offering 49 | to access and/or use the SaaS Offering ("Prohibited SaaS Offering"); (v) 50 | circumvent the limitations on use of Elastic Software provided to You in 51 | Object Code format that are imposed or preserved by any License Key, or (vi) 52 | alter or remove any Marks and Notices in the Elastic Software. If You have any 53 | question as to whether a specific SaaS Offering constitutes a Prohibited SaaS 54 | Offering, or are interested in obtaining Elastic's permission to engage in 55 | commercial or non-commercial distribution of the Elastic Software, please 56 | contact elastic_license@elastic.co. 57 | 58 | 1.3 Third Party Open Source Software. The Commercial Software may contain or 59 | be provided with third party open source libraries, components, utilities and 60 | other open source software (collectively, "Open Source Software"), which Open 61 | Source Software may have applicable license terms as identified on a website 62 | designated by Elastic. Notwithstanding anything to the contrary herein, use of 63 | the Open Source Software shall be subject to the license terms and conditions 64 | applicable to such Open Source Software, to the extent required by the 65 | applicable licensor (which terms shall not restrict the license rights granted 66 | to You hereunder, but may contain additional rights). To the extent any 67 | condition of this Agreement conflicts with any license to the Open Source 68 | Software, the Open Source Software license will govern with respect to such 69 | Open Source Software only. Elastic may also separately provide you with 70 | certain open source software that is licensed by Elastic. Your use of such 71 | Elastic open source software will not be governed by this Agreement, but by 72 | the applicable open source license terms. 73 | 74 | 2. COMMERCIAL SOFTWARE SOURCE CODE 75 | 76 | 2.1 Limited License. Subject to the terms and conditions of Section 2.2 of 77 | this Agreement, Elastic hereby grants to You, AT NO CHARGE and for so long as 78 | you are not in breach of any provision of this Agreement, a limited, 79 | non-exclusive, non-transferable, fully paid up royalty free right and license 80 | to the Commercial Software in Source Code format, without the right to grant 81 | or authorize sublicenses, to prepare Derivative Works of the Commercial 82 | Software, provided You (i) do not hack the licensing mechanism, or otherwise 83 | circumvent the intended limitations on the use of Elastic Software to enable 84 | features other than Basic Features and Functions or those features You are 85 | entitled to as part of a Subscription, and (ii) use the resulting object code 86 | only for reasonable testing purposes. 87 | 88 | 2.2 Restrictions. Nothing in Section 2.1 grants You the right to (i) use the 89 | Commercial Software Source Code other than in accordance with Section 2.1 90 | above, (ii) use a Derivative Work of the Commercial Software outside of a 91 | Non-production Environment, in any production capacity, on a temporary or 92 | permanent basis, or (iii) transfer, sell, rent, lease, distribute, sublicense, 93 | loan or otherwise make available the Commercial Software Source Code, in whole 94 | or in part, to any third party. Notwithstanding the foregoing, You may 95 | maintain a copy of the repository in which the Source Code of the Commercial 96 | Software resides and that copy may be publicly accessible, provided that you 97 | include this Agreement with Your copy of the repository. 98 | 99 | 3. TERMINATION 100 | 101 | 3.1 Termination. This Agreement will automatically terminate, whether or not 102 | You receive notice of such Termination from Elastic, if You breach any of its 103 | provisions. 104 | 105 | 3.2 Post Termination. Upon any termination of this Agreement, for any reason, 106 | You shall promptly cease the use of the Elastic Software in Object Code format 107 | and cease use of the Commercial Software in Source Code format. For the 108 | avoidance of doubt, termination of this Agreement will not affect Your right 109 | to use Elastic Software, in either Object Code or Source Code formats, made 110 | available under the Apache License Version 2.0. 111 | 112 | 3.3 Survival. Sections 1.2, 2.2. 3.3, 4 and 5 shall survive any termination or 113 | expiration of this Agreement. 114 | 115 | 4. DISCLAIMER OF WARRANTIES AND LIMITATION OF LIABILITY 116 | 117 | 4.1 Disclaimer of Warranties. TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE 118 | LAW, THE ELASTIC SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, 119 | AND ELASTIC AND ITS LICENSORS MAKE NO WARRANTIES WHETHER EXPRESSED, IMPLIED OR 120 | STATUTORY REGARDING OR RELATING TO THE ELASTIC SOFTWARE. TO THE MAXIMUM EXTENT 121 | PERMITTED UNDER APPLICABLE LAW, ELASTIC AND ITS LICENSORS SPECIFICALLY 122 | DISCLAIM ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 123 | PURPOSE AND NON-INFRINGEMENT WITH RESPECT TO THE ELASTIC SOFTWARE, AND WITH 124 | RESPECT TO THE USE OF THE FOREGOING. FURTHER, ELASTIC DOES NOT WARRANT RESULTS 125 | OF USE OR THAT THE ELASTIC SOFTWARE WILL BE ERROR FREE OR THAT THE USE OF THE 126 | ELASTIC SOFTWARE WILL BE UNINTERRUPTED. 127 | 128 | 4.2 Limitation of Liability. IN NO EVENT SHALL ELASTIC OR ITS LICENSORS BE 129 | LIABLE TO YOU OR ANY THIRD PARTY FOR ANY DIRECT OR INDIRECT DAMAGES, 130 | INCLUDING, WITHOUT LIMITATION, FOR ANY LOSS OF PROFITS, LOSS OF USE, BUSINESS 131 | INTERRUPTION, LOSS OF DATA, COST OF SUBSTITUTE GOODS OR SERVICES, OR FOR ANY 132 | SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES OF ANY KIND, IN CONNECTION WITH 133 | OR ARISING OUT OF THE USE OR INABILITY TO USE THE ELASTIC SOFTWARE, OR THE 134 | PERFORMANCE OF OR FAILURE TO PERFORM THIS AGREEMENT, WHETHER ALLEGED AS A 135 | BREACH OF CONTRACT OR TORTIOUS CONDUCT, INCLUDING NEGLIGENCE, EVEN IF ELASTIC 136 | HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 137 | 138 | 5. MISCELLANEOUS 139 | 140 | This Agreement completely and exclusively states the entire agreement of the 141 | parties regarding the subject matter herein, and it supersedes, and its terms 142 | govern, all prior proposals, agreements, or other communications between the 143 | parties, oral or written, regarding such subject matter. This Agreement may be 144 | modified by Elastic from time to time, and any such modifications will be 145 | effective upon the "Posted Date" set forth at the top of the modified 146 | Agreement. If any provision hereof is held unenforceable, this Agreement will 147 | continue without said provision and be interpreted to reflect the original 148 | intent of the parties. This Agreement and any non-contractual obligation 149 | arising out of or in connection with it, is governed exclusively by Dutch law. 150 | This Agreement shall not be governed by the 1980 UN Convention on Contracts 151 | for the International Sale of Goods. All disputes arising out of or in 152 | connection with this Agreement, including its existence and validity, shall be 153 | resolved by the courts with jurisdiction in Amsterdam, The Netherlands, except 154 | where mandatory law provides for the courts at another location in The 155 | Netherlands to have jurisdiction. The parties hereby irrevocably waive any and 156 | all claims and defenses either might otherwise have in any such action or 157 | proceeding in any of such courts based upon any alleged lack of personal 158 | jurisdiction, improper venue, forum non conveniens or any similar claim or 159 | defense. A breach or threatened breach, by You of Section 2 may cause 160 | irreparable harm for which damages at law may not provide adequate relief, and 161 | therefore Elastic shall be entitled to seek injunctive relief without being 162 | required to post a bond. You may not assign this Agreement (including by 163 | operation of law in connection with a merger or acquisition), in whole or in 164 | part to any third party without the prior written consent of Elastic, which 165 | may be withheld or granted by Elastic in its sole and absolute discretion. 166 | Any assignment in violation of the preceding sentence is void. Notices to 167 | Elastic may also be sent to legal@elastic.co. 168 | 169 | 6. DEFINITIONS 170 | 171 | The following terms have the meanings ascribed: 172 | 173 | 6.1 "Affiliate" means, with respect to a party, any entity that controls, is 174 | controlled by, or which is under common control with, such party, where 175 | "control" means ownership of at least fifty percent (50%) of the outstanding 176 | voting shares of the entity, or the contractual right to establish policy for, 177 | and manage the operations of, the entity. 178 | 179 | 6.2 "Basic Features and Functions" means those features and functions of the 180 | Elastic Software that are eligible for use under a Basic license, as set forth 181 | at https://www.elastic.co/subscriptions, as may be modified by Elastic from 182 | time to time. 183 | 184 | 6.3 "Commercial Software" means the Elastic Software Source Code in any file 185 | containing a header stating the contents are subject to the Elastic License or 186 | which is contained in the repository folder labeled "x-pack", unless a LICENSE 187 | file present in the directory subtree declares a different license. 188 | 189 | 6.4 "Derivative Work of the Commercial Software" means, for purposes of this 190 | Agreement, any modification(s) or enhancement(s) to the Commercial Software, 191 | which represent, as a whole, an original work of authorship. 192 | 193 | 6.5 "License" means a limited, non-exclusive, non-transferable, fully paid up, 194 | royalty free, right and license, without the right to grant or authorize 195 | sublicenses, solely for Your internal business operations to (i) install and 196 | use the applicable Features and Functions of the Elastic Software in Object 197 | Code, and (ii) permit Contractors and Your Affiliates to use the Elastic 198 | software as set forth in (i) above, provided that such use by Contractors must 199 | be solely for Your benefit and/or the benefit of Your Affiliates, and You 200 | shall be responsible for all acts and omissions of such Contractors and 201 | Affiliates in connection with their use of the Elastic software that are 202 | contrary to the terms and conditions of this Agreement. 203 | 204 | 6.6 "License Key" means a sequence of bytes, including but not limited to a 205 | JSON blob, that is used to enable certain features and functions of the 206 | Elastic Software. 207 | 208 | 6.7 "Marks and Notices" means all Elastic trademarks, trade names, logos and 209 | notices present on the Documentation as originally provided by Elastic. 210 | 211 | 6.8 "Non-production Environment" means an environment for development, testing 212 | or quality assurance, where software is not used for production purposes. 213 | 214 | 6.9 "Object Code" means any form resulting from mechanical transformation or 215 | translation of Source Code form, including but not limited to compiled object 216 | code, generated documentation, and conversions to other media types. 217 | 218 | 6.10 "Source Code" means the preferred form of computer software for making 219 | modifications, including but not limited to software source code, 220 | documentation source, and configuration files. 221 | 222 | 6.11 "Subscription" means the right to receive Support Services and a License 223 | to the Commercial Software. 224 | 225 | 226 | GOVERNMENT END USER ADDENDUM TO THE ELASTIC LICENSE AGREEMENT 227 | 228 | This ADDENDUM TO THE ELASTIC LICENSE AGREEMENT (this "Addendum") applies 229 | only to U.S. Federal Government, State Government, and Local Government 230 | entities ("Government End Users") of the Elastic Software. This Addendum is 231 | subject to, and hereby incorporated into, the Elastic License Agreement, 232 | which is being entered into as of even date herewith, by Elastic and You (the 233 | "Agreement"). This Addendum sets forth additional terms and conditions 234 | related to Your use of the Elastic Software. Capitalized terms not defined in 235 | this Addendum have the meaning set forth in the Agreement. 236 | 237 | 1. LIMITED LICENSE TO DISTRIBUTE (DSOP ONLY). Subject to the terms and 238 | conditions of the Agreement (including this Addendum), Elastic grants the 239 | Department of Defense Enterprise DevSecOps Initiative (DSOP) a royalty-free, 240 | non-exclusive, non-transferable, limited license to reproduce and distribute 241 | the Elastic Software solely through a software distribution repository 242 | controlled and managed by DSOP, provided that DSOP: (i) distributes the 243 | Elastic Software complete and unmodified, inclusive of the Agreement 244 | (including this Addendum) and (ii) does not remove or alter any proprietary 245 | legends or notices contained in the Elastic Software. 246 | 247 | 2. CHOICE OF LAW. The choice of law and venue provisions set forth shall 248 | prevail over those set forth in Section 5 of the Agreement. 249 | 250 | "For U.S. Federal Government Entity End Users. This Agreement and any 251 | non-contractual obligation arising out of or in connection with it, is 252 | governed exclusively by U.S. Federal law. To the extent permitted by 253 | federal law, the laws of the State of Delaware (excluding Delaware choice 254 | of law rules) will apply in the absence of applicable federal law. 255 | 256 | For State and Local Government Entity End Users. This Agreement and any 257 | non-contractual obligation arising out of or in connection with it, is 258 | governed exclusively by the laws of the state in which you are located 259 | without reference to conflict of laws. Furthermore, the Parties agree that 260 | the Uniform Computer Information Transactions Act or any version thereof, 261 | adopted by any state in any form ('UCITA'), shall not apply to this 262 | Agreement and, to the extent that UCITA is applicable, the Parties agree to 263 | opt out of the applicability of UCITA pursuant to the opt-out provision(s) 264 | contained therein." 265 | 266 | 3. ELASTIC LICENSE MODIFICATION. Section 5 of the Agreement is hereby 267 | amended to replace 268 | 269 | "This Agreement may be modified by Elastic from time to time, and any 270 | such modifications will be effective upon the "Posted Date" set forth at 271 | the top of the modified Agreement." 272 | 273 | with: 274 | 275 | "This Agreement may be modified by Elastic from time to time; provided, 276 | however, that any such modifications shall apply only to Elastic Software 277 | that is installed after the "Posted Date" set forth at the top of the 278 | modified Agreement." 279 | 280 | V100820.0 281 | -------------------------------------------------------------------------------- /src/handler/linux/helper.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | # global vars 5 | DISTRO_OS="" 6 | LOGS_FOLDER="" 7 | CONFIG_FILE="" 8 | STATUS_FOLDER="" 9 | CLOUD_ID="" 10 | USERNAME="" 11 | PASSWORD="" 12 | BASE64_AUTH="" 13 | ELASTICSEARCH_URL="" 14 | STACK_VERSION="" 15 | KIBANA_URL="" 16 | POLICY_ID="" 17 | LINUX_CERT_PATH="/var/lib/waagent" 18 | IS_NEW_CONFIG="" 19 | OLD_STACK_VERSION="" 20 | OLD_ELASTICSEARCH_URL="" 21 | OLD_KIBANA_URL="" 22 | OLD_USERNAME="" 23 | OLD_PASSWORD="" 24 | OLD_BASE64_AUTH="" 25 | OLD_CONFIG_FILE="" 26 | OLD_CLOUD_ID="" 27 | OLD_PROTECTED_SETTINGS="" 28 | OLD_THUMBPRINT="" 29 | IS_FLEET_SERVER="" 30 | HAS_FLAG_VERSION="" 31 | POLICY_NAME="Azure VM extension policy" 32 | 33 | # checkOS checks distro 34 | checkOS() 35 | { 36 | if dpkg -S /bin/ls >/dev/null 2>&1 || dpkg -S /usr/bin/ls >/dev/null 2>&1 37 | then 38 | DISTRO_OS="DEB" 39 | echo "[checkOS] distro is $DISTRO_OS" "INFO" 40 | elif rpm -q -f /bin/ls >/dev/null 2>&1 41 | then 42 | DISTRO_OS="RPM" 43 | echo "[checkOS] distro is $DISTRO_OS" "INFO" 44 | else 45 | DISTRO_OS="OTHER" 46 | echo "[checkOS] distro is $DISTRO_OS" "INFO" 47 | fi 48 | } 49 | 50 | # get_logs_location gets log path from the HandlerEnvironment file 51 | get_logs_location() 52 | { 53 | SCRIPT=$(readlink -f "$0") 54 | ES_EXT_DIR=$(dirname "$SCRIPT") 55 | if [ -e $ES_EXT_DIR/HandlerEnvironment.json ]; then 56 | LOGS_FOLDER=$(jq -r '.[0].handlerEnvironment.logFolder' $ES_EXT_DIR/HandlerEnvironment.json) 57 | else 58 | clean_and_exit 1 59 | fi 60 | } 61 | 62 | # get_status_location gets status path from the HandlerEnvironment file 63 | get_status_location() 64 | { 65 | SCRIPT=$(readlink -f "$0") 66 | ES_EXT_DIR=$(dirname "$SCRIPT") 67 | if [ -e $ES_EXT_DIR/HandlerEnvironment.json ] 68 | then 69 | STATUS_FOLDER=$(jq -r '.[0].handlerEnvironment.statusFolder' $ES_EXT_DIR/HandlerEnvironment.json) 70 | else 71 | clean_and_exit 1 72 | fi 73 | } 74 | 75 | # log will log events in the azure logs 76 | log() 77 | { 78 | if [ "$LOGS_FOLDER" = "" ]; then 79 | get_logs_location 80 | fi 81 | echo \[$(date +%H:%M:%ST%d-%m-%Y)\] "$1" "$2" 82 | echo \[$(date +%H:%M:%ST%d-%m-%Y)\] "$1" "$2" >> "$LOGS_FOLDER"/es-agent.log 83 | } 84 | 85 | # checkShasum checks shasum 86 | checkShasum() 87 | { 88 | local archive_file_name="${1}" 89 | local authentic_checksum_file="${2}" 90 | echo --check <(grep "\s${archive_file_name}$" "${authentic_checksum_file}") 91 | if $(which shasum >/dev/null 2>&1); then 92 | shasum \ 93 | -a 256 \ 94 | --check <(grep "\s${archive_file_name}$" "${authentic_checksum_file}") 95 | else 96 | echo "shasum is not available for use" >&2 97 | return 1 98 | fi 99 | } 100 | 101 | #get_configuration_location retrieves configuration file path from HandlerEnvironment file 102 | get_configuration_location() 103 | { 104 | SCRIPT=$(readlink -f "$0") 105 | ES_EXT_DIR=$(dirname "$SCRIPT") 106 | if [ -e "$ES_EXT_DIR/HandlerEnvironment.json" ]; then 107 | config_folder=$(jq -r '.[0].handlerEnvironment.configFolder' "$ES_EXT_DIR/HandlerEnvironment.json") 108 | config_files_path="$config_folder/*.settings" 109 | CONFIG_FILE=$(ls $config_files_path 2>/dev/null | sort -V | tail -1) 110 | log "INFO" "[get_configuration_location] configuration file $CONFIG_FILE found" 111 | else 112 | log "ERROR" "[get_configuration_location] HandlerEnvironment.json file not found" 113 | clean_and_exit 1 114 | fi 115 | } 116 | 117 | # get_cloud_id retrieves the cloudID from the current configuration file 118 | get_cloud_id() 119 | { 120 | get_configuration_location 121 | if [ "$CONFIG_FILE" != "" ]; then 122 | CLOUD_ID=$(jq -r '.runtimeSettings[0].handlerSettings.publicSettings.cloudId' $CONFIG_FILE) 123 | log "INFO" "[get_cloud_id] Found cloud id $CLOUD_ID" 124 | else 125 | log "[get_cloud_id] Configuration file not found" "ERROR" 126 | clean_and_exit 1 127 | fi 128 | } 129 | 130 | # get_protected_settings retrieves the private/protected settings from the current configuration file 131 | get_protected_settings() 132 | { 133 | get_configuration_location 134 | if [ "$CONFIG_FILE" != "" ]; then 135 | PROTECTED_SETTINGS=$(jq -r '.runtimeSettings[0].handlerSettings.protectedSettings' $CONFIG_FILE) 136 | log "INFO" "[get_protected_settings] Found protected settings" 137 | else 138 | log "[get_protected_settings] Configuration file not found" "ERROR" 139 | clean_and_exit 1 140 | fi 141 | } 142 | 143 | # get_thumbprint retrieves the thumbprint value from the protected settings 144 | get_thumbprint() 145 | { 146 | get_configuration_location 147 | if [ "$CONFIG_FILE" != "" ]; then 148 | THUMBPRINT=$(jq -r '.runtimeSettings[0].handlerSettings.protectedSettingsCertThumbprint' $CONFIG_FILE) 149 | log "INFO" "[get_thumbprint] Found thumbprint $THUMBPRINT" 150 | else 151 | log "[get_thumbprint] Configuration file not found" "ERROR" 152 | clean_and_exit 1 153 | fi 154 | } 155 | 156 | # get_username retrieves the username from the current configuration file n.settings 157 | get_username() 158 | { 159 | get_configuration_location 160 | if [ "$CONFIG_FILE" != "" ]; then 161 | USERNAME=$(jq -r '.runtimeSettings[0].handlerSettings.publicSettings.username' $CONFIG_FILE) 162 | log "INFO" "[get_username] Found username $USERNAME" 163 | else 164 | log "ERROR" "[get_username] Configuration file not found" 165 | clean_and_exit 1 166 | fi 167 | } 168 | 169 | # get_kibana_host retrieves the kibana URL from the cloud ID value (encoding and parsing it) 170 | get_kibana_host () { 171 | get_cloud_id 172 | if [ "$CLOUD_ID" != "" ]; then 173 | cloud_hash=$(echo $CLOUD_ID | cut -f2 -d:) 174 | cloud_tokens=$(echo $cloud_hash | base64 -d -) 175 | host_port=$(echo $cloud_tokens | cut -f1 -d$) 176 | KIBANA_URL="https://$(echo $cloud_tokens | cut -f3 -d$).${host_port}" 177 | log "INFO" "[get_kibana_host] Found Kibana uri $KIBANA_URL" 178 | else 179 | log "ERROR" "[get_kibana_host] Cloud ID could not be parsed" 180 | clean_and_exit 1 181 | fi 182 | 183 | } 184 | 185 | # get_elasticsearch_host retrieves the es URL from the cloud ID value (encoding and parsing it) 186 | get_elasticsearch_host () { 187 | get_cloud_id 188 | if [ "$CLOUD_ID" != "" ]; then 189 | cloud_hash=$(echo $CLOUD_ID | cut -f2 -d:) 190 | cloud_tokens=$(echo $cloud_hash | base64 -d -) 191 | host_port=$(echo $cloud_tokens | cut -f1 -d$) 192 | ELASTICSEARCH_URL="https://$(echo $cloud_tokens | cut -f2 -d$).${host_port}" 193 | log "INFO" "[get_elasticsearch_host] Found ES uri $ELASTICSEARCH_URL" 194 | else 195 | log "ERROR" "[get_elasticsearch_host] Cloud ID could not be parsed" 196 | clean_and_exit 1 197 | fi 198 | } 199 | 200 | # get_cloud_stack_version retrieves the stack version by pinging the es cluster and parsing the result 201 | get_cloud_stack_version () { 202 | log "INFO" "[get_cloud_stack_version] Get ES cluster URL" 203 | get_elasticsearch_host 204 | if [ "$ELASTICSEARCH_URL" = "" ]; then 205 | log "ERROR" "[get_cloud_stack_version] Elasticsearch URL could not be found" 206 | clean_and_exit 1 207 | fi 208 | get_password 209 | get_base64Auth 210 | if [ "$PASSWORD" = "" ] && [ "$BASE64_AUTH" = "" ]; then 211 | log "ERROR" "[get_cloud_stack_version] Both PASSWORD and BASE64AUTH key could not be found" 212 | clean_and_exit 1 213 | fi 214 | local cred="" 215 | if [ "$PASSWORD" != "" ] && [ "$PASSWORD" != "null" ]; then 216 | get_username 217 | if [ "$USERNAME" = "" ]; then 218 | log "ERROR" "[get_cloud_stack_version] USERNAME could not be found" 219 | clean_and_exit 1 220 | fi 221 | cred=${USERNAME}:${PASSWORD} 222 | else 223 | cred=$(echo "$BASE64_AUTH" | base64 --decode) 224 | fi 225 | json_result=$(curl "${ELASTICSEARCH_URL}" -H 'Content-Type: application/json' -u $cred) 226 | local EXITCODE=$? 227 | if [ $EXITCODE -ne 0 ]; then 228 | log "ERROR" "[get_cloud_stack_version] error pinging $ELASTICSEARCH_URL" 229 | clean_and_exit $EXITCODE 230 | fi 231 | STACK_VERSION=$(echo $json_result | jq -r '.version.number') 232 | log "INFO" "[get_cloud_stack_version] Stack version found is $STACK_VERSION" 233 | } 234 | 235 | # parse_yaml used for reading the agent id from the fleet.yml file 236 | function parse_yaml { 237 | local s='[[:space:]]*' w='[a-zA-Z0-9_]*' fs=$(echo @|tr @ '\034') 238 | sed -ne "s|^\($s\):|\1|" \ 239 | -e "s|^\($s\)\($w\)$s:$s[\"']\(.*\)[\"']$s\$|\1$fs\2$fs\3|p" \ 240 | -e "s|^\($s\)\($w\)$s:$s\(.*\)$s\$|\1$fs\2$fs\3|p" $1 | 241 | awk -F$fs '{ 242 | indent = length($1)/2; 243 | vname[indent] = $2; 244 | for (i in vname) {if (i > indent) {delete vname[i]}} 245 | if (length($3) > 0) { 246 | vn=""; for (i=0; i "$STATUS_FOLDER"/"$sequenceNumber".status 366 | fi 367 | } 368 | 369 | # encrypt will encrypt text 370 | encrypt() { 371 | cert_path=".../waagent/$1.crt" 372 | private_key_path=".../waagent/$1.prv" 373 | if [[ -f "$cert_path" ]] && [[ -f "$private_key_path" ]]; then 374 | openssl cms -encrypt -in <(echo "$2") -inkey $private_key_path -recip $cert_path -inform dem 375 | else 376 | echo "ERROR" "[decrypt] Decryption failed. Could not find certificates" 377 | clean_and_exit 1 378 | fi 379 | } 380 | 381 | # get_password will retrieve the password from the protected settings and decrypt the value 382 | get_password() { 383 | get_protected_settings 384 | get_thumbprint 385 | cert_path="$LINUX_CERT_PATH/$THUMBPRINT.crt" 386 | private_key_path="$LINUX_CERT_PATH/$THUMBPRINT.prv" 387 | if [[ -f "$cert_path" ]] && [[ -f "$private_key_path" ]]; then 388 | protected_settings=$(openssl cms -decrypt -in <(echo "$PROTECTED_SETTINGS" | base64 --decode) -inkey "$private_key_path" -recip "$cert_path" -inform dem) 389 | PASSWORD=$(echo "$protected_settings" | jq -r '.password') 390 | else 391 | log "ERROR" "[get_password] Decryption failed. Could not find certificates" 392 | clean_and_exit 1 393 | fi 394 | } 395 | 396 | # get_base64Auth will retrieve the base64auth from the protected settings and decrypt the value 397 | get_base64Auth() { 398 | get_protected_settings 399 | get_thumbprint 400 | cert_path="$LINUX_CERT_PATH/$THUMBPRINT.crt" 401 | private_key_path="$LINUX_CERT_PATH/$THUMBPRINT.prv" 402 | if [[ -f "$cert_path" ]] && [[ -f "$private_key_path" ]]; then 403 | protected_settings=$(openssl cms -decrypt -in <(echo "$PROTECTED_SETTINGS" | base64 --decode) -inkey "$private_key_path" -recip "$cert_path" -inform dem) 404 | BASE64_AUTH=$(echo "${protected_settings}" | jq -r '.base64Auth') 405 | else 406 | log "ERROR" "[get_base64Auth] Decryption failed. Could not find certificates" 407 | clean_and_exit 1 408 | fi 409 | } 410 | 411 | #is_new_config will check if is an extension update/ clean installation/configuration update 412 | is_new_config(){ 413 | log "INFO" "[is_new_config] Check if new config" 414 | currentSequence="" 415 | newSequence="" 416 | isUpdate="" 417 | get_configuration_location 418 | if [ "$CONFIG_FILE" != "" ]; then 419 | filename="$(basename -- $CONFIG_FILE)" 420 | newSequence=$(echo $filename | cut -f1 -d.) 421 | else 422 | log "[get_sequence] Configuration file not found" "ERROR" 423 | clean_and_exit 1 424 | fi 425 | if [ "$LOGS_FOLDER" = "" ]; then 426 | get_logs_location 427 | fi 428 | if [ -f "$LOGS_FOLDER/update.txt" ]; then 429 | isUpdate=true 430 | else 431 | isUpdate=false 432 | fi 433 | if [ -f "$LOGS_FOLDER/current.sequence" ]; then 434 | currentSequence=$(< "$LOGS_FOLDER/current.sequence") 435 | else 436 | currentSequence="" 437 | fi 438 | log "INFO" "[is_new_config] Current sequence is $currentSequence and new sequence is $newSequence" 439 | if [[ "$newSequence" = "" ]]; then 440 | IS_NEW_CONFIG=false 441 | elif [[ "$isUpdate" = true ]]; then 442 | log "INFO" "[is_new_config] Part of the update" 443 | IS_NEW_CONFIG=false 444 | elif [[ "$newSequence" = "$currentSequence" ]]; then 445 | IS_NEW_CONFIG=false 446 | else 447 | IS_NEW_CONFIG=true 448 | fi 449 | } 450 | 451 | # set_update_var will set a flag that the operation is a vm extension update 452 | set_update_var() { 453 | log "INFO" "[set_update_var] Verified update" 454 | if [ "$LOGS_FOLDER" = "" ]; then 455 | get_logs_location 456 | fi 457 | echo "1" > "$LOGS_FOLDER/update.txt" 458 | } 459 | 460 | # set_sequence_to_file will set sequence flag for current configuration sequence file n.settings 461 | function set_sequence_to_file 462 | { 463 | log "INFO" "[set_sequence_to_file] Setting new sequence" 464 | get_configuration_location 465 | if [ "$CONFIG_FILE" != "" ]; then 466 | filename="$(basename -- $CONFIG_FILE)" 467 | newSequence=$(echo $filename | cut -f1 -d.) 468 | if [ "$LOGS_FOLDER" = "" ]; then 469 | get_logs_location 470 | fi 471 | #json="{\"sequence\":\"$newSequence\",\"update\":\"false\"}" 472 | echo "$newSequence" > "$LOGS_FOLDER/current.sequence" 473 | rm "$LOGS_FOLDER/update.txt" 474 | log "INFO" "[set_sequence_to_file] Sequence has been set" 475 | else 476 | log "[set_sequence_to_file] Configuration file not found" "ERROR" 477 | clean_and_exit 1 478 | fi 479 | } 480 | 481 | # get_prev_configuration_location retrieves previous configuration file 482 | get_prev_configuration_location() 483 | { 484 | SCRIPT=$(readlink -f "$0") 485 | ES_EXT_DIR=$(dirname "$SCRIPT") 486 | log "INFO" "[get_prev_configuration_location] main directory found $ES_EXT_DIR" 487 | if [ -e "$ES_EXT_DIR/HandlerEnvironment.json" ]; then 488 | config_folder=$(jq -r '.[0].handlerEnvironment.configFolder' "$ES_EXT_DIR/HandlerEnvironment.json") 489 | log "INFO" "[get_prev_configuration_location] configuration folder $config_folder found" 490 | config_files_path="$config_folder/*.settings" 491 | OLD_CONFIG_FILE=$(ls $config_files_path 2>/dev/null | sort -V | tail -n 2 | head -n 1) 492 | log "INFO" "[get_prev_configuration_location] configuration file $OLD_CONFIG_FILE found" 493 | else 494 | log "ERROR" "[get_prev_configuration_location] HandlerEnvironment.json file not found" 495 | clean_and_exit 1 496 | fi 497 | } 498 | 499 | # get_prev_username retrieves previous username configuration option 500 | get_prev_username() 501 | { 502 | get_prev_configuration_location 503 | if [ "$OLD_CONFIG_FILE" != "" ]; then 504 | OLD_USERNAME=$(jq -r '.runtimeSettings[0].handlerSettings.publicSettings.username' $OLD_CONFIG_FILE) 505 | log "INFO" "[get_prev_username] Found username OLD_USERNAME" 506 | else 507 | log "ERROR" "[get_prev_username] Configuration file not found" 508 | clean_and_exit 1 509 | fi 510 | } 511 | 512 | # get_prev_cloud_id retrieves previous cloudID configuration option 513 | get_prev_cloud_id() 514 | { 515 | get_prev_configuration_location 516 | if [ "$OLD_CONFIG_FILE" != "" ]; then 517 | OLD_CLOUD_ID=$(jq -r '.runtimeSettings[0].handlerSettings.publicSettings.cloudId' $OLD_CONFIG_FILE) 518 | log "INFO" "[get_prev_cloud_id] Found cloud id $OLD_CLOUD_ID" 519 | else 520 | log "[get_prev_cloud_id] Configuration file not found" "ERROR" 521 | clean_and_exit 1 522 | fi 523 | } 524 | 525 | # get_prev_kibana_host retrieves previous kibana URL configuration option 526 | get_prev_kibana_host () { 527 | get_prev_cloud_id 528 | if [ "$OLD_CLOUD_ID" != "" ]; then 529 | cloud_hash=$(echo $OLD_CLOUD_ID | cut -f2 -d:) 530 | cloud_tokens=$(echo $cloud_hash | base64 -d -) 531 | host_port=$(echo $cloud_tokens | cut -f1 -d$) 532 | OLD_KIBANA_URL="https://$(echo $cloud_tokens | cut -f3 -d$).${host_port}" 533 | log "INFO" "[get_prev_kibana_host] Found Kibana uri $OLD_KIBANA_URL" 534 | else 535 | log "ERROR" "[get_prev_kibana_host] Cloud ID could not be parsed" 536 | clean_and_exit 1 537 | fi 538 | 539 | } 540 | 541 | # get_prev_elasticsearch_host retrieves previous es URL configuration option 542 | get_prev_elasticsearch_host () { 543 | get_prev_cloud_id 544 | if [ "$OLD_CLOUD_ID" != "" ]; then 545 | cloud_hash=$(echo $OLD_CLOUD_ID | cut -f2 -d:) 546 | cloud_tokens=$(echo $cloud_hash | base64 -d -) 547 | host_port=$(echo $cloud_tokens | cut -f1 -d$) 548 | OLD_ELASTICSEARCH_URL="https://$(echo $cloud_tokens | cut -f2 -d$).${host_port}" 549 | log "INFO" "[get_prev_elasticsearch_host] Found ES uri $OLD_ELASTICSEARCH_URL" 550 | else 551 | log "ERROR" "[get_prev_elasticsearch_host] Cloud ID could not be parsed" 552 | clean_and_exit 1 553 | fi 554 | } 555 | 556 | # get_prev_protected_settings retrieves previous protected settings configuration option 557 | get_prev_protected_settings() 558 | { 559 | get_prev_configuration_location 560 | if [ "$OLD_CONFIG_FILE" != "" ]; then 561 | OLD_PROTECTED_SETTINGS=$(jq -r '.runtimeSettings[0].handlerSettings.protectedSettings' $OLD_CONFIG_FILE) 562 | log "INFO" "[get_prev_protected_settings] Found protected settings $OLD_PROTECTED_SETTINGS" 563 | else 564 | log "[get_prev_protected_settings] Configuration file not found" "ERROR" 565 | clean_and_exit 1 566 | fi 567 | } 568 | 569 | # get_prev_thumbprint retrieves previous thumbprint configuration option 570 | get_prev_thumbprint() 571 | { 572 | get_prev_configuration_location 573 | if [ "$OLD_CONFIG_FILE" != "" ]; then 574 | OLD_THUMBPRINT=$(jq -r '.runtimeSettings[0].handlerSettings.protectedSettingsCertThumbprint' $OLD_CONFIG_FILE) 575 | log "INFO" "[get_prev_thumbprint] Found thumbprint $OLD_THUMBPRINT" 576 | else 577 | log "[get_prev_thumbprint] Configuration file not found" "ERROR" 578 | clean_and_exit 1 579 | fi 580 | } 581 | 582 | # get_prev_password retrieves previous password configuration option 583 | get_prev_password() { 584 | get_prev_protected_settings 585 | get_prev_thumbprint 586 | cert_path="$LINUX_CERT_PATH/$OLD_THUMBPRINT.crt" 587 | private_key_path="$LINUX_CERT_PATH/$OLD_THUMBPRINT.prv" 588 | log "INFO" "Found cerficate $cert_path and $private_key_path" 589 | if [[ -f "$cert_path" ]] && [[ -f "$private_key_path" ]]; then 590 | protected_settings=$(openssl cms -decrypt -in <(echo "$OLD_PROTECTED_SETTINGS" | base64 --decode) -inkey "$private_key_path" -recip "$cert_path" -inform dem) 591 | OLD_PASSWORD=$(echo "$protected_settings" | jq -r '.password') 592 | else 593 | log "ERROR" "[get_prev_password] Decryption failed. Could not find certificates" 594 | clean_and_exit 1 595 | fi 596 | } 597 | 598 | # get_prev_base64Auth retrieves previous base64auth configuration option 599 | get_prev_base64Auth() { 600 | get_prev_protected_settings 601 | get_prev_thumbprint 602 | cert_path="$LINUX_CERT_PATH/$OLD_THUMBPRINT.crt" 603 | private_key_path="$LINUX_CERT_PATH/$OLD_THUMBPRINT.prv" 604 | if [[ -f "$cert_path" ]] && [[ -f "$private_key_path" ]]; then 605 | protected_settings=$(openssl cms -decrypt -in <(echo "$OLD_PROTECTED_SETTINGS" | base64 --decode) -inkey "$private_key_path" -recip "$cert_path" -inform dem) 606 | OLD_BASE64_AUTH=$(echo "${protected_settings}" | jq -r '.base64Auth') 607 | else 608 | log "ERROR" "[get_prev_base64Auth] Decryption failed. Could not find certificates" 609 | clean_and_exit 1 610 | fi 611 | } 612 | 613 | # get_prev_cloud_stack_version retrieves previous stack version 614 | get_prev_cloud_stack_version () { 615 | log "INFO" "[get_prev_cloud_stack_version] Get ES cluster URL" 616 | get_prev_elasticsearch_host 617 | if [ "$OLD_ELASTICSEARCH_URL" = "" ]; then 618 | log "ERROR" "[get_prev_cloud_stack_version] Elasticsearch URL could not be found" 619 | clean_and_exit 1 620 | fi 621 | get_prev_password 622 | get_prev_base64Auth 623 | if [ "$OLD_PASSWORD" = "" ] && [ "$OLD_BASE64_AUTH" = "" ]; then 624 | log "ERROR" "[get_prev_cloud_stack_version] Both PASSWORD and BASE64AUTH key could not be found" 625 | clean_and_exit 1 626 | fi 627 | local cred="" 628 | if [ "$OLD_PASSWORD" != "" ] && [ "$OLD_PASSWORD" != "null" ]; then 629 | get_prev_username 630 | if [ "$OLD_USERNAME" = "" ]; then 631 | log "ERROR" "[get_prev_cloud_stack_version] USERNAME could not be found" 632 | clean_and_exit 1 633 | fi 634 | cred=${OLD_USERNAME}:${OLD_PASSWORD} 635 | else 636 | cred=$(echo "$OLD_BASE64_AUTH" | base64 --decode) 637 | fi 638 | json_result=$(curl "${OLD_ELASTICSEARCH_URL}" -H 'Content-Type: application/json' -u $cred) 639 | local EXITCODE=$? 640 | if [ $EXITCODE -ne 0 ]; then 641 | log "ERROR" "[get_prev_cloud_stack_version] error pinging $OLD_ELASTICSEARCH_URL" 642 | clean_and_exit $EXITCODE 643 | fi 644 | OLD_STACK_VERSION=$(echo $json_result | jq -r '.version.number') 645 | log "INFO" "[get_prev_cloud_stack_version] Stack version found is $OLD_STACK_VERSION" 646 | } 647 | 648 | # clean_and_exit cleans up temporary files and exits 649 | clean_and_exit() { 650 | log "INFO" "[clean_and_exit] Cleaning up temporary files" 651 | if [ -f "$CONFIG_FILE" ]; then 652 | log "INFO" "[clean_and_exit] emptying $CONFIG_FILE" 653 | echo "{}" > $CONFIG_FILE 654 | fi 655 | 656 | if [ -n "$1" ]; then 657 | exit $1 658 | else 659 | exit 1 660 | fi 661 | } 662 | -------------------------------------------------------------------------------- /src/handler/windows/scripts/helper.ps1: -------------------------------------------------------------------------------- 1 | function GetDirectory 2 | { 3 | $Invocation = (Get-Variable MyInvocation -Scope 1).Value 4 | Split-Path $Invocation.MyCommand.Path 5 | } 6 | 7 | $scriptDir = GetDirectory 8 | 9 | $extensionRoot = [System.IO.Path]::GetFullPath("$scriptDir\\..") 10 | 11 | # Get-PowershellVersion gets running version of powershell, necessary to detect which commands apply/are compatible 12 | function Get-PowershellVersion { 13 | if(!$powershellVersion) 14 | { 15 | $global:powershellVersion = $PSVersionTable.PSVersion.Major 16 | } 17 | return $powershellVersion 18 | } 19 | 20 | function Run-Powershell2-With-Dot-Net4 { 21 | $powershellVersion = Get-PowershellVersion 22 | if ( $powershellVersion -lt 3 ) { 23 | reg add hklm\software\microsoft\.netframework /v OnlyUseLatestCLR /t REG_DWORD /d 1 /f 24 | reg add hklm\software\wow6432node\microsoft\.netframework /v OnlyUseLatestCLR /t REG_DWORD /d 1 /f 25 | } 26 | } 27 | 28 | # Get-CloudId gets the cloudID from the n.settings configuration file 29 | function Get-CloudId($powershellVersion) { 30 | $cloudId = Get-PublicSettings-From-Config-Json "cloudId" $powershellVersion 31 | if ( $cloudId){ 32 | return $cloudId 33 | } 34 | return "" 35 | } 36 | 37 | # Get-Username gets the username from the n.settings configuration file 38 | function Get-Username($powershellVersion) { 39 | $username = Get-PublicSettings-From-Config-Json "username" $powershellVersion 40 | if ( $username){ 41 | return $username 42 | } 43 | return "" 44 | } 45 | 46 | # Get-Elasticsearch-URL retrieves the es url by encoding and parsing the cloudID value 47 | function Get-Elasticsearch-URL($powershellVersion) { 48 | $powershellVersion = Get-PowershellVersion 49 | $cloudId = Get-CloudId $powershellVersion 50 | if ( $cloudId -ne ""){ 51 | $cloudHash=$cloudId.split(":")[-1] 52 | $cloudTokens=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($cloudHash)) 53 | $cloudElems=$cloudTokens.split("$") 54 | $hostPort= $cloudElems[0] 55 | return "https://$($cloudElems[1]).$(${hostPort})" 56 | } 57 | return "" 58 | } 59 | 60 | # Get-Kibana-URL retrieves the Kibana url by encoding and parsing the cloudID value 61 | function Get-Kibana-URL ($powershellVersion) { 62 | $cloudId = Get-CloudId $powershellVersion 63 | if ( $cloudId -ne ""){ 64 | $cloudHash=$cloudId.split(":")[-1] 65 | $cloudTokens=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($cloudHash)) 66 | $cloudElems=$cloudTokens.split("$") 67 | $hostPort= $cloudElems[0] 68 | return "https://$($cloudElems[2]).$(${hostPort})" 69 | } 70 | return "" 71 | } 72 | 73 | # Get-Stack-Version retrieves the stack version by pinging the es cluster and reading output 74 | function Get-Stack-Version { 75 | $powershellVersion = Get-PowershellVersion 76 | $elasticsearchUrl = Get-Elasticsearch-URL $powershellVersion 77 | if (-Not $elasticsearchUrl) { 78 | throw "Elasticsearch URL could not be found" 79 | } 80 | $password = Get-Password $powershellVersion 81 | $base64Auth = Get-Base64Auth $powershellVersion 82 | if (-Not $password -And -Not $base64Auth) { 83 | throw "Password or base64auto key could not be found" 84 | } 85 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 86 | if ( $powershellVersion -gt 3 ) { 87 | $headers.Add("Accept","application/json") 88 | } 89 | #cred 90 | $encodedCredentials = "" 91 | if ($password) { 92 | $username = Get-Username $powershellVersion 93 | if (-Not $username) { 94 | throw "Username could not be found" 95 | } 96 | $pair = "$($username):$($password)" 97 | $encodedCredentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair)) 98 | } else { 99 | $encodedCredentials = $base64Auth 100 | } 101 | $headers.Add('Authorization', "Basic $encodedCredentials") 102 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 103 | $jsonResult = Invoke-WebRequest -Uri "$($elasticsearchUrl)" -Method 'GET' -Headers $headers -UseBasicParsing 104 | if ($jsonResult.statuscode -eq '200') { 105 | $keyValue= ConvertFrom-Json $jsonResult.Content | Select-Object -expand "" 106 | $stackVersion=$keyValue.version.number 107 | Write-Log "Found stack version $stackVersion" "INFO" 108 | return $stackVersion 109 | }else { 110 | Write-Log "Error pinging elastic cluster $elasticsearchUrl" "ERROR" 111 | return "" 112 | } 113 | return "" 114 | } 115 | 116 | # HasFleetServer checks if stack version has Fleet Server, expecting this to start with 7.13 117 | function HasFleetServer { 118 | param([string]$esVersion) 119 | $major=$esVersion.split(".")[0] 120 | $minor=$esVersion.split(".")[1] 121 | $intMajor = [int]$major 122 | $intMinor= [int]$minor 123 | if ($intMajor -gt 7) 124 | { 125 | return $true 126 | } elseif ($intMinor -gt 12 ) { 127 | return $true 128 | } 129 | return $false 130 | } 131 | 132 | # Get-PublicSettings-From-Config-Json gets all public settings from the current configuration file n.settings 133 | function Get-PublicSettings-From-Config-Json($key, $powershellVersion) { 134 | Try 135 | { 136 | $azureConfigFile = Get-Azure-Latest-Config-File($powershellVersion) 137 | $jsonContents = Get-Content $azureConfigFile 138 | $normalizedJson = normalize-json($jsonContents) 139 | if ( $powershellVersion -ge 3 ) { 140 | $keyVal = ($normalizedJson | ConvertFrom-Json | Select -expand runtimeSettings | Select -expand handlerSettings | Select -expand publicSettings).$key 141 | } 142 | else { 143 | $ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer 144 | $keyVal = $ser.DeserializeObject($normalizedJson).runtimeSettings[0].handlerSettings.publicSettings.$key 145 | } 146 | return $keyVal 147 | } 148 | Catch 149 | { 150 | $ErrorMessage = $_.Exception.Message 151 | $FailedItem = $_.Exception.ItemName 152 | Write-Log "Failed to read file: $FailedItem. The error message was $ErrorMessage" "ERROR" 153 | throw "Error in Get-PublicSettings-From-Config-Json. Couldn't parse $azureConfigFile" 154 | } 155 | } 156 | 157 | # Get-ProtectedSettings-From-Config-Json gets all private/encrypted settings from the current configuration file n.settings 158 | function Get-ProtectedSettings-From-Config-Json($key, $powershellVersion) { 159 | Try 160 | { 161 | $azureConfigFile = Get-Azure-Latest-Config-File($powershellVersion) 162 | $jsonContents = Get-Content $azureConfigFile 163 | $normalizedJson = normalize-json($jsonContents) 164 | if ( $powershellVersion -ge 3 ) { 165 | $keyVal = ($normalizedJson | ConvertFrom-Json | Select -expand runtimeSettings | Select -expand handlerSettings).$key 166 | } 167 | else { 168 | $ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer 169 | $keyVal = $ser.DeserializeObject($normalizedJson).runtimeSettings[0].handlerSettings.$key 170 | } 171 | return $keyVal 172 | } 173 | Catch 174 | { 175 | $ErrorMessage = $_.Exception.Message 176 | $FailedItem = $_.Exception.ItemName 177 | Write-Log "Failed to read file: $FailedItem. The error message was $ErrorMessage" "ERROR" 178 | throw "Error in Get-ProtectedSettings-From-Config-Json. Couldn't parse $azureConfigFile" 179 | } 180 | } 181 | 182 | # Get-Azure-Logs-Path retrieves the log path from HandlerEnvironment.json file 183 | function Get-Azure-Logs-Path() { 184 | try 185 | { 186 | $powershellVersion = Get-PowershellVersion 187 | $handlerFile = "$extensionRoot\\HandlerEnvironment.json" 188 | if ( $powershellVersion -ge 3 ) { 189 | $logsFolder = (((Get-Content $handlerFile) | ConvertFrom-Json)[0] | Select -expand handlerEnvironment).logFolder 190 | } 191 | else { 192 | add-type -assembly system.web.extensions 193 | $ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer 194 | $logsFolder = ($ser.DeserializeObject($(Get-Content $handlerFile)))[0].handlerEnvironment.logFolder 195 | } 196 | return $logsFolder 197 | } 198 | catch 199 | { 200 | $ErrorMessage = $_.Exception.Message 201 | $FailedItem = $_.Exception.ItemName 202 | Write-Host "Failed to read file: $FailedItem. The error message was $ErrorMessage" 203 | throw "Error in Get-Azure-Logs-Path. Couldn't parse the HandlerEnvironment.json file" 204 | } 205 | } 206 | 207 | # Get-Azure-Latest-Config-File retrieves the latest configuration file n.settings 208 | function Get-Azure-Latest-Config-File($powershellVersion) { 209 | Try 210 | { 211 | $handlerFile = "$extensionRoot\HandlerEnvironment.json" 212 | if ( $powershellVersion -ge 3 ) { 213 | $configFolder = (((Get-Content $handlerFile) | ConvertFrom-Json)[0] | Select -expand handlerEnvironment).configFolder 214 | } 215 | else { 216 | add-type -assembly system.web.extensions 217 | $ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer 218 | $configFolder = ($ser.DeserializeObject($(Get-Content $handlerFile)))[0].handlerEnvironment.configFolder 219 | } 220 | 221 | # Get the last .settings file 222 | $configFileName = Get-Latest-Settings-File($configFolder) 223 | $azureConfigFile = "$configFolder\$configFileName" 224 | Write-Log "The latest configuration file is $azureConfigFile" "INFO" 225 | $configFileIsFolder = (Get-Item $azureConfigFile) -is [System.IO.DirectoryInfo] 226 | 227 | # In case of update, the n.settings file doesn't exists initially in the 228 | # folder of the new extension. Hence using the n.settings file copied into 229 | # the C:\Elastic folder during enable 230 | if ( $configFileIsFolder ) { 231 | Write-Log "n.settings file doesn't exist in the extension folder." "ERROR" 232 | throw "Error in Get-Azure-Latest-Config-File. Missing settings file" 233 | } 234 | return $azureConfigFile 235 | } 236 | Catch 237 | { 238 | $ErrorMessage = $_.Exception.Message 239 | $FailedItem = $_.Exception.ItemName 240 | Write-Log "Failed to read file: $FailedItem. The error message was $ErrorMessage" "ERROR" 241 | throw "Error in Get-Azure-Latest-Config-File. Couldn't parse the HandlerEnvironment.json file" 242 | } 243 | } 244 | 245 | # Get-Azure-Status-Path retrieves the status path from HandlerEnvironment.json file 246 | function Get-Azure-Status-Path($powershellVersion) { 247 | Try 248 | { 249 | $handlerFile = "$extensionRoot\\HandlerEnvironment.json" 250 | 251 | if ( $powershellVersion -ge 3 ) { 252 | $statusFolder = (((Get-Content $handlerFile) | ConvertFrom-Json)[0] | Select -expand handlerEnvironment).statusFolder 253 | } 254 | else { 255 | add-type -assembly system.web.extensions 256 | $ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer 257 | $statusFolder = ($ser.DeserializeObject($(Get-Content $handlerFile)))[0].handlerEnvironment.statusFolder 258 | } 259 | return $statusFolder 260 | } 261 | Catch 262 | { 263 | $ErrorMessage = $_.Exception.Message 264 | $FailedItem = $_.Exception.ItemName 265 | Write-Log "Failed to read file: $FailedItem. The error message was $ErrorMessage" "ERROR" 266 | throw "Error in Get-Azure-Status-Path. Couldn't parse the HandlerEnvironment.json file" 267 | } 268 | } 269 | 270 | # Get-Latest-Settings-File retrieves the latest file from a directory 271 | function Get-Latest-Settings-File($configFolder) { 272 | $configFiles = get-childitem $configFolder -recurse | where {$_.extension -eq ".settings"} 273 | 274 | if($configFiles -is [system.array]) { 275 | $configFileName = $configFiles[-1].Name 276 | } 277 | else { 278 | $configFileName = $configFiles.Name 279 | } 280 | return $configFileName 281 | } 282 | 283 | # Get-Sequence retrieves latest sequence number in the configuration directory 284 | function Get-Sequence() { 285 | $settingsSequence = "0" 286 | $powershellVersion = Get-PowershellVersion 287 | $azureConfigFile = Get-Azure-Latest-Config-File($powershellVersion) 288 | if ($azureConfigFile) { 289 | $outputFile = Split-Path $azureConfigFile -leaf 290 | $items = $outputFile.split(".") 291 | $settingsSequence = $items[0] 292 | } 293 | return $settingsSequence 294 | } 295 | 296 | # DownloadFile downloads the elastic agent artifact from either public repo or staging (for testing) 297 | function DownloadFile { 298 | Param( 299 | [Parameter(Mandatory=$True)] 300 | [hashtable]$Params, 301 | [int]$Retries = 3 302 | ) 303 | $package = $Params['Package'] 304 | $outFile = $Params['OutFile'] 305 | [int]$trials = 0 306 | $webClient = New-Object net.webclient 307 | $algorithm="512" 308 | $shasum="$package.sha$algorithm" 309 | $shasumUrl="https://artifacts.elastic.co/downloads/beats/elastic-agent/${shasum}" 310 | $releasedUrl= "https://artifacts.elastic.co/downloads/beats/elastic-agent/${package}" 311 | $stagingUrl="https://artifacts-api.elastic.co/v1/downloads/beats/${package}" 312 | do { 313 | try { 314 | $trials +=1 315 | $webClient.DownloadFile($releasedUrl, $outFile) 316 | Write-Log "Elastic Agent downloaded" "INFO" 317 | break 318 | } catch [System.Net.WebException] { 319 | $statusCode= $_.Exception.Response.StatusCode.Value__ 320 | if ( $statusCode = "404") { 321 | try { 322 | $webClient.DownloadFile($stagingUrl, $outFile) 323 | Write-Log "Elastic Agent downloaded" "INFO" 324 | break 325 | } catch { 326 | Write-Log "Problem downloading $stagingUrl `tTrial $trials `n` tException: $_.Exception.Message" "ERROR" 327 | throw "Problem downloading $stagingUrl `tTrial $trials `n` tException: $_.Exception.Message" 328 | } 329 | } 330 | else 331 | { 332 | Write-Log "Problem downloading $releasedUrl `tTrial $trials `n` tException: $_.Exception.Message" "ERROR" 333 | throw "Problem downloading $releasedUrl `tTrial $trials `n` tException: $_.Exception.Message" 334 | } 335 | } 336 | } 337 | while ($trials -lt $Retries) 338 | } 339 | 340 | # Get-Latest-Status-File gets the latest status file in the directory 341 | function Get-Latest-Status-File($statusFolder) { 342 | $statusFiles = get-childitem $statusFolder -recurse | where {$_.extension -eq ".status"} 343 | 344 | if($statusFiles -is [system.array]) { 345 | $statusFileName = $statusFiles[-1].Name 346 | } 347 | else { 348 | $statusFileName = $statusFiles.Name 349 | } 350 | return $statusFileName 351 | } 352 | 353 | # Write-Status writes status in the status file, required for azure vm extension install process 354 | function Write-Status 355 | { 356 | Param 357 | ( 358 | [Parameter(Mandatory=$true, Position=0)] 359 | [string] $name, 360 | [Parameter(Mandatory=$true, Position=1)] 361 | [string] $operation, 362 | [Parameter(Mandatory=$true, Position=2)] 363 | [string] $mainStatus, 364 | [Parameter(Mandatory=$true, Position=3)] 365 | [string] $mainMessage, 366 | [Parameter(Mandatory=$true, Position=4)] 367 | [string] $subname, 368 | [Parameter(Mandatory=$true, Position=5)] 369 | [string] $subStatus, 370 | [Parameter(Mandatory=$true, Position=6)] 371 | [string] $subMessage 372 | ) 373 | $sequenceNumber = Get-Sequence 374 | $code = 0 375 | $statusPath = Get-Azure-Status-Path 376 | if ( $statusPath) { 377 | $statusFile = $statusPath + "\\" + $sequenceNumber + ".status" 378 | #transitioning, error, success and warning 379 | if ($subStatus -eq "error") { 380 | $code = 1 381 | } 382 | $timestampUTC = (Get-Date -Format u).Replace(" ", "T") 383 | $jsonRequest = @( 384 | @{ 385 | version="1.0" 386 | timestampUTC = "$timestampUTC" 387 | status= @{ 388 | name = "$name" 389 | operation = "$operation" 390 | status = "$mainStatus" 391 | formattedMessage =@{ 392 | lang = "en-US" 393 | message = "$mainMessage" 394 | } 395 | substatus = @( 396 | @{ 397 | name = "$subName" 398 | status = "$subStatus" 399 | code = $code 400 | formattedMessage =@{ 401 | lang = "en-US" 402 | message = "$subMessage" 403 | } 404 | } 405 | ) 406 | } 407 | } 408 | ) 409 | if ( $(Get-PowershellVersion) -ge 3) { 410 | ConvertTo-Json -Compress $jsonRequest -Depth 6 | Out-File -filePath $statusFile 411 | } 412 | } 413 | } 414 | 415 | # normalize-json helper function to normalize json 416 | function normalize-json($json) { 417 | $json -Join " " 418 | } 419 | 420 | # Get-Agent-Id gets the agent id value from the fleet.yml file 421 | function Get-Agent-Id($fileLocation){ 422 | $text = Get-Content -Path "$fileLocation" 423 | $regex = '(?ms)(^)agent:(?:.+?)id:\s?(.*?)(?:[\r\n]|$)' 424 | $text = $text -join "`n" 425 | $OutputText = [regex]::Matches($text, $regex) | 426 | foreach {$_.Groups[2].Value -split $regex} 427 | return $OutputText 428 | } 429 | 430 | # Get-Azure-Policy retrieves default policy from the list of policies 431 | function Get-Azure-Policy($content){ 432 | foreach ($policy in $content) { 433 | if ($policy.name -like $policyName -And $policy.status -eq "active" -And $policy.id -notlike "*elastic-agent-on-cloud*") { 434 | return $policy.id 435 | } 436 | } 437 | } 438 | 439 | # Create-Azure-Policy creates a dedicated Azure VM extension policy 440 | function Create-Azure-Policy($content){ 441 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 442 | $headers.Add("kbn-xsrf", "true") 443 | $encodedCredentials = "" 444 | if ($password) { 445 | $username = Get-Username $powershellVersion 446 | if (-Not $username) { 447 | throw "Username could not be found" 448 | } 449 | $pair = "$($username):$($password)" 450 | $encodedCredentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair)) 451 | } else { 452 | $encodedCredentials = $base64Auth 453 | } 454 | $headers.Add('Authorization', "Basic $encodedCredentials") 455 | if ( $powershellVersion -gt 3 ) { 456 | $headers.Add("Accept","application/json") 457 | } 458 | 459 | $Body = @{ 460 | name = $policyName 461 | description = 'Default agent policy for Azure VM extension' 462 | namespace = 'default' 463 | monitoring_enabled = '["logs","metrics"]' 464 | 465 | } 466 | $jsonResult = Invoke-WebRequest -Uri "$($kibanaUrl)/api/fleet/agent_policies?sys_monitoring=true" -Method 'POST' -Headers $headers -Body $Body -UseBasicParsing 467 | if ($jsonResult.statuscode -eq '200') { 468 | Write-Log "Successfully created the Azure VM extension policy $jsonResult" "INFO" 469 | } 470 | else { 471 | throw "Creating Azure VM extension policy failed with $jsonResult.statuscode" 472 | } 473 | 474 | # get new policy id 475 | $policy= ConvertFrom-Json $jsonResult.Content | Select-Object -expand "item" 476 | if ($policy.name -eq $policyName -And $policy.status -eq "active") { 477 | return $policy.id 478 | } 479 | } 480 | 481 | #region encryption 482 | 483 | # Encrypt will encrypt text based on certificate thumprint 484 | Function Encrypt { 485 | [CmdletBinding()] 486 | [OutputType([System.String])] 487 | param( 488 | [Parameter(Position=0, Mandatory=$true)][ValidateNotNullOrEmpty()][System.String] 489 | $ClearText, 490 | [Parameter(Position=1, Mandatory=$true)][ValidateNotNullOrEmpty()][System.String] 491 | $CertThumbprint 492 | ) 493 | $store = new-object System.Security.Cryptography.X509Certificates.X509Store([System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine) 494 | $store.open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly) 495 | $cert = $store.Certificates | Where-Object {$_.thumbprint -eq $CertThumbprint} 496 | 497 | $utf8EncrypedByteArray = [System.Text.Encoding]::UTF8.GetBytes($ClearText) 498 | $content = New-Object Security.Cryptography.Pkcs.ContentInfo -argumentList (,$utf8EncrypedByteArray) 499 | $env = New-Object Security.Cryptography.Pkcs.EnvelopedCms $content 500 | $recpient = (New-Object System.Security.Cryptography.Pkcs.CmsRecipient($cert)) 501 | $env.Encrypt($recpient) 502 | $base64string = [Convert]::ToBase64String($env.Encode()) 503 | Return $base64string 504 | } 505 | 506 | # Decrypt will decrypt text based on certificate thumprint 507 | function Decrypt 508 | { 509 | [CmdletBinding()] 510 | [OutputType([System.String])] 511 | param( 512 | [Parameter(Position=0, Mandatory=$true)][ValidateNotNullOrEmpty()][System.String] 513 | $EncryptedBase64String, 514 | [Parameter(Position=1, Mandatory=$true)][ValidateNotNullOrEmpty()][System.String] 515 | $CertThumbprint 516 | ) 517 | [System.Reflection.Assembly]::LoadWithPartialName("System.Security") | out-null 518 | $encryptedByteArray = [Convert]::FromBase64String($EncryptedBase64String) 519 | $envelope = New-Object System.Security.Cryptography.Pkcs.EnvelopedCms 520 | 521 | # get certificate from local machine store 522 | $store = new-object System.Security.Cryptography.X509Certificates.X509Store([System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine) 523 | $store.open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadOnly) 524 | $cert = $store.Certificates | Where-Object {$_.thumbprint -eq $CertThumbprint} 525 | if($cert) { 526 | $envelope.Decode($encryptedByteArray) 527 | $envelope.Decrypt($cert) 528 | $decryptedBytes = $envelope.ContentInfo.Content 529 | $decryptedResult = [System.Text.Encoding]::UTF8.GetString($decryptedBytes) 530 | Return $decryptedResult 531 | } 532 | Return "" 533 | } 534 | 535 | # Get-Password will retrieve the private setting from the configruation file, decrypt it and return it 536 | function Get-Password($powershellVersion) { 537 | Try 538 | { 539 | $thumbprint = Get-ProtectedSettings-From-Config-Json "protectedSettingsCertThumbprint" $powershellVersion 540 | $protectedSettings = Get-ProtectedSettings-From-Config-Json "protectedSettings" $powershellVersion 541 | if ( $thumbprint -ne "" -and $protectedSettings -ne "") { 542 | $jsonKeys = Decrypt $protectedSettings $thumbprint 543 | if ($jsonKeys) { 544 | $normalizedJsonKeys = normalize-json($jsonKeys) 545 | if ( $powershellVersion -ge 3 ) { 546 | $value = ($normalizedJsonKeys | ConvertFrom-Json).password 547 | } 548 | else { 549 | $ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer 550 | $value = $ser.DeserializeObject($normalizedJsonKeys).password 551 | } 552 | Return $value 553 | } 554 | } 555 | } 556 | Catch 557 | { 558 | $ErrorMessage = $_.Exception.Message 559 | $FailedItem = $_.Exception.ItemName 560 | Write-Log "Failed to read file: $FailedItem. The error message was $ErrorMessage" "ERROR" 561 | throw "Error in Get-ProtectedSettings-From-Config-Json. Couldn't parse configuration file" 562 | } 563 | } 564 | 565 | # Get-Base64Auth will retrieve the private setting from the configruation file, decrypt it and return it 566 | function Get-Base64Auth($powershellVersion) { 567 | Try 568 | { 569 | $thumbprint = Get-ProtectedSettings-From-Config-Json "protectedSettingsCertThumbprint" $powershellVersion 570 | $protectedSettings = Get-ProtectedSettings-From-Config-Json "protectedSettings" $powershellVersion 571 | if ( $thumbprint -ne "" -and $protectedSettings -ne "") { 572 | $jsonKeys = Decrypt $protectedSettings $thumbprint 573 | if ($jsonKeys) { 574 | $normalizedJsonKeys = normalize-json($jsonKeys) 575 | if ( $powershellVersion -ge 3 ) { 576 | $value = ($normalizedJsonKeys | ConvertFrom-Json).base64Auth 577 | } 578 | else { 579 | $ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer 580 | $value = $ser.DeserializeObject($normalizedJsonKeys).base64Auth 581 | } 582 | Return $value 583 | } 584 | } 585 | } 586 | Catch 587 | { 588 | $ErrorMessage = $_.Exception.Message 589 | $FailedItem = $_.Exception.ItemName 590 | Write-Log "Failed to read file: $FailedItem. The error message was $ErrorMessage" "ERROR" 591 | throw "Error in Get-ProtectedSettings-From-Config-Json. Couldn't parse configuration file" 592 | } 593 | } 594 | 595 | 596 | #Env 597 | 598 | # Is-New-Config checks if user has entered new configurations options for the elastic agent 599 | function Is-New-Config { 600 | $currentSequence = [Environment]::GetEnvironmentVariable('ELASTICAGENTSEQUENCE', 'Machine') 601 | $newSequence = [Environment]::GetEnvironmentVariable("ConfigSequenceNumber") 602 | $isUpdate = [Environment]::GetEnvironmentVariable("ELASTICAGENTUPDATE") 603 | Write-Log "Current sequence is $currentSequence and new sequence is $newSequence" "INFO" 604 | if (!$newSequence) { 605 | return $false 606 | } 607 | if ($isUpdate -eq "1") { 608 | Write-Log "Part of update" "INFO" 609 | return $false 610 | } 611 | if ($currentSequence -eq $newSequence ) { 612 | return $false 613 | } 614 | return $true 615 | } 616 | 617 | # Set-SequenceEnvVariables sets env variables to check for new sequence numbers 618 | function Set-SequenceEnvVariables 619 | { 620 | $newSequence = [Environment]::GetEnvironmentVariable("ConfigSequenceNumber") 621 | if (!$newSequence) { 622 | $newSequence = Get-Sequence 623 | } 624 | [Environment]::SetEnvironmentVariable("ELASTICAGENTSEQUENCE", $newSequence, "Machine") 625 | [Environment]::SetEnvironmentVariable("ELASTICAGENTUPDATE", 0, "Machine") 626 | } 627 | 628 | # Set-UpdateEnvVariables sets env variable if is vm extension update 629 | function Set-UpdateEnvVariables 630 | { 631 | [Environment]::SetEnvironmentVariable("ELASTICAGENTUPDATE", 1, "Machine") 632 | } 633 | 634 | #Get-Prev-Settings-File gets previous configuration file in specific folder 635 | function Get-Prev-Settings-File($configFolder) { 636 | $configFiles = get-childitem $configFolder -recurse | where {$_.extension -eq ".settings"} 637 | if($configFiles -is [system.array]) { 638 | $configFileName = $configFiles[-2].Name 639 | } 640 | else { 641 | $configFileName = $configFiles.Name 642 | } 643 | return $configFileName 644 | } 645 | 646 | # previous section includes similar functions to the latest ones, they will retrieve the previous configuration setup in order to unenroll the elastic agent from the old Fleet env and uninstall it 647 | 648 | #Get-Azure-Prev-Config-File gets previous configuration file 649 | function Get-Azure-Prev-Config-File($powershellVersion) { 650 | Try 651 | { 652 | $handlerFile = "$extensionRoot\HandlerEnvironment.json" 653 | if ( $powershellVersion -ge 3 ) { 654 | $configFolder = (((Get-Content $handlerFile) | ConvertFrom-Json)[0] | Select -expand handlerEnvironment).configFolder 655 | } 656 | else { 657 | add-type -assembly system.web.extensions 658 | $ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer 659 | $configFolder = ($ser.DeserializeObject($(Get-Content $handlerFile)))[0].handlerEnvironment.configFolder 660 | } 661 | # Get the last .settings file 662 | $configFileName = Get-Prev-Settings-File($configFolder) 663 | $azureConfigFile = "$configFolder\$configFileName" 664 | Write-Log "The previous file is $azureConfigFile" "INFO" 665 | $configFileIsFolder = (Get-Item $azureConfigFile) -is [System.IO.DirectoryInfo] 666 | 667 | # In case of update, the n.settings file doesn't exists initially in the 668 | # folder of the new extension. Hence using the n.settings file copied into 669 | # the C:\Elastic folder during enable 670 | if ( $configFileIsFolder ) { 671 | Write-Log "n.settings file doesn't exist in the extension folder." "ERROR" 672 | throw "Error in Get-Azure-Prev-Config-File. Missing settings file" 673 | } 674 | return $azureConfigFile 675 | } 676 | Catch 677 | { 678 | $ErrorMessage = $_.Exception.Message 679 | $FailedItem = $_.Exception.ItemName 680 | Write-Log "Failed to read file: $FailedItem. The error message was $ErrorMessage" "ERROR" 681 | throw "Error in Get-Azure-Prev-Config-File. Couldn't parse the HandlerEnvironment.json file" 682 | } 683 | } 684 | 685 | # Get-Prev-ProtectedSettings-From-Config-Json retrieves the previous private configuration settings 686 | function Get-Prev-ProtectedSettings-From-Config-Json($key, $powershellVersion) { 687 | Try 688 | { 689 | $azureConfigFile = Get-Azure-Prev-Config-File($powershellVersion) 690 | $jsonContents = Get-Content $azureConfigFile 691 | $normalizedJson = normalize-json($jsonContents) 692 | if ( $powershellVersion -ge 3 ) { 693 | $keyVal = ($normalizedJson | ConvertFrom-Json | Select -expand runtimeSettings | Select -expand handlerSettings).$key 694 | } 695 | else { 696 | $ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer 697 | $keyVal = $ser.DeserializeObject($normalizedJson).runtimeSettings[0].handlerSettings.$key 698 | } 699 | return $keyVal 700 | } 701 | Catch 702 | { 703 | $ErrorMessage = $_.Exception.Message 704 | $FailedItem = $_.Exception.ItemName 705 | Write-Log "Failed to read file: $FailedItem. The error message was $ErrorMessage" "ERROR" 706 | throw "Error in Get-Prev-ProtectedSettings-From-Config-Json. Couldn't parse $azureConfigFile" 707 | } 708 | } 709 | 710 | # Get-Prev-Password retrieves password from previous configuration options 711 | function Get-Prev-Password($powershellVersion) { 712 | Try 713 | { 714 | $thumbprint = Get-Prev-ProtectedSettings-From-Config-Json "protectedSettingsCertThumbprint" $powershellVersion 715 | $protectedSettings = Get-Prev-ProtectedSettings-From-Config-Json "protectedSettings" $powershellVersion 716 | if ( $thumbprint -ne "" -and $protectedSettings -ne "") { 717 | $jsonKeys = Decrypt $protectedSettings $thumbprint 718 | if ($jsonKeys) { 719 | $normalizedJsonKeys = normalize-json($jsonKeys) 720 | if ( $powershellVersion -ge 3 ) { 721 | $value = ($normalizedJsonKeys | ConvertFrom-Json).password 722 | } 723 | else { 724 | $ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer 725 | $value = $ser.DeserializeObject($normalizedJsonKeys).password 726 | } 727 | Return $value 728 | } 729 | } 730 | } 731 | Catch 732 | { 733 | $ErrorMessage = $_.Exception.Message 734 | $FailedItem = $_.Exception.ItemName 735 | Write-Log "Failed to read file: $FailedItem. The error message was $ErrorMessage" "ERROR" 736 | throw "Error in Get-Prev-ProtectedSettings-From-Config-Json. Couldn't parse configuration file" 737 | } 738 | } 739 | 740 | # Get-Prev-Base64Auth retrieves base64auth from previous configuration options 741 | function Get-Prev-Base64Auth($powershellVersion) { 742 | Try 743 | { 744 | $thumbprint = Get-Prev-ProtectedSettings-From-Config-Json "protectedSettingsCertThumbprint" $powershellVersion 745 | $protectedSettings = Get-Prev-ProtectedSettings-From-Config-Json "protectedSettings" $powershellVersion 746 | if ( $thumbprint -ne "" -and $protectedSettings -ne "") { 747 | $jsonKeys = Decrypt $protectedSettings $thumbprint 748 | if ($jsonKeys) { 749 | $normalizedJsonKeys = normalize-json($jsonKeys) 750 | if ( $powershellVersion -ge 3 ) { 751 | $value = ($normalizedJsonKeys | ConvertFrom-Json).base64Auth 752 | 753 | } 754 | else { 755 | $ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer 756 | $value = $ser.DeserializeObject($normalizedJsonKeys).base64Auth 757 | } 758 | Return $value 759 | } 760 | } 761 | } 762 | Catch 763 | { 764 | $ErrorMessage = $_.Exception.Message 765 | $FailedItem = $_.Exception.ItemName 766 | Write-Log "Failed to read file: $FailedItem. The error message was $ErrorMessage" "ERROR" 767 | throw "Error in Get-Prev-ProtectedSettings-From-Config-Json. Couldn't parse configuration file" 768 | } 769 | } 770 | 771 | # Get-Prev-PublicSettings-From-Config-Json retrieves previous public settings 772 | function Get-Prev-PublicSettings-From-Config-Json($key, $powershellVersion) { 773 | Try 774 | { 775 | $azureConfigFile = Get-Azure-Prev-Config-File($powershellVersion) 776 | $jsonContents = Get-Content $azureConfigFile 777 | $normalizedJson = normalize-json($jsonContents) 778 | if ( $powershellVersion -ge 3 ) { 779 | $keyVal = ($normalizedJson | ConvertFrom-Json | Select -expand runtimeSettings | Select -expand handlerSettings | Select -expand publicSettings).$key 780 | } 781 | else { 782 | $ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer 783 | $keyVal = $ser.DeserializeObject($normalizedJson).runtimeSettings[0].handlerSettings.publicSettings.$key 784 | } 785 | return $keyVal 786 | } 787 | Catch 788 | { 789 | $ErrorMessage = $_.Exception.Message 790 | $FailedItem = $_.Exception.ItemName 791 | echo "Failed to read file: $FailedItem. The error message was $ErrorMessage" 792 | throw "Error in Get-Prev-PublicSettings-From-Config-Json. Couldn't parse $azureConfigFile" 793 | } 794 | } 795 | 796 | # Get-Prev-CloudId retrieves previous cloudID 797 | function Get-Prev-CloudId($powershellVersion) { 798 | $cloudId = Get-Prev-PublicSettings-From-Config-Json "cloudId" $powershellVersion 799 | if ( $cloudId){ 800 | return $cloudId 801 | } 802 | return "" 803 | } 804 | 805 | # Get-Prev-Username retrieves previous username 806 | function Get-Prev-Username($powershellVersion) { 807 | $username = Get-Prev-PublicSettings-From-Config-Json "username" $powershellVersion 808 | if ( $username){ 809 | return $username 810 | } 811 | return "" 812 | } 813 | 814 | # Get-Prev-Kibana-URL retrieves previous kibana URL 815 | function Get-Prev-Kibana-URL($powershellVersion) { 816 | $cloudId = Get-Prev-CloudId $powershellVersion 817 | if ( $cloudId -ne ""){ 818 | $cloudHash=$cloudId.split(":")[-1] 819 | $cloudTokens=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($cloudHash)) 820 | $cloudElems=$cloudTokens.split("$") 821 | $hostPort= $cloudElems[0] 822 | return "https://$($cloudElems[2]).$(${hostPort})" 823 | } 824 | return "" 825 | } 826 | 827 | # Get-Prev-Elasticsearch-URL retrieves previous ES URL 828 | function Get-Prev-Elasticsearch-URL($powershellVersion) { 829 | $powershellVersion = Get-PowershellVersion 830 | $cloudId = Get-Prev-CloudId $powershellVersion 831 | if ( $cloudId -ne ""){ 832 | $cloudHash=$cloudId.split(":")[-1] 833 | $cloudTokens=[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($cloudHash)) 834 | $cloudElems=$cloudTokens.split("$") 835 | $hostPort= $cloudElems[0] 836 | return "https://$($cloudElems[1]).$(${hostPort})" 837 | } 838 | return "" 839 | } 840 | 841 | # Get-Prev-Stack-Version retrieves previous stack version 842 | function Get-Prev-Stack-Version { 843 | $powershellVersion = Get-PowershellVersion 844 | $elasticsearchUrl = Get-Prev-Elasticsearch-URL $powershellVersion 845 | if (-Not $elasticsearchUrl) { 846 | throw "Elasticsearch URL could not be found" 847 | } 848 | $password = Get-Prev-Password $powershellVersion 849 | $base64Auth = Get-Prev-Base64Auth $powershellVersion 850 | if (-Not $password -And -Not $base64Auth) { 851 | throw "Password or base64auto key could not be found" 852 | } 853 | $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]" 854 | if ( $powershellVersion -gt 3 ) { 855 | $headers.Add("Accept","application/json") 856 | } 857 | #cred 858 | $encodedCredentials = "" 859 | if ($password) { 860 | $username = Get-Prev-Username $powershellVersion 861 | if (-Not $username) { 862 | throw "Username could not be found" 863 | } 864 | $pair = "$($username):$($password)" 865 | $encodedCredentials = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair)) 866 | } else { 867 | $encodedCredentials = $base64Auth 868 | } 869 | $headers.Add('Authorization', "Basic $encodedCredentials") 870 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 871 | $jsonResult = Invoke-WebRequest -Uri "$($elasticsearchUrl)" -Method 'GET' -Headers $headers -UseBasicParsing 872 | if ($jsonResult.statuscode -eq '200') { 873 | $keyValue= ConvertFrom-Json $jsonResult.Content | Select-Object -expand "" 874 | $stackVersion=$keyValue.version.number 875 | Write-Log "Found stack version $stackVersion" "INFO" 876 | return $stackVersion 877 | }else { 878 | Write-Log "Error pinging elastic cluster $elasticsearchUrl" "ERROR" 879 | return "" 880 | } 881 | return "" 882 | } 883 | 884 | # Clean-And-Exit cleans up the config file and exits 885 | function Clean-And-Exit($exitCode) { 886 | $powershellVersion = Get-PowershellVersion 887 | Try 888 | { 889 | $azureConfigFile = Get-Azure-Latest-Config-File($powershellVersion) 890 | Set-Content -Path $azureConfigFile -Value "{}" 891 | } 892 | Catch 893 | { 894 | $ErrorMessage = $_.Exception.Message 895 | $FailedItem = $_.Exception.ItemName 896 | Write-Log "Failed to clear config file: $FailedItem. The error message was $ErrorMessage" "ERROR" 897 | } 898 | 899 | exit $exitCode 900 | } 901 | --------------------------------------------------------------------------------