Category Archives: Logs, Tips, and Scripts

ESXi windows.iso error not found

Bagi kamu yang sudah upgrade ESXi dan tiba-tiba gak bisa upgrade atau install VMware Tools untuk VM guest, dengan pesan error The required vmware tools ISO image does not exist or is inaccessible. Vix 21001. coba lakukan ini untuk memperbaiki symbolic link productLocker;

mv /usr/lib/vmware/isoimages /usr/lib/vmware/isoimages.tmp

rm /productLocker 

ln -s /vmfs/volumes/datastore2/Tools /productLocker

mv /usr/lib/vmware/isoimages.tmp /usr/lib/vmware/isoimages

Solusi ini sebenernya merujuk pada KB 2129825, tapi setelah saya coba KB tersebut malah ada beberapa command yang gak bisa. YMMV, bisa dicoba dengan 4 lines command saya di atas.

Putting MySQL database file on external drive

Currently hosting websites on a Raspberry Pi 4, with all web data already on external SSD drive. But then why not put the database files also on the SSD? Since there’s no point to have fast SSD if DB query still capped by crappy SD card read/write speed.

I’d create a custom file /etc/mysql/mariadb.conf.d/99-local.cnf with your changes in it. That would override the original file without having to change it or worrying about it getting overwritten during a software upgrade.

# This is /etc/mysql/mariadb.conf.d/99-local.cnf
datadir = /ssd/mysql

The external SSD drive must be mounted before the mariadb server starts, otherwise it will fail. You can verify the changes by using mysql command SHOW VARIABLES LIKE '%datadir%';

root@buggy-sunshine:~# mysql -uroot
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 247
Server version: 10.5.10-MariaDB-0ubuntu0.21.04.1 Ubuntu 21.04

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> SHOW VARIABLES LIKE '%datadir%';
| Variable_name | Value       |
| datadir       | /ssd/mysql/ |
1 row in set (0.004 sec)

MariaDB [(none)]>

FTP user isolation on Windows Server 2019

Run these commands on PowerShell with Admin Privilege;

# add FTP site
# -Name [any name you like]
# -IPAddress [listening IP address] (below is (all))
# -Port [listening port]
PS C:\Users\Administrator> New-WebFtpSite -Name "FTPRoot" -IPAddress "*" -Port 21 

Name             ID   State      Physical Path                  Bindings
----             --   -----      -------------                  --------
FTPRoot          2    Started                                   ftp *:21:

# set physical folder that is used for FTP site
# example below, create a [FTPSite01] folder under the [C:\inetpub\ftproot] that is created by default and set it
PS C:\Users\Administrator> Set-ItemProperty "IIS:\Sites\FTPRoot" -Name physicalPath -Value 'C:\inetpub\ftproot' 

# set SSL/TLS setting (example below is allowing No SSL)
PS C:\Users\Administrator> Set-ItemProperty "IIS:\Sites\FTPRoot" -Name -Value "SslAllow" 
PS C:\Users\Administrator> Set-ItemProperty "IIS:\Sites\FTPRoot" -Name -Value "SslAllow" 

# set basic authentication
PS C:\Users\Administrator> Set-ItemProperty "IIS:\Sites\FTPRoot" -Name -Value $true 

# set read and write authority to all local users
PS C:\Users\Administrator> Add-WebConfiguration "/system.ftpServer/security/authorization" -Location FTPRoot -PSPath IIS:\ -Value @{accessType="Allow";users="*";permissions="Read,Write"} 

# set user isolation
PS C:\Users\Administrator> Set-ItemProperty "IIS:\Sites\FTPRoot" -Name ftpServer.userIsolation.mode -Value "IsolateRootDirectoryOnly" 

# set external IP address (the one client computers can connect - completely optional, if there is no host firewall on your server)
PS C:\Users\Administrator> Set-ItemProperty "IIS:\Sites\FTPRoot" -Name ftpServer.firewallSupport.externalIp4Address -Value "" 

# create the [LocalUser] folder under the Path you set as physical path of FTP site (it is needed on this setting)
# if Domain users, create [(FTP root)\(%UserDomain%)]
PS C:\Users\Administrator> mkdir C:\inetpub\ftproot\LocalUser 

    Directory: C:\inetpub\ftproot

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----         9/5/2019  10:19 PM                LocalUser

# restart FTP site
PS C:\Users\Administrator> Restart-WebItem -PSPath 'IIS:\Sites\FTPRoot' 

# create folders for each local user that each folder name is the same with thier username
# naming rule ⇒ [(FTP root)\LocalUser\(Username)] (example below is for [ariw] user)
PS C:\Users\Administrator> mkdir C:\inetpub\ftproot\LocalUser\ariw 
PS C:\Users\Administrator> icacls "C:\inetpub\ftproot\LocalUser\ariw" /grant "ariw:(OI)(CI)(F)" 
processed file: C:\inetpub\ftproot\LocalUser\ariw
Successfully processed 1 files; Failed processing 0 files

CloudFlare dynamic DNS update

#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail

# Automatically update your CloudFlare DNS record to your dynamic IP address
# Can retrieve cloudflare Domain id and list zones, because, lazy

