Tag: acm


Automating the Deployment of Encrypted Web Services with the AWS SDK for PHP (Part 2)

by Joseph Fontes | on | in PHP | Permalink | Comments |  Share

In the first post of this series, we focused on how to use Amazon Route 53 for domain registration and use Amazon Certificate Manager (ACM) to create SSL certificates. With our newly registered domain available for use, we can proceed to deploy and configure the services we need to host the www.dev-null.link website across an encrypted connection. Once complete, the infrastructure configuration will reflect the diagrams below.

Diagram 1

Diagram 2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

The first diagram shows the use of Route 53 to route traffic between AWS Elastic Beanstalk environments across multiple regions. The second example adds Amazon CloudFront support to the design.

AWS Elastic Beanstalk

Our first step is to create the Elastic Beanstalk application, which will provide the necessary infrastructure to host our website. The following is the order of the methods used for the AWS Elastic Beanstalk deployment:

  • createApplication
  • createApplicationVersion
  • createConfigurationTemplate
  • createEnvironment

We start by creating the Elastic Beanstalk application.

$ebCreateApplicationData = [ 'ApplicationName' => "DevNullDemo",
            'Description' => "Demo application for ACM deployment" ];

$ebCreateApplicationResult = $ebClient->createApplication($ebCreateApplicationData);

print_r($ebCreateApplicationResult);

Result

