OmniAICreator/Qiita-1.07M huggingface dataset:
Amazon Simple Storage Service (S3) の7つのストレージクラス

![Screen Shot 2021-06-02 at 15.06.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/431877/2ac7bad7-91cb-99b8-5834-50f217f41c23.png)
#はじめに
AWSのサービスの中でも中核を担うS3には7つの種類（ストレージクラス）がある。作成したいアプリケーションの要件に合わせてストレージクラスを選択することで、コスト最適化を図ることができる。
※本記事は2021.6.2時点かつ東京リージョンの情報をもとに作成しています。
※上の画像では6つのストレージクラスのアイコンが載っていますが、RRSに関するアイコンは載っていません。

# 特徴など
・高い耐久性（あるデータが1年間で消失してしまう確率が0.000000001%。逆に消失しない確率は99.999999999%で9が11回続くことから高い耐久性を表す言葉として`イレブンナイン`がある）
・`バージョニング機能`がある（オブジェクト単位で管理されており、誤ってデータを削除したときなどに以前のバージョンに復元が可能）
・`クロスリージョンレプリケーション`機能がある（別リージョンのS3バケットにデータを複製することができる）
・1ファイルの容量には`5TB`までという制限あり
・key-value型の`オブジェクトストレージ`（オブジェクト指定の例`s3://bucketname/prefix/file.txt`）
・データの新規登録には`書き込み後の読み込み整合性`がある（データ保存後にS3から完了のレスポンスがあると参照できる）
・~~データの更新・削除には`結果整合性`がある（操作後にデータにアクセスしてもすぐに反映されている保証はない）~~
・GET, PUT, DELETE に対して`書き込み後の読み込み整合性`がある（2020.12のアップデート。これにより結果整合性から強い一貫性へ。[参考](https://aws.amazon.com/jp/blogs/aws/amazon-s3-update-strong-read-after-write-consistency/)）
・デフォルトではバケットやオブジェクトは`プライベートアクセス権限`に設定
・`マルチパートアップロード`はファイルを分割してアップロードできる機能で、スループットの向上を図れる。また、アップロードが中断されても、途中から再開される。100MBを超えるファイルはこの機能を使うことが推奨されている。
・`S3 Select`という機能があり、単一ファイルの対してSQLライクな構文でデータを取得することができる
・`署名付きURL`によりAWSにログインしていないユーザーも一定時間の間アップロード等の操作が可能

# ストレージクラス
### 1.  スタンダード（Standard）
S3のデフォルトのストレージクラス。データは3つのアベイラビリティゾーン（AZ）に自動的に複製されるため、高い耐久性を有している。

### 2.  標準低頻度アクセス（Standard-IA）
Standardと同等の耐久性を備え、かつStandardよりもデータ格納コストが抑えられたクラス。ただしデータのリクエストに関してはStandardよりもコストががかかる。さらにデータの読み出し容量に対しても課金が生じる。
※IAはInfrequent Acccessの略。

### 3.  1ゾーン低頻度アクセス（One Zone-IA）
1つのAZでデータが保存されるクラス（複製に関してはStandard, Standard-IAと同様）。AZ障害に弱いという点ではStandard, Standard-IAに比べて耐久性が劣るが、Standard-IAよりもデータ格納コストが抑えられたクラス。データのリクエストに関してはStandard-IAと同等のコストがかかる。

### 4.  低冗長化ストレージ（RRS）
2つのAZに冗長化されたクラス。AZ障害に弱いという点ではStandard, Standard-IAに比べて耐久性が劣る。コストに関してはRSSは「Standard, Standard-IAよりも低い」という記述を見かけるが、実際はRRSの方が高いという逆転現象が起こってしまっている。これはStandardの価格が下げられたことによるものらしい。
※RSSはReduced Redundancy Service の略

### 5.  Intelligent-Tiering
高頻度と低頻度という2つのストレージ層があり、S3上に格納したデータへのアクセス頻度によって最適なストレージ層を自動的に使い分けるクラス。

### 6.  Glacier
アーカイブ目的で利用されることが多い大容量のデータを安価に格納できるクラス。ただしデータの取り出しには時間を要する。取り出しには3つのオプション（Expedited: 1-5分、Standard: 3-5時間, Bulk: 5-12時間）がある。
ボールトロックという機能があり、ファイルの削除などの操作を制限することができる。
Expedited は250MB以上の容量を除いて、通常1~5分以内で使用可能となる。

### 7.  Glacier Deep Archive
Glacierよりも安価に利用できる。
48時間以内に取り出し可能。


# 参考

https://aws.amazon.com/jp/s3/storage-classes/

https://aws.amazon.com/jp/s3/reduced-redundancy/

https://xtech.nikkei.com/it/atcl/column/16/041400085/022200021/
constなグローバル変数は内部リンケージを持つ

c言語において

```c:header.h
#ifndef HEADER_H
#define HEADER_H

const double PI = 3.14;

#endif
```

```c:a.c
#include "header.h"
```

```c:main.c
#include "header.h"

int main(void) {}
```

のようなコードをコンパイルすることはできません。

```bash
$ gcc main.c a.c
/usr/bin/ld: /tmp/cc4BWsZc.o:(.rodata+0x0): multiple definition of `PI'; /tmp/ccXsx13d.o:(.rodata+0x0): first defined here
collect2: error: ld returned 1 exit status
```

C++17以降では同一のコードをコンパイルすることができます。

`nm`コマンドでシンボル情報を見てみます。

```bash
$ g++ main.cpp a.cpp
$ nm --demangle a.out
0000000000003e00 d _DYNAMIC
0000000000003fc0 d _GLOBAL_OFFSET_TABLE_
0000000000002000 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000002008 r PI　// シンボルタイプが小文字であればローカルシンボル
0000000000002010 r PI
略
0000000000001129 T main
00000000000010a0 t register_tm_clones
```

`PI`がローカルシンボルであることが分かります。

C言語において`static const double PI = 3.14;`と同じことになります。

## 結論

C言語で定数がほしかったら`#define`じゃなくって`static const`でいいってこと？

`constexpr`でよくね？

```bash
$ gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ g++ --version
g++ (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
```

<https://timsong-cpp.github.io/cppwp/n4659/diff.basic>
<https://timsong-cpp.github.io/cppwp/n4659/basic.link#3>

<details><summary>余談</summary><div>

https://timsong-cpp.github.io/cppwp/n4659/basic.def#2.18

に

> int a; // defines a

とあるので

```c
int a;
int a;

int main() {}
```

というコードはC言語でコンパイルできますがC++ではコンパイルできません。

<https://docs.microsoft.com/ja-jp/cpp/cpp/declarations-and-definitions-cpp?view=msvc-160#definitions>

ここには宣言とあるのでmsvcではコンパイルできるのかと思いましたが

[compier explorer](https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(fontScale:16,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:7,endLineNumber:2,positionColumn:7,positionLineNumber:2,selectionStartColumn:7,selectionStartLineNumber:2,startColumn:7,startLineNumber:2),source:'int+a%3B%0Aint+a%3B%0A%0Aint+main()+%7B%7D'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:50,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:vcpp_v19_latest_x64,filters:(b:'0',binary:'1',commentOnly:'0',demangle:'0',directives:'0',execute:'1',intel:'0',libraryCode:'0',trim:'1'),fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'',selection:(endColumn:90,endLineNumber:4,positionColumn:90,positionLineNumber:4,selectionStartColumn:90,selectionStartLineNumber:4,startColumn:90,startLineNumber:4),source:1),l:'5',n:'0',o:'x64+msvc+v19.latest+(Editor+%231,+Compiler+%231)+C%2B%2B',t:'0')),k:50,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4)

ではコンパイルできませんでした。

</div></details>
Windows証明書ストアからCNG署名

「そうか！そうだったのか！」
男は突然つぶやいた。男はCAPI（CryptoAPI）を使ってWindows証明書ストアから選択した（秘密鍵が関連付いている）証明書にてRSA方式のデジタル署名を生成するプログラムをデバッグしていた。これまでは問題無く動作していたのだが、**Adobe Acrobat**から**「IDを追加」-「今すぐデジタルIDを新規作成」-「Windows証明書ストア」**を使って登録した秘密鍵（署名鍵）を使うとエラーになると言う問題で苦しんでいたのである。CAPIの実装は以下のようになっていた。

```
HCRYPTPROV hProv = NULL;
HCRYPTPROV hKeyProv = NULL;
HCERTSTORE hStore = NULL;
DWORD dwKeySpec;
BOOL fCallerFreeProv = FALSE;
HCRYPTHASH hHash = NULL;
// -- 証明書ストアから証明書の取得
CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_FULL, NULL);
hStore = CertOpenSystemStore(hProv, L"MY");
PCCERT_CONTEXT pcCert = CertFindCertificateInStore(hStore, ...);
// -- 秘密鍵（署名鍵）の取得
int hasPrivKey = CryptAcquireCertificatePrivateKey(pcCert, 0, NULL, 
                     &hKeyProv, &dwKeySpec, &fCallerFreeProv);
if(hasPrivKey != 1)
    return ERR;    // 秘密鍵が関連付いていない
// -- 署名対象ハッシュ値のセット（仮にSHA-256利用）
CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash);
CryptSetHashParam(hHash, HP_HASHVAL, pbHash, 0);
// -- 署名結果のサイズを取得
CryptSignHash(hHash, dwKeySpec, NULL, 0, NULL, &cbSign);
// -- pbSignをcbSign分確保してデジタル署名実行
CryptSignHash(hHash, dwKeySpec, NULL, 0, pbSign, &cbSign);
// -- 署名終了（注：pbSignに格納された結果は反転している）

```