# Credits:
# curl > /usr/local/bin/ && chmod +x /usr/local/bin/
# run `crontab -e` and add next line:
# */1 * * * * /usr/local/bin/ >/dev/null 2>&1
# or if you need log:
# */1 * * * * /usr/local/bin/ >> /var/log/cf-ddns.log 2>&1

# Usage:
# -k cloudflare-api-key \
#            -u \
#            -h \     # fqdn of the record you want to update
#            -z \          # will show you all zones if forgot, but you need this
#            -t A|AAAA                 # specify ipv4/ipv6, default: ipv4

# Optional flags:
#            -f false|true \           # force dns update, disregard local stored ip

# default config

# API key, see,
# incorrect api-key results in E_UNAUTH error

# Username, eg:

# Zone name, eg:

# Hostname to update, eg:

# Record type, A(IPv4)|AAAA(IPv6), default IPv4

# Cloudflare TTL for record, between 120 and 86400 seconds

# Ignore local file, update ip anyway


# Site to retrieve WAN ip, other examples are:, ...
if [ "$CFRECORD_TYPE" = "A" ]; then
elif [ "$CFRECORD_TYPE" = "AAAA" ]; then
  echo "$CFRECORD_TYPE specified is invalid, CFRECORD_TYPE can only be A(for IPv4)|AAAA(for IPv6)"
  exit 2

# get parameter
while getopts k:u:h:z:t:f: opts; do
  case ${opts} in
    k) CFKEY=${OPTARG} ;;
    u) CFUSER=${OPTARG} ;;
    f) FORCE=${OPTARG} ;;

# If required settings are missing just exit
if [ "$CFKEY" = "" ]; then
  echo "Missing api-key, get at:"
  echo "and save in ${0} or using the -k flag"
  exit 2
if [ "$CFUSER" = "" ]; then
  echo "Missing username, probably your email-address"
  echo "and save in ${0} or using the -u flag"
  exit 2
if [ "$CFRECORD_NAME" = "" ]; then 
  echo "Missing hostname, what host do you want to update?"
  echo "save in ${0} or using the -h flag"
  exit 2

# If the hostname is not a FQDN
if [ "$CFRECORD_NAME" != "$CFZONE_NAME" ] && ! [ -z "${CFRECORD_NAME##*$CFZONE_NAME}" ]; then
  echo " => Hostname is not a FQDN, assuming $CFRECORD_NAME"

# Get current and old WAN ip
WAN_IP=`curl -s ${WANIPSITE}`
if [ -f $WAN_IP_FILE ]; then
  echo "No file, need IP"

# If WAN IP is unchanged an not -f flag, exit here
if [ "$WAN_IP" = "$OLD_WAN_IP" ] && [ "$FORCE" = false ]; then
  echo "WAN IP Unchanged, to update anyway use flag -f true"
  exit 0

# Get zone_identifier & record_identifier
if [ -f $ID_FILE ] && [ $(wc -l $ID_FILE | cut -d " " -f 1) == 4 ] \
  && [ "$(sed -n '3,1p' "$ID_FILE")" == "$CFZONE_NAME" ] \
  && [ "$(sed -n '4,1p' "$ID_FILE")" == "$CFRECORD_NAME" ]; then
    CFZONE_ID=$(sed -n '1,1p' "$ID_FILE")
    CFRECORD_ID=$(sed -n '2,1p' "$ID_FILE")
    echo "Updating zone_identifier & record_identifier"
    CFZONE_ID=$(curl -s -X GET "$CFZONE_NAME" -H "X-Auth-Email: $CFUSER" -H "X-Auth-Key: $CFKEY" -H "Content-Type: application/json" | grep -Po '(?<="id":")[^"]*' | head -1 )
    CFRECORD_ID=$(curl -s -X GET "$CFZONE_ID/dns_records?name=$CFRECORD_NAME" -H "X-Auth-Email: $CFUSER" -H "X-Auth-Key: $CFKEY" -H "Content-Type: application/json"  | grep -Po '(?<="id":")[^"]*' | head -1 )
    echo "$CFZONE_ID" > $ID_FILE
    echo "$CFRECORD_ID" >> $ID_FILE
    echo "$CFZONE_NAME" >> $ID_FILE
    echo "$CFRECORD_NAME" >> $ID_FILE

# If WAN is changed, update cloudflare
echo "Updating DNS to $WAN_IP"

RESPONSE=$(curl -s -X PUT "$CFZONE_ID/dns_records/$CFRECORD_ID" \
  -H "X-Auth-Email: $CFUSER" \
  -H "X-Auth-Key: $CFKEY" \
  -H "Content-Type: application/json" \
  --data "{\"id\":\"$CFZONE_ID\",\"type\":\"$CFRECORD_TYPE\",\"name\":\"$CFRECORD_NAME\",\"content\":\"$WAN_IP\", \"ttl\":$CFTTL}")

if [ "$RESPONSE" != "${RESPONSE%success*}" ] && [ "$(echo $RESPONSE | grep "\"success\":true")" != "" ]; then
  echo "Updated succesfuly!"
  echo $WAN_IP > $WAN_IP_FILE
  echo 'Something went wrong :('
  echo "Response: $RESPONSE"
  exit 1