…
[Application] => Array
	(
	[ApplicationName] => DevNullDemo
	[Description] => Demo application for ACM deployment
…

Now we’ll create the initial version of the application and name it DevNullDemo. You can use the application archive of your choice, although a simple PHP demo site is available here.

$ebCreateAppVersionData = [ 'ApplicationName' => "DevNullDemo",
                            'VersionLabel' => 'v1',
                            'Description' => 'Initial Create',
                            'SourceBundle' => [ 'S3Bucket' => 'pub-materials',
                                                'S3Key' => 'Sample-App.zip' ] ];

$ebCreateAppVersionResult = $ebClient->createApplicationVersion($ebCreateAppVersionData);

print_r($ebCreateAppVersionResult);

Result

…
            [ApplicationVersion] => Array
                (
                    [ApplicationName] => DevNullDemo
                    [Description] => Initial Create
                    [VersionLabel] => v1
                    [SourceBundle] => Array
                        (
                            [S3Bucket] => pub-materials
                            [S3Key] => Sample-App.zip
                        )
                    [Status] => UNPROCESSED
                )
…

Next, we need to create an Elastic Beanstalk configuration template. This template requires the selection of an Elastic Beanstalk solution stack prior to calling the method, createConfigurationTemplate. The solution stack is the platform you choose to run your application within Elastic Beanstalk. You can find a list of available solution stack choices by using the listAvailableSolutionStacks method.

$ebSolutionStacks = $ebClient->listAvailableSolutionStacks();
print_r($ebSolutionStacks);

Result

…
            [SolutionStacks] => Array
                (
                    [0] => 64bit Windows Server Core 2012 R2 v1.2.0 running IIS 8.5
…
                    [5] => 64bit Amazon Linux 2016.03 v2.1.6 running Java 7
…
                    [9] => 64bit Amazon Linux 2014.03 v1.1.0 running Node.js
                    [14] => 64bit Amazon Linux 2016.03 v2.1.7 running PHP 7.0
                    [15] => 64bit Amazon Linux 2015.09 v2.0.6 running PHP 5.6
                    [16] => 64bit Amazon Linux 2015.09 v2.0.4 running PHP 5.6
…
                    [14] => Array
                        (
                            [SolutionStackName] => 64bit Amazon Linux 2016.03 v2.1.7 running PHP 7.0
                            [PermittedFileTypes] => Array
                                (
                                    [0] => zip
                                )

                        )
…

For our demonstration, we’ll use the, 64bit Amazon Linux 2016.03 v2.1.7 running PHP 7.0 solution stack.

$ebConfigTemplateData = [ 'ApplicationName' => "DevNullDemo",
                          'TemplateName' => 'DevNullDemoTemplate',
                          'SolutionStackName' => '64bit Amazon Linux 2016.03 v2.1.7 running PHP 7.0',
                          'Description' => 'EB Environment template for blog deployment.' ];

$ebConfigTemplateCreateResult = $ebClient->createConfigurationTemplate($ebConfigTemplateData);

print_r($ebConfigTemplateCreateResult);

Result

…
        (
            [SolutionStackName] => 64bit Amazon Linux 2016.03 v2.1.7 running PHP 7.0
            [ApplicationName] => DevNullDemo
            [TemplateName] => DevNullDemoTemplate
            [Description] => EB Environment template for blog deployment.
…

Now we can create and start the infrastructure by using the createEnvironment method. The following example sets additional options such as instance type and ACM SSL certificate. You need to replace the [CERTIFICATEARN] value with the AWS ACM certificate ARN created in part 1 of this series. You can also find this value by using the AWS ACM listCertificates method. For these examples, we’ve created a certificate across multiple regions for the host name eb.dev-null.link, in addition to the previously created www.dev-null.link certificate.

$ebCreateEnvData = [ 'ApplicationName' => "DevNullDemo",
                        'EnvironmentName' => "DevNullEnv",
                        'Description' => "Demo environment for ACM EB deployment.",
                        'TemplateName' => "DevNullDemoTemplate",
                        'VersionLabel' => 'v1',
                        'OptionSettings' => [

                                [ 'Namespace' => 'aws:elb:listener:443',
                                  'OptionName' => 'ListenerProtocol',
                                  'Value' => 'HTTPS' ],

                                [ 'Namespace' => 'aws:elb:listener:443',
                                  'OptionName' => 'SSLCertificateId',
                                  'Value' => '[CERTIFICATEARN]' ],

                                [ 'Namespace' => 'aws:elb:listener:443',
                                  'OptionName' => 'InstancePort',
                                  'Value' => '80' ],

                                [ 'Namespace' => 'aws:elb:listener:443',
                                  'OptionName' => 'InstanceProtocol',
                                  'Value' => 'HTTP' ],

                                [ 'Namespace' => 'aws:autoscaling:launchconfiguration',
                                  'OptionName' => 'InstanceType',
                                  'Value' => 't2.nano' ],

                                ],
                        'Tier' => [ 'Name' => 'WebServer',
                                    'Type' => 'Standard',
                                    'Version' => ' ' ],
                        ];

$ebCreateEnvData = $ebClient->createEnvironment($ebCreateEnvData);

print_r($ebCreateEnvData);

Result

…
            [EnvironmentName] => DevNullEnv
            [EnvironmentId] => e-fnvhjptdjd
            [ApplicationName] => DevNullDemo
            [VersionLabel] => v1
            [SolutionStackName] => 64bit Amazon Linux 2016.03 v2.1.7 running PHP 7.0
            [Description] => Demo environment for ACM EB deployment.

            [Status] => Launching
            [Health] => Grey
            [Tier] => Array
                (
                    [Name] => WebServer
                    [Type] => Standard
                    [Version] =>
                )
…

As the results show, the current status of our environment is Launching. We can periodically check the status with the describeEnvironments method.

$ebDescEnvResult = $ebClient->describeEnvironments();

foreach($ebDescEnvResult['Environments'] as $ebEnvList) {
        print "Name:\t".$ebEnvList['EnvironmentName']."\n";
        print "ID:\t".$ebEnvList['EnvironmentId']."\n";
        print "CNAME:\t".$ebEnvList['CNAME']."\n";
        print "Status:\t".$ebEnvList['Status']."\n\n";
}

Result

Name:  	DevNullEnv
ID:    	[ID]
CNAME: 	DevNullEnv.[ID].[Region].elasticbeanstalk.com
Status:	Ready

When the environment has a status of Ready, we can proceed to create the necessary DNS records. You can also check that the site is functional by pasting the CNAME value into a web browser. Be sure to record this CNAME value so you can use it later.

Demo App

You will want to repeat this process across additional AWS Regions to demonstrate latency-based DNS resolution.

Amazon Route 53

Our next step is to create a Route 53 hosted zone. This hosted zone will define a domain name (or subdomain) for which we are authoritative and, thus, allowed to create DNS records. We’ll start with the createHostedZone method.

$route53Client = $sdk->createRoute53();

$route53Data = [ 'Name' => "dev-null.link",
            'CallerReference' => "BLOGPOSTREF001",
            'HostedZoneConfig' => [ 'Comment' => "AWS SDK sample dev-null.link" ] ];

$route53Result = $route53Client->createHostedZone($route53Data);

Result

            [HostedZone] => Array
                (
                    [Id] => /hostedzone/[Amazon Route 53 Zone ID]
                    [Name] => dev-null.link.
                    [CallerReference] => BLOGPOSTREF001
                    [Config] => Array
                        (
                            [Comment] => AWS SDK sample dev-null.link
                            [PrivateZone] =>
                        )

                    [ResourceRecordSetCount] => 2
                )
…
            [DelegationSet] => Array
                (
                    [NameServers] => Array
                        (
                            [0] => ns-999.awsdns-60.net
		…
)
                )

You should copy the ID of the hosted zone from this result. You can also find a list of all hosted zone ID values by using the listHostedZones method.

$route53ListResult = $route53Client->listHostedZones();

foreach($route53ListResult['HostedZones'] as $zoneItem) {
        print "Name:\t".substr($zoneItem['Name'],0,-1)."\n";
        print "ID:\t".$zoneItem['Id']."\n\n";
}

Result

Name:  	dev-null.link
ID:    	/hostedzone/[Amazon Route 53 Zone ID]

We’ll now create a new DNS entry so that our website is visible via web browser with the eb.dev-null.link host name. For this, we need to use the CNAME value from our Elastic Beanstalk application.

$currentDate = date("r");
$hostedZoneId = "/hostedzone/[Amazon Route 53 Zone ID]";
$subDomain = "eb.dev-null.link";
$ebCname = "DevNullEnv.[EB ID].[Region].elasticbeanstalk.com";

$recordComment = "Created $subDomain record on $currentDate";

$route53RecordData = [ 'HostedZoneId' => $hostedZoneId,
                    'ChangeBatch' => [ 'Comment' => $recordComment,
                    'Changes' => [
                                       [ 'Action' => 'CREATE',
                                         'ResourceRecordSet' => [ 'Name' => $subDomain,
                                                                  'Type' => 'CNAME',
                                                                  'TTL' => 60,
                                                                  'ResourceRecords' => [ [ 'Value' => $ebCname ] ] ]
] ] ] ];

$route53ChangeResult = $route53Client->changeResourceRecordSets($route53RecordData);

print_r($route53ChangeResult);

Result

…
            [ChangeInfo] => Array
                (
                    [Id] => /change/[ChangeInfo ID]
                    [Status] => PENDING
…
                    [Comment] => Created test.jf.unicorn.rentals record on Thu, 15 Sep 2016 12:25:07 -0700
                )
…

As we can see from the result, the status of the change is PENDING. We can check the status with the getChange method using the value of the ChangeInfo ID.

$route53ChangeData = [ 'Id' => "/change/[ChangeInfo ID]" ];
$route53ChangeResult = $route53Client->getChange($route53ChangeData);
print_r($route53ChangeResult);

Result

…
            [ChangeInfo] => Array
                (
                    [Id] => /change/<ChangeInfo ID>
                    [Status] => INSYNC
…
                    [Comment] => Created test.jf.unicorn.rentals record on Thu, 15 Sep 2016 12:25:07 -0700
                )
…

Now that our change has the status of INSYNC, we can view the secure URL in our browser window with the URL https://eb.dev-null.link/.
Demo App

Deploying Across Multiple Regions

This deployment is now serving our website across an encrypted connection in a single AWS Region. For those who would like to use multiple regions, we can expand our current configuration. An AWS ACM certificate is needed in each AWS Region used for the deployment. Because we’ll be using CloudFront, we have to ensure that a certificate is created in the us-east-1 region because CloudFront will source the available AWS ACM certificates from there. You can reference the previous blog post for instructions on creating an AWS ACM certificate in additional regions. Next, run the Elastic Beanstalk creation methods shown earlier in each additional region where you want to deploy the application. Be sure to record the CNAME value for each environment.

After we have all of the necessary Elastic Beanstalk environments running, we need to delete the Route 53 resource record for eb.dev-null.link so that we can replace it with a latency-based record set.

$recordComment = "Deleted $subDomain record on $currentDate";

$route53RecordData = [ 'HostedZoneId' => $hostedZoneId,
                    'ChangeBatch' => [ 'Comment' => $recordComment,
                    'Changes' => [
                                       [ 'Action' => 'DELETE',
                                         'ResourceRecordSet' => [ 'Name' => $subDomain,
                                                                  'Type' => 'CNAME',
                                                                  'TTL' => 60,
                                                                  'ResourceRecords' => [ [ 'Value' => $ebCname ] ] ]
] ] ] ];

$route53ChangeResult = $route53Client->changeResourceRecordSets($route53RecordData);

You might notice that the method and instructions to delete the record are almost identical to the instructions used to create the record. They even use the same method, changeResourceRecordSets.

Result

…
            [ChangeInfo] => Array
                (
                    [Id] => /change/[ChangeInfo ID]
                    [Status] => PENDING

                    [Comment] => Deleted www.dev-null.link record on Thu, 15 Sep 2016 14:58:51 -0700
                )
…

Our next step is to add the latency-based routing rules. This example provides the CNAME of the Elastic Beanstalk environment via the describeEnvironments method.

$currentDate = date("r");
$hostedZoneId = "HOSTED ZONE ID";

$ebDescEnvData = [ 'EnvironmentNames' => [ 'DevNullEnvProd' ] ];
$ebDescEnvResult = $ebClient->describeEnvironments($ebDescEnvData);

$ebCname = $ebDescEnvResult['Environments'][0]['CNAME'];

$recordComment = "Created www record on $currentDate";

$route53RecordData = [ 'HostedZoneId' => $hostedZoneId,
                    'ChangeBatch' => [ 'Comment' => $recordComment,
                    'Changes' => [
                                       [ 'Action' => 'CREATE',
                                         'ResourceRecordSet' => [
                                                'Name' => "eb.dev-null.link",
                                                'Type' => 'CNAME',
                                                'TTL' => 60,
                                                'Region' => $region,
                                                'SetIdentifier' => str_replace("-","",$region),
                                                'ResourceRecords' => [ [ 'Value' => $ebCname ], ],
                                                ],
                                        ],
                                ],
                        ],
                ];

$route53ChangeResult = $route53Client->changeResourceRecordSets($route53RecordData);

Result

…
            [ChangeInfo] => Array
                (
                    [Id] => /change/[ChangeInfo ID]
                    [Status] => PENDING
…

Demo App

In the Route 53 console, we can see there are two CNAMEs that now resolve the host name, eb.dev-null.link. When a user visits the website, the URL returned will correspond to the lower of the record latencies.

Amazon CloudFront

To enhance the user experience, we’ll now configure and deploy a content delivery network solution named Amazon CloudFront. This AWS service provides content delivery acceleration for the media provided with our web application. Each CloudFront deployment is composed of a CloudFront distribution with each distribution having one or more origins. An origin defines the mapping of a URL to a particular destination. The host name, www.dev-null.link, will resolve to our CloudFront distribution, which will then serve pages from the backend eb.dev-null.link load-balanced site. We first create our distribution with the createDistribution method. The value of $cfCreateDistData can be found in this Github Gist.

$cfCreateDistResult = $cfClient->createDistribution($cfCreateDistData);
print_r($cfCreateDistResult);

Result

Aws\Result Object
(
    [data:Aws\Result:private] => Array
        (
            [Distribution] => Array
                (
                    	[Id] => [CF ID Value]
[DomainName] => [CloudFront ID].cloudfront.net
…

Once complete, we need to save the value of DomainName from the result returned. Next, we’ll create a new Route 53 CNAME record that points www.dev-null.link to our CloudFront distribution.

$subDomain = "www.dev-null.link";
$cfCname = "[CloudFront ID].cloudfront.net";

$recordComment = "Created $subDomain record on $currentDate";

$route53RecordData = [ 'HostedZoneId' => $hostedZoneId,
                    'ChangeBatch' => [ 'Comment' => $recordComment,
                    'Changes' => [
                                       [ 'Action' => 'CREATE',
                                         'ResourceRecordSet' => [ 'Name' => $subDomain,
                                                                  'Type' => 'CNAME',
                                                                  'TTL' => 60,
                                                                  'ResourceRecords' => [ [ 'Value' => $cfCname ] ] ]
] ] ] ];

$route53ChangeResult = $route53Client->changeResourceRecordSets($route53RecordData);

Result

Aws\Result Object
(
    [data:Aws\Result:private] => Array
        (
            [ChangeInfo] => Array
                (
[Status] => PENDING
…

Once complete, we can test the new deployment by navigating to the URL (https://www.dev-null.link/) in a web browser.

Conclusion

With our infrastructure configuration completed, we now have a globally load balanced web application that’s accessible via encrypted communications. We’ve shown we can use the AWS SDK for PHP to automate these deployments, which provides the agility to reproduce these environments for customers on demand. Next, we’ll continue this series by reviewing deployments that use Amazon S3 for static content hosting, and the AWS Application Load Balancer and Elastic Load Balancing with Amazon EC2 instance deployments.

Automating the Deployment of Encrypted Web Services with the AWS SDK for PHP

by Joseph Fontes | on | in PHP | Permalink | Comments |  Share

Having worked in the web hosting space, one of the areas I find so fun about AWS is the ease of automating tasks that have historically been quite disjointed.  The process of supporting a customer request to register a domain, create or update DNS entries, configure the load balancer, deploy servers, etc., had me working across a multitude of systems, interfaces, and APIs.  Now, with the release of AWS Certificate Manager (ACM) in addition to existing AWS services, AWS provides all the tools and capabilities needed to support the provisioning of these services within customer accounts.

In this three-part series of posts, we’ll review how to use the AWS SDK for PHP to automate web service deployment, domain registration, DNS administration, and SSL certificate generation and assignment.  Using the examples outlined in these posts, as well as other features and functions of the AWS SDK for PHP, you’ll learn how to programmatically create a process for automatically purchasing a domain and then deploying an HTTPS (SSL-secured) web service on AWS by using either Amazon EC2 or AWS Elastic Beanstalk.

The examples in this post focus on using Amazon Route 53 to automate the registration of domain names and DNS administration.  Next, we’ll showcase how to use ACM to create and manage SSL certificates.  In subsequent posts, we will show how to automate the setup of encrypted HTTPS web services with the new domain and newly created certificates on Elastic Beanstalk.  Then, we’ll show how to automate deployments to EC2 and Elastic Load Balancing.  Once complete, we’ll have two web application stacks.  We’ll run the www.dev-null.link site from Elastic Beanstalk, and use EC2 and Elastic Load Balancing to run the second web application stack.  The following diagrams illustrate the final designs.

   

Amazon Route53 Domain Registration

The first task in building the web infrastructure is to identify and register an available domain name.  We can use the AWS SDK for PHP to check domain name availability.  We will use a method called checkDomainAvailability, which is part of the Route 53 Domains client.  We can automate the process of testing domains until we have a name that meets our application’s needs and is also available for registration.  The example below loops through an array of domain names, listing their current status for registration.

$route53Client = $sdk->createRoute53Domains();

$domainNames = [ "test.com", "dev.com", "dev-null.link", "null38.link" ];

foreach($domainNames as $domainNameElement) {
        $route53CheckDomainAvailData = [ 'DomainName' => $domainNameElement ];
        $route53CheckDomainResults = $route53Client->checkDomainAvailability($route53CheckDomainAvailData);
        print "Domain $domainNameElement is ".$route53CheckDomainResults['Availability']."n";
}

You can view the results of the check below.

There are two domain names available for registration.  In this example, we’ll register the domain dev-null.link.  This name contains the “.link” top-level domain (TLD), and the “dev-null” second-level domain. Now, register the domain by using the registerDomain method.  The registration has several required fields that we need to complete. These requirements are specific to each top level domain. For this example, we can use the following data ( provided in this Github Gist):
$route53DomainRegData = [
    'AdminContact' => [
        'AddressLine1' => $address1,
        'AddressLine2' => $address2,
        'City' => $city,
        'ContactType' => 'PERSON',
        'CountryCode' => 'US',
.....

$route53Client = $sdk->createRoute53Domains();
$route53CreateDomRes = $r53Client->registerDomain($route53DomainRegData);

print_r($route53DomainRegData);

Notice that the PhoneNumber data element must be in the format of  “+1.1231231212” to be valid.

We can now register the domain as follows.
[user@dev1 scripts]# php aws-route53-register-domain.php 
...
            [statusCode] => 200 
            [effectiveUri] => https://route53domains.us-east-1.amazonaws.com 
...

While we wait for the registration process to finish, we can check on the status of the domain.  First, use the listOperations method to print the list of current operations, and then enter the operation number into the getOperationDetail method.  Let’s look at all of the pending operations with the code.

$route53ListOperationsResults = $route53Client->listOperations();
print_r($route53ListOperationsResults);

$route53OperDetails = [ 'OperationId' => $operationId ];
$route53OperResults = $route53Client->getOperationDetail($route53OperDetails);
print_r($route53OperResults);

Result

[user@dev1 scripts]#
[user@dev1 scripts]# php aws-route53-list-operations.php
…
[Status] => IN_PROGRESS
                    [Type] => REGISTER_DOMAIN
…

AWS Certificate Manager

With ACM, we no longer have to worry about certificate expirations, securing the certificate private keys, copying self-signed CA certificates to clients, making sure servers all have the right certificate, or even the cost of a managed SSL certificate.  AWS provides managed SSL certificates at no cost.  Also, AWS handles the responsibility of renewing the certificate and placing it on the devices used to terminate SSL connections.  ACM can be used across managed AWS services such as ELB, Amazon CloudFront, and Elastic Beanstalk.

ACM in Action

Let’s go through how to create multiple certificates to secure connections to different websites.  We must first request a new certificate with the corresponding public and private keys.  The requestCertificate method automates this process.

The following example shows how to generate the certificate for our first domain.

$acmClient = $sdk->createAcm();

$acmRequestCertData = [ 'DomainName' => "www.dev-null.link",
    'DomainValidationOptions' => [
        [
            'DomainName' => "www.dev-null.link",
            'ValidationDomain' => "dev-null.link",
        ],
    ],
    'IdempotencyToken' => 'TOKENSTRINGDEVNULL01',
    'SubjectAlternativeNames' => ['dev-null.link', 'images.dev-null.link'],
];

$acmRequestCertResults = $acmClient->requestCertificate($acmRequestCertData);
print_r($acmRequestCertResults);

Result

[user@dev1 scripts]# php aws-acm-requestCertificate.php
...
    [CertificateArn] => arn:aws:acm:us-east-1:ACCOUNTID:certificate/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
    [@metadata] => Array
        (
            [statusCode] => 200
...
 

SubjectAlternativeNames identifies other DNS entries that the certificate should cover.  In this example, they are all subdomains of dev-null.link but can also include other domain names that could be used synonymously with our requested domain.  You can repeat this call for any additional certificates you need.  Be sure to update the value of IdempotencyToken for each certificate request created.  You should save the value of CertificateArn because you’ll need to use it later.

We want to secure the primary hostname www.dev-null.link.  ACM requires validation for the domain from an email address that is tied to the registration information.  Domain validation requests are sent to multiple locations.  These validation emails are sent to domain email addresses in the following order: admin@dev-null.link, administrator@dev-null.link, hostmaster@dev-null.link, postmaster@dev-null.link, and webmaster@dev-null.link.  In addition, a validation request is also sent to the email contacts for the Administrative, Technical, and Domain Registrant.  The following figure shows a copy of the received email.

When you click the link in the email, you’re taken to the page shown below.

Next, click I Approve.  A confirmation page appears that you can save for your records.

Let’s now use the listCertificates and describeCertificate methods to show all of the certificates we’ve generated.

$acmListCertResults = $acmClient->listCertificates();
print_r($acmListCertResults);

Result

[user@dev1 scripts]# php aws-acm-list.php
...
    [CertificateSummaryList] => Array
...
                    [CertificateArn] => arn:aws:acm:us-east-1:ACCOUNTID:certificate/CERTIFICATE-ID
                    [DomainName] => www.dev-null.link
...
                    [CertificateArn] => arn:aws:acm:us-east-1:ACCOUNTID:certificate/CERTIFICATE-ID
                    [DomainName] => api.dev-null.link
...
 
You can view details about the certificates by calling the describeCertificates method with the CertificateARN received from the 
previous listCertificates call.
$certificateArn = $acmListCertResults['CertificateSummaryList'][0]['CertificateArn'];

$acmDescribeCertData = [ 'CertificateArn' => $certificateArn ];
$acmDescribeCertResults = $acmClient->describeCertificate($acmDescribeCertData);

print_r($acmDescribeCertResults);

You can view the full output here with abbreviated output shown below.

[Certificate] => ...
Array
(
        [CertificateArn] => CERTIFICATE-ARN
        [DomainName] => www.dev-null.link
        [SubjectAlternativeNames] => Array
        (
                [0] => www.dev-null.link
                [1] => dev-null.link
                [2] => images.dev-null.link
        )
...

Finally, view the full certificate with the certificate chain.

$acmGetCertificateData = [ 'CertificateArn' => $certificateArn ];

$acmGetCertificateResults = $acmClient->getCertificate($acmGetCertificateData);

print_r($acmGetCertificateResults);
Result
[Certificate] => -----BEGIN CERTIFICATE-----
…
-----END CERTIFICATE-----

 [CertificateChain] => -----BEGIN CERTIFICATE-----
…
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
…
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
…
-----END CERTIFICATE-----
    [@metadata] => Array
        (
            [statusCode] => 200
…
This code will return the public certificate and the chain of trust leading to the public CA that is signing the certificate allowing web browsers to trust the website being visited.
To get more information about the certificate and the request process, we can use the describeCertificate method.  This method takes the certificateArn value as input and produces information about the referenced certificate.  This information includes the list of email addresses that received validation emails, the encryption algorithm used, the certificate creation and expiration dates, and the full list of host names covered by the certificate.  Looking forward, we can delete certificates and resend the validation email with the available ACM methods.
Now that we’ve covered the setup, configuration, and deployment of ACM and DNS, we now have a registered domain available for use.  In the next post, we’ll review how to use the domain, dev-null.link, for our website.  We’ll deploy an HTTPS website that is secured with SSL/TLS by using the AWS SDK with ELB, Amazon EC2, and Elastic Beanstalk.  We’ll also cover creating and deleting Amazon Route 53 resource records, and how to assign our ACM certificate to newly created load balancers.