普通はこれでうまく動作するのだが、Acrobatから登録した証明書だとCryptAcquireCertificatePrivateKeyの結果が 0（秘密鍵無し）と返され GetLastError() の値が 0x8009001b（provider type does not match registered value）となってしまう。つまり秘密鍵の取得自体が出来ないように見える。困った。

色々試していてCryptAcquireCertificatePrivateKeyのdwFlagsに**CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG**を指定してみたところ、hasPrivKeyが 1（秘密鍵有り）で返ってきた。CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAGは**CNG（Cryptography API: Next Generation）**と呼ばれる新しい暗号API用の鍵も取得するフラグである。

と言うことはAcrobatから登録するとCNG形式で秘密鍵が登録されていることになる。これが冒頭の男のつぶやき「そうか！そうだったのか！」に繋がっていたのである。

取得した秘密鍵がCNGの場合には**dwKeySpec**が**CERT_NCRYPT_KEY_SPEC**と返されているので判別がつけられる。またCNG鍵ハンドルは**hProv**に**NCRYPT_KEY_HANDLE**として返される。

さて次なる問題はCNG形式の秘密鍵と言うことはCAPIを使っては署名が出来ない。つまりCNGのAPIを使って署名する必要があると言うことになる。NCryptSignHashを使えば良いのだが、以下にCAPIと同じ結果を得る為のソースを示す。

```
// -- 秘密鍵（署名鍵）の取得
int hasPrivKey = CryptAcquireCertificatePrivateKey(pcCert, 
                     CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG, NULL, 
                     &hKeyProv, &dwKeySpec, &fCallerFreeProv);
if(hasPrivKey != 1)
    return ERR;    // 秘密鍵が関連付いていない
if(dwKeySpec == CERT_NCRYPT_KEY_SPEC)
{
    // -- CNGによる署名値計算
    NCRYPT_KEY_HANDLE hCngKey = (NCRYPT_KEY_HANDLE)hKeyProv;
    // -- パディングはCAPIのCryptSignHashと同じPKCS#1とする
    BCRYPT_PKCS1_PADDING_INFO padding_PKCS1 = BCRYPT_PKCS1_PADDING_INFO();
    // -- ハッシュアルゴリズムのセット（仮にSHA-256利用）
    padding_PKCS1.pszAlgId = BCRYPT_SHA256_ALGORITHM;
    // -- 署名結果のサイズを取得
    NCryptSignHash(hCngKey, &padding_PKCS1, pbHash, cbHash, NULL, 0, &cbSign, BCRYPT_PAD_PKCS1);
    // -- pbSignをcbSign分確保してデジタル署名実行
    NCryptSignHash(hCngKey, &padding_PKCS1, pbHash, cbHash, pbSign, cbSign, &cbSign, BCRYPT_PAD_PKCS1);
    // -- 署名終了（注：pbSignに格納された結果は反転していない）
} else {
    // CAPIによる署名値計算（略）
       :
}

```

「ふむ。これで良さそうだ。新しい知識が増えたし今晩はビールくらい飲んでも良かろう。」
男は今度は満足げにつぶやいた。
【BigQuery】論理削除フラグを更新し、差分データを追加

# 背景

人材管理ツールで現状の部署は管理できるが、異動遷移がみれなかったので、
前と差が生じていている場合にそのレコードのみ追加するコードを書きました。

加えて理論削除を加えて、現状使われているデータだどれかを出力しやすいくしています。

## 論理削除とは？
こちらを参考に項目名は`is_deleted` で1の場合は論理削除されたとみなしています。
https://qiita.com/wanko5296/items/f1af9c7bf020e867c2dd


# データを用意

2つのデータを用意します。
実際はPKとなるidがありますが、簡易的に名前にしています。

```sql:元データ
create table `test.db1`
(name string,
departmanet string,
update_date date,
is_deleted int64);

insert into `test.db1`
values
('ウメハラ', '営業', date_add(current_date(), interval -20 day), 0),
('タカハシ', '営業', date_add(current_date(), interval -20 day), 0),
('ムカイ', '営業', date_add(current_date(), interval -20 day), 0)
```

![2021-06-02_17h58_57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428239/f011a0c2-19c0-9625-612c-32d242ae6509.png)



日付は減らし方は公式サイトを参照。
https://cloud.google.com/bigquery/docs/reference/standard-sql/date_functions?hl=ja


上記のデータに対して部署移動が発生した想定です。


テーブルのデータを書き換えたい場合は、
`create table`を`create or repalce table`に変更すると、
作りなおすことが可能です。


```sql:新しいデータ
create table `test.db2`
(name string,
departmanet string,
update_date date,
is_deleted int64);

insert into `test.db2`
values
('ウメハラ', 'エンジニア', current_date(), 0),
('タカハシ', 'マネージャー', current_date(), 0),
('ムカイ', '営業', current_date(), 0)
```

![2021-06-02_17h59_21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428239/dd1547f7-5f9b-f2b9-d418-8a15618e465b.png)



# 実行方法

手順としては下記を行いました。

1. DECLAREで変更したいprimary_keyの一覧を取得
2. 1のkeyと一致する元データの論理削除フラグを変更
3. 1のkeyと一致する更新データを追加する

作成時にこちらの記事を参考にさせていただきました。

https://medium.com/google-cloud-jp/bigquery-scripting%E3%81%8Cbeta%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%B9%E3%81%95%E3%82%8C%E3%81%9F%E3%81%AE%E3%81%A7%E8%BB%BD%E3%81%8F%E3%82%A6%E3%82%A9%E3%83%BC%E3%82%AF%E3%82%B9%E3%83%AB%E3%83%BC%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B-1408bab2c026


```sql
DECLARE pramary_keys ARRAY<STRING>;

-- ここで一致しない(部署移動したkeyを取得)
set pramary_keys = (
    select ARRAY_AGG(name) 
    from `test.db2` as s --最新データ
    where not exists(
        select *
        from `test.db1` as t --変更するデータ
        where t.departmanet = s.departmanet
        -- 他の項目も確認したい場合はand条件でいくらでも追加可能
        -- and t.column = s.column
    )
);

-- keysを元に論理削除のフラグをつける
update `test.db1` as t
set is_deleted = 1
where exists(
    select name
    from UNNEST(pramary_keys) as pramary_keys
    where t.name = pramary_keys
);

-- keysを元に必要なデータを追加する
insert `test.db1`
select *
from `test.db2` as s
where exists(
    select *
    from UNNEST(pramary_keys) as pramary_keys
    where s.name = pramary_keys
)
```

実行結果を確認すると、
ウメハラ、タカハシの営業の列に論理削除のフラグがたっており、
同じくウメハラ、タカハシの移動先の部署が入っています。

![2021-06-02_18h02_52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/428239/c0cd2343-ccf4-1f56-82e7-a6f1d1d43373.png)
SysMLのPort


#full port 「完全ポート」

ブロックの外部のエンティティーと直接通信する、ブロックのパーツを表します。
block has a Full portの関係

#Proxy port「代理ポート」

ブロックの属性や操作などのフィーチャーを外部インターフェイスにとして表します。
 «interfaceBlock» stereotypeが使われる
EC2にNodeをセットアップする

EC2にNodeをセットアップする方法のメモです。
EC2のインスタンスを立ち上げてからの手順をまとめておこうと思います。
(Amazon Linux 2 へのインストール方法です。）

# インスタンスのアップデート
`yum update`を実行し、インスタンスをアップデートします。

```common_lisp
$ sudo yum update
```

# g++コンパイラのインストール
```common_lisp
$ sudo yum -y install gcc-c++
```

# gitのインストール
```common_lisp
$ sudo yum -y install git
```

# nvmのインストール
GitHubから`nvm`をクローンします。

```common_lisp
$ git clone https://github.com/creationix/nvm.git ~/.nvm
```

`nvm`へのパスを通します。
`.bash_profile`へ設定を書き込み、永続的に有効となるようにします。

```common_lisp
$ vi .bash_profile
```

以下の設定を記載します。

```common_lisp
# nvm
if [[ -s ~/.nvm/nvm.sh ]] ; then
        source ~/.nvm/nvm.sh ;
fi
```

ログアウトし、`nvm`コマンドが有効化となっているかを確認します。

```common_lisp
$ nvm --version
0.38.0
```

# Node.jsのインストール

インストール可能な`node`のバージョンを調べます。

```common_lisp
$ nvm ls-remote
...
    v16.15.0   (LTS: Gallium)
    v16.15.1   (LTS: Gallium)
    v16.16.0   (LTS: Gallium)
    v16.17.0   (LTS: Gallium)
    v16.17.1   (LTS: Gallium)
    v16.18.0   (LTS: Gallium)
    v16.18.1   (LTS: Gallium)
    v16.19.0   (LTS: Gallium)
    v16.19.1   (LTS: Gallium)
    v16.20.0   (Latest LTS: Gallium)
...
```

`v16.20.0`をインストールします。

```common_lisp
nvm install v16.20.0
```

インストールされた`node`のバージョンを確認します。

```common_lisp
node --version
v16.20.0
```

インストールしたバージョンが表示されれば、インストールは成功です。

# Nodeが複数バージョンインストールされている場合のDefault設定
Nodeが複数インストールされている場合、以下のコマンドを使って、Defaultで使用するNodeを指定する必要がある。

```common_lisp
nvm alias default v16.20.0
```


** nodeアプリケーションの配置については、別の記事で記載しようと思います。

<br><br>
PHPで2 行目で与えられる N 個の整数の入力

2 行目で与えられる N 個の整数の入力を改行して標準出力に1行ずつ出力するPHPのプログラムです。
Twitterでご質問頂きました内容です。

```PHP
<?php
//入力行数を入力する
$count = fgets(STDIN);
//1行に入れる数字を入力する
$line = trim(fgets(STDIN));
//半角スペースで文字列を分割して、$lines配列に格納する。
$lines = explode(' ',$line);

//for文で$lines配列で1要素ずつ改行して値を取り出す。
for($i=0;$i<$count;$i++){
    echo $lines[$i]."\n";
}
    
?>
```
Vuzix Bladeのエミュレータがimportできないときの対処法【Android Studio】

#はじめに
[Vuzix公式](URL"https://www.vuzix.com/Developer/KnowledgeBase/Detail/1066")(アカウント登録必須)より仮想デバイスの設定で躓いたので備忘録

## エラー
```
<d:devices xmlns:d="http://schemas.android.com/sdk/devices/3" 
```
この名前空間を使おうとするとimportできない（何も起こらない）

## 改善策
```
<d:devices xmlns:d="http://schemas.android.com/sdk/devices/5" 
```
最後の数字を3から**5**にした
.android以下にあるdevicesというXMLドキュメントを見ると、"d"という名前空間が上のように宣言されていたため、試してみたらうまくいった

##さらなるエラー
デバイスが選択できるようになったため、次はsystem imageを選択することになった
![AndroidStudioErr1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/990205/2a81e384-3555-2789-1c63-83753fc449f4.png)
次はこれを解決してみようと思う
「AWSome Day Online Conference 20210602」に参加した際のメモ

この記事は、タイトルにあるように「AWSome Day Online Conference 20210602」に参加した際のメモです。
なので、後で理解を深めるために加筆修正するかもしれませんが、基本的には走り書きです。

----

# そもそもクラウドの利点
必要なサーバーやストレージのキャパシティ予想が不要（規模に合わせて自動で素早く可変できる）

# リージョン
地理的ロケーション
最低2つ以上のアベイラビリティーゾーンがある

# アベイラビリティーゾーン（AZ）
データセンターのクラスタ
他のアベイラビリティーゾーンの障害から分離される
複数のAZを利用することでAZレベルの障害からの保護できる


# VPC
AWSクラウド内の仮想プライベートネットワーク
インターネットゲートウェイをつけると、外のネットワークにも繋げる


# ec2
仮装サーバー
OSはユーザーによって選択可能（サービス利用単価も変わる）
インスタンスタイプ
→汎用（ウェブサイト）、コンピューティング最適化、高速コンピューティング、メモリ最適化、ストレージ最適化


# ELB
負荷分散サービス
複数のインスタンスにトラフィックを分散
正常に動作してないec2インスタンスを検出して除去するためのヘルスチェックをサポート
厳密に言うと、3つのサービスの総称
ALB、NLB、CLB
application load balancer
リクエストレベルで動作する。
network lead balancer
接続レベルで動作する。卓越したパフォーマンスとアプリケーション用の静的IP。
TCPトラフィックのロードバランシング



# ec2 auto scaling
ユーザーのアプリケーションの負荷を処理するのに適切な数のec2インスタンスを生成。
負荷の増大→スケールアウト（インスタンスの起動）
負荷の減少→スケールイン（インスタンスの削除）
ELBがcloud watchを監視→ターゲット超過をEC2 auto scalingに通知→インスタンスを起動するのを依頼。



# データストア
サーバー自身からしかアクセスできないもの、複数のサーバーからアクセスできるもの

EBS
elastic block store
永続的なブロックレベルのストレージボリューム
外付けストレージのイメージでok
AZ内で自動的にレプリケート（複製）できる


# S3
simple storage service
データのアップロード、ダウンロードをできる、容量無制限
オブジェクトは、少なくとも3つのAZに保存される
保存先のバケット名は一意のものでないといけない（世界でユニーク）

一般的なユースケース
静的コンテンツの保存と配信
静的ウェブサイトホスティング
バックアップと災害対策



# amazon RDS
RDB＝リレーショナルデータベース
トランザクション処理を実行可能
データベース管理タスクが不要になる
→アプリケーション最適化に注力できる（スケーリングやバックアップを任せる事ができる）
自動バックアップ、手動スナップショット

VPCの中にRDS DBインスタンスを構築
→プライマリとスタンバイを作成（別のAZに）
プライマリが更新されたら、スタンバイの方のデータも更新
もしスタンバイの方を利用するとしても、自動で出来る（フェイルオーバー）


amazon DynamoDB
NoSQLサービス


amazon Redshift
データウェアサービス


# AWSセキュリティ
AWSの責任共有モデル
→AWS側とユーザー側でセキュリティをそれぞれ守りましょう（機能提供をするのでそれぞれの領域のセキュリティを守りましょう）
例：ストレージのセキュリティはAWSだが、ネットワークのセキュリティはユーザー側で

VPCのネットワーク制御機能
ファイアウォール
ルーティング

セキュリティグループの制御
IPアドレスや、セキュリティグループを指定して、アクセスを許可する事ができる
（セキュリティグループの指定のメリットは、ec2 auto scalingでインスタンスが増加した場合に対応するため）
ネットワークセグメントとサブネットのレイヤーが1:1にならない


# IAM
identity and access management
認証・認可の制御
AWSのリソースに対するアクセス（EC2やRDS）を司る
認証
その人がアクセスしてよいか判断
認可
その人がどんなことをしたら良いか許可を与える

ユーザー

ポリシー（権限）
→最小権限を作成する

グループ
→複数のIAMユーザー。基本はこれを利用する
ロール
→ポリシーを人やサービスに付与
→リソース同士の認証認可（EC2がS3にアクセス）
→ヘルメットのイメージ（色々なものに着せることが簡易にできる）
→例：EC２がS3にアクセスする場合、アクセスキーはEC2のサービス内で保持しないで、ロールで判断するようにする

AWS CLIでの認証認可
アクセスキーとシークレットアクセスキーが必要


ルートユーザー
→ルートユーザーでEC2の作成などはセキュリティの観点からNG
1, ルートユーザーのアクセスキーは削除する
2, IAMユーザーを作成する
3, 管理者アクセス権を付与する
4, IAM認証情報を使用してAWSを操作する



# AWSの運用について
well-architected framework（W-A）
→クラウド設計・運用のベストプラクティス集。毎年アップデートされる。

必要キャパシティの推測不要
本稼働スケールでシステムをテストする
自動化によってアーキテクチャでの実験を容易にする
発展するアーキテクチャが可能
データに基づいてアーキテクチャを進化
ゲームデーを利用して改善する

質問例：
運用上の優秀性の柱
・どのようにデプロイのリスクを軽減してますか
・オペレーションの正常性をどのように把握してますか

セキュリティの柱
・認証情報と認証をどのように管理してますか？
・ネットワークをどのように保護していますか？

信頼性の柱
・システムが需要の変化にどのように対応していますか
・リソースをどのようにモニタリングしていますか

パフォーマンス効率の柱
・コンピューティングソリューションをどのように選択してますか

コスト最適化の柱
・使用状況とコストをどのようにモニタリングしてますか




# 料金の基礎
従量課金制
予約による値引き
→リソースを予め購入すると、一定期間分は値引かれる
ボリュームディスカウント

EC2
秒/時間単位による請求
OS, メモリなどによる

RDS
時間単位による請求
エンジンによる

S3
データ転送、ストレージクラスによる

見積もりツール
aws pricing calculator
Python勉強記録 #3 関数の定義, 関数の呼び出し

# 関数とは
関数とは、ほかのコードから切り離され、名前がつけられたコードです。
任意の型、任意の入力引数をとり、任意の型、任意の個数の結果を出力します。
つまり、お金を入れてボタンを押したらジュースが出てくる自動販売機みたいなもの（？）
的確に例えるのは難しいですね。

# 関数の定義
ともあれ、まずは関数を定義する必要があります。
それでは、「リストの中の数字を偶数と奇数に分けてくれる関数」を定義してみます。

```python
def calc(x):

    odd, even = [], []
    for number in x:
        if number %2 == 0:
            even.append(number)
        else:
            odd.append(number)

    return even,odd
```

`def`は definition(=定義) の略です。
`clac`は適当につけた関数名
`(x)`は引数（関数に渡す値のこと）です。関数を定義する時、ここにはどんなワードを入れても大丈夫のようですが、何も書かずに`()`とするとエラーになります。
`return`はこの関数が返す値のことです。even, odd の順番で返すという書き方になっています。
（それ以外の説明は、一つ前の記事を参照。）
## `return`について補足
例えば、`return len(even)`と書けば、`even`のリスト内にある要素の数を返すことができます。

# 関数の呼び出し
関数を呼び出して、リストの中身を偶数と奇数に分けてもらいましょう。
`calc()`に引数を与えます。

```python
numbers = list(range(1, 10))
# range(1,10) は1から10までの数字という意味

even, odd = calc(numbers)

print(even, odd)
"""
出力結果 : [2, 4, 6, 8] [1, 3, 5, 7, 9]
"""
```

このように関数は一度定義すると、他の引数にも利用することができるので大変便利です。
在ubuntu系统中使用vscode调试bitcoin-qt(视频)

### 什么是 [GDB](https://www.gnu.org/software/gdb/)

GDB是GNU开源组织发布的一个强大的UNIX下的程序调试工具。
既然是debug工具，在 IDE中基本的调试功能，比如  加断点，单步调试，查看变量...... 在 gdb都可以操作，只不过都要通过命令行的方式执行，操作起来比较麻烦。

### 什么是[Visual Studio Code](https://code.visualstudio.com/)

Visual Studio Code，大家一般称vscode，是微软发布的一款轻量级编辑器，估计已经成了最流行的编辑器，还支持 Debug，实在是居家旅行之必备。
vscode+gdb 专克 cpp，**在ubuntu上录制了3个视频，当初在录制的时候，没有外接声源，后期(对就是2019年05月)制成了纯音乐版**。

- [使用ubuntu系统编译 bitcoin](https://www.bilibili.com/video/av52929031)
- [使用gdb调试bitcoin-qt](https://www.bilibili.com/video/av54106553)
- [使用vscode+gdb调试bitcoin-qt](https://www.bilibili.com/video/av16686717/)

### 为什么录制了视频

之前写了3个小时的文章，感觉还不如10min的视频有干货，这种操作类的还是视频更干脆直接。由于ubuntu是工作电脑，就用ubuntu录制了，跟在macos上操作还差不多。

**有条件还是要上linux，Mac os开发cpp真心不算好用** ，依赖问题就会让你头疼不止。

---

参考:

[bitcoin: 编译与运行](https://www.jianshu.com/p/660601be4681)
[在macOS下使用Visual Studio Code进行C/C++开发](https://stidio.github.io/2017/01/use_vscode_for_c_c++_development_in_macos/)
http://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/gdb.html
https://code.visualstudio.com/
https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools
http://blog.csdn.net/haoel/article/details/2879
FragmentでJetpack Composeをつかう

## 前書き
FragmentでJetpack Composeを使う時の書き方を忘れてしまうので、メモとして残します。

### 純正Compose
``` kotlin
class ComposeUIFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return ComposeView(requireContext()).apply {
            setContent {
                Text(text = "Hello world.")
            }
        }
    }
}
```

### compose + xmlのハイブリッド
``` kotlin
class ComposeUIFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_compose_ui, container, false).apply {
            findViewById<ComposeView>(R.id.composeView).setContent {
                Text(text = "Hello world.")
            }
        }
    }
}
```


以上ですw
准确计算Ethereum的发行量(流通量)

>![](https://upload-images.jianshu.io/upload_images/47789-119525903c010574.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

最初的标题是 《计算Ethereum的流通量》

流通量感觉不太准确，按照本人的理解 流通量 是指在这个网络中里能用于交换的数量。 而Ethereum网络中，存在着大量的丢失的 /锁定的币，这些是不用于流通的，
但是很难统计，很多网站都用流通量。所以混用了这个说法，流通量即发行量。

### 计算bitcoin的发行量
先普及下bitcoin的发行量，是很容易计算的，bitcoin只有一种方式可以增发，那就是挖矿奖励。
btc遵循这种规律，第一次的`21w (210000)`高度的时候，每挖出一个块奖励50个btc。
下一次的`21w`，也就是 `21w~42w`高度。每挖出一个块奖励25个btc。每21w个高度就是一个减半周期，挖出的奖励减半了。

btc的发行量 跟块高度紧密相关
咋计算42w个块发行了多少btc呢?   `21w * 50 + 21w*25`
如果是50w呢?  `21w * 50 + 21w*25 + (50w-42w)*12.5`

现在高度是[532223 ]现在高度是[532223 ](https://btc.com/00000000000000000009dce52e227d46a6bdf38a8c1f2e88c6044893289c2bf0)，发行量是多少呢? 大家感兴趣的话，可以计算下哈

bitcoin总量还有个上限，就是不超过2100w个。

### 计算ethereum的发行量

ethereum的发行量比较麻烦了，有四种增发方式

- Pre-mine（挖矿前）
- Block rewards（区块奖励） 
- Uncle rewards（叔块奖励）
- Uncle referencing rewards（叔块引用奖励）
 
挖矿前: `72 009 990.50 Eth`

区块奖励：
区块奖励在4370000高度前都是 5Eth
4370000后，eth启动大都会后奖励变成3Eth

叔块奖励：
计算公式 `(uncle_num + 8 - block_num) * R / 8`
R是定值，在4370000高度前都是 5Eth，4370000高度后变成3Eth

叔块引用奖励：
如果block 不单有父块，还引用叔块，block有额外的奖励
计算公式 `1/32*R*uncle_count  (0 ≤ uncle_count ≤ 2)`
R是定值，在4370000高度前都是 5Eth，4370000高度后变成3Eth

**这需要找出所有引用的叔块**，并计算每个叔块的奖励
**eth 总量目前没有上限**

数据库记录了这些数据

```sql
select count(*) from  block_rewards;

  count  
---------
 5903060

select count(*) from  uncle_rewards;

 count  
--------
 657883

select sum(reward) from  block_rewards;

    sum       
----------------
 26532159.15625

select sum(reward) from  uncle_rewards;

    sum     
-------------
 1933525.625


select 26532159.15625 + 1933525.625 + 72009990.50 as total;

    total      
-----------------
 100475675.28125
```

目前遍历了 `5903055` 区块高度的eth块数据以及叔块数据，计算出的发行量是 `100 475 675.28125`，有一定的误差。

---

参考:

https://etherscan.io/stat/supply
https://ethereum.stackexchange.com/questions/27172/different-uncles-reward
Ethereum 地址是如何生成的

>![](https://upload-images.jianshu.io/upload_images/47789-119525903c010574.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

以太坊生成地址的方式跟[比特币生成地址](https://qiita.com/shooter/items/85246781a27a42e3785c)比较类似，也是 私钥 -> 公钥 -> 地址，以太坊只是在公钥 -> 地址做了简化。
**以太坊使用 Secp256k1 椭圆曲线得到私钥、公钥，比特币使用的也是相同的椭圆曲线算法。**
得到公钥后，对公钥做 Keccak-256 哈希运算，然后取最后的 40 位 16 进制字符，得到的就是以太坊地址。

**生成以太坊地址跟比特币地址都是不需要连接网络的**

## python code 生成以太坊地址

python 首先安装类库
`pip install ecdsa`
`pip install pysha3`

```python
import binascii
import sha3
from ecdsa import SigningKey, SECP256k1

priv = SigningKey.generate(curve=SECP256k1) #生成私钥
pub = priv.get_verifying_key() #生成公钥

keccak = sha3.keccak_256()
keccak.update( pub.to_string()) #keccak_256 哈希运算
address = "0x" + keccak.hexdigest()[24:]

priv_key = binascii.hexlify( priv.to_string())
pub_key = binascii.hexlify( pub.to_string())

print("Private key: " + priv_key.decode() )
print("Public key:  " + pub_key.decode() )
print("Address:     " + address)

# Address: 0xd38d3c226d0a86ce9932608edac39163fcbc550e
```

## golang code

```golang
package main

import (
	"encoding/hex"
	"fmt"

	"github.com/ethereum/go-ethereum/crypto"
)

func main() {
	genEthAddr()
}

func genEthAddr() {
	key, _ := crypto.GenerateKey()

	privateKey := hex.EncodeToString(key.D.Bytes())
	address := crypto.PubkeyToAddress(key.PublicKey).Hex()

	fmt.Printf("privateKey: 0x%s\n", privateKey)
	fmt.Printf("addr: %s\n", address)
}

// GOOS=linux GOARCH=amd64 go build -o main main.go && zip main.zip main

```

## 来个骚操作，用同一份私钥来管理比特币与以太坊

既然**以太坊使用 Secp256k1 椭圆曲线得到私钥、公钥，比特币使用的是完全相同的算法**。

那我们就用 [Bitcoin 地址是如何生成的](https://www.jianshu.com/p/954e143e97d2)得到的公钥
`04d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f691757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d`，生成一个以太坊地址。
这个公钥是通过 openSSL 得到的**未压缩公钥**，以太坊不需要使用这种格式的公钥，只要把首位的`04`去掉即可。
有未压缩 就有 压缩公钥，[压缩公钥与未压缩公钥](https://www.jianshu.com/p/6f08bebc5aa6)的区别了解下。


```python
import sha3
import binascii

_openssl_pub_key= "04d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f691757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d"

_pub_key = _openssl_pub_key[2:]
_pub_hex = binascii.unhexlify(_pub_key)

keccak = sha3.keccak_256()
keccak.update(_pub_hex)

address = "0x" + keccak.hexdigest()[24:]

print address #0x9156a7cdab767ffe161ed21a0cb0b688b545b01f
```

这说明啥？我用完全相同的私钥，分别生成了比特币地址与以太坊地址，这两个完全不一样的地址用的是同一个私钥。

以太坊地址 [0x9156a7cdab767ffe161ed21a0cb0b688b545b01f](https://etherscan.io/address/0x9156a7cdab767ffe161ed21a0cb0b688b545b01f)
与比特币地址 [14xfJr1DArtYR156XBs28FoYk6sQqirT2s](https://btc.com/14xfJr1DArtYR156XBs28FoYk6sQqirT2s)的私钥竟然是相同的。

**这给资产管理带来了很大方便，但如果一份私钥泄露了，2 份资产都有可能被盗。** 管理一时爽，泄露全都火葬场。

---

参考：
http://blog.luoyuanhang.com/2018/04/17/eth-basis-accounts-address-pubkey-prikey/
https://www.jianshu.com/p/efc6bfdf4988
https://www.bilibili.com/video/av27153321
https://zhuanlan.zhihu.com/p/34363341
https://www.myetherwallet.com/
bitcoin计算Block Hash

> ![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169726/3bde7778-917f-d068-79e7-2d757ae8d396.jpeg)

bitcoin的创世块的数据是已 hard code的形式写在源码里,
之后的block hash都是通过固定的方式计算出来的. 怎么计算出来呢?

每一个block都有:
 `version` 即网络节点的版本号
`prev_block` 前一个块的hash,创世块没有,以后的块都有
`mrkl_root`,  是 [Merkle tree](https://www.jianshu.com/p/4e08d0d037a6), 即默克尔树
`time` 即时间戳,当前时间
`bits` 网络的难度
`nonce` 随机数

这个 **nonce 就是 [Pow](http://www.jianshu.com/p/fdcfd91d315e) 要计算的随机量**, 区别是要计算2次hash.
先把 `version  prev_block mrkl_root time bits nonce`当做字符串合并到一起, 得到结果 `result`. 得到 `result` 后,  做2次 sha256运算,  得到 `hash`, 再然后`hash` 做大小端转换, 最后的结果就是这个block的hash.

不过 `version time  bits nonce` 要转换为 unsigned long型小字端,
`prev_block mrkl_root` 要转换为16进制并大小端转换, 这一步确实很麻烦.

---

测试下 `height=1` 的 block hash值

```
curl https://blockchain.info/rawblock/00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048

{
  hash: "00000000839a8e6886ab5951d76f411475428afc90947ee320161bbf18eb6048",
  ver: 1,
  prev_block: "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f",
  mrkl_root: "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098",
  time: 1231469665,
  bits: 486604799,
  fee: 0,
  nonce: 2573394689,
  n_tx: 1,
  size: 215,
  block_index: 14850,
  main_chain: true,
  height: 1

  ......
}
```

#### python script

```python
import hashlib
import struct

ver = 1
prev_block = "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"
mrkl_root = "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098"
time = 1231469665
bits = 486604799
nonce = 2573394689

hex_str = struct.pack("<L", ver) + prev_block.decode('hex')[::-1] +\
  mrkl_root.decode('hex')[::-1] + struct.pack("<LLL", time, bits, nonce)

hash_str = hashlib.sha256(hashlib.sha256(hex_str).digest()).digest()
# 这就是bitcoin矿机的工作 , 找到一个合适的nonce  
# 使得做2次sha256运算的结果符合某个条件

block_hash = hash_str[::-1].encode('hex_codec')
```
---

**挖矿做的工作**

在一个block的结构中,  `version  prev_block mrkl_root time bits`都是很容易计算的. 只有 `nonce`这个随机数不确定.

bitcoin的 pow 就是 找到一个合适的 `nonce`,  使得 `version  prev_block mrkl_root time bits nonce`合并的结果 `reuslt`,再经过2次sha256计算, 达到一个符合bitcoin网络难度的数值. bitcoin的网络难度通过 bits计算, 难度即最后计算的hash的前n位是零.

每一个block hash的计算, 都包含了prev_block_hash,这也是链的体现, 增加了攻击bitcoin的难度. 如果有人改了一个block的hash, 这个块之后的所有的块hash都要重新计算.

#### 字节序

[python处理字节序](https://www.jianshu.com/p/7623cec7620a)

---

参考:

https://en.bitcoin.it/wiki/Block_hashing_algorithm
http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html
https://en.bitcoin.it/wiki/Difficulty
https://bitcoin.org/en/developer-guide#term-merkle-tree
FirebseAuthでアカウントを作成するコード

今回は備忘録的な感じでFirebaseのAuthで登録する際に使うコードを残していきたいと思います。
#コードはこんな感じ

```swift:ViewController.swift
private func createUserToFireAuth(){
        guard let email = emailTextField.text else {return}
        guard let password = passwordTextField.text else {return}
        
        Auth.auth().createUser(withEmail: email, password: password){(auth, err) in
            if let err = err{
                print("You cannot create New Account", err)
                return
            }
            guard let uid = auth?.user.uid else {return}
            print("You success & create New Account")
        }
    }
```

#コード解説
ここでemailTextFieldで打ち込まれたメールアドレスやパスワードが空文字でないかをチェックする
割と大事な書き方なので覚えておくと便利です！

```swift
guard let email = emailTextField.text else {return}
guard let password = passwordTextField.text else {return}
```

ここでFirebseAuthに情報を渡し、もし作成ができたら"You success & create New Account"で返す。
できなかったら"You cannot create New Account"とエラー内容が表示される。

```swift
Auth.auth().createUser(withEmail: email, password: password){(auth, err) in
            if let err = err{
                print("You cannot create New Account", err)
                return
            }
            guard let uid = auth?.user.uid else {return}
            print("You success & create New Account")
        }
```

#これどうやって使うの？
登録ボタンの中にぶち込む。以上

```swift
@IBAction func RegistUser(_ sender: Any) {
    createUserToFireAuth()
}
```

#ちなみに
uidは自動で生成されるアカウント識別番号のようなもので今後FireStorageやFireRealtimeDatabaseなどさまざまなものとFirebaseAuthを連携させるときに多用するので覚えててね！

#次回
『[FirebaseAuthとFirestoreをuidで繋げる](https://qiita.com/agachan0930/items/2d51469f594cf2046e40)』
【Kotlin研修1日目】イベント処理とKotlinによるアクティビティの記述

# イベント/イベントハンドラ/リスナ
## イベント
ユーザが画面に対して行う`操作`。

## イベントハンドラ
`イベント`に対応して行う`処理`。

## リスナ
`イベント`の`検出`を行うもの。

# サンプルコード

```kotlin:MainActivity.kt
package com.example.hello

import ...

// アクティビティクラスは、Activityクラス(の子クラス)を継承して作成
// AppCompatActivity: Activity
class MainActivity: AppCompatActivity() {

    // onCreate()メソッド: 起動時に実行されるメソッド
    // onCreate()メソッドはActivityクラスで定義されているため、overrideして使用
    override fun onCreate(savedInstanceState: Bundle?) {

        // 親クラス(AppCompatActivity)のonCreate()も処理しなければならない
        super.onCreate(savedInstanceState)

        // 表示する画面の設定を読み込む
        // Rクラス: リソース("@+id"など)を識別するJavaのint型定数をまとめたクラス
        // R値: Rクラス中の定数(=リソース)
        // -> R.layout.activity_main = res/layout/activity_main.xml
        setContentView(R.layout.activity_main)

        // リスナとするオブジェクトの定義
        val btClick = findViewById<Button>(R.id.btClick)
        // リスナクラスのインスタンス化(実体化)
        val listener = HelloListener()
        // オブジェクトをリスナとして設定
        btClick.setOnClickListener(listener)

        // リスナとするオブジェクトの定義
        val btClear = findViewById<Button>(R.id.btClear)
        // オブジェクトをリスナとして設定
        btClear.setOnClickListener(listener)
    }

    // ボタンをタップした場合のリスナクラス(リスナ)
    // View.OnClickListenerインタフェース: "タップ"イベントを検知するリスナインタフェース
    private inner class HelloListener: View.OnClickListener {
        // タップ時の処理(イベントハンドラ)
        // view: (タップされた)ビュー
        override fun onClick(view: View) {
            // EditText(@+id/etName)に入力された値
            val input = findViewById<EditText>(R.id.etName)
            // TextView(@+id/tvOutput)に出力する値
            val output = findViewById<TextView>(R.id.tvOutput)

            // タップされたボタンのid(R値)に応じた分岐処理
            when(view.id) {

                // "greet"ボタンの場合
                R.id.btClick -> {
                    // EditTextのtextプロパティを「String型」に変換・代入
                    // <- findViewById<EditText>()メソッドの返却型は「Editable型」
                    val inputStr = input.text.toString()

                    // TextView(ラベル)のtextプロパティの上書き
                    output.text = "Hello, $inputStr."
                }

                // "clear"ボタンの場合
                R.id.btClear -> {
                    // EditTextのtextプロパティを空にする
                    // setText(): String型の引数をtextプロパティに代入
                    input.setText("")
                    // TextView(ラベル)のtextプロパティを空にする
                    output.text = ""
                }
            }
        }
    }
}
```

## オブジェクト式とラムダ式

```kotlin:MainActivity.kt
// 発展1: オブジェクト式による記述
btClick.setOnClickListener(object: View.OnClickListener {
    override fun onClick(view: View) {
        ...
    }
})

// 発展2: ラムダ式による記述
btClick.setOnClickListener { view: View ->
    ...
}
```
iOSの低電力モードをJSで判定する

## 低電力モードとは
iOSのいわゆる「節電」モード。
低電力モードがONの場合、
ウェブコンテンツにおける動画や音声のなど、電池を消耗するような演出の自動再生がされない。
また、JSのTimerイベント処理なども期待値より遅くなったりする。

![IMG_0967.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48396/709499ad-489e-3aa6-db3c-12cd9dc2b955.png)


## やりたいこと
例えば 背面にmp4動画を敷いて自動再生させる演出のウェブサイトの場合、
低電力モード状態でアクセスすると、背面の動画が停止し、ブラウザ側の再生ボタンが表示されてしまう。

これは不格好なので、「低電力モードの場合」だけ、背景を静止画版にするなど、処理を出し分けたい。


## やり方
残念ながら `低電力モード` かどうかを判定する正式なメソッドはなさそう。
そこで以下のようなコードで低電力モードかどうかを判定するアイデアが紹介されていた。

デモ(Codepen)はこちら:
> 低電力モード判定テスト
> https://codepen.io/skwbr/pen/dyNpEQm

肝になる部分のコードを抜粋で解説。

```html:html側
<video id="detect-video" preload="auto" autoplay playsinline muted>
  <source src="//skwbr.net/asset/detect.mp4" type="video/mp4">
</video>
```
低電力モードの場合に動画の自動再生がされるかどうかの挙動が変わるのでここを上手く使う。
まずHTML側には 検出用のmp4動画 `video#detect-video` を設置。
`autoplay` `playsinline` `muted` の属性がそろっていると、低電力モードでない限りは、動画はスマホでも自動再生される。

あくまで検出用なので、この例では真っ白の10x10ピクセルの軽量なmp4にし、CSS側で見えないようにしている。
 
```css:CSS側
#detect-video {
  opacity: 0;
  width: 0;
  height: 0;
}
```

JS側は、以下のような処理とする。

```js:JS側
var isLowPowerMode = false;
var detectVideo = document.getElementById('detect-video');

//detectVideo の再生エラーを検出する
var video_promise = detectVideo.play();
video_promise.catch(function(error){
  isLowPowerMode = true;

  //演出目的の大きい動画のautoplay属性を外し、不要なリクエストを発生させない
  largeVideo.removeAttribute("autoplay"); 
});
```

HTMLに配置した `video#detect-video` の再生メソッドに対する try catch文を書く。
低電力モードの場合、 再生メソッドに対し、以下のような「不許可エラー」が出ていることがわかる。

```:console.log(error)してみると
NotAllowedError: The request is not allowed by the user agent or the platform in the current context, possibly because the user denied permission.
```

あとは、 `.catch(function(error){ })` 内 に動画再生の不許可エラーが出た時 (つまり低電力モードの時) にしたい処理を記述する。
一度低電力モードだと言うことがわかれば、
演出目的で配置している大きいサイズの動画の autoplay属性を除去するなど、
使わない動画資源のリクエストをさせない配慮があると良いかもしれない。


## デモ
> 低電力モード判定テスト
> https://codepen.io/skwbr/pen/dyNpEQm

#### 低電力モードをOFF(通常時)でアクセス
![IMG_0969.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48396/94a89a97-bc9c-bfeb-d6fa-ebdefa671133.png)

#### 低電力モードをONにしてアクセス
![IMG_0968.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/48396/b2b95d28-79ed-b5ab-011c-b6e54545770f.png)


## ちなみに
`低電力モード` の判定は上述の方法を取るしかなさそうだが、
ちなみに `設定 > アクセシビリティ > 動作 > 視差効果を減らす` の判定は、以下でできる。

```
window.matchMedia('(prefers-reduced-motion)');
```
Python勉強記録 #2 リスト, for文, if文

# リストの作り方

変数名 ： numbers
値 : 2,5,7
のリストを作るとします。

```python
numbers = [2, 5, 7]
```
のように書きます。

## リストの個数の表示
リストのサイズ（要素数）を取得するときは、
`len(numbers)`で取得できます。


# リストの足し算

```python
a = [2, 5, 7]
b = [3, 2, 2]
print(a + b)
"""
出力結果 : [2, 5, 7, 3, 2, 2]
"""
```
普通に足し算すると、リストに追加されてしまいます。
aとbのそれぞれの一番目、二番目、三番目を足すには、

```python:list_addition.py
a = [2, 5, 7]
b = [3, 2, 2]
c = [_a + _b for (_a, _b) in zip(a, b)]
print(c)
"""
出力結果 : [5, 7, 9]
"""
```

このようにfor文を使って書きます。

# for文
`for`は繰り返しをする時の"文"です。
for文と呼ばれ、

```python
for 変数 in オブジェクト:
    実行する処理
```
のように使われます。
上の list_addition.py では`zip()`も使われていますが、
これは、複数のリストをまとめる関数です。
なので、
`[_a + _b for (_a, _b) in zip(a, b)]`
の意味としては、
「aとbの要素を繰り返し_aと_bに代入していく。そして、その_aと_bを足し算する。その足し算したものを一番目から順番にリストにしていく。」
という意味になります。


# リストの分割, if文
1から10までの数字が入ったリストを偶数と奇数で分割します。

```python:list_split.py
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

odd, even = [], []
for number in numbers:
    if number %2 == 0:
        even.append(number)
    else:
        odd.append(number)
print(odd, even)
"""
出力結果 : ([1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
"""
```
ここでは、`if`が使われています。
`for`との同時出現で少しややこしいですが、
`odd, even = [], []`
odd, even　は空のリストであると定義。
`for number in numbers:`
numbers の中から、順番にnumberに代入していく。
`    if number %2 == 0:`
もし、number が2で割り切れるなら、
`        even.append(number)`
even に number を追加。
`    else:`
そうでないなら、
`        odd.append(number)`
odd に number を追加。

という意味になります。
難しそうに見えても、一文ずつ理解していけば分かりやすいですね。

## 条件をつなげたい時
条件をつなげたい時は
`if x > 50 and x < 200:`
のように記述すれば、「xが50より大きく200より小さい時」という条件にできます。


# タプル
[]ではなく、（）で囲うとリストではなく、タプル(tuple)になります。

```python
numbers = (2, 5, 7)
```

タプルは後から中の要素を変更することはできません。
たった3ヶ月のプログラミング経験のみを引っ下げてセブ島オフショア開発拠点に乗り込んだ話(前編)

この記事は、プログラミングほぼ未経験だった私が数年前にフィリピンのセブ島にあるオフショア開発現場にてブリッジSEとしてのキャリアをスタートした頃の経験についてです。前編ではブリッジSEとして入社するまでの経緯について、後編では入社前の手続き関係から入社後の経験について書いていきます。

ブリッジSEに挑戦したい方、海外就職に興味がある方に向けて、少しでも参考になれば幸いです。

#ブリッジSEになる前の話

私は元々、大学卒業後に新卒で家電量販店に入社して店頭での販売員をしていました。そして退職後にセブ島留学とカナダでのワーキングホリデーに行き、２０１７年８月からセブ島のプログラミングの学校に3ヶ月通っていました。内容は最初の1ヶ月でPhotoshopやIllustrator、HTML、CSS、Bootstrapなどを簡単に学び、残りの２ヶ月でPHPを使ってとても簡単なHPを作る、というコースでした。

もちろんPHPを２ヶ月触っただけでは実践的なスキルは身に付かず、更に元々全てにおいて感覚を頼りに生きてきた私はプログラミング的思考がどうも苦手で、その学校の卒業時も仕事で使えるレベルのプログラミングスキルは持ち合わせていませんでした。

むしろプログラマーになりたいという気持ちも卒業時には既に持っていませんでした。諦めです。笑

#ブリッジSEの求人との出会い

プログラミングの学校に入って2ヶ月目、PHPの勉強も本格的になってきてそろそろコーディングに限界を感じていた頃の話です。

またその頃はプログラミングの学校の卒業を1ヶ月半後に控え、卒業後の進路についても考え始めたある日の午後、その学校のスタッフさんが突然1枚のチラシを渡してくれました。

どうやらたまたまランチに行ったレストランにそのチラシが置いてあったみたいです。そのチラシは日本の某IT大手企業がセブ島にオフィスを出すに当たりその日と翌日の2日間の日程で採用イベントを開いているという案内でした。

就職について漠然としか考えていなかった当時の私。更にそのチラシはフィリピン人向けの案内で、唯一当てはまりそうな求人も「Nihongo-Skilled Project Coordinator」という、日本語が話せるフィリピン人向けのものでした。

当時、やっとプログラミングのコードが記号ではなく文字に見えるようになってきた段階の私、もちろんオフショア開発なんていう言葉も知らず、その会社がセブ島で何をするのか全く理解していませんでした。

更に英語の履歴書も持っていなかったですし、面接に行くならその翌日しかなかったので今日の明日で面接を受ける気はゼロだったのですが、そのスタッフさんの猛プッシュと、午後の担当のフィリピン人講師が授業そっちのけで英語の履歴書作りを手伝ってくれたこともあり、翌日の朝一で採用イベントに突撃してみることにしました。

#採用イベント当日

そんなこんなで準備不足のまま迎えた翌日、恐る恐る会場のあるマクドナルドの２階の飲食スペースに足を運んだ人見知りの私。

ビビりながら階段を登り、いきなり受付の人と目があって一気に緊張が走る中、私が発した第一声は「日本人なのですが求人に興味があります。問題ありませんか？」でした。

ですがそこは流石のフィリピンの国民性。満面の笑みで「来てくれてありがとう！中に入って！」と歓迎モードで一安心。更にマクドナルドのチキンとライスセット（デザート付き）を出してくれるという、フィリピンでの高待遇で迎えてくれました。

#マクドナルド2階での採用試験

案内の資料を読みながらチキンを食べ終えた私。その後まず筆記試験を受けました。

内容は大きく２種類に分かれており、どちらもプログラミングに関するものではなく一般的な能力テストと日本語能力のテストでした。

ポジション的にプログラミング能力は問われず、日本語能力が大事だったということですね。日本人として日本語テストに間違えるわけにはいかないので謎のプレッシャーがありました。笑

無事に筆記試験を終えた後、すぐ人事面接がありました。緊張もあり英語に詰まる場面はありましたがなんとかこなしました。唯一意識をしたことは「プログラミングを勉強中ということをドヤ顔でアピールすること」でした。入社後に気付いたのですが、私が勉強していたPHPはその会社では一切使っていなかったのでアピールに意味はありませんでした。。笑

ただ日本でも同じだとは思いますが、自信を持っているように見せたのは効果あったと思います。

#色々想定外だった英語でのマネージャー電話面接

人事面接の後はフィリピン人マネージャーによる日本語面接があるとの案内を受け、彼女が来るのをマクドナルドの２階で待っていました。

しばらくして人事担当者に声を掛けられ、マネージャーが来れなくなったから電話面接にすると言われ、いきなり電話を渡されました。

対面での面接に向けて心の準備をしていたので、いきなり電話を渡されドギマギしたのが正直なところ。更に、実際は日本語のスピーキング能力を図る兼最終面接だったので日本語での面接と聞いていたのですが、私は日本人でマネージャー（フィリピン人）よりももちろん日本語が話せるので電話面接開始10秒で英語に切り替わるという災難。

人見知りにはなんともハードルの高い面接が始まりました。

#採用結果とその後

そしてなんとかその面接も乗り切り、ホッとして同じマクドナルドの２階で待つこと数分、人事担当者が満面の笑みで「Congratulations!」と言って握手を求めてきました。一瞬なんのことか分からなかったのですが、採用が決まったことを改めて伝えられ、大きく安心したことを今でも覚えています。

ただ正直、周りに流されて準備をし、当日も準備不足のまま会場に行き言われるがまま面接を受けたので実感はあまりありませんでした。

そんなこんなで後日無事に給与の交渉も終え、私のブリッジSE人生がスタートする運びとなりました。

もちろん当時はブリッジSEという職種が具体的に何をするポジションなのか知らないまま、会社のネームバリューとセブ島オフィスに日本人がいないというチャレンジ感に惹かれて内定承諾をしました。笑

ここまで長くなってしまいましたが、入社決定までの経緯についてのお話でした。入社前後に関しては後編に続きます。
Docker で Ruby (2.7) on Rails (6.x) を立ち上げる (and 脱 Webpacker)

# はじめに

はじめまして。今のチームでイチから Rails を立ち上げなければいけなかったので、作業手順をメモとして残します。

Ruby 2.7 系、Rails 6 系です。
Webpacker は後々負債になることが多いので使いません 😡

# 事前準備

## 作業ディレクトリつくる

```bash
$ mkdir myapp 
$ cd myapp
```

## Dockerfile つくる

2.7.3 を引っ張ってきます。

```Dockerfile
FROM ruby:2.7.3
RUN apt-get update -qq && apt-get install -y nodejs postgresql-client
RUN mkdir /myapp
WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]

```

# Gemfile つくる

Rails 6 を引っ張ってきます。

```Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 6'
```

## Gemfile.lock つくる

勝手に内容吐き出されるので空で OK。

```Gemfile.lock
```

## entrypoint.sh つくる

```entrypoint.sh
#!/bin/bash
set -e

# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid

# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
```

## docker-compose.yml つくる

```docker-compose.yml
version: '3'
services:
  db:
    image: postgres
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=password
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db
```

上記のファイルの中で、以下の記載部分がないと Rails の起動時にエラーが出ます 🥺

```docker-compose.yml
    environment:
      - POSTGRES_PASSWORD=password
```

# Rails を立ち上げる

## Rails のプロジェクトを生成する

```bash
$ docker-compose run web rails new . --force --no-deps --database=postgresql --skip-bundle --skip-javascript
```

`--skip-javascript` のオプションによって、デフォルトで入ってくる Webpacker の混入を防ぎます。

## config/database.yml

生成された `config/database.yml` を以下の内容で上書きします。

```config/database.yml
default: &default
  adapter: postgresql
  encoding: unicode
  host: db
  username: postgres
  password: password
  pool: 5

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test
```

## bundle install する

```bash
$ docker-compose build
```

## db を生成する

```bash
$ docker-compose run web rake db:create
```

## 立ち上げるっぺ

```bash
$ docker-compose up
```

# 環境のスクラップ & ビルドに便利なコマンドメモ

```bash
docker stop $(docker ps -q)
docker rm $(docker ps -q -a)
docker rmi $(docker images -q)
docker-compose run web rails new . --force --no-deps --database=postgresql --skip-bundle --skip-javascript
```

# 参考にさせていただいた記事

https://qiita.com/kodai_0122/items/795438d738386c2c1966

https://qiita.com/at-946/items/2fb75cec5355fad4050d

おしまい。
対象のファイルを一気にコピーしてバックアップとるシェルスクリプト

タイトル通り設定変更のたびに
バックアップ⇒取れたか確認⇒設定変更
が面倒だからリストにまとめて一気にとる用のメモ
#用意するファイル
①ファイルリストを記載するファイル
　```fileslist.txt ```　
　※端末で作成するときは改行コードでエラー起こすため注意（改行コードを「LF(UNIX)」に変更して保存）
②シェルスクリプト
　```getfile.sh```
下記のように配置

```
/どこでも好きなディレクトリ/　
       ┣ getfile.sh
       ┗ fileslist.txt
```
私はいつも/tmp/getfile/を作ってそこに入れてる

#ファイルの中身
```getfile.sh
#!/bin/sh/
#取得したいファイルをfileslist.txtに記載し、
#getfiles.shと同じディレクトリに格納する

FILELIST=fileslist.txt
#ディレクトを作成
BKDIR=./`uname -n `_`date +%Y%m%d_%H%M`/
if [ ! -d $BKDIR ]; then
     mkdir $BKDIR
fi

while read line
do
if [ -f $line ]; then
    cp --parents $line $BKDIR
    RES=$?
    if [ $RES -eq 0 ]; then
        echo $line SUCCESS
        else
        echo $line FAILED
    fi
    else
    echo $line SKIP
fi

done < $FILELIST
```
```fileslist.txt（取りたいファイルをフルパスで記載）
/etc/hosts
/etc/logrotate.conf
/etc/group
/etc/passwd
```

#実行方法
シェルスクリプトを格納したディレクトリに移動しgetfile.sh実行
特に権限とかも気にしてないのでshコマンドで実行

```
# cd /格納ディレクトリ/
# sh getfile.sh
/etc/hosts SUCCESS
/etc/logrotate.conf SUCCESS
/etc/group SUCCESS
/etc/passwd SUCCESS
```
コピーできていれば「SUCCESS」
ファイルが見つからない場合は「SKIP」
上記どちらでもない場合は「FAILED」と表示される


#実行後
カレントディレクトリに
「ホスト名_YYYYMMDD_HHMMディレクトリ」が作成され
親ディレクトリも含めコピーされる

おわり
シェルスクリプトで進めていいか確認したいときのやつ 

#シェルスクリプトで進めていいか確認したいときのやつ
テスト項目とかスクリプトにして結果表示させつつ確認していく、とかってスクリプトつくりたいなって思ったのでメモ

```#!/bin/bash

echo -n "よろしいですか？ [Y/n]: "
read ANS

case $ANS in
  "" | [Yy]* )
    # ここに「Yes」の時の処理を書く
    echo "Yes"
    ;;
  * )
    # ここに「No」の時の処理を書く
    echo "No"
    ;;
esac
Amplify でのVSCodeリモートコンテナでのnpm start と amplify mock api の実行

ローカルで、`npm start` と `amplify mock api` を同時に実行すると、ローカルでDynamoDB localが動きWeb画面側のGraphQLが連動して動かせるようになります。


amplify のワークショップ https://amplify-sns.workshop.aws/ja/30_mock/30_post_front_end.html
にて

```
こまでの変更をクラウドに反映して確認していきましょう。(実行には数分かかります)

amplify push

$ amplify mock apiと$ npm startを同時に実行し、$ amplify pushせずに動作確認を行うことも可能です。

ただし、作業環境にコンテナをお使いの場合はaws-exports.jsファイルの

キーが"aws_appsync_graphqlEndpoint"の値を"http://localhost:20002/graphql"に変更する必要があることにご注意ください。

```
とあるので、そのようにやる。

- 以下の & を付けた方法（コマンド１行）だと、画面側からGraphQLが呼び出しされてDynamoDBへのアクセスができていない
（理由までは不明）

```
npm start & amplify mock api
または
npm start & amplify mock api & 
```
- 対処 
VSCodeの メニューバーの「Terminal」 -> New Terminal にてターミナルを複数起動する
それぞれのターミナルで、`npm start` と `amplify mock api` を実行する

- ファイル `src/aws-exports.js`

```
/* eslint-disable */
// WARNING: DO NOT EDIT. This file is automatically generated by AWS Amplify. It will be overwritten.

const awsmobile = {
    "aws_project_region": "us-west-2",
    "aws_cognito_identity_pool_id": "us-west-2:d7d643b5-xxx",
    "aws_cognito_region": "us-west-2",
    "aws_user_pools_id": "us-west-2_xxx",
    "aws_user_pools_web_client_id": "54fxxx",
    "oauth": {},
    "aws_appsync_graphqlEndpoint": "http://localhost:20002/graphql",
    "aws_appsync_region": "us-west-2",
    "aws_appsync_authenticationType": "AMAZON_COGNITO_USER_POOLS",
    "aws_appsync_apiKey": "da2-fakeApiId123456",
    "aws_appsync_dangerously_connect_to_http_endpoint_for_testing": true
};

export default awsmobile;
```



- npm start で起動後
![VSCodeとremote-container_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/8347ddbc-845b-bb04-a7ab-868327b300c8.png)

- amplify mock api　起動後
![VSCodeとremote-container_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1482752/e86752f3-bf46-a500-9805-1d98c1a1502a.png)


- クラウドにpushするとクラウドのDynamoDBを更新したり、定義を変えてしまうことなく、自身のローカルでAttributeやGSIの確認、PK/SKの確認を行うことができるようになる


- なお、 https://qiita.com/ssugimoto/items/bdf7a55dc11f23fd6d48 の記事は、`amplify mock api`で起動したDynamoDBへの接続方法のため異なります。
historyコマンドに実行日時を記載する

#historyコマンドに実行日時を記載する
いつもやろうと思って入れるの忘れるからとりあえずメモ

下記ファイルに後述を記載
/etc/profile

```
HISTSIZE=10000
HISTFILESIZE=10000
HISTTIMEFORMAT='%F %T '
```

あとはこれを入れることを忘れないようにするためにどうしすればいいかが問題。。。
CentOS7でパスを追加

$PATHで「/usr/local/bin」にパスが通ってなかった。

```
[prompt] # echo $PATH
/sbin:/bin:/usr/sbin:/usr/bin
```
そこで「usr/local/bin」もパスを通す。
大元のbashrc「/etc/bashrc」にパス追加の記述

``` /etc/bashrc
PATH="$PATH":/usr/local/bin
```
反映させる

```
[prompt] # source /etc/bashrc
[prompt] # echo $PATH
/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin
```
無事、追加出来た。
Azure IAMのスコーピングについて / Scoping of Azure IAM configuration

## はじめに
Azureユーザーへのロール割り当てにおいて、権限範囲をきちんと分けて担当外のリソースは設定変更できないようにしてみました。具体的には以下2つを実装しました

1. 特定のスコープで特定の操作のみを許可する: 今回は1サブスクリプションをリソースグループで分けて使っている場合を想定し、リソースグループをスコープとしました（複数サブスクリプションを使い分けている（例: 開発用と本番用とか）の場合は、サブスクリプションがスコープになると思います）
2. リソース毎のロール割当はしない（細かくなりすぎて管理が難しくなる）

ちなみにベストプラクティスのドキュメントには「ユーザー毎ではなくセキュリティグループに対してロールを割り当てるべし」とありますが、今回の対象はユーザー数が管理しきれる範囲内（10名前後）なので、ユーザー毎に割り当てています

https://docs.microsoft.com/ja-jp/azure/security/fundamentals/identity-management-best-practices#use-role-based-access-control

## 設定手順

- Azure Portalでの操作に関するドキュメントはこちら

https://docs.microsoft.com/ja-jp/azure/role-based-access-control/role-assignments-portal?tabs=current

https://docs.microsoft.com/ja-jp/azure/role-based-access-control/role-assignments-remove

### 手順
1. 数名の所有者を残し、サブスクリプションのIAMから全ロールを削除する
2. その後リソースグループのIAMにおいて、必要なメンバーに対し適切なロールを設定する

（1.まずこの状態にする）
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327770/307c6c2c-4003-6baa-74df-0a0d2779a68e.png)
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327770/e6362fe0-6628-906e-60b3-9f2514aa80ed.png)

（2.次にリソースグループ毎にロールを割り当てる）
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327770/19d71712-0bbd-8f4b-c4e0-dae17e8e6cb9.png)


### 注意点
- リソースグループのIAMから「サブスクリプション（継承済み）」であるロール割当を削除すると、サブスクリプションレベルで削除がなされます
- Azureのロール設定は加算方式のため、サブスクリプションレベルで所有者や共同作成者の場合、リソースグループレベルで閲覧者を設定してもoverrideできず効果がありません

https://docs.microsoft.com/ja-jp/azure/role-based-access-control/overview#multiple-role-assignments

- 「従来の管理者」タブのロールはサブスクリプション所有者ロールに相当するので、あわせて削除する。但し共同管理者以外は削除できません

https://docs.microsoft.com/ja-jp/azure/role-based-access-control/rbac-and-directory-admin-roles#classic-subscription-administrator-roles

### サブスクリプションのレベルにおいて、権限的に強いロール（所有者や"従来の管理者"のサービス管理者等）を持つユーザーに対する制約のかけ方

単にロールを削除するだけだと、他ユーザーにロール割当ができなくなって普段の運用に困ったり、そもそもロールの削除自体できなかったりします（"従来の管理者"のサービス管理者）。

強い権限を持つユーザーは普段は使わず、別ユーザーを作りそれに対し適切なロールを割り当て、普段はそれを使う、が一番簡単です。

https://docs.microsoft.com/ja-jp/azure/active-directory/fundamentals/add-users-azure-active-directory

その上で更に、
- サブスクリプションレベルでは、強いロールは剥奪し代わりにユーザーアクセス管理者ロールを割り当てる
- その上で、リソースグループレベルで適切なロールを割り当てる

とすると、誤操作を防げてよりよいと思います
![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327770/6480225d-f059-0f90-d449-ea62dc2a07d4.png)
配列処理　for　foreach　while   使い分け

###参考サイト

https://php-junkie.net/beginner/which-loop/


## 基本
for、foreach、whileのどれを使っても配列処理はできますが、それぞれの長所を生かした書き方をすると直感的にわかりやすくあとで見返したときに可読性が良くなる。その結果バグが混入しにくくなる。


###◎for文

####for文の得意なこと
for文は「繰り返し回数が定まっているとき」が得意

####for文の苦手なこと
回数が一定に決まらない処理

###◎foreach文

####foreach文の得意なこと
foreach文は「連想配列の処理が得意（絶対的な強さを誇る）」です。

####foreach文の苦手なこと
指定回数を繰り返す必要があるときや、空の配列を指定数作りたいとき、そもそものデータが配列ではないときは使えません。
また、処理を開始するときに分解したいデータが配列でない場合はNoticeが発生します。
(Noticeの発生を防ぐために条件文を追加すると安全)

###◎while文

####while文の得意なこと
while文は「指定の条件を満たすまでのループ処理」が得意です。

####while文の苦手なこと
条件を満たすまで実行し続けるので、無限ループ事故を引き起こしやすいのが難点です。

####補足：do – while文
do – while文は「何があろうとも一回は必ず処理を行いたい」、「その後、条件によってはループして処理したい」ときに使います。